extraButtons implementation

This commit is contained in:
Henry Jameson 2025-01-11 20:02:53 +02:00
parent 08f8b975b6
commit eb7406c663
5 changed files with 182 additions and 51 deletions

View file

@ -166,26 +166,24 @@
</div>
</template>
<template #trigger>
<span class="button-unstyled popover-trigger">
<FALayers class="fa-old-padding-layer">
<FAIcon
class="fa-scale-110 "
icon="ellipsis-h"
/>
<FAIcon
v-show="!expanded"
class="focus-marker"
transform="shrink-6 up-8 right-16"
icon="plus"
/>
<FAIcon
v-show="expanded"
class="focus-marker"
transform="shrink-6 up-8 right-16"
icon="times"
/>
</FALayers>
</span>
<FALayers class="fa-old-padding-layer">
<FAIcon
class="fa-scale-110 "
icon="ellipsis-h"
/>
<FAIcon
v-show="!expanded"
class="focus-marker"
transform="shrink-6 up-8 right-16"
icon="plus"
/>
<FAIcon
v-show="expanded"
class="focus-marker"
transform="shrink-6 up-8 right-16"
icon="times"
/>
</FALayers>
<teleport to="#modal">
<ConfirmModal
v-if="showingDeleteDialog"

View file

@ -264,13 +264,11 @@
.status-actions {
position: relative;
width: 100%;
display: flex;
display: grid;
grid-template-columns: 1fr;
grid-auto-columns: 1fr;
grid-auto-flow: column;
margin-top: var(--status-margin);
> * {
max-width: 4em;
flex: 1;
}
}
.muted {

View file

@ -1,17 +1,52 @@
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { mapState } from 'vuex'
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
import Popover from 'src/components/popover/popover.vue'
import genRandomSeed from 'src/services/random_seed/random_seed.service.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faRetweet,
faPlus,
faMinus,
faCheck
faCheck,
faTimes,
faReply,
faRetweet,
faStar,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
} from '@fortawesome/free-solid-svg-icons'
import {
faStar as faStarRegular
} from '@fortawesome/free-regular-svg-icons'
library.add(
faRetweet,
faPlus,
faMinus,
faCheck
faCheck,
faTimes,
faReply,
faRetweet,
faStar,
faStarRegular,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
)
const PRIVATE_SCOPES = new Set(['private', 'direct'])
const PUBLIC_SCOPES = new Set(['public', 'unlisted'])
@ -27,6 +62,8 @@ const BUTTONS = [{
anon: true,
anonLink: true,
toggleable: true,
closeIndicator: 'times',
activeIndicator: 'none',
action ({ emit }) {
emit('toggleReplying')
return Promise.resolve()
@ -230,7 +267,10 @@ const BUTTONS = [{
}
}].map(button => {
return Object.fromEntries(
Object.entries(button).map(([k, v]) => [k, typeof v === 'function' ? v : () => v])
Object.entries(button).map(([k, v]) => [
k,
(typeof v === 'function' || k === 'name') ? v : () => v
])
)
})
@ -243,15 +283,26 @@ const StatusActionButtons = {
currentConfirmTitle: '',
currentConfirmOkText: '',
currentConfirmCancelText: '',
currentConfirmAction: () => {}
currentConfirmAction: () => {},
randomSeed: genRandomSeed()
}
},
components: {
Popover,
ConfirmModal
},
computed: {
...mapState({
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedStatusActions)
}),
buttons () {
return BUTTONS.filter(x => x.if(this.funcArg))
return BUTTONS.filter(x => x.if ? x.if(this.funcArg) : true)
},
quickButtons () {
return this.buttons.filter(x => this.pinnedItems.has(x.name))
},
extraButtons () {
return this.buttons.filter(x => !this.pinnedItems.has(x.name))
},
funcArg () {
return {
@ -265,6 +316,15 @@ const StatusActionButtons = {
currentUser: this.$store.state.users.currentUser,
loggedIn: !!this.$store.state.users.currentUser
}
},
triggerAttrs () {
return {
title: this.$t('status.more_actions'),
id: `popup-trigger-${this.randomSeed}`,
'aria-controls': `popup-menu-${this.randomSeed}`,
'aria-expanded': this.expanded,
'aria-haspopup': 'menu'
}
}
},
methods: {
@ -272,7 +332,7 @@ const StatusActionButtons = {
this.doActionReal(button)
},
doActionReal (button) {
button.action(this.funcArg(button))
button.action(this.funcArg)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
@ -287,8 +347,8 @@ const StatusActionButtons = {
},
getClass (button) {
return {
[button.name() + '-button']: true,
'-active': button.active?.(this.funcArg()),
[button.name + '-button']: true,
'-active': button.active?.(this.funcArg),
'-interactive': !!this.$store.state.users.currentUser
}
},

View file

@ -3,7 +3,7 @@
<span class="quick-action-buttons">
<span
class="quick-action"
v-for="button in buttons"
v-for="button in quickButtons"
:key="button.name"
>
<component
@ -23,16 +23,22 @@
/>
<template v-if="button.toggleable?.(funcArg) && button.active">
<FAIcon
v-show="!button.active(funcArg)"
class="focus-marker"
transform="shrink-6 up-9 right-17"
icon="plus"
v-if="button.active(funcArg)"
class="active-marker"
transform="shrink-6 up-9 right-12"
:icon="button.activeIndicator?.(funcArg) || 'check'"
/>
<FAIcon
v-show="button.active(funcArg)"
v-if="!button.active(funcArg)"
class="focus-marker"
transform="shrink-6 up-9 right-17"
icon="times"
transform="shrink-6 up-9 right-12"
:icon="button.openIndicator?.(funcArg) || 'plus'"
/>
<FAIcon
v-else
class="focus-marker"
transform="shrink-6 up-9 right-12"
:icon="button.closeIndicator?.(funcArg) || 'minus'"
/>
</template>
</FALayers>
@ -44,7 +50,55 @@
{{ button.counter?.(funcArg) }}
</span>
</span>
<Popover
trigger="click"
:trigger-attrs="triggerAttrs"
:tabindex="0"
placement="top"
:offset="{ y: 5 }"
:bound-to="{ x: 'container2' }"
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>
</template>
<template #content="{close}">
<div
:id="`popup-menu-${randomSeed}`"
class="dropdown-menu"
role="menu"
>
<component
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"
>
<FAIcon
class="fa-scale-110"
:icon="button.icon(funcArg)"
/><span>{{ $t(button.label(funcArg)) }}</span>
</component>
</div>
</template>
</Popover>
</span>
<teleport to="#modal">
<confirm-modal
v-if="showingConfirmDialog"
@ -66,11 +120,7 @@
@import "../../mixins";
.StatusActionButtons {
width: 100%;
.quick-action-buttons {
position: relative;
width: 100%;
display: grid;
grid-template-columns: 1fr;
grid-auto-flow: column;
@ -121,12 +171,20 @@
.focus-marker {
visibility: hidden;
}
.active-marker {
visibility: visible;
}
}
@include focused-style {
.focus-marker {
visibility: visible;
}
.active-marker {
visibility: hidden;
}
}
}
}

View file

@ -1,5 +1,17 @@
import { toRaw } from 'vue'
import { isEqual, cloneDeep, set, get, clamp, flatten, groupBy, findLastIndex, takeRight, uniqWith } from 'lodash'
import {
isEqual,
cloneDeep,
set,
get,
clamp,
flatten,
groupBy,
findLastIndex,
takeRight,
uniqWith,
merge
} from 'lodash'
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
export const VERSION = 1
@ -26,6 +38,7 @@ export const defaultState = {
collapseNav: false
},
collections: {
pinnedStatusActions: ['reply', 'retweet', 'favorite', 'emoji'],
pinnedNavItems: ['home', 'dms', 'chats']
}
},
@ -110,7 +123,11 @@ export const _getRecentData = (cache, live) => {
console.debug('Both sources are invalid, start from scratch')
result.needUpload = true
}
return result
result.recent = merge(defaultState, result.recent)
result.stale = merge(defaultState, result.stale)
return merge(defaultState, result)
}
export const _getAllFlags = (recent, stale) => {