logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/
commit: 96fd7f91c43035eed918d08fe887f145751f7570
parent eb7406c6635e476f633163bbfbcff26e6cb1964d
Author: Henry Jameson <me@hjkos.com>
Date:   Sun, 12 Jan 2025 01:46:10 +0200

more work + dropdown items overhaul

Diffstat:

Asrc/components/popover/popover.scss105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/popover/popover.vue99+------------------------------------------------------------------------------
Msrc/components/post_status_form/post_status_form.vue2+-
Msrc/components/status_action_buttons/status_action_buttons.js30++++++++++++++++++++++++------
Asrc/components/status_action_buttons/status_action_buttons.scss112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/status_action_buttons/status_action_buttons.vue171++++++++++++++++++++++++++++++-------------------------------------------------
Msrc/components/user_list_menu/user_list_menu.js5+++++
Msrc/components/user_list_menu/user_list_menu.vue17+++++++++--------
Msrc/modules/serverSideStorage.js26++++++++++++++++++--------
9 files changed, 340 insertions(+), 227 deletions(-)

diff --git a/src/components/popover/popover.scss b/src/components/popover/popover.scss @@ -0,0 +1,105 @@ +.popover-trigger-button { + display: inline-block; +} + +.popover { + z-index: var(--ZI_popover_override, var(--ZI_popovers)); + position: fixed; + min-width: 0; + max-width: calc(100vw - 20px); + box-shadow: var(--shadow); +} + +.popover-default { + &::after { + content: ""; + position: absolute; + top: -1px; + bottom: -1px; + left: -1px; + right: -1px; + z-index: -1px; + box-shadow: var(--shadow); + pointer-events: none; + } + + border-radius: var(--roundness); + border-color: var(--border); + border-style: solid; + border-width: 1px; + background-color: var(--background); +} + +.dropdown-menu { + display: block; + padding: 0; + font-size: 1em; + text-align: left; + list-style: none; + max-width: 100vw; + z-index: var(--ZI_popover_override, var(--ZI_popovers)); + white-space: nowrap; + background-color: var(--background); + + .dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid var(--border); + } + + .dropdown-item:not(button, a) { + padding: 0; + } + + a.dropdown-item, + button.dropdown-item, + .dropdown-item:not(button, a) > button:first-child, + .dropdown-item:not(button, a) > a:first-child { + box-sizing: border-box; + padding: var(--__horizontal-gap) var(--__horizontal-gap); + grid-gap: var(--__horizontal-gap); + display: grid; + border: none; + align-items: center; + grid-template-columns: 1fr var(--__line-height); + grid-auto-flow: column; + grid-auto-columns: auto; + cursor: pointer; + + .menu-checkbox { + display: inline-block; + vertical-align: middle; + min-width: calc(var(--__line-height) + 1px); + max-width: calc(var(--__line-height) + 1px); + min-height: calc(var(--__line-height) + 1px); + max-height: calc(var(--__line-height) + 1px); + line-height: var(--__line-height); + text-align: center; + border-radius: 0; + box-shadow: var(--shadow); + margin-right: var(--__horizontal-gap); + + &.menu-checkbox-checked::after { + font-size: 1.25em; + content: "✓"; + } + + &.-radio { + border-radius: 9999px; + + &.menu-checkbox-checked::after { + font-size: 2em; + content: "•"; + } + } + } + } + + a.dropdown-item-icon, + button.dropdown-item-icon, + .dropdown-item-icon:not(button, a) > button:first-child, + .dropdown-item-icon:not(button, a) > a:first-child { + grid-template-columns: var(--__line-height) 1fr; + } +} diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue @@ -41,101 +41,4 @@ <script src="./popover.js" /> -<style lang="scss"> -.popover-trigger-button { - display: inline-block; -} - -.popover { - z-index: var(--ZI_popover_override, var(--ZI_popovers)); - position: fixed; - min-width: 0; - max-width: calc(100vw - 20px); - box-shadow: var(--shadow); -} - -.popover-default { - &::after { - content: ""; - position: absolute; - top: -1px; - bottom: -1px; - left: -1px; - right: -1px; - z-index: -1px; - box-shadow: var(--shadow); - pointer-events: none; - } - - border-radius: var(--roundness); - border-color: var(--border); - border-style: solid; - border-width: 1px; - background-color: var(--background); -} - -.dropdown-menu { - display: block; - padding: 0; - font-size: 1em; - text-align: left; - list-style: none; - max-width: 100vw; - z-index: var(--ZI_popover_override, var(--ZI_popovers)); - white-space: nowrap; - background-color: var(--background); - - .dropdown-divider { - height: 0; - margin: 0.5rem 0; - overflow: hidden; - border-top: 1px solid var(--border); - } - - .dropdown-item { - border: none; - - &-icon { - svg { - width: var(--__line-height); - margin-right: var(--__horizontal-gap); - } - } - - &.-has-submenu { - .chevron-icon { - margin-right: 0.25rem; - margin-left: 2rem; - } - } - - .menu-checkbox { - display: inline-block; - vertical-align: middle; - min-width: calc(var(--__line-height) + 1px); - max-width: calc(var(--__line-height) + 1px); - min-height: calc(var(--__line-height) + 1px); - max-height: calc(var(--__line-height) + 1px); - line-height: var(--__line-height); - text-align: center; - border-radius: 0; - box-shadow: var(--shadow); - margin-right: var(--__horizontal-gap); - - &.menu-checkbox-checked::after { - font-size: 1.25em; - content: "✓"; - } - - &.-radio { - border-radius: 9999px; - - &.menu-checkbox-checked::after { - font-size: 2em; - content: "•"; - } - } - } - } -} -</style> +<style src="./popover.scss" lang="scss"></style> diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue @@ -337,7 +337,7 @@ > <button v-if="!disableDraft" - class="menu-item dropdown-item dropdown-item-icon" + class="menu-item dropdown-item" role="menu" :disabled="!safeToSaveDraft" :class="{ disabled: !safeToSaveDraft }" diff --git a/src/components/status_action_buttons/status_action_buttons.js b/src/components/status_action_buttons/status_action_buttons.js @@ -113,11 +113,11 @@ const BUTTONS = [{ counter: ({ status }) => status.fave_num, anonLink: true, toggleable: true, - action ({ status, store }) { + action ({ status, dispatch }) { if (!status.favorited) { - return store.dispatch('favorite', { id: status.id }) + return dispatch('favorite', { id: status.id }) } else { - return store.dispatch('unfavorite', { id: status.id }) + return dispatch('unfavorite', { id: status.id }) } } }, { @@ -125,7 +125,7 @@ const BUTTONS = [{ // EMOJI REACTIONS // ========= name: 'emoji', - label: 'tool_lip.add_reaction', + label: 'tool_tip.add_reaction', icon: ['far', 'smile-beam'], anonLink: true, popover: 'emoji-picker' @@ -279,6 +279,7 @@ const StatusActionButtons = { emits: ['toggleReplying'], data () { return { + showPin: true, showingConfirmDialog: false, currentConfirmTitle: '', currentConfirmOkText: '', @@ -304,6 +305,9 @@ const StatusActionButtons = { extraButtons () { return this.buttons.filter(x => !this.pinnedItems.has(x.name)) }, + currentUser () { + return this.$store.state.users.currentUser + }, funcArg () { return { status: this.status, @@ -313,8 +317,8 @@ const StatusActionButtons = { state: this.$store.state, getters: this.$store.getters, router: this.$router, - currentUser: this.$store.state.users.currentUser, - loggedIn: !!this.$store.state.users.currentUser + currentUser: this.currentUser, + loggedIn: !!this.currentUser } }, triggerAttrs () { @@ -336,6 +340,18 @@ const StatusActionButtons = { .then(() => this.$emit('onSuccess')) .catch(err => this.$emit('onError', err.error.error)) }, + isPinned (button) { + console.log(this.pinnedItems, button.name) + return this.pinnedItems.has(button.name) + }, + unpin (button) { + this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedStatusActions', value: button.name }) + this.$store.dispatch('pushServerSideStorage') + }, + pin (button) { + this.$store.commit('addCollectionPreference', { path: 'collections.pinnedStatusActions', value: button.name }) + this.$store.dispatch('pushServerSideStorage') + }, component (button) { if (!this.$store.state.users.currentUser && button.anonLink) { return 'a' @@ -348,6 +364,8 @@ const StatusActionButtons = { getClass (button) { return { [button.name + '-button']: true, + '-pin-edit': this.showPin, + '-dropdown': button.dropdown?.(), '-active': button.active?.(this.funcArg), '-interactive': !!this.$store.state.users.currentUser } diff --git a/src/components/status_action_buttons/status_action_buttons.scss b/src/components/status_action_buttons/status_action_buttons.scss @@ -0,0 +1,112 @@ +@import "../../mixins"; + +.StatusActionButtons { + .quick-action-buttons { + display: grid; + grid-template-columns: 1fr; + grid-auto-flow: column; + grid-auto-columns: 1fr; + grid-gap: 1em; + margin-top: var(--status-margin); + + .quick-action { + display: grid; + grid-template-columns: 1fr auto; + + .action-button { + display: grid; + grid-template-columns: max-content auto; + grid-gap: 1em; + align-items: center; + } + + &.-pin { + margin: calc(-2px - 0.25em); + padding: 0.25em; + border: 2px dashed var(--icon); + border-radius: var(--roundness); + } + + &.-pin, + &.-dropdown { + grid-template-columns: 1fr max-content; + } + + .reply-button { + &:hover, + &.-active { + .svg-inline--fa { + color: var(--cBlue); + } + } + } + + .retweet-button { + &:hover, + &.-active { + .svg-inline--fa { + color: var(--cGreen); + } + } + } + + .favorite-button { + &:hover, + &.-active { + .svg-inline--fa { + color: var(--cOrange); + } + } + } + + > button, + > a { + padding: 0.5em; + margin: -0.5em; + + @include unfocused-style { + .focus-marker { + visibility: hidden; + } + + .active-marker { + visibility: visible; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + + .active-marker { + visibility: hidden; + } + } + } + } + } +} +// popover +.extra-action-buttons { + .extra-action { + display: grid; + grid-template-columns: 1fr; + grid-auto-flow: column; + grid-auto-columns: auto; + grid-gap: 1em; + + .pin-action-button { + margin: 0; + padding: var(--__horizontal-gap) var(--__horizontal-gap); + + &::before { + content: ""; + height: 1em; + width: 1px; + border-left: 1px solid var(--icon); + margin-right: 0.5em; + } + } + } +} diff --git a/src/components/status_action_buttons/status_action_buttons.vue b/src/components/status_action_buttons/status_action_buttons.vue @@ -3,12 +3,13 @@ <span class="quick-action-buttons"> <span class="quick-action" + :class="{ '-pin': showPin, '-toggle': button.dropdown?.() }" v-for="button in quickButtons" :key="button.name" > <component :is="component(button)" - class="button-unstyled" + class="button-unstyled action-button" :class="getClass(button)" role="button" :tabindex="0" @@ -16,7 +17,7 @@ @click.stop="component(button) === 'button' && doAction(button)" :href="component(button) == 'a' ? button.link?.(funcArg) || getRemoteInteractionLink : undefined" > - <FALayers class="fa-old-padding"> + <FALayers> <FAIcon class="fa-scale-110" :icon="button.icon(funcArg)" @@ -42,13 +43,28 @@ /> </template> </FALayers> + <span + class="action-counter" + v-if="button.counter?.(funcArg) > 0" + > + {{ button.counter?.(funcArg) }} + </span> </component> - <span - class="action-counter" - v-if="button.counter?.(funcArg) > 0" + <button + v-if="showPin && currentUser" + type="button" + class="button-unstyled pin-action-button" + :title="$t('general.unpin')" + :aria-pressed="true" + @click.stop.prevent="unpin(button)" > - {{ button.counter?.(funcArg) }} - </span> + <FAIcon + v-if="showPin && currentUser" + fixed-width + class="fa-scale-110" + icon="thumbtack" + /> + </button> </span> <Popover trigger="click" @@ -56,44 +72,61 @@ :tabindex="0" placement="top" :offset="{ y: 5 }" - :bound-to="{ x: 'container2' }" + :bound-to="{ x: 'container' }" remove-padding @show="onShow" @close="onClose" > <template #trigger> - <span class="popover-trigger"> - <FALayers class="fa-old-padding-layer"> - <FAIcon - class="fa-scale-110 " - icon="ellipsis-h" - /> - </FALayers> - </span> + <FAIcon + class="fa-scale-110 " + icon="ellipsis-h" + /> </template> <template #content="{close}"> <div :id="`popup-menu-${randomSeed}`" - class="dropdown-menu" + class="dropdown-menu extra-action-buttons" role="menu" > - <component + <div v-for="button in extraButtons" :key="button.name" - :is="component(button)" - class="menu-item dropdown-item dropdown-item-icon" - role="menuitem" - :class="getClass(button)" - :tabindex="0" - @click.stop="component(button) === 'button' && doAction(button)" - @click="close" - :href="component(button) == 'a' ? button.link?.(funcArg) || getRemoteInteractionLink : undefined" + class="menu-item dropdown-item extra-action dropdown-item-icon" > - <FAIcon - class="fa-scale-110" - :icon="button.icon(funcArg)" - /><span>{{ $t(button.label(funcArg)) }}</span> - </component> + <component + :is="component(button)" + class="main-button" + role="menuitem" + :class="getClass(button)" + :tabindex="0" + @click.stop="component(button) === 'button' && doAction(button)" + @click="close" + :href="component(button) == 'a' ? button.link?.(funcArg) || getRemoteInteractionLink : undefined" + > + <FAIcon + class="fa-scale-110" + fixed-width + :icon="button.icon(funcArg)" + /><span>{{ $t(button.label(funcArg)) }}</span> + </component> + <button + v-if="showPin && currentUser" + type="button" + class="button-unstyled pin-action-button" + :title="$t('general.pin' )" + :aria-pressed="false" + @click.stop.prevent="pin(button)" + > + <FAIcon + v-if="showPin && currentUser" + fixed-width + class="fa-scale-110 veryfaint" + transform="rotate-45" + icon="thumbtack" + /> + </button> + </div> </div> </template> </Popover> @@ -116,78 +149,4 @@ <script src="./status_action_buttons.js"></script> -<style lang="scss"> -@import "../../mixins"; - -.StatusActionButtons { - .quick-action-buttons { - display: grid; - grid-template-columns: 1fr; - grid-auto-flow: column; - grid-auto-columns: 1fr; - grid-gap: 1em; - margin-top: var(--status-margin); - - .quick-action { - display: grid; - grid-template-columns: auto 1fr; - grid-gap: 0.5em; - max-width: 4em; - - .reply-button { - &:hover, - &.-active { - .svg-inline--fa { - color: var(--cBlue); - } - } - } - - .retweet-button { - &:hover, - &.-active { - .svg-inline--fa { - color: var(--cGreen); - } - } - } - - .favorite-button { - &:hover, - &.-active { - .svg-inline--fa { - color: var(--cOrange); - } - } - } - - > button, - > a { - padding: 0.5em; - margin: -0.5em; - } - - @include unfocused-style { - .focus-marker { - visibility: hidden; - } - - .active-marker { - visibility: visible; - } - } - - @include focused-style { - .focus-marker { - visibility: visible; - } - - .active-marker { - visibility: hidden; - } - } - } - } -} - -</style> +<style lang="scss" src="./status_action_buttons.scss"></style> diff --git a/src/components/user_list_menu/user_list_menu.js b/src/components/user_list_menu/user_list_menu.js @@ -34,6 +34,11 @@ const UserListMenu = { ...list, inList: this.inListsSet.has(list.id) })) + }, + triggerAttrs () { + return { + class: 'menu-item dropdown-item -has-submenu' + } } }, methods: { diff --git a/src/components/user_list_menu/user_list_menu.vue b/src/components/user_list_menu/user_list_menu.vue @@ -3,6 +3,7 @@ <Popover trigger="hover" placement="left" + :trigger-attrs="triggerAttrs" remove-padding > <template #content> @@ -10,7 +11,7 @@ <button v-for="list in lists" :key="list.id" - class="menu-item dropdown-item" + class="menu-item dropdown-item dropdown-item-icon" @click="toggleList(list.id)" > <span @@ -22,14 +23,14 @@ </div> </template> <template #trigger> - <button class="menu-item dropdown-item -has-submenu"> + <span> {{ $t('lists.manage_lists') }} - <FAIcon - class="chevron-icon" - size="lg" - icon="chevron-right" - /> - </button> + </span> + <FAIcon + class="chevron-icon" + size="lg" + icon="chevron-right" + /> </template> </Popover> </div> diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js @@ -9,8 +9,7 @@ import { groupBy, findLastIndex, takeRight, - uniqWith, - merge + uniqWith } from 'lodash' import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' @@ -124,10 +123,21 @@ export const _getRecentData = (cache, live) => { result.needUpload = true } - result.recent = merge(defaultState, result.recent) - result.stale = merge(defaultState, result.stale) + const merge = (a, b) => ({ + needUpload: b.needUpload ?? a.needUpload, + prefsStorage: { + ...a.prefsStorage, + ...b.prefsStorage + }, + flagStorage: { + ...a.flagStorage, + ...b.flagStorage + } + }) + result.recent = result.recent && merge(defaultState, result.recent) + result.stale = result.stale && merge(defaultState, result.stale) - return merge(defaultState, result) + return result } export const _getAllFlags = (recent, stale) => { @@ -309,7 +319,7 @@ export const mutations = { cache = _doMigrations(cache) - let { recent, stale, needsUpload } = _getRecentData(cache, live) + let { recent, stale, needUpload } = _getRecentData(cache, live) const userNew = userData.created_at > NEW_USER_DATE const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage @@ -323,7 +333,7 @@ export const mutations = { }) } - if (!needsUpload && recent && stale) { + if (!needUpload && recent && stale) { console.debug('Checking if data needs merging...') // discarding timestamps and versions const { _timestamp: _0, _version: _1, ...recentData } = recent @@ -352,7 +362,7 @@ export const mutations = { recent.flagStorage = { ...flagsTemplate, ...totalFlags } recent.prefsStorage = { ...defaultState.prefsStorage, ...totalPrefs } - state.dirty = dirty || needsUpload + state.dirty = dirty || needUpload state.cache = recent // set local timestamp to smaller one if we don't have any changes if (stale && recent && !state.dirty) {