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: fe84a52dcc0a1cfe5088363edd5116bd0a61f36e
parent 35409ad9ebb366202bfe4f685c62bd05d433df99
Author: Henry Jameson <me@hjkos.com>
Date:   Thu,  9 Jan 2025 17:43:48 +0200

initial work on quick actions

Diffstat:

Msrc/components/status/status.js4+++-
Msrc/components/status/status.vue10++++++++--
Msrc/components/status_action_buttons/status_action_buttons.js117+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/components/status_action_buttons/status_action_buttons.vue126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/i18n/en.json1+
5 files changed, 205 insertions(+), 53 deletions(-)

diff --git a/src/components/status/status.js b/src/components/status/status.js @@ -16,6 +16,7 @@ import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import UserLink from '../user_link/user_link.vue' import MentionsLine from 'src/components/mentions_line/mentions_line.vue' import MentionLink from 'src/components/mention_link/mention_link.vue' +import StatusActionButtons from 'src/components/status_action_buttons/status_action_buttons.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import { muteWordHits } from '../../services/status_parser/status_parser.js' @@ -119,7 +120,8 @@ const Status = { MentionLink, MentionsLine, UserPopover, - UserLink + UserLink, + StatusActionButtons }, props: [ 'statusoid', diff --git a/src/components/status/status.vue b/src/components/status/status.vue @@ -65,7 +65,6 @@ v-if="retweet" class="left-side repeater-avatar" :show-actor-type-indicator="showActorTypeIndicator" - :better-shadow="betterShadow" :user="statusoid.user" /> <div class="right-side faint"> @@ -120,7 +119,6 @@ class="post-avatar" :show-actor-type-indicator="showActorTypeIndicator" :compact="compact" - :better-shadow="betterShadow" :user="status.user" /> </UserPopover> @@ -537,6 +535,12 @@ :status="status" /> + <StatusActionButtons + v-if="!noHeading && !isPreview" + :status="status" + :replying="replying" + @toggleReplying="toggleReplying" + /> <div v-if="!noHeading && !isPreview" class="status-actions" @@ -600,12 +604,14 @@ <PostStatusForm ref="postStatusForm" class="reply-body" + :closeable="true" :reply-to="status.id" :attentions="status.attentions" :replied-user="status.user" :copy-message-scope="status.visibility" :subject="replySubject" @posted="doToggleReplying" + @draft-done="doToggleReplying" @can-close="doToggleReplying" /> </div> diff --git a/src/components/status_action_buttons/status_action_buttons.js b/src/components/status_action_buttons/status_action_buttons.js @@ -28,7 +28,8 @@ const BUTTONS = [{ anonLink: true, toggleable: true, action ({ emit }) { - emit('toggle') + emit('toggleReplying') + return Promise.resolve() } }, { // ========= @@ -44,11 +45,16 @@ const BUTTONS = [{ }, animated: true, active: ({ status }) => status.repeated, - counter: ({ status }) => status.replies_count, + counter: ({ status }) => status.repeat_num, anonLink: true, interactive: ({ status }) => !PRIVATE_SCOPES.has(status.visibility), toggleable: true, confirm: ({ status, getters }) => !status.repeated && getters.mergedConfig.modalOnRepeat, + confirmStrings: { + title: 'status.repeat_confirm_title', + confirm: 'status.repeat_confirm_accept_button', + cancel: 'status.repeat_confirm_cancel_button' + }, action ({ status, store }) { if (!status.repeated) { return store.dispatch('retweet', { id: status.id }) @@ -62,10 +68,12 @@ const BUTTONS = [{ // ========= name: 'favorite', label: 'tool_tip.favorite', - icon: 'star', + icon: ({ status }) => status.favorited + ? ['fas', 'star'] + : ['far', 'star'], animated: true, active: ({ status }) => status.favorited, - counter: ({ status }) => status.fave_count, + counter: ({ status }) => status.fave_num, anonLink: true, toggleable: true, action ({ status, store }) { @@ -81,11 +89,9 @@ const BUTTONS = [{ // ========= name: 'emoji', label: 'tool_lip.add_reaction', - icon: 'smile-beam', + icon: ['far', 'smile-beam'], anonLink: true, - action ({ emojiPicker }) { - emojiPicker.show() - } + popover: 'emoji-picker' }, { // ========= // MUTE CONVERSATION, my beloved @@ -141,7 +147,8 @@ const BUTTONS = [{ } else { return dispatch('bookmark', { id: status.id }) } - } + }, + popover: 'bookmark-folders' }, { // ========= // EDIT @@ -180,8 +187,13 @@ const BUTTONS = [{ currentUser.privileges.includes('messages_delete') ) }, + confirmStrings: { + title: 'status.delete_confirm_title', + confirm: 'status.delete_confirm_cancel_button', + cancel: 'status.delete_confirm_accept_button' + }, action ({ dispatch, status }) { - dispatch('deleteStatus', { id: status.id }) + return dispatch('deleteStatus', { id: status.id }) } }, { // ========= @@ -195,6 +207,7 @@ const BUTTONS = [{ state.instance.server, router.resolve({ name: 'conversation', params: { id: status.id } }).href ].join('')) + return Promise.resolve() } }, { // ========= @@ -210,58 +223,74 @@ const BUTTONS = [{ // ========= name: 'report', icon: 'flag', - label: 'status.report', + label: 'user_card.report', if: ({ loggedIn }) => loggedIn, action ({ dispatch, status }) { dispatch('openUserReportingModal', { userId: status.user.id, statusIds: [status.id] }) } -}] +}].map(button => { + return Object.fromEntries( + Object.entries(button).map(([k, v]) => [k, typeof v === 'function' ? v : () => v]) + ) +}) +console.log(BUTTONS) const StatusActionButtons = { - props: ['status'], - components: { - ConfirmModal - }, + props: ['status', 'replying'], + emits: ['toggleReplying'], data () { return { + buttons: BUTTONS, + showingConfirmDialog: false, + currentConfirmTitle: '', + currentConfirmOkText: '', + currentConfirmCancelText: '', + currentConfirmAction: () => {} } }, + components: { + ConfirmModal + }, methods: { - retweet () { - if (!this.status.repeated && this.shouldConfirmRepeat) { - this.showConfirmDialog() - } else { - this.doRetweet() - } + doAction (button) { + this.doActionReal(button) + }, + doActionReal (button) { + button.action(this.funcArg(button)) + .then(() => this.$emit('onSuccess')) + .catch(err => this.$emit('onError', err.error.error)) }, - doRetweet () { - if (!this.status.repeated) { - this.$store.dispatch('retweet', { id: this.status.id }) + component (button) { + if (!this.$store.state.users.currentUser && button.anonLink) { + return 'a' + } else if (button.action == null && button.link != null) { + return 'a' } else { - this.$store.dispatch('unretweet', { id: this.status.id }) + return 'button' } - this.animated = true - setTimeout(() => { - this.animated = false - }, 500) - this.hideConfirmDialog() }, - showConfirmDialog () { - this.showingConfirmDialog = true + funcArg () { + return { + status: this.status, + replying: this.replying, + emit: this.$emit, + dispatch: this.$store.dispatch, + state: this.$store.state, + getters: this.$store.getters, + router: this.$router, + currentUser: this.$store.state.users.currentUser, + loggedIn: !!this.$store.state.users.currentUser + } }, - hideConfirmDialog () { - this.showingConfirmDialog = false - } - }, - computed: { - mergedConfig () { - return this.$store.getters.mergedConfig + getClass (button) { + return { + [button.name() + '-button']: true, + '-active': button.active?.(this.funcArg()), + '-interactive': !!this.$store.state.users.currentUser + } }, - remoteInteractionLink () { + getRemoteInteractionLink () { return this.$store.getters.remoteInteractionLink({ statusId: this.status.id }) - }, - shouldConfirmRepeat () { - return this.mergedConfig.modalOnRepeat } } } diff --git a/src/components/status_action_buttons/status_action_buttons.vue b/src/components/status_action_buttons/status_action_buttons.vue @@ -1,21 +1,135 @@ <template> + <div class="StatusActionButtons"> + <span class="quick-action-buttons"> + <span + class="quick-action" + v-for="button in buttons" + :key="button.name()" + > + <component + :is="component(button)" + class="button-unstyled" + :class="getClass(button)" + role="button" + :tabindex="0" + :title="$t(button.label(funcArg()))" + @click.stop="component(button) === 'button' && doAction(button)" + :href="component(button) == 'a' ? button.link?.(funcArg()) || getRemoteInteractionLink : undefined" + > + <FALayers class="fa-old-padding"> + <FAIcon + class="fa-scale-110" + :icon="button.icon(funcArg())" + /> + <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" + /> + <FAIcon + v-show="button.active(funcArg())" + class="focus-marker" + transform="shrink-6 up-9 right-17" + icon="times" + /> + </template> + </FALayers> + </component> + <span + class="action-counter" + v-if="button.counter?.(funcArg()) > 0" + > + {{ button.counter?.(funcArg()) }} + </span> + </span> + </span> + <teleport to="#modal"> + <confirm-modal + v-if="showingConfirmDialog" + :title="currentConfirmTitle" + :confirm-text="currentConfirmOkText" + :cancel-text="currentConfirmCancelText" + @accepted="currentConfirmAction" + @cancelled="hideConfirmDialog" + > + {{ $t('status.repeat_confirm') }} + </confirm-modal> + </teleport> + </div> </template> -<script src="./status_buttons.js"></script> +<script src="./status_action_buttons.js"></script> <style lang="scss"> @import "../../mixins"; -.status-actions { -position: relative; +.StatusActionButtons { + width: 100%; + + .quick-action-buttons { + position: relative; width: 100%; - display: flex; + 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; - flex: 1; + + .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; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + } } } +} </style> diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -1410,6 +1410,7 @@ "mentions": "Mentions", "repeat": "Repeat", "reply": "Reply", + "add_reaction": "Add reaction", "favorite": "Favorite", "add_reaction": "Add Reaction", "user_settings": "User Settings",