Page MenuHomePhorge

No OneTemporary

Size
40 KB
Referenced Files
None
Subscribers
None
diff --git a/src/components/list/list.js b/src/components/list/list.js
index 65165dad9d..c7b924258a 100644
--- a/src/components/list/list.js
+++ b/src/components/list/list.js
@@ -1,154 +1,158 @@
import { isEmpty } from 'lodash'
import Checkbox from 'src/components/checkbox/checkbox.vue'
const List = {
props: {
boxOnly: {
type: Boolean,
default: false,
},
fetchFunction: {
type: Function,
default: null,
},
getKey: {
type: Function,
default: (item) => item.id,
},
getClass: {
type: Function,
default: () => '',
},
+ preSelect: {
+ type: Array,
+ default: [],
+ },
nonInteractive: {
type: Boolean,
default: false,
},
scrollable: {
type: Boolean,
default: false,
},
selectable: {
type: Boolean,
default: false,
},
externalItems: {
type: Array,
default: null,
},
},
emits: ['fetchRequested', 'select'],
components: {
Checkbox,
},
data() {
return {
items: [],
- selected: new Set([]),
+ selected: new Set(this.preSelect),
loading: false,
bottomedOut: true,
error: null,
page: 1,
total: null,
}
},
computed: {
allKeys() {
return new Set(this.finalItems.map(this.getKey))
},
selectedItems() {
return this.items.filter((item) => this.selected.has(this.getKey(item)))
},
allSelected() {
return (
this.selected.size !== 0 &&
this.selected.size === this.finalItems.length
)
},
noneSelected() {
return this.selected.size === 0
},
someSelected() {
return !this.allSelected && !this.noneSelected
},
finalItems() {
return this.externalItems || this.items
},
},
created() {
window.addEventListener('scroll', this.scrollLoad)
if (this.fetchFunction && this.items.length === 0) {
this.fetchEntries()
}
},
unmounted() {
window.removeEventListener('scroll', this.scrollLoad)
},
methods: {
fetchEntries() {
if (this.loading) return
this.loading = true
this.error = null
this.fetchFunction(this.page)
.then((result) => {
this.loading = false
this.bottomedOut = isEmpty(result.items)
if (this.externalItems) return
this.page += 1
this.total = result.count
this.items.push(...result.items)
})
.catch((error) => {
this.loading = false
this.error = error
console.error('Error loading list data:', error)
})
},
reset() {
this.items = []
this.page = 1
this.total = null
this.error = null
this.loading = false
this.fetchEntries()
},
scrollLoad(e) {
if (this.fetchFunction) {
const bodyBRect = document.body.getBoundingClientRect()
const height = Math.max(bodyBRect.height, -bodyBRect.y)
if (
this.$el.offsetHeight > 0 &&
window.innerHeight + window.pageYOffset >= height - 750
) {
this.fetchEntries()
}
}
},
isSelected(item) {
return this.selected.has(this.getKey(item))
},
toggle(checked, item) {
const key = this.getKey(item)
if (checked) {
this.selected.add(key)
} else {
this.selected.delete(key)
}
this.$emit('select', this.selected)
},
toggleAll(value) {
if (value) {
this.selected = new Set([...this.allKeys])
} else {
this.selected = new Set([])
}
this.$emit('select', this.selected)
},
},
}
export default List
diff --git a/src/components/settings_modal/admin_tabs/users_tab.js b/src/components/settings_modal/admin_tabs/users_tab.js
index b4489c73c6..939a5a8567 100644
--- a/src/components/settings_modal/admin_tabs/users_tab.js
+++ b/src/components/settings_modal/admin_tabs/users_tab.js
@@ -1,121 +1,120 @@
import { isEmpty } from 'lodash'
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import List from 'src/components/list/list.vue'
import ModerationTools from 'src/components/moderation_tools/moderation_tools.vue'
import Select from 'src/components/select/select.vue'
import AdminUserCard from 'src/components/settings_modal/admin_tabs/admin_user_card.vue'
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
const UsersTab = {
components: {
Checkbox,
Select,
BasicUserCard,
List,
AdminUserCard,
ModerationTools,
},
data() {
return {
filtersOrigin: 'local',
filtersActivity: 'all',
filtersPrivileges: 'all',
filtersNeedApproval: false,
filtersUnconfirmed: false,
filtersQuery: '',
filtersName: '',
filtersEmail: '',
expandedUser: null,
}
},
computed: {
/**
* do we filter for admins?
* @returns {boolean}
*/
filtersIsAdmin() {
return (
this.filtersPrivileges === 'admin' ||
this.filtersPrivileges === 'modsnadmins'
)
},
/**
* do we filter for moderators?
* @returns {boolean}
*/
filtersIsModerator() {
return (
this.filtersPrivileges === 'moderator' ||
this.filtersPrivileges === 'modsnadmins'
)
},
/**
* do we filter for active users?
* @returns {boolean}
*/
filtersActive() {
return this.filtersActivity === 'active'
},
/**
* do we filter for deactivated users?
* @returns {boolean}
*/
filtersDeactivated() {
return this.filtersActivity === 'deactivated'
},
/**
* do we filter for local users?
* @returns {boolean}
*/
filtersLocal() {
return this.filtersOrigin === 'local'
},
/**
* do we filter for external users?
* @return {boolean}
*/
filtersExternal() {
return this.filtersOrigin === 'external'
},
fetchOptions() {
const filters = {
isAdmin: this.filtersIsAdmin,
isModerator: this.filtersIsModerator,
active: this.filtersActive,
deactivated: this.filtersDeactivated,
local: this.filtersLocal,
external: this.filtersExternal,
needApproval: this.filtersNeedApproval,
unconfirmed: this.filtersUnconfirmed,
}
return {
query: this.filtersQuery,
name: this.filtersName,
email: this.filtersEmail,
pageSize: 50,
filters,
}
},
},
methods: {
fetchUsers(page) {
return useAdminSettingsStore()
.fetchUsers({
...this.fetchOptions,
page,
})
- .then(({ count, users }) => ({ count, items: users }))
},
},
watch: {
fetchOptions() {
- this.$refs.usersList.reset()
+ this.$refs.usersList?.reset()
},
},
}
export default UsersTab
diff --git a/src/components/status_action_buttons/buttons_definitions.js b/src/components/status_action_buttons/buttons_definitions.js
index c47e9b9037..d8e4e3cd36 100644
--- a/src/components/status_action_buttons/buttons_definitions.js
+++ b/src/components/status_action_buttons/buttons_definitions.js
@@ -1,315 +1,317 @@
import { useEditStatusStore } from 'src/stores/editStatus.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useReportsStore } from 'src/stores/reports.js'
import { useStatusHistoryStore } from 'src/stores/statusHistory.js'
const PRIVATE_SCOPES = new Set(['private', 'direct'])
const PUBLIC_SCOPES = new Set(['public', 'unlisted'])
export const BUTTONS = [
{
// =========
// REPLY
// =========
name: 'reply',
label: 'tool_tip.reply',
icon: 'reply',
active: ({ replying }) => replying,
counter: ({ status }) => status.replies_count,
anon: true,
anonLink: true,
toggleable: true,
closeIndicator: 'times',
activeIndicator: null,
action({ emit }) {
emit('toggleReplying')
return Promise.resolve()
},
},
{
// =========
// REPEAT
// =========
name: 'retweet',
label: ({ status }) =>
status.repeated ? 'tool_tip.unrepeat' : 'tool_tip.repeat',
icon({ status, currentUser }) {
if (
currentUser.id !== status.user.id &&
PRIVATE_SCOPES.has(status.visibility)
) {
return 'lock'
}
return 'retweet'
},
animated: true,
active: ({ status }) => status.repeated,
counter: ({ status }) => status.repeat_num,
anonLink: true,
interactive: ({ status, currentUser }) =>
!!currentUser &&
(currentUser.id === status.user.id ||
!PRIVATE_SCOPES.has(status.visibility)),
toggleable: true,
confirm: ({ status, getters }) =>
!status.repeated && useMergedConfigStore().mergedConfig.modalOnRepeat,
confirmStrings: {
title: 'status.repeat_confirm_title',
body: 'status.repeat_confirm',
confirm: 'status.repeat_confirm_accept_button',
cancel: 'status.repeat_confirm_cancel_button',
},
action({ status, dispatch }) {
if (!status.repeated) {
return dispatch('retweet', { id: status.id })
} else {
return dispatch('unretweet', { id: status.id })
}
},
},
{
// =========
// FAVORITE
// =========
name: 'favorite',
label: ({ status }) =>
status.favorited ? 'tool_tip.unfavorite' : 'tool_tip.favorite',
icon: ({ status }) =>
status.favorited ? ['fas', 'star'] : ['far', 'star'],
animated: true,
active: ({ status }) => status.favorited,
counter: ({ status }) => status.fave_num,
anonLink: true,
toggleable: true,
action({ status, dispatch }) {
if (!status.favorited) {
return dispatch('favorite', { id: status.id })
} else {
return dispatch('unfavorite', { id: status.id })
}
},
},
{
// =========
// EMOJI REACTIONS
// =========
name: 'emoji',
label: 'tool_tip.add_reaction',
icon: ['far', 'face-smile-beam'],
interactive: () => true,
active: ({ emojiPickerShown }) => emojiPickerShown,
toggleable: true,
anonLink: true,
},
{
// =========
// MUTE
// =========
name: 'mute',
icon: 'eye-slash',
label: 'status.mute_ellipsis',
if: ({ loggedIn }) => loggedIn,
toggleable: false,
dropdown: true,
action({ status, dispatch, emit }) {
/* prevent hiding */
},
},
{
// =========
// PIN STATUS
// =========
name: 'pin',
icon: 'thumbtack',
label: ({ status }) => (status.pinned ? 'status.unpin' : 'status.pin'),
if({ status, loggedIn, currentUser }) {
return (
loggedIn &&
status.user.id === currentUser.id &&
PUBLIC_SCOPES.has(status.visibility)
)
},
action({ status, dispatch }) {
if (status.pinned) {
return dispatch('unpinStatus', status.id)
} else {
return dispatch('pinStatus', status.id)
}
},
},
{
// =========
// BOOKMARK
// =========
name: 'bookmark',
icon: ({ status }) =>
status.bookmarked ? ['fas', 'bookmark'] : ['far', 'bookmark'],
toggleable: true,
active: ({ status }) => status.bookmarked,
label: ({ status }) =>
status.bookmarked ? 'status.unbookmark' : 'status.bookmark',
if: ({ loggedIn }) => loggedIn,
action({ status, dispatch }) {
if (status.bookmarked) {
return dispatch('unbookmark', { id: status.id })
} else {
return dispatch('bookmark', { id: status.id })
}
},
},
{
// =========
// EDIT HISTORY
// =========
name: 'editHistory',
icon: 'history',
label: 'status.status_history',
if({ status, state }) {
return (
useInstanceCapabilitiesStore().editingAvailable &&
status.edited_at !== null
)
},
action({ status }) {
const originalStatus = { ...status }
const stripFieldsList = [
'attachments',
'created_at',
'emojis',
'text',
'raw_html',
'nsfw',
'poll',
'summary',
'summary_raw_html',
]
stripFieldsList.forEach((p) => delete originalStatus[p])
useStatusHistoryStore().openStatusHistoryModal(originalStatus)
return Promise.resolve()
},
},
{
// =========
// EDIT
// =========
name: 'edit',
icon: 'pen',
label: 'status.edit',
if({ status, loggedIn, currentUser, state }) {
return (
loggedIn &&
useInstanceCapabilitiesStore().editingAvailable &&
status.user.id === currentUser.id
)
},
action({ dispatch, status }) {
return dispatch('fetchStatusSource', { id: status.id }).then((data) =>
useEditStatusStore().openEditStatusModal({
statusId: status.id,
subject: data.spoiler_text,
statusText: data.text,
statusIsSensitive: status.nsfw,
statusPoll: status.poll,
statusFiles: [...status.attachments],
visibility: status.visibility,
statusContentType: data.content_type,
}),
)
},
},
{
// =========
// DELETE
// =========
name: 'delete',
icon: 'times',
label: 'status.delete',
if({ status, loggedIn, currentUser }) {
return (
loggedIn &&
(status.user.id === currentUser.id ||
currentUser.privileges.has('messages_delete'))
)
},
confirm: ({ getters }) => useMergedConfigStore().mergedConfig.modalOnDelete,
confirmStrings: {
title: 'status.delete_confirm_title',
body: 'status.delete_confirm',
confirm: 'status.delete_confirm_accept_button',
cancel: 'status.delete_confirm_cancel_button',
},
action({ dispatch, status }) {
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
// =========
name: 'share',
icon: 'share-alt',
label: 'status.copy_link',
action({ state, status, router }) {
navigator.clipboard.writeText(
[
useInstanceStore().server,
router.resolve({ name: 'conversation', params: { id: status.id } })
.href,
].join(''),
)
return Promise.resolve()
},
},
{
// =========
// EXTERNAL
// =========
name: 'external',
icon: 'external-link-alt',
label: 'status.external_source',
link: ({ status }) => status.external_url,
},
{
// =========
// REPORT
// =========
name: 'report',
icon: 'flag',
label: 'user_card.report',
if: ({ loggedIn }) => loggedIn,
action({ status }) {
- return useReportsStore().openUserReportingModal({
+ useReportsStore().openUserReportingModal({
userId: status.user.id,
statusIds: [status.id],
})
+
+ return Promise.resolve()
},
},
].map((button) => {
return Object.fromEntries(
Object.entries(button).map(([k, v]) => [
k,
typeof v === 'function' || k === 'name' ? v : () => v,
]),
)
})
diff --git a/src/components/user_reporting_modal/user_reporting_modal.js b/src/components/user_reporting_modal/user_reporting_modal.js
index d55b5a9626..9aed47399e 100644
--- a/src/components/user_reporting_modal/user_reporting_modal.js
+++ b/src/components/user_reporting_modal/user_reporting_modal.js
@@ -1,127 +1,105 @@
+import { mapState } from 'pinia'
+
import Checkbox from 'src/components/checkbox/checkbox.vue'
import List from 'src/components/list/list.vue'
import Modal from 'src/components/modal/modal.vue'
import UserLink from 'src/components/user_link/user_link.vue'
import { useReportsStore } from 'src/stores/reports.js'
const UserReportingModal = {
components: {
List,
Checkbox,
Modal,
UserLink,
},
data() {
return {
comment: '',
forward: false,
- statusIdsToReport: [],
+ statusIdsToReport: new Set(),
processing: false,
error: false,
}
},
computed: {
- reportModal() {
- return useReportsStore().reportModal
- },
isLoggedIn() {
return !!this.$store.state.users.currentUser
},
isOpen() {
+ console.log(this.reportModal)
return this.isLoggedIn && this.reportModal.activated
},
userId() {
return this.reportModal.userId
},
user() {
return this.$store.getters.findUser(this.userId)
},
remoteInstance() {
return (
!this.user.is_local &&
this.user.screen_name.substr(this.user.screen_name.indexOf('@') + 1)
)
},
- statuses() {
- return this.reportModal.statuses
- },
- preTickedIds() {
- return this.reportModal.preTickedIds
- },
+ ...mapState(useReportsStore, ['reportModal']),
},
watch: {
userId: 'resetState',
- preTickedIds(newValue) {
- this.statusIdsToReport = newValue
- },
},
methods: {
resetState() {
// Reset state
this.comment = ''
this.forward = false
- this.statusIdsToReport = this.preTickedIds
+ this.statusIdsToReport = new Set(this.reportModal.preTickedIds)
this.processing = false
this.error = false
},
closeModal() {
useReportsStore().closeUserReportingModal()
},
+ onListSelect(selected) {
+ this.statusIdsToReport = selected
+ },
reportUser() {
this.processing = true
this.error = false
const params = {
userId: this.userId,
comment: this.comment,
forward: this.forward,
- statusIds: this.statusIdsToReport,
+ statusIds: [...this.statusIdsToReport],
}
this.$store.state.api.backendInteractor
.reportUser({ ...params })
.then(() => {
this.processing = false
this.resetState()
this.closeModal()
})
.catch(() => {
this.processing = false
this.error = true
})
},
clearError() {
this.error = false
},
- isChecked(statusId) {
- return this.statusIdsToReport.indexOf(statusId) !== -1
- },
- toggleStatus(checked, statusId) {
- if (checked === this.isChecked(statusId)) {
- return
- }
-
- if (checked) {
- this.statusIdsToReport.push(statusId)
- } else {
- this.statusIdsToReport.splice(
- this.statusIdsToReport.indexOf(statusId),
- 1,
- )
- }
- },
resize(e) {
const target = e.target || e
if (!(target instanceof window.Element)) {
return
}
// Auto is needed to make textbox shrink when removing lines
target.style.height = 'auto'
target.style.height = `${target.scrollHeight}px`
if (target.value === '') {
target.style.height = null
}
},
},
}
export default UserReportingModal
diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue
index e99d4c0531..a028ebeb6f 100644
--- a/src/components/user_reporting_modal/user_reporting_modal.vue
+++ b/src/components/user_reporting_modal/user_reporting_modal.vue
@@ -1,160 +1,162 @@
<template>
<Modal
v-if="isOpen"
@backdrop-clicked="closeModal"
>
<div class="user-reporting-panel panel">
<div class="panel-heading">
<i18n-t
tag="h1"
keypath="user_reporting.title"
class="title"
>
<UserLink
class="user-link"
:user="user"
/>
</i18n-t>
</div>
<div class="panel-body">
<div class="user-reporting-panel-left">
<div>
<p>{{ $t('user_reporting.add_comment_description') }}</p>
<textarea
v-model="comment"
class="input form-control"
:placeholder="$t('user_reporting.additional_comments')"
rows="1"
@input="resize"
/>
</div>
<div v-if="!user.is_local">
<p>{{ $t('user_reporting.forward_description') }}</p>
<Checkbox v-model="forward">
{{ $t('user_reporting.forward_to', [remoteInstance]) }}
</Checkbox>
</div>
<div>
<button
class="btn button-default"
:disabled="processing"
@click="reportUser"
>
{{ $t('user_reporting.submit') }}
</button>
<div
v-if="error"
class="alert error"
>
{{ $t('user_reporting.generic_error') }}
</div>
</div>
</div>
<div class="user-reporting-panel-right">
<List
- :external-items="statuses"
+ :external-items="reportModal.statuses"
+ :pre-select="reportModal.preTickedIds"
selectable
+ @select="onListSelect"
>
<template #item="{item}">
<Status
:in-conversation="false"
:focused="false"
:statusoid="item"
/>
</template>
</List>
</div>
</div>
</div>
</Modal>
</template>
<script src="./user_reporting_modal.js"></script>
<style lang="scss">
.user-reporting-panel {
width: 90vw;
max-width: 50rem;
min-height: 20vh;
max-height: 80vh;
.user-link {
display: inline
}
.panel-body {
display: flex;
flex-direction: column-reverse;
border-top: 1px solid;
border-color: var(--border);
overflow: hidden;
}
&-left {
padding: 1.1em 0.7em 0.7em;
line-height: var(--post-line-height);
box-sizing: border-box;
> div {
margin-bottom: 1em;
&:last-child {
margin-bottom: 0;
}
}
p {
margin-top: 0;
}
textarea.form-control {
line-height: 1.1;
resize: none;
overflow: hidden;
transition: min-height 200ms 100ms;
min-height: 44px;
width: 100%;
}
.btn {
min-width: 10em;
padding: 0 2em;
}
.alert {
margin: 1em 0 0;
line-height: 1.3em;
}
}
&-right {
display: flex;
flex-direction: column;
overflow-y: auto;
}
@media all and (width >= 801px) {
.panel-body {
flex-direction: row;
}
&-left {
width: 50%;
max-width: 320px;
border-right: 1px solid;
border-color: var(--border);
padding: 1.1em;
> div {
margin-bottom: 2em;
}
}
&-right {
width: 50%;
flex: 1 1 auto;
margin-bottom: 12px;
}
}
}
</style>
diff --git a/src/stores/admin_settings.js b/src/stores/admin_settings.js
index 04c89da22d..b07315034b 100644
--- a/src/stores/admin_settings.js
+++ b/src/stores/admin_settings.js
@@ -1,465 +1,466 @@
import { cloneDeep, differenceWith, flatten, get, isEqual, set } from 'lodash'
import { defineStore } from 'pinia'
import { parseStatus } from 'src/services/entity_normalizer/entity_normalizer.service.js'
export const defaultState = {
frontends: [],
loaded: false,
needsReboot: null,
config: null,
modifiedPaths: null,
descriptions: null,
draft: null,
dbConfigEnabled: null,
}
export const newUserFlags = {
...defaultState.flagStorage,
}
export const useAdminSettingsStore = defineStore('adminSettings', {
state: () => ({
...cloneDeep(defaultState),
backendInteractor: window.vuex.state.api.backendInteractor,
}),
actions: {
// Configuration Stuff
setInstanceAdminNoDbConfig() {
this.loaded = false
this.dbConfigEnabled = false
},
updateAdminSettings({ config, modifiedPaths }) {
this.loaded = true
this.dbConfigEnabled = true
this.config = config
this.modifiedPaths = modifiedPaths
},
updateAdminDescriptions({ descriptions }) {
this.descriptions = descriptions
},
updateAdminDraft({ path, value }) {
const [group, key, subkey] = path
const parent = [group, key, subkey]
set(this.draft, path, value)
// force-updating grouped draft to trigger refresh of group settings
if (path.length > parent.length) {
set(this.draft, parent, cloneDeep(get(this.draft, parent)))
}
},
resetAdminDraft() {
this.draft = cloneDeep(this.config)
},
loadAdminStuff() {
this.backendInteractor.fetchInstanceDBConfig().then((backendDbConfig) => {
if (backendDbConfig.error) {
if (backendDbConfig.error.status === 400) {
backendDbConfig.error.json().then((errorJson) => {
if (/configurable_from_database/.test(errorJson.error)) {
this.setInstanceAdminNoDbConfig()
}
})
}
} else {
this.setInstanceAdminSettings({ backendDbConfig })
}
})
if (this.descriptions === null) {
this.backendInteractor
.fetchInstanceConfigDescriptions()
.then((backendDescriptions) =>
this.setInstanceAdminDescriptions({ backendDescriptions }),
)
}
},
setInstanceAdminSettings({ backendDbConfig }) {
const config = this.config || {}
const modifiedPaths = new Set()
backendDbConfig.configs.forEach((c) => {
const path = [c.group, c.key]
if (c.db) {
// Path elements can contain dot, therefore we use ' -> ' as a separator instead
// Using strings for modified paths for easier searching
c.db.forEach((x) => modifiedPaths.add([...path, x].join(' -> ')))
}
// we need to preserve tuples on second level only, possibly third
// but it's not a case right now.
const convert = (value, preserveTuples, preserveTuplesLv2) => {
if (Array.isArray(value) && value.length > 0 && value[0].tuple) {
if (!preserveTuples) {
return value.reduce((acc, c) => {
if (c.tuple == null) {
return {
...acc,
[c]: c,
}
}
return {
...acc,
[c.tuple[0]]: convert(c.tuple[1], preserveTuplesLv2),
}
}, {})
} else {
return value.map((x) => x.tuple)
}
} else {
if (!preserveTuples) {
return value
} else {
return value.tuple
}
}
}
// for most stuff we want maps since those are more convenient
// however this doesn't allow for multiple values per same key
// so for those cases we want to preserve tuples as-is
// right now it's made exclusively for :pleroma.:rate_limit
// so it might not work properly elsewhere
const preserveTuples = path.find((x) => x === ':rate_limit')
set(config, path, convert(c.value, false, preserveTuples))
})
// patching http adapter config to be easier to handle
const adapter = config[':pleroma'][':http'][':adapter']
if (Array.isArray(adapter)) {
config[':pleroma'][':http'][':adapter'] = {
[':ssl_options']: {
[':versions']: [],
},
}
}
this.updateAdminSettings({ config, modifiedPaths })
this.resetAdminDraft()
},
setInstanceAdminDescriptions({ backendDescriptions }) {
const convert = (
{ children, description, label, key = '<ROOT>', group, suggestions },
path,
acc,
) => {
const newPath = group ? [group, key] : [key]
const obj = { description, label, suggestions }
if (Array.isArray(children)) {
children.forEach((c) => {
convert(c, newPath, obj)
})
}
set(acc, newPath, obj)
}
const descriptions = {}
backendDescriptions.forEach((d) => convert(d, '', descriptions))
this.updateAdminDescriptions({ descriptions })
},
// This action takes draft state, diffs it with live config state and then pushes
// only differences between the two. Difference detection only work up to subkey (third) level.
pushAdminDraft() {
// TODO cleanup paths in modifiedPaths
const convert = (value) => {
if (typeof value !== 'object') {
return value
} else if (Array.isArray(value)) {
return value.map(convert)
} else {
return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] }))
}
}
// Getting all group-keys used in config
const allGroupKeys = flatten(
Object.entries(this.config).map(([group, lv1data]) =>
Object.keys(lv1data).map((key) => ({ group, key })),
),
)
// Only using group-keys where there are changes detected
const changedGroupKeys = allGroupKeys.filter(({ group, key }) => {
return !isEqual(this.config[group][key], this.draft[group][key])
})
// Here we take all changed group-keys and get all changed subkeys
const changed = changedGroupKeys.map(({ group, key }) => {
const config = this.config[group][key]
const draft = this.draft[group][key]
// We convert group-key value into entries arrays
const eConfig = Object.entries(config)
const eDraft = Object.entries(draft)
// Then those entries array we diff so only changed subkey entries remain
// We use the diffed array to reconstruct the object and then shove it into convert()
return {
group,
key,
value: convert(
Object.fromEntries(differenceWith(eDraft, eConfig, isEqual)),
),
}
})
window.vuex.state.api.backendInteractor
.pushInstanceDBConfig({
payload: {
configs: changed,
},
})
.then(() =>
window.vuex.state.api.backendInteractor.fetchInstanceDBConfig(),
)
.then((backendDbConfig) =>
this.setInstanceAdminSettings({ backendDbConfig }),
)
},
pushAdminSetting({ path, value }) {
const [group, key, ...rest] = Array.isArray(path)
? path
: path.split(/\./g)
const clone = {} // not actually cloning the entire thing to avoid excessive writes
set(clone, rest, value)
// TODO cleanup paths in modifiedPaths
const convert = (value) => {
if (typeof value !== 'object') {
return value
} else if (Array.isArray(value)) {
return value.map(convert)
} else {
return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] }))
}
}
window.vuex.state.api.backendInteractor
.pushInstanceDBConfig({
payload: {
configs: [
{
group,
key,
value: convert(clone),
},
],
},
})
.then(() =>
window.vuex.state.api.backendInteractor.fetchInstanceDBConfig(),
)
.then((backendDbConfig) =>
this.setInstanceAdminSettings({ backendDbConfig }),
)
},
resetAdminSetting({ path }) {
const [group, key, subkey] = Array.isArray(path)
? path
: path.split(/\./g)
this.modifiedPaths.delete(path)
return window.vuex.state.api.backendInteractor
.pushInstanceDBConfig({
payload: {
configs: [
{
group,
key,
delete: true,
subkeys: [subkey],
},
],
},
})
.then(() =>
window.vuex.state.api.backendInteractor.fetchInstanceDBConfig(),
)
.then((backendDbConfig) =>
this.setInstanceAdminSettings({ backendDbConfig }),
)
},
// Frontends Stuff
loadFrontendsStuff() {
this.backendInteractor
.fetchAvailableFrontends()
.then((frontends) => this.setAvailableFrontends({ frontends }))
},
setAvailableFrontends({ frontends }) {
this.frontends = frontends.map((f) => {
f.installedRefs = f.installed_refs
if (f.name === 'pleroma-fe') {
f.refs = ['master', 'develop']
} else {
f.refs = [f.ref]
}
return f
})
},
// Statuses stuff
async fetchStatuses(opts) {
const { total, activities } =
await this.backendInteractor.adminListStatuses({
opts,
})
+ const statuses = activities.map(parseStatus)
+
+ await window.vuex.dispatch('addNewStatuses', { statuses })
+
return {
- items: activities.map(parseStatus),
+ items: statuses,
count: total,
}
},
async changeStatusScope(opts) {
const raw = await this.backendInteractor.adminChangeStatusScope({
opts,
})
const status = parseStatus(raw)
- await window.vuex.dispatch('addNewStatuses', {
- statuses: [status],
- userId: false,
- })
+ await window.vuex.dispatch('addNewStatuses', { statuses: [status] })
},
// Users stuff
async fetchUsers(opts) {
const { users, count } = await this.backendInteractor.adminListUsers({
opts,
})
return {
items: await Promise.all(
users.map(
async (userAdminData) =>
await window.vuex.dispatch('updateUserAdminData', {
userAdminData,
}),
),
),
count,
}
},
async getUserData({ user }) {
const api = this.backendInteractor.adminGetUserData
const { screen_name } = user
const result = await api({ screen_name })
window.vuex.commit('updateUserAdminData', { user: result })
},
async deleteUsers({ users }) {
const screen_names = users.map((u) => u.screen_name)
const api = this.backendInteractor.adminDeleteAccounts
const resultUserIds = await api({ screen_names })
resultUserIds.forEach((userId) => {
window.vuex.dispatch(
'markStatusesAsDeleted',
(status) => userId === status.user.id,
)
// TODO when migrated to pinia, also remove user
})
return resultUserIds
},
resendConfirmationEmail({ users }) {
const screen_names = users.map((u) => u.screen_name)
return this.backendInteractor.adminResendConfirmationEmail({
screen_names,
})
},
requirePasswordChange({ users }) {
const screen_names = users.map((u) => u.screen_name)
return this.backendInteractor.adminRequirePasswordChange({
screen_names,
})
},
// Singular only!
disableMFA({ user }) {
const { screen_name } = user
return this.backendInteractor.adminDisableMFA({ screen_name })
},
async setUsersTags({ users, tags, value }) {
const screen_names = users.map((u) => u.screen_name)
const api = this.backendInteractor.adminSetUsersTags
await api({
screen_names,
tags,
value,
})
users.forEach((user) => {
this.getUserData({ user })
})
},
async setUsersRight({ users, right, value }) {
const screen_names = users.map((u) => u.screen_name)
const api = this.backendInteractor.adminSetUsersRight
await api({
screen_names,
right,
value,
})
users.forEach((user) => {
window.vuex.commit('updateRight', { user, right, value })
})
},
async setUsersActivationStatus({ users, value }) {
const screen_names = users.map((u) => u.screen_name)
const api = this.backendInteractor.adminSetUsersActivationStatus
const resultUsers = await api({
screen_names,
value,
})
resultUsers.forEach((user) => {
window.vuex.commit('updateUserAdminData', { user })
})
},
async setUsersSuggestionStatus({ users, value }) {
const screen_names = users.map((u) => u.screen_name)
const api = this.backendInteractor.adminSetUsersSuggestionStatus
const resultUsers = await api({
screen_names,
value,
})
resultUsers.forEach((user) => {
window.vuex.commit('updateUserAdminData', { user })
})
},
async setUsersConfirmationStatus({ users }) {
const screen_names = users.map((u) => u.screen_name)
const api = this.backendInteractor.adminSetUsersConfirmationStatus
await api({ screen_names })
users.forEach((user) => {
this.getUserData({ user })
})
},
async setUsersApprovalStatus({ users }) {
const screen_names = users.map((u) => u.screen_name)
const api = this.backendInteractor.adminSetUsersApprovalStatus
const resultUsers = await api({
screen_names,
})
resultUsers.forEach((user) => {
window.vuex.commit('updateUserAdminData', { user })
})
},
},
})
diff --git a/src/stores/reports.js b/src/stores/reports.js
index d3acebcb46..b5e2307c45 100644
--- a/src/stores/reports.js
+++ b/src/stores/reports.js
@@ -1,58 +1,60 @@
import { filter } from 'lodash'
import { defineStore } from 'pinia'
import { useInterfaceStore } from 'src/stores/interface.js'
export const useReportsStore = defineStore('reports', {
state: () => ({
reportModal: {
userId: null,
statuses: [],
preTickedIds: [],
activated: false,
},
reports: {},
}),
actions: {
openUserReportingModal({ userId, statusIds = [] }) {
+ console.log('ASS')
const preTickedStatuses = statusIds.map(
(id) => window.vuex.state.statuses.allStatusesObject[id],
)
const preTickedIds = statusIds
+ console.log(preTickedStatuses)
const statuses = preTickedStatuses.concat(
filter(
window.vuex.state.statuses.allStatuses,
(status) =>
status.user.id === userId && !preTickedIds.includes(status.id),
),
)
this.reportModal.userId = userId
this.reportModal.statuses = statuses
this.reportModal.preTickedIds = preTickedIds
this.reportModal.activated = true
},
closeUserReportingModal() {
this.reportModal.activated = false
},
setReportState({ id, state }) {
const oldState = this.reports[id].state
this.reports[id].state = state
window.vuex.state.api.backendInteractor
.setReportState({ id, state })
.catch((e) => {
console.error('Failed to set report state', e)
useInterfaceStore().pushGlobalNotice({
level: 'error',
messageKey: 'general.generic_error_message',
messageArgs: [e.message],
timeout: 5000,
})
this.reports[id].state = oldState
})
},
addReport(report) {
this.reports[report.id] = report
},
},
})

File Metadata

Mime Type
text/x-diff
Expires
Fri, Sep 18, 11:17 PM (28 m, 45 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1768447
Default Alt Text
(40 KB)

Event Timeline