Merge branch 'improve_settings_reusability' into shigusegubu-vue3

This commit is contained in:
Henry Jameson 2023-04-13 00:04:26 +03:00
commit c3b9b41106
17 changed files with 633 additions and 431 deletions

View file

@ -30,14 +30,14 @@
"body-scroll-lock": "3.1.5",
"chromatism": "3.0.0",
"click-outside-vue3": "4.0.1",
"cropperjs": "1.5.12",
"cropperjs": "1.5.13",
"escape-html": "1.0.3",
"js-cookie": "3.0.1",
"localforage": "1.10.0",
"parse-link-header": "2.0.0",
"phoenix": "1.6.2",
"punycode.js": "2.3.0",
"qrcode": "1.5.0",
"qrcode": "1.5.1",
"querystring-es3": "0.2.1",
"url": "0.11.0",
"utf8": "3.0.0",
@ -49,18 +49,18 @@
"vuex": "4.1.0"
},
"devDependencies": {
"@babel/core": "7.21.0",
"@babel/eslint-parser": "7.19.1",
"@babel/plugin-transform-runtime": "7.21.0",
"@babel/preset-env": "7.20.2",
"@babel/core": "7.21.4",
"@babel/eslint-parser": "7.21.3",
"@babel/plugin-transform-runtime": "7.21.4",
"@babel/preset-env": "7.21.4",
"@babel/register": "7.21.0",
"@intlify/vue-i18n-loader": "5.0.0",
"@intlify/vue-i18n-loader": "5.0.1",
"@ungap/event-target": "0.2.3",
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/compiler-sfc": "3.2.45",
"@vue/test-utils": "2.2.8",
"autoprefixer": "10.4.13",
"autoprefixer": "10.4.14",
"babel-loader": "9.1.2",
"babel-plugin-lodash": "3.3.4",
"chai": "4.3.7",
@ -99,14 +99,14 @@
"lodash": "4.17.21",
"mini-css-extract-plugin": "2.7.2",
"mocha": "10.2.0",
"nightwatch": "2.6.11",
"nightwatch": "2.6.19",
"opn": "5.5.0",
"ora": "0.4.1",
"postcss": "8.4.20",
"postcss-html": "^1.5.0",
"postcss-loader": "7.0.2",
"postcss-scss": "^4.0.6",
"sass": "1.57.1",
"sass": "1.60.0",
"sass-loader": "13.2.0",
"selenium-server": "2.53.1",
"semver": "7.3.8",

View file

@ -1,9 +1,13 @@
<template>
<div class="list">
<div
class="list"
role="list"
>
<div
v-for="item in items"
:key="getKey(item)"
class="list-item"
role="listitem"
>
<slot
name="item"

View file

@ -45,6 +45,7 @@ const NavPanel = {
privateMode: state => state.instance.private,
federating: state => state.instance.federating,
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
supportsAnnouncements: state => state.announcements.supportsAnnouncements,
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
}),
pinnedList () {
@ -56,6 +57,7 @@ const NavPanel = {
],
{
hasChats: this.pleromaChatMessagesAvailable,
hasAnnouncements: this.supportsAnnouncements,
isFederating: this.federating,
isPrivate: this.privateMode,
currentUser: this.currentUser
@ -75,6 +77,7 @@ const NavPanel = {
],
{
hasChats: this.pleromaChatMessagesAvailable,
hasAnnouncements: this.supportsAnnouncements,
isFederating: this.federating,
isPrivate: this.privateMode,
currentUser: this.currentUser

View file

@ -35,96 +35,6 @@
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('admin_dash.instance.access') }}</h2>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:public">
PUBLIC
</BooleanSetting>
</li>
<li>
<h3>{{ $t('admin_dash.instance.restrict.header') }}</h3>
</li>
<li>
<ChoiceSetting path=":pleroma.:instance.:limit_to_local_content">
SEARCH RESTRICTION
</ChoiceSetting>
</li>
<li>
<h4>{{ $t('admin_dash.instance.restrict.timelines') }}</h4>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:timelines.:local"
indeterminate-state=":if_instance_is_private"
>
LOCAL TIMELINES
</BooleanSetting>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:timelines.:federated"
indeterminate-state=":if_instance_is_private"
>
FED TIMELINES
</BooleanSetting>
</li>
<li>
<GroupSetting path=":pleroma.:restrict_unauthenticated.:timelines">
TIMELINES
</GroupSetting>
</li>
<li>
<h4>{{ $t('admin_dash.instance.restrict.profiles') }}</h4>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:profiles.:local"
indeterminate-state=":if_instance_is_private"
>
LOCAL PROFILES
</BooleanSetting>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:profiles.:remote"
indeterminate-state=":if_instance_is_private"
>
FED PROFILES
</BooleanSetting>
</li>
<li>
<GroupSetting path=":pleroma.:restrict_unauthenticated.:profiles">
PROFILES
</GroupSetting>
</li>
<li>
<h4>{{ $t('admin_dash.instance.restrict.activities') }}</h4>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:activities.:local"
indeterminate-state=":if_instance_is_private"
>
LOCAL STATUSES
</BooleanSetting>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:activities.:remote"
indeterminate-state=":if_instance_is_private"
>
FED STATUSES
</BooleanSetting>
</li>
<li>
<GroupSetting path=":pleroma.:restrict_unauthenticated.:activities">
STATUSES
</GroupSetting>
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('admin_dash.instance.registrations') }}</h2>
<ul class="setting-list">
@ -210,6 +120,108 @@
</li>
</ul>
</div>
<div class="setting-item">
<h2>{{ $t('admin_dash.instance.access') }}</h2>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:public">
PUBLIC
</BooleanSetting>
</li>
<li>
<h3>{{ $t('admin_dash.instance.restrict.header') }}</h3>
</li>
<li>
<ChoiceSetting path=":pleroma.:instance.:limit_to_local_content">
SEARCH RESTRICTION
</ChoiceSetting>
</li>
<li>
<h4>{{ $t('admin_dash.instance.restrict.timelines') }}</h4>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:timelines.:local"
indeterminate-state=":if_instance_is_private"
swap-description-and-label
hide-description
>
LOCAL TIMELINES
</BooleanSetting>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:timelines.:federated"
indeterminate-state=":if_instance_is_private"
swap-description-and-label
hide-description
>
FED TIMELINES
</BooleanSetting>
</li>
<li>
<GroupSetting path=":pleroma.:restrict_unauthenticated.:timelines">
TIMELINES
</GroupSetting>
</li>
<li>
<h4>{{ $t('admin_dash.instance.restrict.profiles') }}</h4>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:profiles.:local"
indeterminate-state=":if_instance_is_private"
swap-description-and-label
hide-description
>
LOCAL PROFILES
</BooleanSetting>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:profiles.:remote"
indeterminate-state=":if_instance_is_private"
swap-description-and-label
hide-description
>
FED PROFILES
</BooleanSetting>
</li>
<li>
<GroupSetting path=":pleroma.:restrict_unauthenticated.:profiles">
PROFILES
</GroupSetting>
</li>
<li>
<h4>{{ $t('admin_dash.instance.restrict.activities') }}</h4>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:activities.:local"
indeterminate-state=":if_instance_is_private"
swap-description-and-label
hide-description
>
LOCAL STATUSES
</BooleanSetting>
</li>
<li>
<BooleanSetting
path=":pleroma.:restrict_unauthenticated.:activities.:remote"
indeterminate-state=":if_instance_is_private"
swap-description-and-label
hide-description
>
FED STATUSES
</BooleanSetting>
</li>
<li>
<GroupSetting path=":pleroma.:restrict_unauthenticated.:activities">
STATUSES
</GroupSetting>
</li>
</ul>
</div>
</div>
</template>

View file

@ -14,7 +14,7 @@
class="label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescription">
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel }}
</template>
<template v-else>

View file

@ -3,7 +3,7 @@
v-if="matchesExpertLevel"
class="ChoiceSetting"
>
<template v-if="backendDescription">
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel }}
</template>
<template v-else>

View file

@ -4,7 +4,7 @@
class="NumberSetting"
>
<label :for="path" :class="{ 'faint': shouldBeDisabled }">
<template v-if="backendDescription">
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else>

View file

@ -33,6 +33,18 @@ export default {
type: String,
default: undefined
},
hideDescription: {
type: Boolean
},
swapDescriptionAndLabel: {
type: Boolean
},
overrideBackendDescription: {
type: Boolean
},
overrideBackendDescriptionLabel: {
type: Boolean
},
draftMode: {
type: Boolean,
default: undefined
@ -95,10 +107,35 @@ export default {
return get(this.$store.state.adminSettings.descriptions, this.path)
},
backendDescriptionLabel () {
return this.backendDescription?.label
if (this.realSource !== 'admin') return ''
if (!this.backendDescription || this.overrideBackendDescriptionLabel) {
return this.$t([
'admin_dash',
'temp_overrides',
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
'label'
].join('.'))
} else {
return this.swapDescriptionAndLabel
? this.backendDescription?.description
: this.backendDescription?.label
}
},
backendDescriptionDescription () {
return this.backendDescription?.description
if (this.realSource !== 'admin') return ''
if (this.hideDescription) return null
if (!this.backendDescription || this.overrideBackendDescription) {
return this.$t([
'admin_dash',
'temp_overrides',
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
'description'
].join('.'))
} else {
return this.swapDescriptionAndLabel
? this.backendDescription?.label
: this.backendDescription?.description
}
},
backendDescriptionSuggestions () {
return this.backendDescription?.suggestions

View file

@ -4,7 +4,7 @@
class="StringSetting"
>
<label :for="path" :class="{ 'faint': shouldBeDisabled }">
<template v-if="backendDescription">
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else>

View file

@ -9,17 +9,20 @@ import DomainMuteCard from 'src/components/domain_mute_card/domain_mute_card.vue
import SelectableList from 'src/components/selectable_list/selectable_list.vue'
import ProgressButton from 'src/components/progress_button/progress_button.vue'
import withSubscription from 'src/components/../hocs/with_subscription/with_subscription'
import withLoadMore from 'src/components/../hocs/with_load_more/with_load_more'
import Checkbox from 'src/components/checkbox/checkbox.vue'
const BlockList = withSubscription({
const BlockList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchBlocks'),
select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),
destroy: () => {},
childPropName: 'items'
})(SelectableList)
const MuteList = withSubscription({
const MuteList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchMutes'),
select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),
destroy: () => {},
childPropName: 'items'
})(SelectableList)

View file

@ -98,7 +98,7 @@ const withLoadMore = ({
</button>
}
{!this.error && this.loading && <FAIcon spin icon="circle-notch"/>}
{!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>}
{!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries} role="button" tabindex="0">{this.$t('general.more')}</a>}
</div>
</div>
)

View file

@ -864,7 +864,14 @@
"instance": "Instance information",
"registrations": "User sign-ups",
"captcha_header": "CAPTCHA",
"kocaptcha": "KoCaptcha settings"
"kocaptcha": "KoCaptcha settings",
"access": "Instance access",
"restrict": {
"header": "Restrict access",
"timelines": "Timelines access",
"profiles": "User profiles access",
"activities": "Statues/activities access"
}
},
"limits": {
"arbitrary_limits": "Arbitrary limits",
@ -890,6 +897,20 @@
"default_frontend_tip": "Default front-end will be shown to all users. Currently there's no way to for a user to select personal front-end. If you switch away from PleromaFE you'll most likely have to use old and buggy AdminFE to do instance configuration until we replace it.",
"default_frontend_tip2": "WIP: Since Pleroma backend doesn't properly list all installed frontends you'll have to enter name and reference manually. List below provides shortcuts to fill the values.",
"available_frontends": "Available for install"
},
"temp_overrides": {
":pleroma": {
":instance": {
":description_limit": {
"label": "Limit",
"description": "Character limit for attachment descriptions"
},
":background_image": {
"label": "Background image",
"description": "Background image (primarily used by PleromaFE)"
}
}
}
}
},
"time": {
@ -939,6 +960,7 @@
"repeat_confirm_accept_button": "Repeat",
"repeat_confirm_cancel_button": "Do not repeat",
"delete": "Delete status",
"delete_error": "Error deleting status: {0}",
"edit": "Edit status",
"edited_at": "(last edited {time})",
"pin": "Pin on profile",

View file

@ -65,6 +65,7 @@ const chats = {
const newChatMessageSideEffects = (chat) => {
maybeShowChatNotification(store, chat)
}
commit('addNewUsers', chats.map(k => k.account).filter(k => k))
commit('addNewChats', { dispatch, chats, rootGetters, newChatMessageSideEffects })
},
updateChat ({ commit }, { chat }) {

View file

@ -615,9 +615,19 @@ const statuses = {
fetchStatusHistory ({ rootState, dispatch }, status) {
return apiService.fetchStatusHistory({ status })
},
deleteStatus ({ rootState, commit }, status) {
commit('setDeleted', { status })
deleteStatus ({ rootState, commit, dispatch }, status) {
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
.then((_) => {
commit('setDeleted', { status })
})
.catch((e) => {
dispatch('pushGlobalNotice', {
level: 'error',
messageKey: 'status.delete_error',
messageArgs: [e.message],
timeout: 5000
})
})
},
deleteStatusById ({ rootState, commit }, id) {
const status = rootState.statuses.allStatusesObject[id]

View file

@ -195,9 +195,15 @@ export const mutations = {
state.currentUser.blockIds.push(blockId)
}
},
setBlockIdsMaxId (state, blockIdsMaxId) {
state.currentUser.blockIdsMaxId = blockIdsMaxId
},
saveMuteIds (state, muteIds) {
state.currentUser.muteIds = muteIds
},
setMuteIdsMaxId (state, muteIdsMaxId) {
state.currentUser.muteIdsMaxId = muteIdsMaxId
},
addMuteId (state, muteId) {
if (state.currentUser.muteIds.indexOf(muteId) === -1) {
state.currentUser.muteIds.push(muteId)
@ -320,10 +326,20 @@ const users = {
.then((inLists) => store.commit('updateUserInLists', { id, inLists }))
}
},
fetchBlocks (store) {
return store.rootState.api.backendInteractor.fetchBlocks()
fetchBlocks (store, args) {
const { reset } = args || {}
const maxId = store.state.currentUser.blockIdsMaxId
return store.rootState.api.backendInteractor.fetchBlocks({ maxId })
.then((blocks) => {
store.commit('saveBlockIds', map(blocks, 'id'))
if (reset) {
store.commit('saveBlockIds', map(blocks, 'id'))
} else {
map(blocks, 'id').map(id => store.commit('addBlockId', id))
}
if (blocks.length) {
store.commit('setBlockIdsMaxId', last(blocks).id)
}
store.commit('addNewUsers', blocks)
return blocks
})
@ -346,10 +362,20 @@ const users = {
editUserNote (store, args) {
return editUserNote(store, args)
},
fetchMutes (store) {
return store.rootState.api.backendInteractor.fetchMutes()
fetchMutes (store, args) {
const { reset } = args || {}
const maxId = store.state.currentUser.muteIdsMaxId
return store.rootState.api.backendInteractor.fetchMutes({ maxId })
.then((mutes) => {
store.commit('saveMuteIds', map(mutes, 'id'))
if (reset) {
store.commit('saveMuteIds', map(mutes, 'id'))
} else {
map(mutes, 'id').map(id => store.commit('addMuteId', id))
}
if (mutes.length) {
store.commit('setMuteIdsMaxId', last(mutes).id)
}
store.commit('addNewUsers', mutes)
return mutes
})

View file

@ -845,7 +845,7 @@ const postStatus = ({
})
if (pollOptions.some(option => option !== '')) {
const normalizedPoll = {
expires_in: poll.expiresIn,
expires_in: parseInt(poll.expiresIn, 10),
multiple: poll.multiple
}
Object.keys(normalizedPoll).forEach(key => {
@ -902,7 +902,7 @@ const editStatus = ({
if (pollOptions.some(option => option !== '')) {
const normalizedPoll = {
expires_in: poll.expiresIn,
expires_in: parseInt(poll.expiresIn, 10),
multiple: poll.multiple
}
Object.keys(normalizedPoll).forEach(key => {
@ -928,8 +928,9 @@ const editStatus = ({
}
const deleteStatus = ({ id, credentials }) => {
return fetch(MASTODON_DELETE_URL(id), {
headers: authHeaders(credentials),
return promisedRequest({
url: MASTODON_DELETE_URL(id),
credentials,
method: 'DELETE'
})
}
@ -1118,8 +1119,12 @@ const generateMfaBackupCodes = ({ credentials }) => {
}).then((data) => data.json())
}
const fetchMutes = ({ credentials }) => {
return promisedRequest({ url: MASTODON_USER_MUTES_URL, credentials })
const fetchMutes = ({ maxId, credentials }) => {
const query = new URLSearchParams({ with_relationships: true })
if (maxId) {
query.append('max_id', maxId)
}
return promisedRequest({ url: `${MASTODON_USER_MUTES_URL}?${query.toString()}`, credentials })
.then((users) => users.map(parseUser))
}
@ -1143,8 +1148,12 @@ const unsubscribeUser = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_UNSUBSCRIBE_USER(id), credentials, method: 'POST' })
}
const fetchBlocks = ({ credentials }) => {
return promisedRequest({ url: MASTODON_USER_BLOCKS_URL, credentials })
const fetchBlocks = ({ maxId, credentials }) => {
const query = new URLSearchParams({ with_relationships: true })
if (maxId) {
query.append('max_id', maxId)
}
return promisedRequest({ url: `${MASTODON_USER_BLOCKS_URL}?${query.toString()}`, credentials })
.then((users) => users.map(parseUser))
}

683
yarn.lock

File diff suppressed because it is too large Load diff