initial work on quick actions
This commit is contained in:
parent
35409ad9eb
commit
fe84a52dcc
5 changed files with 206 additions and 54 deletions
|
@ -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',
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'],
|
||||
props: ['status', 'replying'],
|
||||
emits: ['toggleReplying'],
|
||||
data () {
|
||||
return {
|
||||
buttons: BUTTONS,
|
||||
showingConfirmDialog: false,
|
||||
currentConfirmTitle: '',
|
||||
currentConfirmOkText: '',
|
||||
currentConfirmCancelText: '',
|
||||
currentConfirmAction: () => {}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ConfirmModal
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
retweet () {
|
||||
if (!this.status.repeated && this.shouldConfirmRepeat) {
|
||||
this.showConfirmDialog()
|
||||
doAction (button) {
|
||||
this.doActionReal(button)
|
||||
},
|
||||
doActionReal (button) {
|
||||
button.action(this.funcArg(button))
|
||||
.then(() => this.$emit('onSuccess'))
|
||||
.catch(err => this.$emit('onError', err.error.error))
|
||||
},
|
||||
component (button) {
|
||||
if (!this.$store.state.users.currentUser && button.anonLink) {
|
||||
return 'a'
|
||||
} else if (button.action == null && button.link != null) {
|
||||
return 'a'
|
||||
} else {
|
||||
this.doRetweet()
|
||||
return 'button'
|
||||
}
|
||||
},
|
||||
doRetweet () {
|
||||
if (!this.status.repeated) {
|
||||
this.$store.dispatch('retweet', { id: this.status.id })
|
||||
} else {
|
||||
this.$store.dispatch('unretweet', { id: this.status.id })
|
||||
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
|
||||
}
|
||||
this.animated = true
|
||||
setTimeout(() => {
|
||||
this.animated = false
|
||||
}, 500)
|
||||
this.hideConfirmDialog()
|
||||
},
|
||||
showConfirmDialog () {
|
||||
this.showingConfirmDialog = true
|
||||
getClass (button) {
|
||||
return {
|
||||
[button.name() + '-button']: true,
|
||||
'-active': button.active?.(this.funcArg()),
|
||||
'-interactive': !!this.$store.state.users.currentUser
|
||||
}
|
||||
},
|
||||
hideConfirmDialog () {
|
||||
this.showingConfirmDialog = false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
mergedConfig () {
|
||||
return this.$store.getters.mergedConfig
|
||||
},
|
||||
remoteInteractionLink () {
|
||||
getRemoteInteractionLink () {
|
||||
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
|
||||
},
|
||||
shouldConfirmRepeat () {
|
||||
return this.mergedConfig.modalOnRepeat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1410,6 +1410,7 @@
|
|||
"mentions": "Mentions",
|
||||
"repeat": "Repeat",
|
||||
"reply": "Reply",
|
||||
"add_reaction": "Add reaction",
|
||||
"favorite": "Favorite",
|
||||
"add_reaction": "Add Reaction",
|
||||
"user_settings": "User Settings",
|
||||
|
|
Loading…
Add table
Reference in a new issue