adding jsdoc

This commit is contained in:
luce 2025-09-09 11:38:25 +02:00
commit 05b4cbe6d6
5 changed files with 250 additions and 80 deletions

View file

@ -5,18 +5,30 @@ const PageList = {
SelectableList SelectableList
}, },
props: { props: {
/**
* only make the checkbox clickable to toggle, not the whole area
*/
boxOnly: { boxOnly: {
type: Boolean, type: Boolean,
default: false default: false
}, },
/**
* how many entries to fetch at once
*/
pageSize: { pageSize: {
type: Number, type: Number,
default: 50 default: 50
}, },
/**
* the function/callback used to fetch new entries (one page)
*/
fetchPage: { fetchPage: {
type: Function, type: Function,
default: async () => [] default: async () => []
}, },
/**
* wether or not this is a single page list (so it won't allow fetching more pages)
*/
singlePage: { singlePage: {
type: Boolean, type: Boolean,
default: false default: false
@ -31,6 +43,9 @@ const PageList = {
} }
}, },
methods: { methods: {
/**
* reset and load first page
*/
reset () { reset () {
this.canLoadMore = true this.canLoadMore = true
this.pageIndex = 1 this.pageIndex = 1
@ -38,6 +53,9 @@ const PageList = {
this.isLoading = false this.isLoading = false
this.loadMore() // load one page this.loadMore() // load one page
}, },
/**
* load another page
*/
loadMore () { loadMore () {
if (!this.isLoading && this.canLoadMore) { if (!this.isLoading && this.canLoadMore) {
this.isLoading = true this.isLoading = true
@ -50,10 +68,17 @@ const PageList = {
}) })
} }
}, },
/**
* get currently selected elements
* @returns {Array}
*/
getSelected () { getSelected () {
return this.$refs.list.selected return this.$refs.list.selected
} }
}, },
/**
* auto-load first page when mounted
*/
mounted () { mounted () {
this.loadMore() this.loadMore()
} }

View file

@ -6,9 +6,23 @@ import Modal from 'src/components/modal/modal.vue'
const AdminCard = { const AdminCard = {
props: { props: {
/**
* minimal user info
* @type {import('vue').PropType<{
* id: string,
* _original: {
* is_approved: boolean;
* is_confirmed: boolean;
* };
* }>}
*/
userDetails: { userDetails: {
type: Object, type: Object,
required: true, required: true,
/**
* @param {any} u
* @returns {u is { id: string; _original: { is_approved; is_confirmed: boolean; } } }
*/
validator (u) { validator (u) {
return ( return (
typeof(u.id) === 'string' && typeof(u.id) === 'string' &&
@ -22,7 +36,8 @@ const AdminCard = {
data () { data () {
return { return {
progress: false, progress: false,
topLevelExpanded: false, detailsExpanded: false,
topLevelExpanded: false, // REMOVE
jsonExpanded: false, jsonExpanded: false,
timelineExpanded: false, timelineExpanded: false,
justApproved: false, justApproved: false,
@ -31,15 +46,28 @@ const AdminCard = {
} }
}, },
computed: { computed: {
/**
* checks if the user is defined
* @returns {boolean}
*/
isLoaded () { isLoaded () {
return typeof(this.user) !== 'undefined' return typeof(this.user) !== 'undefined'
}, },
/**
* @returns {object} user info
*/
user () { user () {
return this.$store.getters.findUser(this.userDetails.id) return this.$store.getters.findUser(this.userDetails.id)
}, },
/**
* @returns {object} user relationship
*/
relationship () { relationship () {
return this.$store.getters.relationship(this.userDetails.id) return this.$store.getters.relationship(this.userDetails.id)
}, },
/**
* @returns {boolean} is user local
*/
isLocal () { isLocal () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
if (typeof(u) !== 'undefined') { if (typeof(u) !== 'undefined') {
@ -47,6 +75,9 @@ const AdminCard = {
} }
return false return false
}, },
/**
* @returns {boolean} is user admin
*/
isAdmin () { isAdmin () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
if (typeof(u) !== 'undefined') { if (typeof(u) !== 'undefined') {
@ -54,6 +85,9 @@ const AdminCard = {
} }
return false return false
}, },
/**
* @returns {boolean} is user moderator
*/
isModerator () { isModerator () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
if (typeof(u) !== 'undefined') { if (typeof(u) !== 'undefined') {
@ -61,6 +95,9 @@ const AdminCard = {
} }
return false return false
}, },
/**
* @returns {boolean} is user active
*/
isActivated () { isActivated () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
if (typeof(u) !== 'undefined') { if (typeof(u) !== 'undefined') {
@ -68,10 +105,16 @@ const AdminCard = {
} }
return false return false
}, },
/**
* @returns {boolean} has this user been confirmed
*/
isConfirmed () { isConfirmed () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
return (u._original.is_confirmed === true) || (this.justConfirmed === true) return (u._original.is_confirmed === true) || (this.justConfirmed === true)
}, },
/**
* @returns {boolean} has this user been approved
*/
isApproved () { isApproved () {
return (this.userDetails._original.is_approved === true) || (this.justApproved === true) return (this.userDetails._original.is_approved === true) || (this.justApproved === true)
} }
@ -84,7 +127,10 @@ const AdminCard = {
Modal Modal
}, },
methods: { methods: {
toggleAdmin (v) { /**
* @param {boolean} v set admin status
*/
setAdmin (v) {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
if (v === true) { if (v === true) {
this.$store.dispatch('adminAddUserToAdminGroup', u) this.$store.dispatch('adminAddUserToAdminGroup', u)
@ -92,7 +138,10 @@ const AdminCard = {
this.$store.dispatch('adminRemoveUserFromAdminGroup', u) this.$store.dispatch('adminRemoveUserFromAdminGroup', u)
} }
}, },
toggleModerator (v) { /**
* @param {boolean} v set moderator status
*/
setModerator (v) {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
if (v === true) { if (v === true) {
this.$store.dispatch('adminAddUserToModeratorGroup', u) this.$store.dispatch('adminAddUserToModeratorGroup', u)
@ -100,7 +149,10 @@ const AdminCard = {
this.$store.dispatch('adminRemoveUserFromModeratorGroup', u) this.$store.dispatch('adminRemoveUserFromModeratorGroup', u)
} }
}, },
toggleActivation (v) { /**
* @param {boolean} v set activation status
*/
setActivation (v) {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
if (v === true) { if (v === true) {
this.$store.dispatch('adminActivateUser', u) this.$store.dispatch('adminActivateUser', u)
@ -108,28 +160,47 @@ const AdminCard = {
this.$store.dispatch('adminDeactivateUser', u) this.$store.dispatch('adminDeactivateUser', u)
} }
}, },
/**
* confirm this user
*/
confirmUser () { confirmUser () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
this.$store.dispatch('adminConfirmUser', u) this.$store.dispatch('adminConfirmUser', u)
this.just_confirmed = true this.just_confirmed = true
}, },
/**
* try resending the confirmation email
*/
resendConfirmationEmail () { resendConfirmationEmail () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
this.$store.dispatch('adminResendConfirmationEmail', u) this.$store.dispatch('adminResendConfirmationEmail', u)
}, },
toggleApproval () { /**
* approve this user
*/
approveUser () {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
this.$store.dispatch('adminApproveUser', u) this.$store.dispatch('adminApproveUser', u)
}, },
/**
* update user info from server
*/
forceUpdateUser () { forceUpdateUser () {
this.$store.dispatch('fetchUser', this.userDetails.id) this.$store.dispatch('fetchUser', this.userDetails.id)
}, },
/**
* delete selected statuses
*/
deleteSelection () { deleteSelection () {
const l = this.$refs.timelineList const l = this.$refs.timelineList
const s = l.getSelected() const s = l.getSelected()
s.forEach(p => this.$store.dispatch('deleteStatus', p)) s.forEach(p => this.$store.dispatch('deleteStatus', p))
l.reset() l.reset()
}, },
/**
* delete this user. keep in mind that user deletion is not intuitive in pleroma backend.
* it actually deletes all content of a user. the user itself will keep showing up in search results.
*/
deleteUser () { deleteUser () {
if (!this.justDeleted) { if (!this.justDeleted) {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
@ -137,7 +208,12 @@ const AdminCard = {
this.justDeleted = true this.justDeleted = true
} }
}, },
fetchStatuses (store, opts) { /**
* @param {object} store
* @param {object} opts
* @returns {Promise<Array<object>>} statuses
*/
async fetchStatuses (store, opts) {
const u = this.$store.getters.findUser(this.userDetails.id) const u = this.$store.getters.findUser(this.userDetails.id)
const res = store.dispatch('adminListStatuses', { user: u, opts: { pageSize: opts.pageSize, godmode: true, withReblogs: true}}) const res = store.dispatch('adminListStatuses', { user: u, opts: { pageSize: opts.pageSize, godmode: true, withReblogs: true}})
return res.then(r => r.activities) return res.then(r => r.activities)

View file

@ -5,37 +5,26 @@
</div> </div>
<div v-else> <div v-else>
<BasicUserCard :user="user" /> <BasicUserCard :user="user" />
<div v-if="!topLevelExpanded"> <button
<button class="button button-default btn"
class="button button-default btn" type="button"
type="button" @click="detailsExpanded = true"
@click="topLevelExpanded = true" >
> {{ $t('admin_dash.users.details') }}
{{ $t('admin_dash.users.expand_user') }} </button>
</button>
</div>
<div <div
v-else v-if="detailsExpanded"
> >
<Modal <Modal
@backdrop-clicked="() => { topLevelExpanded = false }" @backdrop-clicked="() => { detailsExpanded = false }"
> >
<ul class="setting-list"> <ul class="setting-list">
<li>
<button
class="button button-default btn"
type="button"
@click="topLevelExpanded = false"
>
{{ $t('admin_dash.users.collapse_user') }}
</button>
</li>
<li <li
v-if="isLocal" v-if="isLocal"
> >
<Checkbox <Checkbox
:model-value="isAdmin" :model-value="isAdmin"
@update:model-value="v => toggleAdmin(v)" @update:model-value="v => setAdmin(v)"
> >
{{ $t('admin_dash.users.is_admin') }} {{ $t('admin_dash.users.is_admin') }}
</Checkbox> </Checkbox>
@ -45,7 +34,7 @@
> >
<Checkbox <Checkbox
:model-value="isModerator" :model-value="isModerator"
@update:model-value="v => toggleModerator(v)" @update:model-value="v => setModerator(v)"
> >
{{ $t('admin_dash.users.is_moderator') }} {{ $t('admin_dash.users.is_moderator') }}
</Checkbox> </Checkbox>
@ -78,7 +67,7 @@
<button <button
class="button button-default btn" class="button button-default btn"
type="button" type="button"
@click="toggleApproval(true)" @click="approveUser()"
> >
{{ $t('admin_dash.users.approve') }} {{ $t('admin_dash.users.approve') }}
</button> </button>
@ -86,7 +75,7 @@
<li> <li>
<Checkbox <Checkbox
:model-value="isActivated" :model-value="isActivated"
@update:model-value="v => toggleActivation(v)" @update:model-value="v => setActivation(v)"
> >
{{ $t('admin_dash.users.is_active') }} {{ $t('admin_dash.users.is_active') }}
</Checkbox> </Checkbox>
@ -195,7 +184,7 @@
{{ $t('admin_dash.users.expand_raw_info') }} {{ $t('admin_dash.users.expand_raw_info') }}
</button> </button>
</div> </div>
<div <div
v-else v-else
class="setting-item" class="setting-item"
> >

View file

@ -4,53 +4,92 @@ import Status from 'src/components/status/status.vue'
import { parseStatus } from 'src/services/entity_normalizer/entity_normalizer.service.js' import { parseStatus } from 'src/services/entity_normalizer/entity_normalizer.service.js'
const AdminStatusCard = { const AdminStatusCard = {
props: ['statusDetails'], props: {
data () { /**
return { * minimal status info
jsonExpanded: false, * @type {import('vue').PropType<{
statusCache: undefined, * id: string
* }>}
*/
statusDetails: {
type: Object,
required: true,
/**
* @param {any} u
* @returns {u is { id: string }}
*/
validator (u) {
return typeof(u.id) === 'string'
} }
}, }
computed: { },
isSensitive () { data () {
return this.statusDetails.sensitive === true return {
}, jsonExpanded: false,
visibility () { statusCache: undefined,
return this.statusDetails.visibility }
} },
}, computed: {
methods: { /**
changeSensitivity (v) { * @returns {boolean} is this status sensitive?
this.$store.dispatch('adminChangeStatusScope', { opts: { id: this.statusDetails.id, sensitive: v }}).then(res => parseStatus(res)).then(s => this.statusCache = s) */
}, isSensitive () {
changeVisibility (v) { return this.statusDetails.sensitive === true
this.$store.dispatch('adminChangeStatusScope', { opts: { id: this.statusDetails.id, visibility: v }}).then(res => parseStatus(res)).then(s => this.statusCache = s) },
}, /**
// show popup * @returns {'public' | 'unlisted' | 'private' | 'direct'} status visibility
confirmSelection(box) { */
this.$refs[box].show() visibility () {
this.$refs.dropdown.hidePopover() return this.statusDetails.visibility
}, }
// do the thing },
selectionConfirmed(action, opts) { methods: {
const restricted = [] /**
const s = this.$refs.userList.getSelected() * @param {boolean} v set sensitive
s.forEach(u => { */
if (restricted.includes(action) !== false || u.id !== this.$store.state.users.currentUser.id) { changeSensitivity (v) {
this.$store.dispatch(action, { id: this.statusDetails.id, ...(opts || {}) }) this.$store.dispatch('adminChangeStatusScope', { opts: { id: this.statusDetails.id, sensitive: v }}).then(res => parseStatus(res)).then(s => this.statusCache = s)
} },
}) /**
this.reset() * @param {boolean} v set visible
} */
}, changeVisibility (v) {
components: { this.$store.dispatch('adminChangeStatusScope', { opts: { id: this.statusDetails.id, visibility: v }}).then(res => parseStatus(res)).then(s => this.statusCache = s)
Checkbox, },
Select, /**
Status, * show the confirmation box for bulk actions.
}, * @param {string} box ref name specified for the confirm component
mounted () { */
this.$store.dispatch('adminChangeStatusScope', { opts: { id: this.statusDetails.id }}).then(res => parseStatus(res)).then(s => this.statusCache = s) confirmSelection(box) {
} this.$refs[box].show()
this.$refs.dropdown.hidePopover()
},
/**
* called when a bulk action was confirmed
* @param {string} action
*/
selectionConfirmed(action, opts) {
const restricted = []
const s = this.$refs.userList.getSelected()
s.forEach(u => {
if (restricted.includes(action) !== false || u.id !== this.$store.state.users.currentUser.id) {
this.$store.dispatch(action, { id: this.statusDetails.id, ...(opts || {}) })
}
})
this.reset()
}
},
components: {
Checkbox,
Select,
Status,
},
/**
* fetch and cache status info
*/
mounted () {
this.$store.dispatch('adminChangeStatusScope', { opts: { id: this.statusDetails.id }}).then(res => parseStatus(res)).then(s => this.statusCache = s)
}
} }
export default AdminStatusCard export default AdminStatusCard

View file

@ -31,21 +31,45 @@ const UsersTab = {
} }
}, },
computed: { computed: {
/**
* do we filter for admins?
* @returns {boolean}
*/
filtersIsAdmin () { filtersIsAdmin () {
return this.filtersPrivileges === 'admin' || this.filtersPrivileges === 'modsnadmins' return this.filtersPrivileges === 'admin' || this.filtersPrivileges === 'modsnadmins'
}, },
/**
* do we filter for moderators?
* @returns {boolean}
*/
filtersIsModerator () { filtersIsModerator () {
return this.filtersPrivileges === 'moderator' || this.filtersPrivileges === 'modsnadmins' return this.filtersPrivileges === 'moderator' || this.filtersPrivileges === 'modsnadmins'
}, },
/**
* do we filter for active users?
* @returns {boolean}
*/
filtersActive () { filtersActive () {
return this.filtersActivity === 'active' return this.filtersActivity === 'active'
}, },
/**
* do we filter for deactivated users?
* @returns {boolean}
*/
filtersDeactivated () { filtersDeactivated () {
return this.filtersActivity === 'deactivated' return this.filtersActivity === 'deactivated'
}, },
/**
* do we filter for local users?
* @returns {boolean}
*/
filtersLocal () { filtersLocal () {
return this.filtersOrigin === 'local' return this.filtersOrigin === 'local'
}, },
/**
* do we filter for external users?
* @return {boolean}
*/
filtersExternal () { filtersExternal () {
return this.filtersOrigin === 'external' return this.filtersOrigin === 'external'
} }
@ -55,13 +79,18 @@ const UsersTab = {
Select, Select,
BasicUserCard, BasicUserCard,
PageList, PageList,
ProgressButton, ProgressButton,
AdminCard, AdminCard,
TabSwitcher, TabSwitcher,
Popover, Popover,
GenericConfirm GenericConfirm
}, },
methods: { methods: {
/**
* fetch a new page of users via admin-api
* @param {object} store
* @param {object} opts
*/
fetchPage (store, opts) { fetchPage (store, opts) {
if(!this.init) return new Promise(() => []) if(!this.init) return new Promise(() => [])
const filters = { const filters = {
@ -83,15 +112,24 @@ const UsersTab = {
const users = store.dispatch('fetchAdminUsers', nopts) const users = store.dispatch('fetchAdminUsers', nopts)
return users return users
}, },
/**
* reset the userlist explicitly
*/
reset () { reset () {
this.$refs.userList.reset() this.$refs.userList.reset()
}, },
// show popup /**
* show the confirmation box for bulk actions.
* @param {string} box ref name specified for the confirm component
*/
confirmSelection(box) { confirmSelection(box) {
this.$refs[box].show() this.$refs[box].show()
this.$refs.dropdown.hidePopover() this.$refs.dropdown.hidePopover()
}, },
// do the thing /**
* called when a bulk action was confirmed
* @param {string} action
*/
selectionConfirmed(action) { selectionConfirmed(action) {
const restricted = [] const restricted = []
const s = this.$refs.userList.getSelected() const s = this.$refs.userList.getSelected()
@ -103,6 +141,9 @@ const UsersTab = {
this.reset() this.reset()
} }
}, },
/**
* mark as initialized and reset user list
*/
mounted () { mounted () {
this.init = true this.init = true
this.reset() this.reset()