mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-03-01 12:44:37 +00:00
fix(webapp): add responsive mobile menu with locale switching and filter support (#9281)
This commit is contained in:
parent
30d88e9b41
commit
9548ad6e31
@ -3,23 +3,40 @@ import gql from 'graphql-tag'
|
||||
export const UpdateUser = gql`
|
||||
mutation (
|
||||
$id: ID!
|
||||
$slug: String
|
||||
$name: String
|
||||
$termsAndConditionsAgreedVersion: String
|
||||
$locationName: String # empty string '' sets it to null
|
||||
$about: String
|
||||
$allowEmbedIframes: Boolean
|
||||
$showShoutsPublicly: Boolean
|
||||
$emailNotificationSettings: [EmailNotificationSettingsInput]
|
||||
$termsAndConditionsAgreedVersion: String
|
||||
$avatar: ImageInput
|
||||
$locationName: String # empty string '' sets it to null
|
||||
$locale: String
|
||||
) {
|
||||
UpdateUser(
|
||||
id: $id
|
||||
slug: $slug
|
||||
name: $name
|
||||
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
|
||||
locationName: $locationName
|
||||
about: $about
|
||||
allowEmbedIframes: $allowEmbedIframes
|
||||
showShoutsPublicly: $showShoutsPublicly
|
||||
emailNotificationSettings: $emailNotificationSettings
|
||||
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
|
||||
avatar: $avatar
|
||||
locationName: $locationName
|
||||
locale: $locale
|
||||
) {
|
||||
id
|
||||
slug
|
||||
name
|
||||
about
|
||||
allowEmbedIframes
|
||||
showShoutsPublicly
|
||||
termsAndConditionsAgreedVersion
|
||||
termsAndConditionsAgreedAt
|
||||
locationName
|
||||
locale
|
||||
location {
|
||||
name
|
||||
nameDE
|
||||
@ -33,6 +50,18 @@ export const UpdateUser = gql`
|
||||
value
|
||||
}
|
||||
}
|
||||
avatar {
|
||||
url
|
||||
alt
|
||||
sensitive
|
||||
aspectRatio
|
||||
type
|
||||
}
|
||||
badgeVerification {
|
||||
id
|
||||
description
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
|
||||
|
||||
defineStep('I select {string} in the language menu', language => {
|
||||
cy.get('.locale-menu')
|
||||
cy.get('.locale-menu:visible')
|
||||
.first()
|
||||
.click()
|
||||
cy.contains('.locale-menu-popover a', language)
|
||||
.click()
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
|
||||
|
||||
defineStep('open the notification menu and click on the first item', () => {
|
||||
cy.get('.notifications-menu')
|
||||
cy.get('.notifications-menu:visible')
|
||||
.first()
|
||||
.invoke('show')
|
||||
.click() // 'invoke('show')' because of the delay for show the menu
|
||||
cy.get('.notification-content a')
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
|
||||
|
||||
defineStep('see {int} unread notifications in the top menu', count => {
|
||||
cy.get('.notifications-menu')
|
||||
cy.get('.notifications-menu:visible')
|
||||
.first()
|
||||
.should('contain', count)
|
||||
})
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
|
||||
|
||||
defineStep('the notification menu button links to the all notifications page', () => {
|
||||
cy.get('.notifications-menu')
|
||||
cy.get('.notifications-menu:visible')
|
||||
.first()
|
||||
.click()
|
||||
cy.location('pathname')
|
||||
.should('contain', '/notifications')
|
||||
|
||||
5
packages/ui/src/ocelot/icons/svgs/language.svg
Normal file
5
packages/ui/src/ocelot/icons/svgs/language.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Font Awesome Free 6.7.2 "language" icon (CC BY 4.0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 640 512">
|
||||
<title>language</title>
|
||||
<path d="M0 128C0 92.7 28.7 64 64 64l192 0 48 0 16 0 256 0c35.3 0 64 28.7 64 64l0 256c0 35.3-28.7 64-64 64l-256 0-16 0-48 0L64 448c-35.3 0-64-28.7-64-64L0 128zm320 0l0 256 256 0 0-256-256 0zM178.3 175.9c-3.2-7.2-10.4-11.9-18.3-11.9s-15.1 4.7-18.3 11.9l-64 144c-4.5 10.1 .1 21.9 10.2 26.4s21.9-.1 26.4-10.2l8.9-20.1 73.6 0 8.9 20.1c4.5 10.1 16.3 14.6 26.4 10.2s14.6-16.3 10.2-26.4l-64-144zM160 233.2L179 276l-38 0 19-42.8zM448 164c11 0 20 9 20 20l0 4 44 0 16 0c11 0 20 9 20 20s-9 20-20 20l-2 0-1.6 4.5c-8.9 24.4-22.4 46.6-39.6 65.4c.9 .6 1.8 1.1 2.7 1.6l18.9 11.3c9.5 5.7 12.5 18 6.9 27.4s-18 12.5-27.4 6.9l-18.9-11.3c-4.5-2.7-8.8-5.5-13.1-8.5c-10.6 7.5-21.9 14-34 19.4l-3.6 1.6c-10.1 4.5-21.9-.1-26.4-10.2s.1-21.9 10.2-26.4l3.6-1.6c6.4-2.9 12.6-6.1 18.5-9.8l-12.2-12.2c-7.8-7.8-7.8-20.5 0-28.3s20.5-7.8 28.3 0l14.6 14.6 .5 .5c12.4-13.1 22.5-28.3 29.8-45L448 228l-72 0c-11 0-20-9-20-20s9-20 20-20l52 0 0-4c0-11 9-20 20-20z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -359,6 +359,7 @@ $media-query-small: "(min-width: 600px)";
|
||||
$media-query-medium: "(min-width: 768px)";
|
||||
$media-query-large: "(min-width: 1024px)";
|
||||
$media-query-x-large: "(min-width: 1200px)";
|
||||
$container-max-width-x-large: 1200px;
|
||||
|
||||
/**
|
||||
* @tokens Background Images
|
||||
|
||||
@ -1,26 +1,28 @@
|
||||
<template>
|
||||
<div class="donation-info">
|
||||
<page-params-link :pageParams="links.DONATE" class="donation-info">
|
||||
<progress-bar :label="label" :goal="goal" :progress="progress">
|
||||
<os-button size="sm" variant="primary" @click="redirectToPage(links.DONATE)">
|
||||
<os-button size="sm" variant="primary">
|
||||
{{ $t('donations.donate-now') }}
|
||||
<template #suffix>
|
||||
<os-icon :icon="icons.heartO" />
|
||||
</template>
|
||||
</os-button>
|
||||
</progress-bar>
|
||||
</div>
|
||||
</page-params-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsButton, OsIcon } from '@ocelot-social/ui'
|
||||
import { iconRegistry } from '~/utils/iconRegistry'
|
||||
import links from '~/constants/links.js'
|
||||
import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParamsLink.vue'
|
||||
import ProgressBar from '~/components/ProgressBar/ProgressBar.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
OsButton,
|
||||
OsIcon,
|
||||
PageParamsLink,
|
||||
ProgressBar,
|
||||
},
|
||||
props: {
|
||||
@ -44,11 +46,6 @@ export default {
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
redirectToPage(pageParams) {
|
||||
pageParams.redirectToPage(this)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -58,5 +55,6 @@ export default {
|
||||
flex: 1;
|
||||
margin-bottom: $space-x-small;
|
||||
margin-top: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<dropdown ref="menu" placement="top-start" :offset="8" class="filter-menu">
|
||||
<dropdown ref="menu" :placement="placement" :offset="offset" class="filter-menu">
|
||||
<template #default="{ toggleMenu }">
|
||||
<os-button
|
||||
variant="primary"
|
||||
:appearance="filterActive ? 'filled' : 'ghost'"
|
||||
circle
|
||||
:aria-label="$t('common.filter')"
|
||||
@click.prevent="toggleMenu()"
|
||||
>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.filter" />
|
||||
</template>
|
||||
<os-icon class="dropdown-arrow" :icon="icons.angleDown" />
|
||||
</os-button>
|
||||
</template>
|
||||
<template #popover>
|
||||
@ -34,8 +34,8 @@ export default {
|
||||
OsIcon,
|
||||
},
|
||||
props: {
|
||||
placement: { type: String },
|
||||
offset: { type: [String, Number] },
|
||||
placement: { type: String, default: 'top-start' },
|
||||
offset: { type: [String, Number], default: 8 },
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
|
||||
@ -61,6 +61,7 @@ export default {
|
||||
|
||||
> .filter-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
padding-left: $space-base;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -6,11 +6,11 @@ import Vuex from 'vuex'
|
||||
const localVue = global.localVue
|
||||
|
||||
const stubs = {
|
||||
'client-only': true,
|
||||
'client-only': { template: '<div><slot /></div>' },
|
||||
}
|
||||
|
||||
describe('LocaleSwitch.vue', () => {
|
||||
let wrapper, mocks, computed, deutschLanguageItem, getters
|
||||
let wrapper, mocks, computed, getters
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
@ -48,10 +48,12 @@ describe('LocaleSwitch.vue', () => {
|
||||
{
|
||||
name: 'English',
|
||||
path: 'en',
|
||||
flag: '🇬🇧',
|
||||
},
|
||||
{
|
||||
name: 'Deutsch',
|
||||
path: 'de',
|
||||
flag: '🇩🇪',
|
||||
},
|
||||
]
|
||||
},
|
||||
@ -71,37 +73,61 @@ describe('LocaleSwitch.vue', () => {
|
||||
}
|
||||
|
||||
describe('with current user', () => {
|
||||
beforeEach(() => {
|
||||
let toggleMenu
|
||||
|
||||
beforeEach(async () => {
|
||||
toggleMenu = jest.fn()
|
||||
wrapper = Wrapper()
|
||||
wrapper.find('.locale-menu').trigger('click')
|
||||
deutschLanguageItem = wrapper.findAll('li').at(1)
|
||||
deutschLanguageItem.trigger('click')
|
||||
await wrapper.vm.changeLanguage('de', toggleMenu)
|
||||
})
|
||||
|
||||
it("sets a user's locale", () => {
|
||||
expect(mocks.$i18n.set).toHaveBeenCalledTimes(1)
|
||||
expect(mocks.$i18n.set).toHaveBeenCalledWith('de')
|
||||
})
|
||||
|
||||
it("updates the user's locale in the database", () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('closes the menu', () => {
|
||||
expect(toggleMenu).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when apollo mutation fails', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper = Wrapper()
|
||||
// First call succeeds (consumes mockResolvedValueOnce)
|
||||
await wrapper.vm.changeLanguage('de', jest.fn())
|
||||
// Second call fails (consumes mockRejectedValueOnce)
|
||||
await wrapper.vm.changeLanguage('en', jest.fn())
|
||||
})
|
||||
|
||||
it('shows an error toast', () => {
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith('Please log in!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('no current user', () => {
|
||||
beforeEach(() => {
|
||||
let toggleMenu
|
||||
|
||||
beforeEach(async () => {
|
||||
toggleMenu = jest.fn()
|
||||
getters = {
|
||||
'auth/user': () => {
|
||||
return null
|
||||
},
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
wrapper.find('.locale-menu').trigger('click')
|
||||
deutschLanguageItem = wrapper.findAll('li').at(1)
|
||||
deutschLanguageItem.trigger('click')
|
||||
await wrapper.vm.changeLanguage('de', toggleMenu)
|
||||
})
|
||||
|
||||
it('does not send a UpdateUser mutation', () => {
|
||||
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('still closes the menu', () => {
|
||||
expect(toggleMenu).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -2,18 +2,22 @@
|
||||
<client-only>
|
||||
<dropdown ref="menu" :placement="placement" :offset="offset">
|
||||
<template #default="{ toggleMenu }">
|
||||
<a
|
||||
<os-button
|
||||
class="locale-menu"
|
||||
href="#"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('localeSwitch.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('localeSwitch.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
@click.prevent="toggleMenu()"
|
||||
>
|
||||
<span class="label">{{ current.code.toUpperCase() }}</span>
|
||||
<os-icon class="dropdown-arrow" :icon="icons.angleDown" />
|
||||
</a>
|
||||
<template #icon>
|
||||
<os-icon :icon="icons.language" />
|
||||
</template>
|
||||
</os-button>
|
||||
</template>
|
||||
<template #popover="{ toggleMenu }">
|
||||
<ds-menu class="locale-menu-popover" :matcher="matcher" :routes="routes">
|
||||
@ -24,6 +28,7 @@
|
||||
:parents="item.parents"
|
||||
@click.stop.prevent="changeLanguage(item.route.path, toggleMenu)"
|
||||
>
|
||||
<span class="locale-flag">{{ item.route.flag }}</span>
|
||||
{{ item.route.name }}
|
||||
</ds-menu-item>
|
||||
</template>
|
||||
@ -34,18 +39,19 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsIcon } from '@ocelot-social/ui'
|
||||
import { OsButton, OsIcon } from '@ocelot-social/ui'
|
||||
import { iconRegistry } from '~/utils/iconRegistry'
|
||||
import gql from 'graphql-tag'
|
||||
import Dropdown from '~/components/Dropdown'
|
||||
import find from 'lodash/find'
|
||||
import orderBy from 'lodash/orderBy'
|
||||
import locales from '~/locales'
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import localeUpdate from '~/mixins/localeUpdate.js'
|
||||
|
||||
export default {
|
||||
mixins: [localeUpdate],
|
||||
components: {
|
||||
Dropdown,
|
||||
OsButton,
|
||||
OsIcon,
|
||||
},
|
||||
props: {
|
||||
@ -62,17 +68,12 @@ export default {
|
||||
return find(this.locales, { code: this.$i18n.locale() })
|
||||
},
|
||||
routes() {
|
||||
const routes = this.locales.map((locale) => {
|
||||
return {
|
||||
name: locale.name,
|
||||
path: locale.code,
|
||||
}
|
||||
})
|
||||
return routes
|
||||
return this.locales.map((locale) => ({
|
||||
name: locale.name,
|
||||
path: locale.code,
|
||||
flag: locale.flag,
|
||||
}))
|
||||
},
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
},
|
||||
created() {
|
||||
this.icons = iconRegistry
|
||||
@ -86,57 +87,11 @@ export default {
|
||||
matcher(locale) {
|
||||
return locale === this.$i18n.locale()
|
||||
},
|
||||
|
||||
...mapMutations({
|
||||
setCurrentUser: 'auth/SET_USER',
|
||||
}),
|
||||
async updateUserLocale() {
|
||||
if (!this.currentUser || !this.currentUser.id) return null
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation ($id: ID!, $locale: String) {
|
||||
UpdateUser(id: $id, locale: $locale) {
|
||||
id
|
||||
locale
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: this.currentUser.id,
|
||||
locale: this.$i18n.locale(),
|
||||
},
|
||||
update: (store, { data: { UpdateUser } }) => {
|
||||
const { locale } = UpdateUser
|
||||
this.setCurrentUser({
|
||||
...this.currentUser,
|
||||
locale,
|
||||
})
|
||||
},
|
||||
})
|
||||
this.$toast.success(this.$t('contribution.success'))
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.locale-menu {
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: $space-xx-small;
|
||||
color: $color-locale-menu;
|
||||
|
||||
> .label {
|
||||
margin: 0 $space-xx-small;
|
||||
}
|
||||
}
|
||||
|
||||
nav.locale-menu-popover {
|
||||
margin-left: -$space-small !important;
|
||||
margin-right: -$space-small !important;
|
||||
@ -146,4 +101,10 @@ nav.locale-menu-popover {
|
||||
padding-right: $space-base;
|
||||
}
|
||||
}
|
||||
|
||||
.locale-flag {
|
||||
margin-right: $space-xx-small;
|
||||
font-size: 1.2em;
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -118,5 +118,9 @@ export default {
|
||||
.progress-bar-button {
|
||||
position: relative;
|
||||
float: right;
|
||||
|
||||
@media (max-width: 810px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -358,6 +358,7 @@ export const updateUserMutation = () => {
|
||||
$termsAndConditionsAgreedVersion: String
|
||||
$avatar: ImageInput
|
||||
$locationName: String # empty string '' sets it to null
|
||||
$locale: String
|
||||
) {
|
||||
UpdateUser(
|
||||
id: $id
|
||||
@ -370,6 +371,7 @@ export const updateUserMutation = () => {
|
||||
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
|
||||
avatar: $avatar
|
||||
locationName: $locationName
|
||||
locale: $locale
|
||||
) {
|
||||
id
|
||||
slug
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<div class="layout-default">
|
||||
<div class="main-navigation">
|
||||
<header-menu :showMobileMenu="isMobile" />
|
||||
<header-menu />
|
||||
</div>
|
||||
<div class="ds-container ds-container-x-large">
|
||||
<div class="main-container">
|
||||
<nuxt />
|
||||
</div>
|
||||
</div>
|
||||
<page-footer v-if="!isMobile" />
|
||||
<page-footer class="desktop-footer" />
|
||||
<div id="overlay" />
|
||||
<client-only>
|
||||
<modal />
|
||||
@ -66,6 +66,12 @@ export default {
|
||||
padding-bottom: 8rem;
|
||||
}
|
||||
|
||||
.desktop-footer {
|
||||
@media (max-width: 810px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-modul {
|
||||
background-color: rgb(233, 228, 228);
|
||||
width: 355px;
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": "Landkarte"
|
||||
},
|
||||
"more": "Mehr",
|
||||
"notifications": {
|
||||
"tooltip": "Benachrichtigungen"
|
||||
}
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": "Map"
|
||||
},
|
||||
"more": "More",
|
||||
"notifications": {
|
||||
"tooltip": "Notifications"
|
||||
}
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": "Mapa"
|
||||
},
|
||||
"more": "Más",
|
||||
"notifications": {
|
||||
"tooltip": "Notificaciones"
|
||||
}
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": "Carte"
|
||||
},
|
||||
"more": "Plus",
|
||||
"notifications": {
|
||||
"tooltip": "Notifications"
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ const locales = [
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
iso: 'en-US',
|
||||
flag: '🇬🇧',
|
||||
enabled: true,
|
||||
dateFnsLocale: enUS,
|
||||
},
|
||||
@ -13,6 +14,7 @@ const locales = [
|
||||
name: 'Deutsch',
|
||||
code: 'de',
|
||||
iso: 'de-DE',
|
||||
flag: '🇩🇪',
|
||||
enabled: true,
|
||||
dateFnsLocale: de,
|
||||
},
|
||||
@ -20,6 +22,7 @@ const locales = [
|
||||
name: 'Nederlands',
|
||||
code: 'nl',
|
||||
iso: 'nl-NL',
|
||||
flag: '🇳🇱',
|
||||
enabled: true,
|
||||
dateFnsLocale: nl,
|
||||
},
|
||||
@ -27,6 +30,7 @@ const locales = [
|
||||
name: 'Français',
|
||||
code: 'fr',
|
||||
iso: 'fr-FR',
|
||||
flag: '🇫🇷',
|
||||
enabled: true,
|
||||
dateFnsLocale: fr,
|
||||
},
|
||||
@ -34,6 +38,7 @@ const locales = [
|
||||
name: 'Italiano',
|
||||
code: 'it',
|
||||
iso: 'it-IT',
|
||||
flag: '🇮🇹',
|
||||
enabled: true,
|
||||
dateFnsLocale: it,
|
||||
},
|
||||
@ -41,6 +46,7 @@ const locales = [
|
||||
name: 'Español',
|
||||
code: 'es',
|
||||
iso: 'es-ES',
|
||||
flag: '🇪🇸',
|
||||
enabled: true,
|
||||
dateFnsLocale: es,
|
||||
},
|
||||
@ -48,6 +54,7 @@ const locales = [
|
||||
name: 'Português',
|
||||
code: 'pt',
|
||||
iso: 'pt-PT',
|
||||
flag: '🇵🇹',
|
||||
enabled: true,
|
||||
dateFnsLocale: pt,
|
||||
},
|
||||
@ -55,6 +62,7 @@ const locales = [
|
||||
name: 'Polski',
|
||||
code: 'pl',
|
||||
iso: 'pl-PL',
|
||||
flag: '🇵🇱',
|
||||
enabled: true,
|
||||
dateFnsLocale: pl,
|
||||
},
|
||||
@ -62,6 +70,7 @@ const locales = [
|
||||
name: 'Русский',
|
||||
code: 'ru',
|
||||
iso: 'ru-RU',
|
||||
flag: '🇷🇺',
|
||||
enabled: true,
|
||||
dateFnsLocale: ru,
|
||||
},
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": null
|
||||
},
|
||||
"more": "Altro",
|
||||
"notifications": {
|
||||
"tooltip": "Notifiche"
|
||||
}
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": null
|
||||
},
|
||||
"more": "Meer",
|
||||
"notifications": {
|
||||
"tooltip": "Notificaties"
|
||||
}
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": null
|
||||
},
|
||||
"more": "Więcej",
|
||||
"notifications": {
|
||||
"tooltip": "Powiadomienia"
|
||||
}
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": null
|
||||
},
|
||||
"more": "Mais",
|
||||
"notifications": {
|
||||
"tooltip": "Notificações"
|
||||
}
|
||||
|
||||
@ -661,6 +661,7 @@
|
||||
"map": {
|
||||
"tooltip": null
|
||||
},
|
||||
"more": "Ещё",
|
||||
"notifications": {
|
||||
"tooltip": "Уведомления"
|
||||
}
|
||||
|
||||
36
webapp/mixins/localeUpdate.js
Normal file
36
webapp/mixins/localeUpdate.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import { updateUserMutation } from '~/graphql/User.js'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
setCurrentUser: 'auth/SET_USER',
|
||||
}),
|
||||
async updateUserLocale() {
|
||||
if (!this.currentUser || !this.currentUser.id) return
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: updateUserMutation(),
|
||||
variables: {
|
||||
id: this.currentUser.id,
|
||||
locale: this.$i18n.locale(),
|
||||
},
|
||||
update: (_store, { data: { UpdateUser } }) => {
|
||||
this.setCurrentUser({
|
||||
...this.currentUser,
|
||||
locale: UpdateUser.locale,
|
||||
})
|
||||
},
|
||||
})
|
||||
this.$toast.success(this.$t('contribution.success'))
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1,7 +1,71 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- create post -->
|
||||
<div :class="POST_ADD_BUTTON_POSITION_TOP ? 'box-add-button-top' : ''">
|
||||
<!-- feed top row: filter (left) + create post (right) -->
|
||||
<div class="feed-top-row">
|
||||
<div v-if="SHOW_CONTENT_FILTER_MASONRY_GRID" class="filterButtonMenu">
|
||||
<os-button
|
||||
class="my-filter-button"
|
||||
v-if="
|
||||
!postsFilter['postType_in'] &&
|
||||
!postsFilter['categories_some'] &&
|
||||
!postsFilter['author'] &&
|
||||
!postsFilter['postsInMyGroups']
|
||||
"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
@click="showFilter = !showFilter"
|
||||
>
|
||||
<template #suffix>
|
||||
<os-icon :icon="filterButtonIcon" />
|
||||
</template>
|
||||
{{ $t('contribution.filterMasonryGrid.noFilter') }}
|
||||
</os-button>
|
||||
|
||||
<header-button
|
||||
v-if="filteredPostTypes.includes('Article')"
|
||||
:title="$t('contribution.filterMasonryGrid.onlyArticles')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetPostType"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="filteredPostTypes.includes('Event')"
|
||||
:title="$t('contribution.filterMasonryGrid.onlyEvents')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetPostType"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="postsFilter['categories_some']"
|
||||
:title="$t('contribution.filterMasonryGrid.myTopics')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetCategories"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="postsFilter['author']"
|
||||
:title="$t('contribution.filterMasonryGrid.myFriends')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetByFollowed"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="postsFilter['postsInMyGroups']"
|
||||
:title="$t('contribution.filterMasonryGrid.myGroups')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetByGroups"
|
||||
/>
|
||||
<div id="my-filter" v-if="showFilter">
|
||||
<div @mouseleave="mouseLeaveFilterMenu">
|
||||
<filter-menu-component @showFilterMenu="showFilterMenu" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<client-only>
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
@ -11,10 +75,7 @@
|
||||
placement: 'left',
|
||||
}"
|
||||
class="post-add-button"
|
||||
:class="[
|
||||
POST_ADD_BUTTON_POSITION_TOP ? 'post-add-button-top' : 'post-add-button-bottom',
|
||||
{ 'hide-filter': hideByScroll },
|
||||
]"
|
||||
:class="{ 'hide-filter': hideByScroll }"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
@ -26,78 +87,12 @@
|
||||
</os-button>
|
||||
</client-only>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="SHOW_CONTENT_FILTER_MASONRY_GRID" class="top-filter-menu">
|
||||
<div class="filterButtonBox">
|
||||
<div class="filterButtonMenu" :class="{ 'hide-filter': hideByScroll }">
|
||||
<os-button
|
||||
class="my-filter-button"
|
||||
v-if="
|
||||
!postsFilter['postType_in'] &&
|
||||
!postsFilter['categories_some'] &&
|
||||
!postsFilter['author'] &&
|
||||
!postsFilter['postsInMyGroups']
|
||||
"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
@click="showFilter = !showFilter"
|
||||
>
|
||||
<template #suffix>
|
||||
<os-icon :icon="filterButtonIcon" />
|
||||
</template>
|
||||
{{ $t('contribution.filterMasonryGrid.noFilter') }}
|
||||
</os-button>
|
||||
|
||||
<header-button
|
||||
v-if="filteredPostTypes.includes('Article')"
|
||||
:title="$t('contribution.filterMasonryGrid.onlyArticles')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetPostType"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="filteredPostTypes.includes('Event')"
|
||||
:title="$t('contribution.filterMasonryGrid.onlyEvents')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetPostType"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="postsFilter['categories_some']"
|
||||
:title="$t('contribution.filterMasonryGrid.myTopics')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetCategories"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="postsFilter['author']"
|
||||
:title="$t('contribution.filterMasonryGrid.myFriends')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetByFollowed"
|
||||
/>
|
||||
|
||||
<header-button
|
||||
v-if="postsFilter['postsInMyGroups']"
|
||||
:title="$t('contribution.filterMasonryGrid.myGroups')"
|
||||
:clickButton="openFilterMenu"
|
||||
:titleRemove="$t('filter-menu.deleteFilter')"
|
||||
:clickRemove="resetByGroups"
|
||||
/>
|
||||
<div id="my-filter" v-if="showFilter">
|
||||
<div @mouseleave="mouseLeaveFilterMenu">
|
||||
<filter-menu-component @showFilterMenu="showFilterMenu" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="hashtag || showDonations" class="newsfeed-controls">
|
||||
<div
|
||||
v-if="hashtag || showDonations"
|
||||
class="newsfeed-controls"
|
||||
:class="{ 'newsfeed-controls--no-filter': !SHOW_CONTENT_FILTER_MASONRY_GRID }"
|
||||
>
|
||||
<div v-if="hashtag">
|
||||
<hashtags-filter :hashtag="hashtag" @clearSearch="clearSearch" />
|
||||
</div>
|
||||
@ -171,7 +166,6 @@ import { filterPosts } from '~/graphql/PostQuery.js'
|
||||
import UpdateQuery from '~/components/utils/UpdateQuery'
|
||||
import FilterMenuComponent from '~/components/FilterMenu/FilterMenuComponent'
|
||||
import { SHOW_CONTENT_FILTER_MASONRY_GRID } from '~/constants/filter.js'
|
||||
import { POST_ADD_BUTTON_POSITION_TOP } from '~/constants/posts.js'
|
||||
import GetCategories from '~/mixins/getCategoriesMixin.js'
|
||||
|
||||
export default {
|
||||
@ -206,7 +200,6 @@ export default {
|
||||
pageSize: 12,
|
||||
hashtag,
|
||||
SHOW_CONTENT_FILTER_MASONRY_GRID,
|
||||
POST_ADD_BUTTON_POSITION_TOP,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -364,65 +357,59 @@ export default {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.box-add-button-top {
|
||||
float: right;
|
||||
.feed-top-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.post-add-button-bottom {
|
||||
.filterButtonMenu {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.post-add-button {
|
||||
height: 54px !important;
|
||||
width: 54px !important;
|
||||
min-height: 54px !important;
|
||||
min-width: 54px !important;
|
||||
font-size: 26px !important;
|
||||
box-shadow: $box-shadow-x-large !important;
|
||||
z-index: $z-index-sticky-float !important;
|
||||
position: fixed !important;
|
||||
bottom: -5px !important;
|
||||
left: 98vw !important;
|
||||
transform: translate(-120%, -120%) !important;
|
||||
box-shadow: $box-shadow-x-large !important;
|
||||
right: max(20px, calc((100vw - $container-max-width-x-large) / 2 + 48px)) !important;
|
||||
top: 81px !important;
|
||||
transition: top 0.3s ease !important;
|
||||
}
|
||||
|
||||
.post-add-button-top {
|
||||
height: 54px !important;
|
||||
width: 54px !important;
|
||||
min-height: 54px !important;
|
||||
min-width: 54px !important;
|
||||
font-size: 26px !important;
|
||||
z-index: $z-index-sticky-float !important;
|
||||
position: fixed !important;
|
||||
top: 80px !important;
|
||||
box-shadow: $box-shadow-x-large !important;
|
||||
.main-navigation:has(.hide-navbar) ~ .ds-container .post-add-button {
|
||||
top: 20px !important;
|
||||
}
|
||||
|
||||
.top-filter-menu {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.top-info-bar,
|
||||
.top-filter-menu {
|
||||
.top-info-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.filterButtonMenu {
|
||||
width: 95%;
|
||||
position: fixed;
|
||||
z-index: $z-index-sticky;
|
||||
margin-top: -45px;
|
||||
padding: 30px 0px 20px 0px;
|
||||
background-color: #f5f4f6;
|
||||
}
|
||||
.newsfeed-controls {
|
||||
margin-top: 46px;
|
||||
margin-top: 8px;
|
||||
|
||||
&.newsfeed-controls--no-filter {
|
||||
margin-top: -16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.top-info-bar {
|
||||
padding-right: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.main-container .grid-column-helper {
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 357px)) !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 656px) {
|
||||
.filterButtonMenu {
|
||||
margin-top: -50px;
|
||||
}
|
||||
}
|
||||
#my-filter {
|
||||
max-width: 1028px;
|
||||
background-color: white;
|
||||
@ -433,7 +420,7 @@ export default {
|
||||
z-index: $z-index-page-submenu;
|
||||
}
|
||||
.grid-margin-top {
|
||||
margin-top: 26px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
@media screen and (min-height: 401px) {
|
||||
#my-filter {
|
||||
@ -475,25 +462,9 @@ export default {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1200px) {
|
||||
.box-add-button-top {
|
||||
padding-right: 40px;
|
||||
}
|
||||
.post-add-button-top {
|
||||
height: 44px !important;
|
||||
width: 44px !important;
|
||||
min-height: 44px !important;
|
||||
min-width: 44px !important;
|
||||
font-size: 23px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 650px) {
|
||||
// .top-filter-menu{
|
||||
// margin-top: 24px;
|
||||
// }
|
||||
|
||||
.newsfeed-controls {
|
||||
margin-top: 32px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user