Merge pull request 'Support selectable visibility of repeats' (#440) from Oneric/akkoma-fe:boost-scopes into develop

Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma-fe/pulls/440
This commit is contained in:
Oneric 2025-06-10 18:37:59 +00:00
commit 4cf4b5e2d0
7 changed files with 81 additions and 37 deletions

View file

@ -10,6 +10,7 @@ import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
import { reject, map, uniqBy, debounce } from 'lodash'
import { usePostLanguageOptions } from 'src/lib/post_language'
import scopeUtils from 'src/lib/scope_utils.js'
import suggestor from '../emoji_input/suggestor.js'
import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
@ -762,15 +763,9 @@ const PostStatusForm = {
this.$store.dispatch('openSettingsModalTab', 'profile')
},
suggestedVisibility () {
if (this.copyMessageScope) {
if (this.copyMessageScope === 'direct') {
return this.copyMessageScope
}
if (this.copyMessageScope !== 'public' && this.$store.state.users.currentUser.default_scope !== 'private') {
return this.copyMessageScope
}
}
return this.$store.state.users.currentUser.default_scope
const maxScope = this.copyMessageScope
const defaultScope = this.$store.state.users.currentUser.default_scope
return scopeUtils.negotiate(defaultScope, maxScope)
}
}
}

View file

@ -1,4 +1,6 @@
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import ScopeSelector from '../scope_selector/scope_selector.vue'
import scopeUtils from 'src/lib/scope_utils.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faRetweet } from '@fortawesome/free-solid-svg-icons'
@ -7,12 +9,14 @@ library.add(faRetweet)
const RetweetButton = {
props: ['status', 'loggedIn', 'visibility'],
components: {
ConfirmModal
ConfirmModal,
ScopeSelector
},
data () {
return {
animated: false,
showingConfirmDialog: false
showingConfirmDialog: false,
retweetVisibility: this.$store.state.users.currentUser.default_scope
}
},
methods: {
@ -25,7 +29,7 @@ const RetweetButton = {
},
doRetweet () {
if (!this.status.repeated) {
this.$store.dispatch('retweet', { id: this.status.id })
this.$store.dispatch('retweet', { id: this.status.id, visibility: this.retweetVisibility })
} else {
this.$store.dispatch('unretweet', { id: this.status.id })
}
@ -40,6 +44,9 @@ const RetweetButton = {
},
hideConfirmDialog () {
this.showingConfirmDialog = false
},
changeVis (visibility) {
this.retweetVisibility = visibility
}
},
computed: {
@ -54,6 +61,15 @@ const RetweetButton = {
},
remoteInteractionLink () {
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
},
userDefaultScope () {
return this.$store.state.users.currentUser.default_scope
},
statusScope () {
return this.status.visibility
},
initialScope () {
return scopeUtils.negotiate(this.userDefaultScope, this.status.visibility)
}
}
}

View file

@ -49,6 +49,12 @@
@cancelled="hideConfirmDialog"
>
{{ $t('status.repeat_confirm') }}
<scope-selector
:user-default="userDefaultScope"
:original-scope="statusScope"
:initial-scope="initialScope"
:on-scope-change="changeVis"
/>
</confirm-modal>
</teleport>
</div>

View file

@ -6,6 +6,8 @@ import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
import scopeUtils from 'src/lib/scope_utils.js'
library.add(
faEnvelope,
faGlobe,
@ -13,18 +15,11 @@ library.add(
faLockOpen
)
const SCOPE_LEVELS = {
'direct': 0,
'private': 1,
'local': 2,
'unlisted': 2,
'public': 3
}
const ScopeSelector = {
props: [
'showAll',
'userDefault',
// scope of parent object
'originalScope',
'initialScope',
'onScopeChange'
@ -39,16 +34,16 @@ const ScopeSelector = {
return !this.showPublic && !this.showUnlisted && !this.showPrivate && !this.showDirect
},
showPublic () {
return this.originalScope !== 'direct' && this.shouldShow('public')
return this.shouldShow('public')
},
showLocal () {
return this.originalScope !== 'direct' && this.shouldShow('local')
return this.shouldShow('local')
},
showUnlisted () {
return this.originalScope !== 'direct' && this.shouldShow('unlisted')
return this.shouldShow('unlisted')
},
showPrivate () {
return this.originalScope !== 'direct' && this.shouldShow('private')
return this.shouldShow('private')
},
showDirect () {
return this.shouldShow('direct')
@ -65,15 +60,10 @@ const ScopeSelector = {
},
methods: {
shouldShow (scope) {
if (!this.originalScope) {
if (!this.originalScope)
return true
}
if (this.originalScope === 'local') {
return scope === 'direct' || scope === 'local'
}
return SCOPE_LEVELS[scope] <= SCOPE_LEVELS[this.originalScope]
else
return scopeUtils.compare(scope, this.originalScope) <= 0
},
changeVis (scope) {
this.currentScope = scope

37
src/lib/scope_utils.js Normal file
View file

@ -0,0 +1,37 @@
const SCOPE_LEVELS = {
'direct': 0,
'private': 1,
'unlisted': 2,
'local': 3,
'public': 3
}
export default {
negotiate: (defaultScope, maxScope) => {
if (!maxScope)
return defaultScope;
if (maxScope === 'local')
return defaultScope === 'direct' ? defaultScope : 'local';
if (SCOPE_LEVELS[defaultScope] <= SCOPE_LEVELS[maxScope])
return defaultScope;
else
return maxScope;
},
compare: (sa, sb) => {
if (sa === 'local') {
if (sb === 'direct')
return 1;
else if (sb === sa)
return 0;
else
return -1;
}
if (sa === sb)
return 0;
return SCOPE_LEVELS[sa] < SCOPE_LEVELS[sb] ? -1 : 1;
}
}

View file

@ -663,10 +663,10 @@ const statuses = {
return rootState.api.backendInteractor.unmuteConversation({ id: statusId })
.then((status) => commit('setMutedStatus', status))
},
retweet ({ rootState, commit }, status) {
retweet ({ rootState, commit }, {id, visibility}) {
// Optimistic retweeting...
commit('setRetweeted', { status, value: true })
rootState.api.backendInteractor.retweet({ id: status.id })
commit('setRetweeted', { status: {id: id}, value: true })
rootState.api.backendInteractor.retweet({ id: id, visibility: visibility })
.then(status => commit('setRetweetedConfirm', { status: status.retweeted_status, user: rootState.users.currentUser }))
},
unretweet ({ rootState, commit }, status) {

View file

@ -822,8 +822,8 @@ const unfavorite = ({ id, credentials }) => {
.then((data) => parseStatus(data))
}
const retweet = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_RETWEET_URL(id), method: 'POST', credentials })
const retweet = ({ id, visibility, credentials }) => {
return promisedRequest({ url: MASTODON_RETWEET_URL(id), method: 'POST', payload: { visibility }, credentials })
.then((data) => parseStatus(data))
}