implemented status visibility change
This commit is contained in:
parent
aa0cef12b1
commit
19d8875196
16 changed files with 225 additions and 98 deletions
|
|
@ -15,7 +15,7 @@ const BasicUserCard = {
|
|||
showLineLabels: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
UserPopover,
|
||||
|
|
|
|||
|
|
@ -185,16 +185,15 @@ const ModerationTools = {
|
|||
entries() {
|
||||
return ENTRIES.map(({ check, label, separator, conditions }) => {
|
||||
if (separator) return 'separator'
|
||||
const [, negateToken, group, name] = /^([!~]?)([a-z-_]+):([a-z-_]+)$/.exec(
|
||||
check,
|
||||
)
|
||||
const [, negateToken, group, name] =
|
||||
/^([!~]?)([a-z-_]+):([a-z-_]+)$/.exec(check)
|
||||
|
||||
const hasTag = this.tagsSet.has(`${group}:${name}`)
|
||||
const noTag = this.tagsSet.has(`!${group}:${name}`)
|
||||
const maybeTag = this.tagsSet.has(`~${group}:${name}`)
|
||||
|
||||
// We are checking for condition to show element, i.e. only show "activate" if user is "deactivated"
|
||||
const checkNegated = (negateToken === '!' || negateToken === '~')
|
||||
const checkNegated = negateToken === '!' || negateToken === '~'
|
||||
|
||||
// Naturally, new value should also be the same
|
||||
const value = checkNegated
|
||||
|
|
@ -442,7 +441,11 @@ const ModerationTools = {
|
|||
return this.$store.state.users.currentUser.privileges.has(privilege)
|
||||
},
|
||||
setTag(tag, value) {
|
||||
useAdminSettingsStore().setUsersTags({ users: this.users, value, tags: [tag] })
|
||||
useAdminSettingsStore().setUsersTags({
|
||||
users: this.users,
|
||||
value,
|
||||
tags: [tag],
|
||||
})
|
||||
},
|
||||
setRight(right, value) {
|
||||
useAdminSettingsStore().setUsersRight({ users: this.users, value, right })
|
||||
|
|
@ -542,9 +545,7 @@ const ModerationTools = {
|
|||
)
|
||||
this.confirmDialogContent =
|
||||
'user_card.admin_menu.confirm_modal.disable_mfa_content'
|
||||
this.confirmDialogConfirm = this.$t(
|
||||
'settings.confirm'
|
||||
)
|
||||
this.confirmDialogConfirm = this.$t('settings.confirm')
|
||||
break
|
||||
}
|
||||
case 'require_password_change': {
|
||||
|
|
@ -554,12 +555,11 @@ const ModerationTools = {
|
|||
)
|
||||
this.confirmDialogContent =
|
||||
'user_card.admin_menu.confirm_modal.require_password_change_content'
|
||||
this.confirmDialogConfirm = this.$t(
|
||||
'settings.confirm'
|
||||
)
|
||||
this.confirmDialogConfirm = this.$t('settings.confirm')
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'state': {
|
||||
switch (name) {
|
||||
|
|
|
|||
|
|
@ -6,19 +6,9 @@ import { parseStatus } from 'src/services/entity_normalizer/entity_normalizer.se
|
|||
|
||||
const AdminStatusCard = {
|
||||
props: {
|
||||
/**
|
||||
* minimal status info
|
||||
* @type {import('vue').PropType<{
|
||||
* id: string
|
||||
* }>}
|
||||
*/
|
||||
statusDetails: {
|
||||
type: Object,
|
||||
required: true,
|
||||
/**
|
||||
* @param {any} u
|
||||
* @returns {u is { id: string }}
|
||||
*/
|
||||
validator(u) {
|
||||
return typeof u.id === 'string'
|
||||
},
|
||||
|
|
@ -31,23 +21,14 @@ const AdminStatusCard = {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* @returns {boolean} is this status sensitive?
|
||||
*/
|
||||
isSensitive() {
|
||||
return this.statusDetails.sensitive === true
|
||||
},
|
||||
/**
|
||||
* @returns {'public' | 'unlisted' | 'private' | 'direct'} status visibility
|
||||
*/
|
||||
visibility() {
|
||||
return this.statusDetails.visibility
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @param {boolean} v set sensitive
|
||||
*/
|
||||
changeSensitivity(v) {
|
||||
this.$store
|
||||
.dispatch('adminChangeStatusScope', {
|
||||
|
|
@ -56,9 +37,6 @@ const AdminStatusCard = {
|
|||
.then((res) => parseStatus(res))
|
||||
.then((s) => (this.statusCache = s))
|
||||
},
|
||||
/**
|
||||
* @param {boolean} v set visible
|
||||
*/
|
||||
changeVisibility(v) {
|
||||
this.$store
|
||||
.dispatch('adminChangeStatusScope', {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
faChevronDown,
|
||||
faChevronRight,
|
||||
faExternalLinkAlt,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faHistory,
|
||||
faMinus,
|
||||
|
|
@ -51,6 +52,7 @@ library.add(
|
|||
faBookmark,
|
||||
faBookmarkRegular,
|
||||
faEyeSlash,
|
||||
faEye,
|
||||
faThumbtack,
|
||||
faShareAlt,
|
||||
faExternalLinkAlt,
|
||||
|
|
|
|||
|
|
@ -3,14 +3,30 @@ import { defineAsyncComponent } from 'vue'
|
|||
import Popover from 'src/components/popover/popover.vue'
|
||||
import ActionButton from './action_button.vue'
|
||||
|
||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faEnvelope,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFolderTree,
|
||||
faGlobe,
|
||||
faLock,
|
||||
faLockOpen,
|
||||
faUser,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(faUser, faGlobe, faFolderTree)
|
||||
library.add(
|
||||
faUser,
|
||||
faGlobe,
|
||||
faFolderTree,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faLock,
|
||||
faLockOpen,
|
||||
faEnvelope,
|
||||
)
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -61,8 +77,27 @@ export default {
|
|||
this.domain,
|
||||
)
|
||||
},
|
||||
availableScopes() {
|
||||
return ['private', 'unlisted', 'direct', 'public'].filter((scope) => {
|
||||
return scope !== this.status.visibility
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
visibilityIcon(visibility) {
|
||||
switch (visibility) {
|
||||
case 'private':
|
||||
return 'lock'
|
||||
case 'unlisted':
|
||||
return 'lock-open'
|
||||
case 'direct':
|
||||
return 'envelope'
|
||||
case 'local':
|
||||
return 'igloo'
|
||||
default:
|
||||
return 'globe'
|
||||
}
|
||||
},
|
||||
unmuteUser() {
|
||||
return this.$store.dispatch('unmuteUser', this.user.id)
|
||||
},
|
||||
|
|
@ -79,6 +114,18 @@ export default {
|
|||
this.$refs.confirmUser.optionallyPrompt()
|
||||
}
|
||||
},
|
||||
setScope(visibility) {
|
||||
return useAdminSettingsStore().changeStatusScope({
|
||||
id: this.status.id,
|
||||
visibility,
|
||||
})
|
||||
},
|
||||
setSensitive(sensitive) {
|
||||
useAdminSettingsStore().changeStatusScope({
|
||||
id: this.status.id,
|
||||
sensitive,
|
||||
})
|
||||
},
|
||||
toggleConversationMute() {
|
||||
if (this.conversationIsMuted) {
|
||||
this.unmuteConversation()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,58 @@
|
|||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<div
|
||||
v-if="button.name === 'changeScope'"
|
||||
:id="`popup-menu-scope-${randomSeed}`"
|
||||
class="dropdown-menu"
|
||||
role="menu"
|
||||
>
|
||||
<div
|
||||
v-for="visibility in availableScopes"
|
||||
class="menu-item dropdown-item extra-action -icon"
|
||||
>
|
||||
<button
|
||||
class="main-button"
|
||||
@click="() => setScope(visibility)"
|
||||
>
|
||||
<FAIcon
|
||||
:icon="visibilityIcon(visibility)"
|
||||
fixed-width
|
||||
/>
|
||||
{{ $t('general.scope_in_timeline.' + visibility) }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="status.nsfw"
|
||||
class="menu-item dropdown-item extra-action -icon"
|
||||
>
|
||||
<button
|
||||
class="main-button"
|
||||
@click="() => setSensitive(false)"
|
||||
>
|
||||
<FAIcon
|
||||
icon="eye"
|
||||
fixed-width
|
||||
/>
|
||||
{{ $t('status.mark_as_non-sensitive') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="menu-item dropdown-item extra-action -icon"
|
||||
>
|
||||
<button
|
||||
class="main-button"
|
||||
@click="() => setSensitive(true)"
|
||||
>
|
||||
<FAIcon
|
||||
icon="eye-slash"
|
||||
fixed-width
|
||||
/>
|
||||
{{ $t('status.mark_as_sensitive') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="button.name === 'mute'"
|
||||
:id="`popup-menu-${randomSeed}`"
|
||||
|
|
|
|||
|
|
@ -243,6 +243,26 @@ export const BUTTONS = [
|
|||
return dispatch('deleteStatus', { id: status.id })
|
||||
},
|
||||
},
|
||||
{
|
||||
// =========
|
||||
// CHANGE SCOPE
|
||||
// =========
|
||||
name: 'changeScope',
|
||||
icon: 'eye',
|
||||
label: 'status.admin_change_scope',
|
||||
if({ status, loggedIn, currentUser }) {
|
||||
return (
|
||||
loggedIn &&
|
||||
(status.user.id === currentUser.id ||
|
||||
currentUser.privileges.has('messages_delete'))
|
||||
)
|
||||
},
|
||||
toggleable: false,
|
||||
dropdown: true,
|
||||
action({ status, dispatch, emit }) {
|
||||
/* prevent hiding */
|
||||
},
|
||||
},
|
||||
{
|
||||
// =========
|
||||
// SHARE/COPY
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ export default {
|
|||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
// Hide action buttons
|
||||
hideButtons: {
|
||||
required: false,
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
// default - open profile, 'zoom' - zoom, function - call function
|
||||
avatarAction: {
|
||||
required: false,
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@
|
|||
/>
|
||||
</a>
|
||||
<AccountActions
|
||||
v-if="isOtherUser && loggedIn"
|
||||
v-if="isOtherUser && loggedIn && !hideButtons"
|
||||
:user="user"
|
||||
:relationship="relationship"
|
||||
/>
|
||||
|
|
@ -228,7 +228,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="loggedIn && isOtherUser"
|
||||
v-if="loggedIn && isOtherUser && !hideButtons"
|
||||
class="user-interactions"
|
||||
>
|
||||
<div class="btn-group">
|
||||
|
|
|
|||
|
|
@ -24,6 +24,21 @@
|
|||
.user-info {
|
||||
margin: 1.2em;
|
||||
}
|
||||
|
||||
&.-admin-view {
|
||||
.list-item {
|
||||
padding: 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
|
||||
.admin-actions {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.Status{
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile-placeholder {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,18 @@
|
|||
import { get } from 'lodash'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import Conversation from 'src/components/conversation/conversation.vue'
|
||||
import List from 'src/components/list/list.vue'
|
||||
import Status from 'src/components/status/status.vue'
|
||||
import UserCard from 'src/components/user_card/user_card.vue'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(faCircleNotch)
|
||||
|
||||
const defaultTabKey = 'statuses'
|
||||
|
||||
const UserProfileAdminView = {
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -24,7 +22,6 @@ const UserProfileAdminView = {
|
|||
},
|
||||
created() {
|
||||
this.userId = this.$route.params.id
|
||||
console.log(this.userId)
|
||||
useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
|
||||
},
|
||||
updated() {
|
||||
|
|
@ -38,8 +35,8 @@ const UserProfileAdminView = {
|
|||
return {
|
||||
pageSize: 20,
|
||||
godmode: this.godmode,
|
||||
userId: this.userId,
|
||||
withReblogs: false
|
||||
id: this.userId,
|
||||
withReblogs: false,
|
||||
}
|
||||
},
|
||||
user() {
|
||||
|
|
@ -48,18 +45,16 @@ const UserProfileAdminView = {
|
|||
},
|
||||
methods: {
|
||||
fetchStatuses(page) {
|
||||
return useAdminSettingsStore()
|
||||
.fetchStatuses({
|
||||
...this.fetchOptions,
|
||||
page,
|
||||
})
|
||||
.then(({ count, users }) => ({ count, items: users }))
|
||||
return useAdminSettingsStore().fetchStatuses({
|
||||
...this.fetchOptions,
|
||||
page,
|
||||
})
|
||||
},
|
||||
},
|
||||
components: {
|
||||
UserCard,
|
||||
List,
|
||||
Conversation,
|
||||
Status,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="user"
|
||||
class="user-profile panel panel-default"
|
||||
class="user-profile -admin-view panel panel-default"
|
||||
>
|
||||
<div class="panel-body card-wrapper">
|
||||
<UserCard
|
||||
:user-id="userId"
|
||||
:compact="compactProfiles"
|
||||
:compact="true"
|
||||
avatar-action="zoom"
|
||||
:hide-bio="true"
|
||||
hide-bio
|
||||
hide-buttons
|
||||
/>
|
||||
</div>
|
||||
<List
|
||||
|
|
@ -17,8 +18,8 @@
|
|||
scrollable
|
||||
>
|
||||
<template #item="{item}">
|
||||
<Conversation
|
||||
:user="item"
|
||||
<Status
|
||||
:statusoid="item"
|
||||
/>
|
||||
</template>
|
||||
</List>
|
||||
|
|
|
|||
|
|
@ -1689,7 +1689,10 @@
|
|||
"invisible_quote": "Quoted status unavailable: {link}",
|
||||
"more_actions": "More actions on this status",
|
||||
"loading": "Loading...",
|
||||
"load_error": "Unable to load status: {error}"
|
||||
"load_error": "Unable to load status: {error}",
|
||||
"admin_change_scope": "Change visibility",
|
||||
"mark_as_sensitive": "Sensitive",
|
||||
"mark_as_non-sensitive": "Non-sensitive"
|
||||
},
|
||||
"user_card": {
|
||||
"approve": "Approve",
|
||||
|
|
|
|||
|
|
@ -186,7 +186,12 @@ const PLEROMA_ADMIN_CONFIRM_USERS_URL =
|
|||
'/api/v1/pleroma/admin/users/confirm_email'
|
||||
const PLEROMA_ADMIN_RESEND_CONFIRMATION_EMAIL_URL =
|
||||
'/api/v1/pleroma/admin/users/resend_confirmation_email'
|
||||
const PLEROMA_ADMIN_LIST_STATUSES_URL = (id, pageSize, godmode, withReblogs) =>
|
||||
const PLEROMA_ADMIN_LIST_STATUSES_URL = ({
|
||||
id,
|
||||
pageSize,
|
||||
godmode,
|
||||
withReblogs,
|
||||
}) =>
|
||||
`/api/v1/pleroma/admin/users/${id}/statuses?page_size=${pageSize}&godmode=${godmode}&with_reblogs=${withReblogs}`
|
||||
const PLEROMA_ADMIN_CHANGE_STATUS_SCOPE_URL = (id) =>
|
||||
`/api/v1/pleroma/admin/statuses/${id}`
|
||||
|
|
@ -1700,14 +1705,12 @@ const adminListUsers = ({ opts, credentials }) => {
|
|||
// the reported list is hardly useful because standards are for dating i guess,
|
||||
// so make sure to fetchIfMissing right afterward using this call
|
||||
const url = PLEROMA_ADMIN_USERS_URL_LIST(opts)
|
||||
|
||||
return promisedRequest({
|
||||
url: url,
|
||||
url,
|
||||
credentials,
|
||||
method: 'GET',
|
||||
}).then((data) => ({
|
||||
...data,
|
||||
users: data.users,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
const adminResendConfirmationEmail = ({
|
||||
|
|
@ -1752,20 +1755,14 @@ const adminDisableMFA = ({ screen_name: nickname, credentials }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const adminListStatuses = ({
|
||||
userId,
|
||||
pageSize = 20,
|
||||
godmode,
|
||||
withReblogs,
|
||||
credentials,
|
||||
}) => {
|
||||
const url = PLEROMA_ADMIN_LIST_STATUSES_URL(
|
||||
userId,
|
||||
pageSize,
|
||||
godmode,
|
||||
withReblogs,
|
||||
)
|
||||
return promisedRequest({ url, credentials, method: 'GET' })
|
||||
const adminListStatuses = ({ opts, credentials }) => {
|
||||
const url = PLEROMA_ADMIN_LIST_STATUSES_URL(opts)
|
||||
|
||||
return promisedRequest({
|
||||
url,
|
||||
credentials,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
const adminChangeStatusScope = ({
|
||||
|
|
@ -2260,7 +2257,6 @@ const listEmojiPacks = ({ page, pageSize }) => {
|
|||
}
|
||||
|
||||
const listRemoteEmojiPacks = ({ instance, page, pageSize }) => {
|
||||
console.log(instance)
|
||||
if (!instance.startsWith('http')) {
|
||||
instance = 'https://' + instance
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,10 @@ export const parseUser = (data) => {
|
|||
output.fields = data.source.fields
|
||||
if (data.source.pleroma) {
|
||||
output.no_rich_text = data.source.pleroma.no_rich_text
|
||||
output.show_role = typeof data.source.pleroma.show_role === 'boolean' ? data.source.pleroma.show_role : true
|
||||
output.show_role =
|
||||
typeof data.source.pleroma.show_role === 'boolean'
|
||||
? data.source.pleroma.show_role
|
||||
: true
|
||||
output.discoverable = data.source.pleroma.discoverable
|
||||
output.show_birthday = data.pleroma.show_birthday
|
||||
output.actor_type = data.source.pleroma.actor_type
|
||||
|
|
|
|||
|
|
@ -301,37 +301,46 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
|
|||
},
|
||||
|
||||
// Statuses stuff
|
||||
fetchStatuses(opts) {
|
||||
return this
|
||||
.backendInteractor
|
||||
.adminListStatuses(opts)
|
||||
.then(({ total, activities }) => ({
|
||||
count: total,
|
||||
items: activities.map(parseStatus)
|
||||
async fetchStatuses(opts) {
|
||||
const { total, activities } =
|
||||
await this.backendInteractor.adminListStatuses({
|
||||
opts,
|
||||
})
|
||||
|
||||
return {
|
||||
items: activities.map(parseStatus),
|
||||
count: total,
|
||||
}
|
||||
},
|
||||
changeStatusScope({ opts }) {
|
||||
return this.backendInteractor.adminChangeStatusScope({
|
||||
async changeStatusScope(opts) {
|
||||
const raw = await this.backendInteractor.adminChangeStatusScope({
|
||||
opts,
|
||||
})
|
||||
const status = parseStatus(raw)
|
||||
|
||||
await window.vuex.dispatch('addNewStatuses', {
|
||||
statuses: [status],
|
||||
userId: false,
|
||||
})
|
||||
},
|
||||
|
||||
// Users stuff
|
||||
async fetchUsers(opts) {
|
||||
const adminData = await this.backendInteractor.adminListUsers({
|
||||
const { users, count } = await this.backendInteractor.adminListUsers({
|
||||
opts,
|
||||
})
|
||||
|
||||
adminData.users = await Promise.all(
|
||||
adminData.users.map(
|
||||
async (userAdminData) =>
|
||||
await window.vuex.dispatch('updateUserAdminData', {
|
||||
userAdminData,
|
||||
}),
|
||||
return {
|
||||
items: await Promise.all(
|
||||
users.map(
|
||||
async (userAdminData) =>
|
||||
await window.vuex.dispatch('updateUserAdminData', {
|
||||
userAdminData,
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return adminData
|
||||
count,
|
||||
}
|
||||
},
|
||||
async getUserData({ user }) {
|
||||
const api = this.backendInteractor.adminGetUserData
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue