mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-01-20 20:01:25 +00:00
Merge pull request #5543 from Ocelot-Social-Community/search-groups
feat: 🍰 Search For Groups
This commit is contained in:
commit
717b36b60c
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -202,6 +202,8 @@ jobs:
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
|
||||
- name: backend | Initialize Database
|
||||
run: docker-compose exec -T backend yarn db:migrate init
|
||||
- name: backend | Migrate Database Up
|
||||
run: docker-compose exec -T backend yarn db:migrate up
|
||||
- name: backend | Unit test
|
||||
run: docker-compose exec -T backend yarn test
|
||||
##########################################################################
|
||||
|
||||
@ -302,6 +302,7 @@ export default shield(
|
||||
searchResults: allow,
|
||||
searchPosts: allow,
|
||||
searchUsers: allow,
|
||||
searchGroups: allow,
|
||||
searchHashtags: allow,
|
||||
embed: allow,
|
||||
Category: allow,
|
||||
|
||||
@ -66,6 +66,21 @@ const searchHashtagsSetup = {
|
||||
limit: 'LIMIT $limit',
|
||||
}
|
||||
|
||||
const searchGroupsSetup = {
|
||||
fulltextIndex: 'group_fulltext_search',
|
||||
match: `MATCH (resource:Group)
|
||||
MATCH (user:User {id: $userId})
|
||||
OPTIONAL MATCH (user)-[membership:MEMBER_OF]->(resource)
|
||||
WITH user, resource, membership`,
|
||||
whereClause: `WHERE score >= 0.0
|
||||
AND NOT (resource.deleted = true OR resource.disabled = true)
|
||||
AND (resource.groupType IN ['public', 'closed']
|
||||
OR membership.role IN ['usual', 'admin', 'owner'])`,
|
||||
withClause: 'WITH resource, membership',
|
||||
returnClause: 'resource { .*, myRole: membership.role, __typename: labels(resource)[0] }',
|
||||
limit: 'LIMIT $limit',
|
||||
}
|
||||
|
||||
const countSetup = {
|
||||
returnClause: 'toString(size(collect(resource)))',
|
||||
limit: '',
|
||||
@ -83,6 +98,10 @@ const countHashtagsSetup = {
|
||||
...searchHashtagsSetup,
|
||||
...countSetup,
|
||||
}
|
||||
const countGroupsSetup = {
|
||||
...searchGroupsSetup,
|
||||
...countSetup,
|
||||
}
|
||||
|
||||
const searchResultPromise = async (session, setup, params) => {
|
||||
return session.readTransaction(async (transaction) => {
|
||||
@ -113,6 +132,7 @@ const multiSearchMap = [
|
||||
{ symbol: '!', setup: searchPostsSetup, resultName: 'posts' },
|
||||
{ symbol: '@', setup: searchUsersSetup, resultName: 'users' },
|
||||
{ symbol: '#', setup: searchHashtagsSetup, resultName: 'hashtags' },
|
||||
{ symbol: '&', setup: searchGroupsSetup, resultName: 'groups' },
|
||||
]
|
||||
|
||||
export default {
|
||||
@ -178,13 +198,36 @@ export default {
|
||||
}),
|
||||
}
|
||||
},
|
||||
searchGroups: async (_parent, args, context, _resolveInfo) => {
|
||||
const { query, groupsOffset, firstGroups } = args
|
||||
let userId = null
|
||||
if (context.user) userId = context.user.id
|
||||
return {
|
||||
groupCount: getSearchResults(
|
||||
context,
|
||||
countGroupsSetup,
|
||||
{
|
||||
query: queryString(query),
|
||||
skip: 0,
|
||||
userId,
|
||||
},
|
||||
countResultCallback,
|
||||
),
|
||||
groups: getSearchResults(context, searchGroupsSetup, {
|
||||
query: queryString(query),
|
||||
skip: groupsOffset,
|
||||
limit: firstGroups,
|
||||
userId,
|
||||
}),
|
||||
}
|
||||
},
|
||||
searchResults: async (_parent, args, context, _resolveInfo) => {
|
||||
const { query, limit } = args
|
||||
let userId = null
|
||||
if (context.user) userId = context.user.id
|
||||
|
||||
const searchType = query.replace(/^([!@#]?).*$/, '$1')
|
||||
const searchString = query.replace(/^([!@#])/, '')
|
||||
const searchType = query.replace(/^([!@#&]?).*$/, '$1')
|
||||
const searchString = query.replace(/^([!@#&])/, '')
|
||||
|
||||
const params = {
|
||||
query: queryString(searchString),
|
||||
@ -197,6 +240,7 @@ export default {
|
||||
return [
|
||||
...(await getSearchResults(context, searchPostsSetup, params)),
|
||||
...(await getSearchResults(context, searchUsersSetup, params)),
|
||||
...(await getSearchResults(context, searchGroupsSetup, params)),
|
||||
...(await getSearchResults(context, searchHashtagsSetup, params)),
|
||||
]
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
union SearchResult = Post | User | Tag
|
||||
union SearchResult = Post | User | Tag | Group
|
||||
|
||||
type postSearchResults {
|
||||
postCount: Int
|
||||
@ -15,9 +15,15 @@ type hashtagSearchResults {
|
||||
hashtags: [Tag]!
|
||||
}
|
||||
|
||||
type groupSearchResults {
|
||||
groupCount: Int
|
||||
groups: [Group]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
searchPosts(query: String!, firstPosts: Int, postsOffset: Int): postSearchResults!
|
||||
searchUsers(query: String!, firstUsers: Int, usersOffset: Int): userSearchResults!
|
||||
searchGroups(query: String!, firstGroups: Int, groupsOffset: Int): groupSearchResults!
|
||||
searchHashtags(query: String!, firstHashtags: Int, hashtagsOffset: Int): hashtagSearchResults!
|
||||
searchResults(query: String!, limit: Int = 5): [SearchResult]!
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ describe('SearchResults', () => {
|
||||
}
|
||||
propsData = {
|
||||
pageSize: 12,
|
||||
search: '',
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
@ -169,7 +170,7 @@ describe('SearchResults', () => {
|
||||
await wrapper.vm.$nextTick()
|
||||
await expect(
|
||||
wrapper.vm.$options.apollo.searchPosts.variables.bind(wrapper.vm)(),
|
||||
).toMatchObject({ query: undefined, firstPosts: 12, postsOffset: 12 })
|
||||
).toMatchObject({ query: '', firstPosts: 12, postsOffset: 12 })
|
||||
})
|
||||
|
||||
it('displays the next page button when next-button is clicked', async () => {
|
||||
@ -199,7 +200,7 @@ describe('SearchResults', () => {
|
||||
await wrapper.vm.$nextTick()
|
||||
await expect(
|
||||
wrapper.vm.$options.apollo.searchPosts.variables.bind(wrapper.vm)(),
|
||||
).toMatchObject({ query: undefined, firstPosts: 12, postsOffset: 24 })
|
||||
).toMatchObject({ query: '', firstPosts: 12, postsOffset: 24 })
|
||||
})
|
||||
|
||||
it('deactivates next page button when next-button is clicked twice', async () => {
|
||||
@ -234,7 +235,7 @@ describe('SearchResults', () => {
|
||||
await wrapper.vm.$nextTick()
|
||||
await expect(
|
||||
wrapper.vm.$options.apollo.searchPosts.variables.bind(wrapper.vm)(),
|
||||
).toMatchObject({ query: undefined, firstPosts: 12, postsOffset: 0 })
|
||||
).toMatchObject({ query: '', firstPosts: 12, postsOffset: 0 })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -59,6 +59,14 @@
|
||||
</base-card>
|
||||
</ds-grid-item>
|
||||
</template>
|
||||
<!-- groups -->
|
||||
<template v-if="activeTab === 'Group'">
|
||||
<ds-grid-item v-for="group in activeResources" :key="group.id" :row-span="2">
|
||||
<base-card :wideContent="true" class="group-teaser-card-wrapper">
|
||||
<group-teaser :group="{ ...group, name: group.groupName }" />
|
||||
</base-card>
|
||||
</ds-grid-item>
|
||||
</template>
|
||||
<!-- hashtags -->
|
||||
<template v-if="activeTab === 'Hashtag'">
|
||||
<ds-grid-item v-for="hashtag in activeResources" :key="hashtag.id" :row-span="2">
|
||||
@ -100,17 +108,19 @@
|
||||
|
||||
<script>
|
||||
import postListActions from '~/mixins/postListActions'
|
||||
import { searchPosts, searchUsers, searchHashtags } from '~/graphql/Search.js'
|
||||
import { searchPosts, searchUsers, searchGroups, searchHashtags } from '~/graphql/Search.js'
|
||||
import HcEmpty from '~/components/Empty/Empty'
|
||||
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid'
|
||||
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem'
|
||||
import PostTeaser from '~/components/PostTeaser/PostTeaser'
|
||||
import TabNavigation from '~/components/_new/generic/TabNavigation/TabNavigation'
|
||||
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||
import GroupTeaser from '~/components/Group/GroupTeaser'
|
||||
import PaginationButtons from '~/components/_new/generic/PaginationButtons/PaginationButtons'
|
||||
import HcHashtag from '~/components/Hashtag/Hashtag'
|
||||
|
||||
export default {
|
||||
name: 'SearchResults',
|
||||
components: {
|
||||
TabNavigation,
|
||||
HcEmpty,
|
||||
@ -119,6 +129,7 @@ export default {
|
||||
PostTeaser,
|
||||
PaginationButtons,
|
||||
UserTeaser,
|
||||
GroupTeaser,
|
||||
HcHashtag,
|
||||
},
|
||||
mixins: [postListActions],
|
||||
@ -135,24 +146,29 @@ export default {
|
||||
return {
|
||||
posts: [],
|
||||
users: [],
|
||||
groups: [],
|
||||
hashtags: [],
|
||||
|
||||
postCount: 0,
|
||||
userCount: 0,
|
||||
groupCount: 0,
|
||||
hashtagCount: 0,
|
||||
|
||||
postPage: 0,
|
||||
userPage: 0,
|
||||
groupPage: 0,
|
||||
hashtagPage: 0,
|
||||
|
||||
activeTab: null,
|
||||
|
||||
firstPosts: this.pageSize,
|
||||
firstUsers: this.pageSize,
|
||||
firstGroups: this.pageSize,
|
||||
firstHashtags: this.pageSize,
|
||||
|
||||
postsOffset: 0,
|
||||
usersOffset: 0,
|
||||
groupsOffset: 0,
|
||||
hashtagsOffset: 0,
|
||||
}
|
||||
},
|
||||
@ -160,18 +176,21 @@ export default {
|
||||
activeResources() {
|
||||
if (this.activeTab === 'Post') return this.posts
|
||||
if (this.activeTab === 'User') return this.users
|
||||
if (this.activeTab === 'Group') return this.groups
|
||||
if (this.activeTab === 'Hashtag') return this.hashtags
|
||||
return []
|
||||
},
|
||||
activeResourceCount() {
|
||||
if (this.activeTab === 'Post') return this.postCount
|
||||
if (this.activeTab === 'User') return this.userCount
|
||||
if (this.activeTab === 'Group') return this.groupCount
|
||||
if (this.activeTab === 'Hashtag') return this.hashtagCount
|
||||
return 0
|
||||
},
|
||||
activePage() {
|
||||
if (this.activeTab === 'Post') return this.postPage
|
||||
if (this.activeTab === 'User') return this.userPage
|
||||
if (this.activeTab === 'Group') return this.groupPage
|
||||
if (this.activeTab === 'Hashtag') return this.hashtagPage
|
||||
return 0
|
||||
},
|
||||
@ -189,6 +208,12 @@ export default {
|
||||
count: this.userCount,
|
||||
disabled: this.userCount === 0,
|
||||
},
|
||||
{
|
||||
type: 'Group',
|
||||
title: this.$t('search.heading.Group', {}, this.groupCount),
|
||||
count: this.groupCount,
|
||||
disabled: this.groupCount === 0,
|
||||
},
|
||||
{
|
||||
type: 'Hashtag',
|
||||
title: this.$t('search.heading.Tag', {}, this.hashtagCount),
|
||||
@ -200,24 +225,27 @@ export default {
|
||||
hasPrevious() {
|
||||
if (this.activeTab === 'Post') return this.postsOffset > 0
|
||||
if (this.activeTab === 'User') return this.usersOffset > 0
|
||||
if (this.activeTab === 'Group') return this.groupsOffset > 0
|
||||
if (this.activeTab === 'Hashtag') return this.hashtagsOffset > 0
|
||||
return false
|
||||
},
|
||||
hasNext() {
|
||||
if (this.activeTab === 'Post') return (this.postPage + 1) * this.pageSize < this.postCount
|
||||
if (this.activeTab === 'User') return (this.userPage + 1) * this.pageSize < this.userCount
|
||||
if (this.activeTab === 'Group') return (this.groupPage + 1) * this.pageSize < this.groupCount
|
||||
if (this.activeTab === 'Hashtag')
|
||||
return (this.hashtagPage + 1) * this.pageSize < this.hashtagCount
|
||||
return false
|
||||
},
|
||||
searchCount() {
|
||||
return this.postCount + this.userCount + this.hashtagCount
|
||||
return this.postCount + this.userCount + this.groupCount + this.hashtagCount
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clearPage() {
|
||||
this.postPage = 0
|
||||
this.userPage = 0
|
||||
this.groupPage = 0
|
||||
this.hashtagPage = 0
|
||||
},
|
||||
switchTab(tabType) {
|
||||
@ -235,6 +263,10 @@ export default {
|
||||
this.userPage--
|
||||
this.usersOffset = this.userPage * this.pageSize
|
||||
break
|
||||
case 'Group':
|
||||
this.groupPage--
|
||||
this.groupsOffset = this.groupPage * this.pageSize
|
||||
break
|
||||
case 'Hashtag':
|
||||
this.hashtagPage--
|
||||
this.hashtagsOffset = this.hashtagPage * this.pageSize
|
||||
@ -252,6 +284,10 @@ export default {
|
||||
this.userPage++
|
||||
this.usersOffset += this.pageSize
|
||||
break
|
||||
case 'Group':
|
||||
this.groupPage++
|
||||
this.groupsOffset += this.pageSize
|
||||
break
|
||||
case 'Hashtag':
|
||||
this.hashtagPage++
|
||||
this.hashtagsOffset += this.pageSize
|
||||
@ -270,7 +306,7 @@ export default {
|
||||
variables() {
|
||||
const { firstHashtags, hashtagsOffset, search } = this
|
||||
return {
|
||||
query: search,
|
||||
query: search.replace(/^([!@#&])/, ''),
|
||||
firstHashtags,
|
||||
hashtagsOffset,
|
||||
}
|
||||
@ -281,7 +317,12 @@ export default {
|
||||
update({ searchHashtags }) {
|
||||
this.hashtags = searchHashtags.hashtags
|
||||
this.hashtagCount = searchHashtags.hashtagCount
|
||||
if (this.postCount === 0 && this.userCount === 0 && this.hashtagCount > 0)
|
||||
if (
|
||||
this.postCount === 0 &&
|
||||
this.userCount === 0 &&
|
||||
this.groupCount === 0 &&
|
||||
this.hashtagCount > 0
|
||||
)
|
||||
this.activeTab = 'Hashtag'
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
@ -293,7 +334,7 @@ export default {
|
||||
variables() {
|
||||
const { firstUsers, usersOffset, search } = this
|
||||
return {
|
||||
query: search,
|
||||
query: search.replace(/^([!@#&])/, ''),
|
||||
firstUsers,
|
||||
usersOffset,
|
||||
}
|
||||
@ -315,7 +356,7 @@ export default {
|
||||
variables() {
|
||||
const { firstPosts, postsOffset, search } = this
|
||||
return {
|
||||
query: search,
|
||||
query: search.replace(/^([!@#&])/, ''),
|
||||
firstPosts,
|
||||
postsOffset,
|
||||
}
|
||||
@ -330,6 +371,29 @@ export default {
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
searchGroups: {
|
||||
query() {
|
||||
return searchGroups(this.i18n)
|
||||
},
|
||||
variables() {
|
||||
const { firstGroups, groupsOffset, search } = this
|
||||
return {
|
||||
query: search.replace(/^([!@#&])/, ''),
|
||||
firstGroups,
|
||||
groupsOffset,
|
||||
}
|
||||
},
|
||||
skip() {
|
||||
return !this.search
|
||||
},
|
||||
update({ searchGroups }) {
|
||||
this.groups = searchGroups.groups
|
||||
this.groupCount = searchGroups.groupCount
|
||||
if (this.postCount === 0 && this.userCount === 0 && this.groupCount > 0)
|
||||
this.activeTab = 'Group'
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -365,6 +429,10 @@ export default {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.group-teaser-card-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-total-search-results {
|
||||
|
||||
38
webapp/components/generic/SearchGroup/SearchGroup.vue
Normal file
38
webapp/components/generic/SearchGroup/SearchGroup.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<section class="search-group">
|
||||
<p class="label">{{ option.groupName | truncate(70) }}</p>
|
||||
<div class="metadata"></div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SearchGroup',
|
||||
props: {
|
||||
option: { type: Object, required: true },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.search-group {
|
||||
display: flex;
|
||||
|
||||
> .label {
|
||||
flex-grow: 1;
|
||||
padding: 0 $space-x-small;
|
||||
}
|
||||
|
||||
> .metadata {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
color: $text-color-softer;
|
||||
font-size: $font-size-small;
|
||||
|
||||
> .counts > .counter-icon {
|
||||
margin: 0 $space-x-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -35,6 +35,12 @@
|
||||
>
|
||||
<search-post :option="option" />
|
||||
</p>
|
||||
<p
|
||||
v-if="option.__typename === 'Group'"
|
||||
:class="{ 'option-with-heading': isFirstOfType(option) }"
|
||||
>
|
||||
<search-group :option="option" />
|
||||
</p>
|
||||
<p
|
||||
v-if="option.__typename === 'Tag'"
|
||||
:class="{ 'option-with-heading': isFirstOfType(option) }"
|
||||
@ -51,6 +57,7 @@
|
||||
import { isEmpty } from 'lodash'
|
||||
import SearchHeading from '~/components/generic/SearchHeading/SearchHeading.vue'
|
||||
import SearchPost from '~/components/generic/SearchPost/SearchPost.vue'
|
||||
import SearchGroup from '~/components/generic/SearchGroup/SearchGroup.vue'
|
||||
import HcHashtag from '~/components/Hashtag/Hashtag.vue'
|
||||
import UserTeaser from '~/components/UserTeaser/UserTeaser.vue'
|
||||
|
||||
@ -58,6 +65,7 @@ export default {
|
||||
name: 'SearchableInput',
|
||||
components: {
|
||||
SearchHeading,
|
||||
SearchGroup,
|
||||
SearchPost,
|
||||
HcHashtag,
|
||||
UserTeaser,
|
||||
@ -140,8 +148,17 @@ export default {
|
||||
this.searchValue = this.previousSearchTerm
|
||||
})
|
||||
},
|
||||
isPost(item) {
|
||||
return item.__typename === 'Post'
|
||||
getRouteName(item) {
|
||||
switch (item.__typename) {
|
||||
case 'Post':
|
||||
return 'post-id-slug'
|
||||
case 'User':
|
||||
return 'profile-id-slug'
|
||||
case 'Group':
|
||||
return 'group-id-slug'
|
||||
default:
|
||||
return null
|
||||
}
|
||||
},
|
||||
isTag(item) {
|
||||
return item.__typename === 'Tag'
|
||||
@ -150,7 +167,7 @@ export default {
|
||||
this.$nextTick(() => {
|
||||
if (!this.isTag(item)) {
|
||||
this.$router.push({
|
||||
name: this.isPost(item) ? 'post-id-slug' : 'profile-id-slug',
|
||||
name: this.getRouteName(item),
|
||||
params: { id: item.id, slug: item.slug },
|
||||
})
|
||||
} else {
|
||||
|
||||
@ -62,6 +62,29 @@ export const postFragment = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const groupFragment = gql`
|
||||
fragment group on Group {
|
||||
id
|
||||
groupName: name
|
||||
slug
|
||||
disabled
|
||||
deleted
|
||||
about
|
||||
description
|
||||
descriptionExcerpt
|
||||
groupType
|
||||
actionRadius
|
||||
categories {
|
||||
id
|
||||
slug
|
||||
name
|
||||
icon
|
||||
}
|
||||
locationName
|
||||
myRole
|
||||
}
|
||||
`
|
||||
|
||||
export const postCountsFragment = gql`
|
||||
fragment postCounts on Post {
|
||||
commentsCount
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import gql from 'graphql-tag'
|
||||
import { userFragment, postFragment, tagsCategoriesAndPinnedFragment } from './Fragments'
|
||||
import {
|
||||
userFragment,
|
||||
postFragment,
|
||||
groupFragment,
|
||||
tagsCategoriesAndPinnedFragment,
|
||||
} from './Fragments'
|
||||
|
||||
export const searchQuery = gql`
|
||||
${userFragment}
|
||||
${postFragment}
|
||||
${groupFragment}
|
||||
|
||||
query ($query: String!) {
|
||||
searchResults(query: $query, limit: 5) {
|
||||
@ -24,6 +30,9 @@ export const searchQuery = gql`
|
||||
... on Tag {
|
||||
id
|
||||
}
|
||||
... on Group {
|
||||
...group
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -52,6 +61,46 @@ export const searchPosts = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const searchGroups = (i18n) => {
|
||||
const lang = i18n ? i18n.locale().toUpperCase() : 'EN'
|
||||
return gql`
|
||||
query ($query: String!, $firstGroups: Int, $groupsOffset: Int) {
|
||||
searchGroups(query: $query, firstGroups: $firstGroups, groupsOffset: $groupsOffset) {
|
||||
groupCount
|
||||
groups {
|
||||
__typename
|
||||
id
|
||||
groupName: name
|
||||
slug
|
||||
createdAt
|
||||
updatedAt
|
||||
disabled
|
||||
deleted
|
||||
about
|
||||
description
|
||||
descriptionExcerpt
|
||||
groupType
|
||||
actionRadius
|
||||
categories {
|
||||
id
|
||||
slug
|
||||
name
|
||||
icon
|
||||
}
|
||||
avatar {
|
||||
url
|
||||
}
|
||||
locationName
|
||||
location {
|
||||
name: name${lang}
|
||||
}
|
||||
myRole
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const searchUsers = gql`
|
||||
${userFragment}
|
||||
|
||||
|
||||
@ -745,11 +745,12 @@
|
||||
"failed": "Nichts gefunden",
|
||||
"for": "Suche nach ",
|
||||
"heading": {
|
||||
"Group": "Gruppe ::: Gruppen",
|
||||
"Post": "Beitrag ::: Beiträge",
|
||||
"Tag": "Hashtag ::: Hashtags",
|
||||
"User": "Benutzer ::: Benutzer"
|
||||
},
|
||||
"hint": "Wonach suchst Du? Nutze !… für Beiträge, @… für Mitglieder, #… für Hashtags",
|
||||
"hint": "Wonach suchst Du? Nutze !… für Beiträge, @… für Mitglieder, &… für Gruppen, #… für Hashtags",
|
||||
"no-results": "Keine Ergebnisse für \"{search}\" gefunden. Versuch' es mit einem anderen Begriff!",
|
||||
"page": "Seite",
|
||||
"placeholder": "Suchen",
|
||||
|
||||
@ -745,11 +745,12 @@
|
||||
"failed": "Nothing found",
|
||||
"for": "Searching for ",
|
||||
"heading": {
|
||||
"Group": "Group ::: Groups",
|
||||
"Post": "Post ::: Posts",
|
||||
"Tag": "Hashtag ::: Hashtags",
|
||||
"User": "User ::: Users"
|
||||
},
|
||||
"hint": "What are you searching for? Use !… for posts, @… for users, #… for hashtags.",
|
||||
"hint": "What are you searching for? Use !… for posts, @… for users, &… for groups, #… for hashtags.",
|
||||
"no-results": "No results found for \"{search}\". Try a different search term!",
|
||||
"page": "Page",
|
||||
"placeholder": "Search",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user