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 UserLink from '../user_link/user_link.vue'
|
||||||
import MentionsLine from 'src/components/mentions_line/mentions_line.vue'
|
import MentionsLine from 'src/components/mentions_line/mentions_line.vue'
|
||||||
import MentionLink from 'src/components/mention_link/mention_link.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 generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||||
import { muteWordHits } from '../../services/status_parser/status_parser.js'
|
import { muteWordHits } from '../../services/status_parser/status_parser.js'
|
||||||
|
@ -119,7 +120,8 @@ const Status = {
|
||||||
MentionLink,
|
MentionLink,
|
||||||
MentionsLine,
|
MentionsLine,
|
||||||
UserPopover,
|
UserPopover,
|
||||||
UserLink
|
UserLink,
|
||||||
|
StatusActionButtons
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
'statusoid',
|
'statusoid',
|
||||||
|
|
|
@ -65,7 +65,6 @@
|
||||||
v-if="retweet"
|
v-if="retweet"
|
||||||
class="left-side repeater-avatar"
|
class="left-side repeater-avatar"
|
||||||
:show-actor-type-indicator="showActorTypeIndicator"
|
:show-actor-type-indicator="showActorTypeIndicator"
|
||||||
:better-shadow="betterShadow"
|
|
||||||
:user="statusoid.user"
|
:user="statusoid.user"
|
||||||
/>
|
/>
|
||||||
<div class="right-side faint">
|
<div class="right-side faint">
|
||||||
|
@ -120,7 +119,6 @@
|
||||||
class="post-avatar"
|
class="post-avatar"
|
||||||
:show-actor-type-indicator="showActorTypeIndicator"
|
:show-actor-type-indicator="showActorTypeIndicator"
|
||||||
:compact="compact"
|
:compact="compact"
|
||||||
:better-shadow="betterShadow"
|
|
||||||
:user="status.user"
|
:user="status.user"
|
||||||
/>
|
/>
|
||||||
</UserPopover>
|
</UserPopover>
|
||||||
|
@ -537,6 +535,12 @@
|
||||||
:status="status"
|
:status="status"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<StatusActionButtons
|
||||||
|
v-if="!noHeading && !isPreview"
|
||||||
|
:status="status"
|
||||||
|
:replying="replying"
|
||||||
|
@toggleReplying="toggleReplying"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="!noHeading && !isPreview"
|
v-if="!noHeading && !isPreview"
|
||||||
class="status-actions"
|
class="status-actions"
|
||||||
|
@ -600,12 +604,14 @@
|
||||||
<PostStatusForm
|
<PostStatusForm
|
||||||
ref="postStatusForm"
|
ref="postStatusForm"
|
||||||
class="reply-body"
|
class="reply-body"
|
||||||
|
:closeable="true"
|
||||||
:reply-to="status.id"
|
:reply-to="status.id"
|
||||||
:attentions="status.attentions"
|
:attentions="status.attentions"
|
||||||
:replied-user="status.user"
|
:replied-user="status.user"
|
||||||
:copy-message-scope="status.visibility"
|
:copy-message-scope="status.visibility"
|
||||||
:subject="replySubject"
|
:subject="replySubject"
|
||||||
@posted="doToggleReplying"
|
@posted="doToggleReplying"
|
||||||
|
@draft-done="doToggleReplying"
|
||||||
@can-close="doToggleReplying"
|
@can-close="doToggleReplying"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -28,7 +28,8 @@ const BUTTONS = [{
|
||||||
anonLink: true,
|
anonLink: true,
|
||||||
toggleable: true,
|
toggleable: true,
|
||||||
action ({ emit }) {
|
action ({ emit }) {
|
||||||
emit('toggle')
|
emit('toggleReplying')
|
||||||
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
// =========
|
// =========
|
||||||
|
@ -44,11 +45,16 @@ const BUTTONS = [{
|
||||||
},
|
},
|
||||||
animated: true,
|
animated: true,
|
||||||
active: ({ status }) => status.repeated,
|
active: ({ status }) => status.repeated,
|
||||||
counter: ({ status }) => status.replies_count,
|
counter: ({ status }) => status.repeat_num,
|
||||||
anonLink: true,
|
anonLink: true,
|
||||||
interactive: ({ status }) => !PRIVATE_SCOPES.has(status.visibility),
|
interactive: ({ status }) => !PRIVATE_SCOPES.has(status.visibility),
|
||||||
toggleable: true,
|
toggleable: true,
|
||||||
confirm: ({ status, getters }) => !status.repeated && getters.mergedConfig.modalOnRepeat,
|
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 }) {
|
action ({ status, store }) {
|
||||||
if (!status.repeated) {
|
if (!status.repeated) {
|
||||||
return store.dispatch('retweet', { id: status.id })
|
return store.dispatch('retweet', { id: status.id })
|
||||||
|
@ -62,10 +68,12 @@ const BUTTONS = [{
|
||||||
// =========
|
// =========
|
||||||
name: 'favorite',
|
name: 'favorite',
|
||||||
label: 'tool_tip.favorite',
|
label: 'tool_tip.favorite',
|
||||||
icon: 'star',
|
icon: ({ status }) => status.favorited
|
||||||
|
? ['fas', 'star']
|
||||||
|
: ['far', 'star'],
|
||||||
animated: true,
|
animated: true,
|
||||||
active: ({ status }) => status.favorited,
|
active: ({ status }) => status.favorited,
|
||||||
counter: ({ status }) => status.fave_count,
|
counter: ({ status }) => status.fave_num,
|
||||||
anonLink: true,
|
anonLink: true,
|
||||||
toggleable: true,
|
toggleable: true,
|
||||||
action ({ status, store }) {
|
action ({ status, store }) {
|
||||||
|
@ -81,11 +89,9 @@ const BUTTONS = [{
|
||||||
// =========
|
// =========
|
||||||
name: 'emoji',
|
name: 'emoji',
|
||||||
label: 'tool_lip.add_reaction',
|
label: 'tool_lip.add_reaction',
|
||||||
icon: 'smile-beam',
|
icon: ['far', 'smile-beam'],
|
||||||
anonLink: true,
|
anonLink: true,
|
||||||
action ({ emojiPicker }) {
|
popover: 'emoji-picker'
|
||||||
emojiPicker.show()
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
// =========
|
// =========
|
||||||
// MUTE CONVERSATION, my beloved
|
// MUTE CONVERSATION, my beloved
|
||||||
|
@ -141,7 +147,8 @@ const BUTTONS = [{
|
||||||
} else {
|
} else {
|
||||||
return dispatch('bookmark', { id: status.id })
|
return dispatch('bookmark', { id: status.id })
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
popover: 'bookmark-folders'
|
||||||
}, {
|
}, {
|
||||||
// =========
|
// =========
|
||||||
// EDIT
|
// EDIT
|
||||||
|
@ -180,8 +187,13 @@ const BUTTONS = [{
|
||||||
currentUser.privileges.includes('messages_delete')
|
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 }) {
|
action ({ dispatch, status }) {
|
||||||
dispatch('deleteStatus', { id: status.id })
|
return dispatch('deleteStatus', { id: status.id })
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
// =========
|
// =========
|
||||||
|
@ -195,6 +207,7 @@ const BUTTONS = [{
|
||||||
state.instance.server,
|
state.instance.server,
|
||||||
router.resolve({ name: 'conversation', params: { id: status.id } }).href
|
router.resolve({ name: 'conversation', params: { id: status.id } }).href
|
||||||
].join(''))
|
].join(''))
|
||||||
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
// =========
|
// =========
|
||||||
|
@ -210,58 +223,74 @@ const BUTTONS = [{
|
||||||
// =========
|
// =========
|
||||||
name: 'report',
|
name: 'report',
|
||||||
icon: 'flag',
|
icon: 'flag',
|
||||||
label: 'status.report',
|
label: 'user_card.report',
|
||||||
if: ({ loggedIn }) => loggedIn,
|
if: ({ loggedIn }) => loggedIn,
|
||||||
action ({ dispatch, status }) {
|
action ({ dispatch, status }) {
|
||||||
dispatch('openUserReportingModal', { userId: status.user.id, statusIds: [status.id] })
|
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 = {
|
const StatusActionButtons = {
|
||||||
props: ['status'],
|
props: ['status', 'replying'],
|
||||||
|
emits: ['toggleReplying'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
buttons: BUTTONS,
|
||||||
|
showingConfirmDialog: false,
|
||||||
|
currentConfirmTitle: '',
|
||||||
|
currentConfirmOkText: '',
|
||||||
|
currentConfirmCancelText: '',
|
||||||
|
currentConfirmAction: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
ConfirmModal
|
ConfirmModal
|
||||||
},
|
},
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
retweet () {
|
doAction (button) {
|
||||||
if (!this.status.repeated && this.shouldConfirmRepeat) {
|
this.doActionReal(button)
|
||||||
this.showConfirmDialog()
|
},
|
||||||
|
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 {
|
} else {
|
||||||
this.doRetweet()
|
return 'button'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doRetweet () {
|
funcArg () {
|
||||||
if (!this.status.repeated) {
|
return {
|
||||||
this.$store.dispatch('retweet', { id: this.status.id })
|
status: this.status,
|
||||||
} else {
|
replying: this.replying,
|
||||||
this.$store.dispatch('unretweet', { id: this.status.id })
|
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 () {
|
getClass (button) {
|
||||||
this.showingConfirmDialog = true
|
return {
|
||||||
|
[button.name() + '-button']: true,
|
||||||
|
'-active': button.active?.(this.funcArg()),
|
||||||
|
'-interactive': !!this.$store.state.users.currentUser
|
||||||
|
}
|
||||||
},
|
},
|
||||||
hideConfirmDialog () {
|
getRemoteInteractionLink () {
|
||||||
this.showingConfirmDialog = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
mergedConfig () {
|
|
||||||
return this.$store.getters.mergedConfig
|
|
||||||
},
|
|
||||||
remoteInteractionLink () {
|
|
||||||
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
|
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
|
||||||
},
|
|
||||||
shouldConfirmRepeat () {
|
|
||||||
return this.mergedConfig.modalOnRepeat
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,135 @@
|
||||||
<template>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script src="./status_buttons.js"></script>
|
<script src="./status_action_buttons.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "../../mixins";
|
@import "../../mixins";
|
||||||
|
|
||||||
.status-actions {
|
.StatusActionButtons {
|
||||||
position: relative;
|
width: 100%;
|
||||||
|
|
||||||
|
.quick-action-buttons {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
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);
|
margin-top: var(--status-margin);
|
||||||
|
|
||||||
> * {
|
.quick-action {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
grid-gap: 0.5em;
|
||||||
max-width: 4em;
|
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>
|
</style>
|
||||||
|
|
|
@ -1410,6 +1410,7 @@
|
||||||
"mentions": "Mentions",
|
"mentions": "Mentions",
|
||||||
"repeat": "Repeat",
|
"repeat": "Repeat",
|
||||||
"reply": "Reply",
|
"reply": "Reply",
|
||||||
|
"add_reaction": "Add reaction",
|
||||||
"favorite": "Favorite",
|
"favorite": "Favorite",
|
||||||
"add_reaction": "Add Reaction",
|
"add_reaction": "Add Reaction",
|
||||||
"user_settings": "User Settings",
|
"user_settings": "User Settings",
|
||||||
|
|
Loading…
Add table
Reference in a new issue