abstraction, made popover optional, initial markup for better mute options

This commit is contained in:
Henry Jameson 2025-01-14 01:42:36 +02:00
parent 5a085d8e36
commit b831f34c06
9 changed files with 248 additions and 265 deletions

View file

@ -0,0 +1,83 @@
import StatusBookmarkFolderMenu from 'src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.vue'
import Popover from 'src/components/popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
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(
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
faReply,
faRetweet,
faStar,
faStarRegular,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
)
export default {
props: [
'button',
'extra',
'status',
'funcArg',
'animationState',
'getClass',
'getComponent',
'doAction',
'close'
],
components: {
StatusBookmarkFolderMenu,
Popover
},
computed: {
buttonClass () {
if (!this.extra) console.log(this.button.name)
return [
this.button.name + '-button',
{
'main-button': this.extra,
'button-unstyled': !this.extra,
'-extra': this.extra,
'-quick': !this.extra,
'-active': this.button.active?.(this.funcArg),
disabled: this.button.interactive ? !this.button.interactive(this.funcArg) : false
}
]
}
}
}

View file

@ -2,6 +2,12 @@
/* stylelint-disable declaration-no-important */
.action-button {
display: grid;
&.-with-extra {
grid-template-columns: 1fr calc(var(--__line-height) + 2 * var(--__horizontal-gap));
}
&.-quick {
display: grid;
grid-template-columns: max-content auto;

View file

@ -1,8 +1,11 @@
<template>
<div>
<div
class="action-button"
:class="{ '-with-extra': button.name === 'bookmark' }"
>
<component
:is="getComponent(button)"
class="main-button action-button"
class="action-button-inner"
:class="buttonClass"
role="menuitem"
:tabindex="0"
@ -38,90 +41,48 @@
:icon="button.closeIndicator?.(funcArg) || 'minus'"
/>
</template>
</FALayers><span>{{ $t(button.label(funcArg)) }}</span>
</FALayers>
<span
v-if="extra"
class="action-label"
>
{{ $t(button.label(funcArg)) }}
</span>
<span
v-if="!extra && button.counter?.(funcArg) > 0"
class="action-counter"
>
{{ button.counter?.(funcArg) }}
</span>
<FAIcon
v-if="button.name === 'mute'"
v-if="button.dropdown?.()"
class="chevron-icon"
size="lg"
icon="chevron-right"
fixed-width
/>
</component>
<Popover
trigger="hover"
placement="right"
:trigger-attrs="{ class: 'extra-button' }"
v-if="button.name === 'bookmark'"
>
<template #trigger>
<FAIcon
class="chevron-icon"
size="lg"
icon="chevron-right"
fixed-width
/>
</template>
<template #content>
<StatusBookmarkFolderMenu v-if="button.name === 'bookmark'" :status="$attrs.status" />
</template>
</Popover>
</div>
</template>
<script>
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
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(
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
faReply,
faRetweet,
faStar,
faStarRegular,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
)
export default {
props: [
'button',
'extra',
'status',
'funcArg',
'animationState',
'getClass',
'getComponent',
'doAction',
'close'
],
computed: {
buttonClass () {
return {
[this.button.name + '-button']: true,
'-extra': this.extra,
'-quick': !this.extra,
'-active': this.button.active?.(this.funcArg),
disabled: this.button.interactive ? !this.button.interactive(this.funcArg) : false
}
}
}
}
</script>
<script src="./action_button.js"/>
<style lang="scss" src="./action_button.scss"/>

View file

@ -0,0 +1,23 @@
import ActionButton from './action_button.vue'
import Popover from 'src/components/popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faUser,
faGlobe,
faFolderTree
} from '@fortawesome/free-solid-svg-icons'
library.add(
faUser,
faGlobe,
faFolderTree
)
export default {
components: {
ActionButton,
Popover
},
props: ['button']
}

View file

@ -0,0 +1,58 @@
<template>
<Popover
trigger="hover"
placement="right"
v-if="button.dropdown?.()"
>
<template #trigger>
{{ props }}
<ActionButton
:button="button"
v-bind="$attrs"
/>
</template>
<template #content>
<div
v-if="button.name === 'mute'"
class="dropdown-menu"
:id="`popup-menu-${randomSeed}`"
role="menu"
>
<div class="menu-item dropdown-item extra-action -icon">
<button
class="main-button"
@click="() => {}"
>
<FAIcon icon="user" fixed-width />
{{ $t('status.mute_user') }}
</button>
</div>
<div class="menu-item dropdown-item extra-action -icon">
<button
class="main-button"
@click="() => {}"
>
<FAIcon icon="folder-tree" fixed-width />
{{ $t('status.mute_conversation') }}
</button>
</div>
<div class="menu-item dropdown-item extra-action -icon">
<button
class="main-button"
@click="() => {}"
>
<FAIcon icon="globe" fixed-width />
{{ $t('status.mute_domain') }}
</button>
</div>
</div>
</template>
</Popover>
<ActionButton
v-else
:button="button"
v-bind="$attrs"
/>
</template>
<script src="./action_button_container.js"/>

View file

@ -1,8 +1,7 @@
import { mapState } from 'vuex'
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
import ActionButton from './action_button.vue'
import StatusBookmarkFolderMenu from 'src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.vue'
import ActionButtonContainer from './action_button_container.vue'
import Popover from 'src/components/popover/popover.vue'
import genRandomSeed from 'src/services/random_seed/random_seed.service.js'
@ -136,22 +135,21 @@ const BUTTONS = [{
popover: 'emoji-picker'
}, {
// =========
// MUTE CONVERSATION, my beloved
// MUTE
// =========
name: 'mute',
icon: 'eye-slash',
label: ({ status }) => status.thread_muted
? 'status.unmute_conversation'
: 'status.mute_conversation',
label: 'status.mute_ellipsis',
if: ({ loggedIn }) => loggedIn,
toggleable: true,
action ({ status, dispatch, emit }) {
if (status.thread_muted) {
return dispatch('unmuteConversation', { id: status.id })
} else {
return dispatch('muteConversation', { id: status.id })
}
}
dropdown: true
// action ({ status, dispatch, emit }) {
// if (status.thread_muted) {
// return dispatch('unmuteConversation', { id: status.id })
// } else {
// return dispatch('muteConversation', { id: status.id })
// }
// }
}, {
// =========
// PIN STATUS
@ -287,6 +285,7 @@ const StatusActionButtons = {
emits: ['toggleReplying'],
data () {
return {
Popover,
animationState: {
retweet: false,
favorite: false
@ -303,8 +302,7 @@ const StatusActionButtons = {
components: {
Popover,
ConfirmModal,
ActionButton,
StatusBookmarkFolderMenu
ActionButtonContainer
},
computed: {
...mapState({

View file

@ -8,84 +8,12 @@
grid-auto-columns: 1fr;
grid-gap: 1em;
margin-top: var(--status-margin);
.quick-action {
display: grid;
grid-template-columns: 1fr auto;
&.-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:not(.disabled) {
&:hover,
&.-active {
.svg-inline--fa {
color: var(--cBlue);
}
}
}
.retweet-button:not(.disabled) {
&:hover,
&.-active {
.svg-inline--fa {
color: var(--cGreen);
}
}
}
.favorite-button:not(.disabled) {
&: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
/* stylelint-disable no-descending-specificity */
.extra-action-buttons {
.extra-action {
display: grid;
grid-template-columns: 1fr;
grid-auto-flow: column;
grid-auto-columns: auto;
grid-gap: 1em;

View file

@ -7,50 +7,17 @@
v-for="button in quickButtons"
:key="button.name"
>
<component
:is="getComponent(button)"
class="button-unstyled action-button"
:class="getClass(button)"
:disabled="getClass(button).disabled"
role="button"
:tabindex="0"
:title="$t(button.label(funcArg))"
@click.stop="getComponent(button) === 'button' && doAction(button)"
:href="getComponent(button) == 'a' ? button.link?.(funcArg) || getRemoteInteractionLink : undefined"
>
<FALayers>
<FAIcon
class="fa-scale-110"
:icon="button.icon(funcArg)"
/>
<template v-if="!getClass(button).disabled && button.toggleable?.(funcArg) && button.active">
<FAIcon
v-if="button.active(funcArg)"
class="active-marker"
transform="shrink-6 up-9 right-12"
:icon="button.activeIndicator?.(funcArg) || 'check'"
/>
<FAIcon
v-if="!button.active(funcArg)"
class="focus-marker"
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>
<span
class="action-counter"
v-if="button.counter?.(funcArg) > 0"
>
{{ button.counter?.(funcArg) }}
</span>
</component>
<ActionButtonContainer
:button="button"
:status="status"
:extra="false"
:funcArg="funcArg"
:get-class="getClass"
:get-component="getComponent"
:animation-state="animationState"
:close="close"
:do-action="doAction"
/>
<button
v-if="showPin && currentUser"
type="button"
@ -111,63 +78,17 @@
:disabled="getClass(button).disabled"
:class="{ disabled: getClass(button).disabled }"
>
<Popover
v-if="getComponent(button) === 'button'"
trigger="hover"
placement="right"
>
<template #trigger>
<ActionButton
:button="button"
:status="status"
:extra="true"
:funcArg="funcArg"
:get-class="getClass"
:get-component="getComponent"
:animation-state="animationState"
:close="close"
:do-action="doAction"
/>
</template>
<template #content>
<template v-if="button.name === 'mute'">
<div
v-for="folder in folders"
:key="folder.id"
class="menu-item dropdown-item -icon"
>
<button
class="main-button"
@click="toggleFolder(folder.id)"
>
<span
class="input menu-checkbox -radio"
:class="{ 'menu-checkbox-checked': status.bookmark_folder_id == folder.id }"
/>
{{ folder.name }}
</button>
</div>
</template>
</template>
</Popover>
<Popover
trigger="hover"
placement="right"
:trigger-attrs="{ class: 'extra-button' }"
v-if="button.name === 'bookmark'"
>
<template #trigger>
<FAIcon
class="chevron-icon"
size="lg"
icon="chevron-right"
fixed-width
/>
</template>
<template #content>
<StatusBookmarkFolderMenu v-if="button.name === 'bookmark'" :status="funcArg.status" />
</template>
</Popover>
<ActionButtonContainer
:button="button"
:status="status"
:extra="true"
:funcArg="funcArg"
:get-class="getClass"
:get-component="getComponent"
:animation-state="animationState"
:close="close"
:do-action="doAction"
/>
<button
v-if="showPin && currentUser"
type="button"

View file

@ -1238,6 +1238,11 @@
"mentions": "Mentions",
"replies_list": "Replies:",
"replies_list_with_others": "Replies (+{numReplies} other): | Replies (+{numReplies} others):",
"mute_ellipsis": "Mute…",
"mute_user": "Mute user",
"unmute_user": "Unmute user",
"mute_domain": "Mute domain",
"unmute_domain": "Unmute domain",
"mute_conversation": "Mute conversation",
"unmute_conversation": "Unmute conversation",
"status_unavailable": "Status unavailable",