mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-01-20 20:01:25 +00:00
Merge branch 'master' into fix-avatar-seeding
This commit is contained in:
commit
4b29b271c3
@ -223,8 +223,7 @@ export default {
|
||||
},
|
||||
searchResults: async (_parent, args, context, _resolveInfo) => {
|
||||
const { query, limit } = args
|
||||
let userId = null
|
||||
if (context.user) userId = context.user.id
|
||||
const userId = context.user?.id || null
|
||||
|
||||
const searchType = query.replace(/^([!@#&]?).*$/, '$1')
|
||||
const searchString = query.replace(/^([!@#&])/, '')
|
||||
|
||||
@ -33,7 +33,7 @@ const matchSomeWordsExactly = (str, boost = 2) => {
|
||||
const matchBeginningOfWords = (str) => {
|
||||
return str
|
||||
.split(' ')
|
||||
.filter((s) => s.length > 3)
|
||||
.filter((s) => s.length >= 2)
|
||||
.map((s) => s + '*')
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ describe('queryString', () => {
|
||||
|
||||
describe('globbing for longer words', () => {
|
||||
it('globs words with more than three characters', () => {
|
||||
expect(queryString('a couple of words')).toContain('couple* words*')
|
||||
expect(queryString('a couple of words')).toContain('couple* of* words*')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -65,10 +65,10 @@ services:
|
||||
|
||||
backend:
|
||||
image: ocelotsocialnetwork/backend-branded:local-${CONFIGURATION}
|
||||
container_name: backend
|
||||
container_name: backend-branded
|
||||
build:
|
||||
dockerfile: src/docker/backend.Dockerfile
|
||||
target: branded-branded
|
||||
target: branded
|
||||
context: .
|
||||
args:
|
||||
- CONFIGURATION=$CONFIGURATION
|
||||
@ -143,7 +143,7 @@ services:
|
||||
|
||||
neo4j:
|
||||
image: ocelotsocialnetwork/neo4j-community:latest
|
||||
container_name: neo4j
|
||||
container_name: neo4j-branded
|
||||
networks:
|
||||
- test-network
|
||||
volumes:
|
||||
|
||||
@ -43,6 +43,14 @@ export default {
|
||||
selectedCategoryIds: this.existingCategoryIds,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
existingCategoryIds() {
|
||||
if (!this.selectedCategoryIds.length && this.existingCategoryIds.length) {
|
||||
this.selectedCategoryIds = this.existingCategoryIds
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
selectedCount() {
|
||||
return this.selectedCategoryIds.length
|
||||
|
||||
@ -177,6 +177,15 @@ export default {
|
||||
groupName() {
|
||||
return this.group && this.group.name
|
||||
},
|
||||
groupCategories() {
|
||||
return this.group && this.group.categories
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
groupCategories() {
|
||||
if (!this.formData.categoryIds.length && this.groupCategories)
|
||||
this.formData.categoryIds = this.groupCategories.map((cat) => cat.id)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
|
||||
@ -37,8 +37,6 @@
|
||||
<ds-text class="select-label">
|
||||
{{ $t('group.type') }}
|
||||
</ds-text>
|
||||
<!-- TODO: change it has to be implemented later -->
|
||||
<!-- TODO: move 'ds-select' from style guide to main code and implement missing translation etc. functionality -->
|
||||
<select
|
||||
class="select ds-input appearance--auto"
|
||||
name="groupType"
|
||||
@ -94,21 +92,10 @@
|
||||
<ds-text class="select-label">
|
||||
{{ $t('group.actionRadius') }}
|
||||
</ds-text>
|
||||
<!-- TODO: move 'ds-select' from styleguide to main code and implement missing translation etc. functionality -->
|
||||
<select
|
||||
class="select ds-input appearance--auto"
|
||||
name="actionRadius"
|
||||
:value="formData.actionRadius"
|
||||
@change="changeActionRadius($event)"
|
||||
>
|
||||
<option
|
||||
v-for="actionRadius in actionRadiusOptions"
|
||||
:key="actionRadius"
|
||||
:value="actionRadius"
|
||||
>
|
||||
{{ $t(`group.actionRadii.${actionRadius}`) }}
|
||||
</option>
|
||||
</select>
|
||||
<action-radius-select
|
||||
v-model="formData.actionRadius"
|
||||
@change.native="changeActionRadius($event)"
|
||||
/>
|
||||
<ds-chip
|
||||
size="base"
|
||||
:color="
|
||||
@ -123,6 +110,7 @@
|
||||
</ds-chip>
|
||||
|
||||
<!-- location -->
|
||||
<!-- TODO: move 'ds-select' from styleguide to main code and implement missing translation etc. functionality -->
|
||||
<ds-select
|
||||
id="city"
|
||||
:label="$t('settings.data.labelCity') + locationNameLabelAddOnOldName"
|
||||
@ -187,6 +175,7 @@ import {
|
||||
DESCRIPTION_WITHOUT_HTML_LENGTH_MIN,
|
||||
} from '~/constants/groups.js'
|
||||
import Editor from '~/components/Editor/Editor'
|
||||
import ActionRadiusSelect from '~/components/Select/ActionRadiusSelect'
|
||||
import { queryLocations } from '~/graphql/location'
|
||||
|
||||
let timeout
|
||||
@ -196,6 +185,7 @@ export default {
|
||||
components: {
|
||||
CategoriesSelect,
|
||||
Editor,
|
||||
ActionRadiusSelect,
|
||||
},
|
||||
props: {
|
||||
update: {
|
||||
@ -216,7 +206,6 @@ export default {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
disabled: false,
|
||||
groupTypeOptions: ['public', 'closed', 'hidden'],
|
||||
actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
|
||||
loadingGeo: false,
|
||||
cities: [],
|
||||
formData: {
|
||||
@ -327,10 +316,10 @@ export default {
|
||||
return false
|
||||
},
|
||||
changeGroupType(event) {
|
||||
this.formData.groupType = event.target.value
|
||||
this.$refs.groupForm.update('groupType', event.target.value)
|
||||
},
|
||||
changeActionRadius(event) {
|
||||
this.formData.actionRadius = event.target.value
|
||||
this.$refs.groupForm.update('actionRadius', event.target.value)
|
||||
},
|
||||
updateEditorDescription(value) {
|
||||
this.$refs.groupForm.update('description', value)
|
||||
|
||||
37
webapp/components/Select/ActionRadiusSelect.spec.js
Normal file
37
webapp/components/Select/ActionRadiusSelect.spec.js
Normal file
@ -0,0 +1,37 @@
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import ActionRadiusSelect from './ActionRadiusSelect'
|
||||
|
||||
const localVue = global.localVue
|
||||
const propsData = { value: 'regional' }
|
||||
|
||||
describe('ActionRadiusSelect.', () => {
|
||||
let wrapper
|
||||
let mocks
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
describe('mount', () => {
|
||||
const Wrapper = () => {
|
||||
return shallowMount(ActionRadiusSelect, { propsData, mocks, localVue })
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
it('renders the select', () => {
|
||||
expect(wrapper.findComponent(ActionRadiusSelect).exists()).toBe(true)
|
||||
})
|
||||
|
||||
describe('when an option is selected', () => {
|
||||
it('emits a change event with the new value', () => {
|
||||
const select = wrapper.find('select')
|
||||
select.trigger('change')
|
||||
expect(wrapper.emitted().change[0]).toEqual(['regional'])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
33
webapp/components/Select/ActionRadiusSelect.vue
Normal file
33
webapp/components/Select/ActionRadiusSelect.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<select
|
||||
class="select ds-input appearance--auto"
|
||||
name="actionRadius"
|
||||
:value="value"
|
||||
@change="onActionRadiusChange"
|
||||
>
|
||||
<option v-for="actionRadius in actionRadiusOptions" :key="actionRadius" :value="actionRadius">
|
||||
{{ $t(`group.actionRadii.${actionRadius}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ActionRadiusSelect',
|
||||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onActionRadiusChange(event) {
|
||||
this.$emit('change', event.target.value)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -63,13 +63,6 @@ describe('SearchableInput.vue', () => {
|
||||
expect(select.element.value).toBe('abcd')
|
||||
})
|
||||
|
||||
it('calls onDelete when the delete key is pressed', () => {
|
||||
const spy = jest.spyOn(wrapper.vm, 'onDelete')
|
||||
select.trigger('input')
|
||||
select.trigger('keyup.delete')
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
describe('navigating to resource', () => {
|
||||
beforeEach(() => {
|
||||
propsData = { options: searchResults }
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div class="searchable-input" aria-label="search" role="search">
|
||||
<ds-select
|
||||
ref="select"
|
||||
type="search"
|
||||
icon="search"
|
||||
v-model="searchValue"
|
||||
v-model="value"
|
||||
:id="id"
|
||||
label-prop="id"
|
||||
:icon-right="null"
|
||||
@ -11,12 +12,11 @@
|
||||
:loading="loading"
|
||||
:filter="(item) => item"
|
||||
:no-options-available="emptyText"
|
||||
:auto-reset-search="!searchValue"
|
||||
:auto-reset-search="!value"
|
||||
:placeholder="$t('search.placeholder')"
|
||||
@focus.capture.native="onFocus"
|
||||
@input.native="handleInput"
|
||||
@input.native="onInput"
|
||||
@keyup.enter.native="onEnter"
|
||||
@keyup.delete.native="onDelete"
|
||||
@keyup.esc.native="clear"
|
||||
@blur.capture.native="onBlur"
|
||||
@input.exact="onSelect"
|
||||
@ -77,23 +77,29 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchValue: '',
|
||||
value: '',
|
||||
unprocessedSearchInput: '',
|
||||
searchProcess: null,
|
||||
previousSearchTerm: '',
|
||||
delay: 300,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
emptyText() {
|
||||
return this.isActive && !this.loading ? this.$t('search.failed') : this.$t('search.hint')
|
||||
return !this.loading && this.isSearchable()
|
||||
? this.$t('search.failed')
|
||||
: this.$t('search.hint')
|
||||
},
|
||||
isActive() {
|
||||
return !isEmpty(this.previousSearchTerm)
|
||||
return !isEmpty(this.value)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isSearchable() {
|
||||
return (
|
||||
!isEmpty(this.value) &&
|
||||
typeof this.value === 'string' &&
|
||||
this.value.replace(/\s+/g, '').length >= 3
|
||||
)
|
||||
},
|
||||
isFirstOfType(option) {
|
||||
return (
|
||||
this.options.findIndex((o) => o === option) ===
|
||||
@ -103,49 +109,36 @@ export default {
|
||||
onFocus(event) {
|
||||
clearTimeout(this.searchProcess)
|
||||
},
|
||||
handleInput(event) {
|
||||
onInput(event) {
|
||||
clearTimeout(this.searchProcess)
|
||||
this.value = event.target ? event.target.value.replace(/\s+/g, ' ').trim() : ''
|
||||
this.unprocessedSearchInput = this.value
|
||||
if (isEmpty(this.value) || this.value.replace(/\s+/g, '').length < 3) {
|
||||
if (!this.isSearchable()) {
|
||||
this.$emit('clearSearch')
|
||||
return
|
||||
}
|
||||
this.searchProcess = setTimeout(() => {
|
||||
this.previousSearchTerm = this.value
|
||||
this.$emit('query', this.value)
|
||||
}, this.delay)
|
||||
},
|
||||
onEnter(event) {
|
||||
this.$router.push({
|
||||
path: '/search/search-results',
|
||||
query: { search: this.unprocessedSearchInput },
|
||||
query: { search: this.value },
|
||||
})
|
||||
this.$emit('clearSearch')
|
||||
},
|
||||
onDelete(event) {
|
||||
clearTimeout(this.searchProcess)
|
||||
const value = event.target ? event.target.value.trim() : ''
|
||||
if (isEmpty(value)) {
|
||||
this.clear()
|
||||
} else {
|
||||
this.handleInput(event)
|
||||
}
|
||||
this.$refs.select.close()
|
||||
},
|
||||
clear() {
|
||||
this.unprocessedSearchInput = ''
|
||||
this.previousSearchTerm = ''
|
||||
this.searchValue = ''
|
||||
this.value = ''
|
||||
this.$emit('clearSearch')
|
||||
clearTimeout(this.searchProcess)
|
||||
},
|
||||
onBlur(event) {
|
||||
this.searchValue = this.previousSearchTerm
|
||||
clearTimeout(this.searchProcess)
|
||||
},
|
||||
onSelect(item) {
|
||||
this.goToResource(item)
|
||||
this.$nextTick(() => {
|
||||
this.searchValue = this.previousSearchTerm
|
||||
this.value = this.$refs.select.$data.searchString
|
||||
})
|
||||
},
|
||||
getRouteName(item) {
|
||||
|
||||
@ -207,6 +207,9 @@ export const groupMembersQuery = () => {
|
||||
name
|
||||
slug
|
||||
myRoleInGroup
|
||||
avatar {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user