Merge branch 'weight-removal' into shigusegubu-themes3
This commit is contained in:
commit
37667b20ea
105 changed files with 1843 additions and 5315 deletions
5
.babelrc
5
.babelrc
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"presets": ["@babel/preset-env"],
|
|
||||||
"plugins": ["@babel/plugin-transform-runtime", "@vue/babel-plugin-jsx"],
|
|
||||||
"comments": true
|
|
||||||
}
|
|
||||||
|
|
@ -14,7 +14,7 @@ labels:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
build:
|
build:
|
||||||
image: docker.io/node:26-alpine
|
image: docker.io/node:24-alpine
|
||||||
commands:
|
commands:
|
||||||
- apk add --no-cache zip git
|
- apk add --no-cache zip git
|
||||||
- yarn --frozen-lockfile
|
- yarn --frozen-lockfile
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ labels:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
build:
|
build:
|
||||||
image: docker.io/node:26-alpine
|
image: docker.io/node:24-alpine
|
||||||
commands:
|
commands:
|
||||||
- yarn --frozen-lockfile
|
- yarn --frozen-lockfile
|
||||||
- yarn build
|
- yarn build
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,10 @@ when:
|
||||||
steps:
|
steps:
|
||||||
install-depends:
|
install-depends:
|
||||||
image: &node-image
|
image: &node-image
|
||||||
docker.io/node:26-alpine
|
docker.io/node:24-alpine
|
||||||
commands:
|
commands:
|
||||||
- yarn --frozen-lockfile
|
- yarn --frozen-lockfile
|
||||||
|
|
||||||
eslint:
|
|
||||||
image: *node-image
|
|
||||||
depends_on: install-depends
|
|
||||||
commands:
|
|
||||||
- yarn ci-eslint
|
|
||||||
|
|
||||||
biome:
|
biome:
|
||||||
image: *node-image
|
image: *node-image
|
||||||
depends_on: install-depends
|
depends_on: install-depends
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ steps:
|
||||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
||||||
entrypoint: *script_file_entrypoint
|
entrypoint: *script_file_entrypoint
|
||||||
commands:
|
commands:
|
||||||
|
- npm install --global yarn
|
||||||
- |
|
- |
|
||||||
if [ "${CI_PIPELINE_EVENT}" != "pull_request" ]; then
|
if [ "${CI_PIPELINE_EVENT}" != "pull_request" ]; then
|
||||||
mkdir -pv $APT_CACHE_DIR && apt-get -qq update
|
mkdir -pv $APT_CACHE_DIR && apt-get -qq update
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
|
"$schema": "https://biomejs.dev/schemas/2.5.11/schema.json",
|
||||||
"vcs": {
|
"vcs": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"clientKind": "git",
|
"clientKind": "git",
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
"vue": "recommended"
|
"vue": "recommended"
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": false,
|
"preset": "none",
|
||||||
"complexity": {
|
"complexity": {
|
||||||
"noAdjacentSpacesInRegex": "error",
|
"noAdjacentSpacesInRegex": "error",
|
||||||
"noExtraBooleanCast": "error",
|
"noExtraBooleanCast": "error",
|
||||||
|
|
@ -80,6 +80,9 @@
|
||||||
"noUselessRegexBackrefs": "error",
|
"noUselessRegexBackrefs": "error",
|
||||||
"noWith": "error",
|
"noWith": "error",
|
||||||
"useGetterReturn": "error"
|
"useGetterReturn": "error"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"useVueMultiWordComponentNames": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
||||||
import vue from 'eslint-plugin-vue'
|
|
||||||
|
|
||||||
export default defineConfig([
|
|
||||||
...vue.configs['flat/recommended'],
|
|
||||||
globalIgnores(['**/*.js', 'build/', 'dist/', 'config/']),
|
|
||||||
{
|
|
||||||
files: ['src/**/*.vue'],
|
|
||||||
languageOptions: {
|
|
||||||
parserOptions: {
|
|
||||||
parser: '@babel/eslint-parser',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'vue/require-prop-types': 0,
|
|
||||||
'vue/multi-word-component-names': 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
23
package.json
23
package.json
|
|
@ -15,13 +15,11 @@
|
||||||
"e2e": "sh ./tools/e2e/run.sh",
|
"e2e": "sh ./tools/e2e/run.sh",
|
||||||
"test": "yarn run unit && yarn run e2e",
|
"test": "yarn run unit && yarn run e2e",
|
||||||
"ci-biome": "yarn exec biome check",
|
"ci-biome": "yarn exec biome check",
|
||||||
"ci-eslint": "yarn exec eslint",
|
|
||||||
"ci-stylelint": "yarn exec stylelint '**/*.scss' '**/*.vue'",
|
"ci-stylelint": "yarn exec stylelint '**/*.scss' '**/*.vue'",
|
||||||
"lint": "yarn ci-biome; yarn ci-eslint; yarn ci-stylelint",
|
"lint": "yarn ci-biome; yarn ci-stylelint",
|
||||||
"lint-fix": "yarn exec eslint -- --fix; yarn exec stylelint '**/*.scss' '**/*.vue' --fix; biome check --write"
|
"lint-fix": "yarn exec stylelint '**/*.scss' '**/*.vue' --fix; biome check --write"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^8.0.0",
|
|
||||||
"@chenfengyuan/vue-qrcode": "2.0.0",
|
"@chenfengyuan/vue-qrcode": "2.0.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "7.1.0",
|
"@fortawesome/fontawesome-svg-core": "7.1.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "7.1.0",
|
"@fortawesome/free-regular-svg-icons": "7.1.0",
|
||||||
|
|
@ -56,11 +54,6 @@
|
||||||
"vue-virtual-scroller": "^3.0.5"
|
"vue-virtual-scroller": "^3.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^8.0.0",
|
|
||||||
"@babel/eslint-parser": "^8.0.0",
|
|
||||||
"@babel/plugin-transform-runtime": "^8.0.0",
|
|
||||||
"@babel/preset-env": "^8.0.0",
|
|
||||||
"@babel/register": "^8.0.0",
|
|
||||||
"@biomejs/biome": "2.5.11",
|
"@biomejs/biome": "2.5.11",
|
||||||
"@pinia/testing": "1.0.3",
|
"@pinia/testing": "1.0.3",
|
||||||
"@ungap/event-target": "0.2.4",
|
"@ungap/event-target": "0.2.4",
|
||||||
|
|
@ -71,24 +64,19 @@
|
||||||
"@vitest/browser-playwright": "^4.1.11",
|
"@vitest/browser-playwright": "^4.1.11",
|
||||||
"@vitest/coverage-v8": "^4.1.11",
|
"@vitest/coverage-v8": "^4.1.11",
|
||||||
"@vitest/ui": "^4.1.11",
|
"@vitest/ui": "^4.1.11",
|
||||||
"@vue/babel-plugin-jsx": "3.0.0",
|
|
||||||
"@vue/compiler-sfc": "3.5.22",
|
"@vue/compiler-sfc": "3.5.22",
|
||||||
"@vue/devtools-api": "^8.1.5",
|
"@vue/devtools-api": "^8.1.5",
|
||||||
"@vue/test-utils": "2.5.0",
|
"@vue/test-utils": "2.5.0",
|
||||||
"autoprefixer": "10.4.21",
|
"autoprefixer": "10.4.21",
|
||||||
"chalk": "5.6.2",
|
"chalk": "5.6.2",
|
||||||
"cross-spawn": "7.0.6",
|
"cross-spawn": "7.0.6",
|
||||||
"eslint": "9.39.2",
|
|
||||||
"eslint-plugin-vue": "10.6.2",
|
|
||||||
"iso-639-1": "3.1.5",
|
"iso-639-1": "3.1.5",
|
||||||
"msw": "2.14.6",
|
"msw": "2.14.6",
|
||||||
"nightwatch": "3.12.2",
|
|
||||||
"playwright": "1.61.0",
|
"playwright": "1.61.0",
|
||||||
"postcss": "8.5.6",
|
"postcss": "8.5.6",
|
||||||
"postcss-html": "^1.5.0",
|
"postcss-html": "^1.5.0",
|
||||||
"postcss-scss": "^4.0.6",
|
"postcss-scss": "^4.0.6",
|
||||||
"sass-embedded": "^1.100.0",
|
"sass-embedded": "^1.100.0",
|
||||||
"selenium-server": "3.141.59",
|
|
||||||
"serve-static": "2.2.0",
|
"serve-static": "2.2.0",
|
||||||
"stylelint": "16.25.0",
|
"stylelint": "16.25.0",
|
||||||
"stylelint-config-html": "^1.1.0",
|
"stylelint-config-html": "^1.1.0",
|
||||||
|
|
@ -97,15 +85,14 @@
|
||||||
"stylelint-config-recommended-vue": "^1.6.0",
|
"stylelint-config-recommended-vue": "^1.6.0",
|
||||||
"stylelint-config-standard": "38.0.0",
|
"stylelint-config-standard": "38.0.0",
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.0",
|
||||||
"vite-plugin-eslint2": "^5.1.0",
|
"vite-plugin-biome": "^1.2.0",
|
||||||
"vite-plugin-stylelint": "^6.1.0",
|
"vite-plugin-stylelint": "^6.1.0",
|
||||||
"vite-plugin-vue-devtools": "^8.2.1",
|
"vite-plugin-vue-devtools": "^8.2.1",
|
||||||
"vitest": "^4.1.11",
|
"vitest": "^4.1.11"
|
||||||
"vue-eslint-parser": "10.2.0"
|
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 26.0.0"
|
"node": ">= 24.0.0"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { throttle } from 'lodash'
|
import { throttle } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
|
@ -49,6 +49,12 @@ export default {
|
||||||
MobilePostStatusButton,
|
MobilePostStatusButton,
|
||||||
MobileNav,
|
MobileNav,
|
||||||
DesktopNav,
|
DesktopNav,
|
||||||
|
FollowRequestConfirm: defineAsyncComponent(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
'src/components/follow_request_confirm/follow_request_confirm.vue'
|
||||||
|
),
|
||||||
|
),
|
||||||
SettingsModal: defineAsyncComponent(
|
SettingsModal: defineAsyncComponent(
|
||||||
() => import('src/components/settings_modal/settings_modal.vue'),
|
() => import('src/components/settings_modal/settings_modal.vue'),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@
|
||||||
<UpdateNotification />
|
<UpdateNotification />
|
||||||
<GlobalError />
|
<GlobalError />
|
||||||
<GlobalNoticeList />
|
<GlobalNoticeList />
|
||||||
|
<FollowRequestConfirm v-if="currentUser" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { snakeCase } from 'lodash'
|
import { snakeCase } from 'lodash-es'
|
||||||
|
|
||||||
import { StatusCodeError } from 'src/services/errors/errors'
|
import { StatusCodeError } from 'src/services/errors/errors'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { last } from 'lodash'
|
import { last } from 'lodash-es'
|
||||||
|
|
||||||
import { paramsString, promisedRequest } from './helpers.js'
|
import { paramsString, promisedRequest } from './helpers.js'
|
||||||
import { fetchFriends, MASTODON_STATUS_URL } from './public.js'
|
import { fetchFriends, MASTODON_STATUS_URL } from './public.js'
|
||||||
|
|
@ -43,9 +43,10 @@ export const MASTODON_FOLLOW_URL = (id) => `/api/v1/accounts/${id}/follow`
|
||||||
export const MASTODON_UNFOLLOW_URL = (id) => `/api/v1/accounts/${id}/unfollow`
|
export const MASTODON_UNFOLLOW_URL = (id) => `/api/v1/accounts/${id}/unfollow`
|
||||||
|
|
||||||
const MASTODON_FOLLOW_REQUESTS_URL = '/api/v1/follow_requests'
|
const MASTODON_FOLLOW_REQUESTS_URL = '/api/v1/follow_requests'
|
||||||
const MASTODON_APPROVE_USER_URL = (id) =>
|
export const MASTODON_APPROVE_USER_URL = (id) =>
|
||||||
`/api/v1/follow_requests/${id}/authorize`
|
`/api/v1/follow_requests/${id}/authorize`
|
||||||
const MASTODON_DENY_USER_URL = (id) => `/api/v1/follow_requests/${id}/reject`
|
export const MASTODON_DENY_USER_URL = (id) =>
|
||||||
|
`/api/v1/follow_requests/${id}/reject`
|
||||||
const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) =>
|
const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) =>
|
||||||
`/api/v1/accounts/relationships/${paramsString({ id, withSuspended })}`
|
`/api/v1/accounts/relationships/${paramsString({ id, withSuspended })}`
|
||||||
export const MASTODON_USER_IN_LISTS = (id) => `/api/v1/accounts/${id}/lists`
|
export const MASTODON_USER_IN_LISTS = (id) => `/api/v1/accounts/${id}/lists`
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { orderBy, uniqueId } from 'lodash'
|
import { orderBy, uniqueId } from 'lodash-es'
|
||||||
|
|
||||||
import ChatMessage from 'src/components/chat_message/chat_message.vue'
|
import ChatMessage from 'src/components/chat_message/chat_message.vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get, maxBy, minBy, sortBy, throttle } from 'lodash'
|
import { get, maxBy, minBy, sortBy, throttle } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { throttle } from 'lodash'
|
import { throttle } from 'lodash-es'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get, reduce } from 'lodash'
|
import { get, reduce } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { take } from 'lodash'
|
import { take } from 'lodash-es'
|
||||||
|
|
||||||
import Popover from 'src/components/popover/popover.vue'
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue'
|
import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue'
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
export default (data) => {
|
export default (data) => {
|
||||||
const emojiCurry = suggestEmoji(data.emoji)
|
const emojiCurry = suggestEmoji(data.emoji)
|
||||||
const usersCurry = data.store && suggestUsers(data.store)
|
const usersCurry = suggestUsers()
|
||||||
return (input, nameKeywordLocalizer) => {
|
return (input, nameKeywordLocalizer) => {
|
||||||
const firstChar = input[0]
|
const firstChar = input[0]
|
||||||
if (firstChar === ':' && data.emoji) {
|
if (firstChar === ':' && data.emoji) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { chunk, debounce, trim } from 'lodash'
|
import { chunk, debounce, trim } from 'lodash-es'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,16 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { mapActions } from 'pinia'
|
||||||
|
|
||||||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||||
|
|
||||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
|
||||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
|
||||||
|
|
||||||
import { approveUser, denyUser } from 'src/api/user.js'
|
|
||||||
|
|
||||||
const FollowRequestCard = {
|
const FollowRequestCard = {
|
||||||
props: ['user'],
|
props: ['user'],
|
||||||
components: {
|
components: {
|
||||||
BasicUserCard,
|
BasicUserCard,
|
||||||
ConfirmModal: defineAsyncComponent(
|
|
||||||
() => import('src/components/confirm_modal/confirm_modal.vue'),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showingApproveConfirmDialog: false,
|
|
||||||
showingDenyConfirmDialog: false,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
findFollowRequestNotificationId() {
|
...mapActions(useFollowRequestsStore, ['approve', 'deny']),
|
||||||
const notif = useNotificationsStore().data.find(
|
|
||||||
(notif) =>
|
|
||||||
notif.from_profile.id === this.user.id &&
|
|
||||||
notif.type === 'follow_request',
|
|
||||||
)
|
|
||||||
return notif?.id
|
|
||||||
},
|
|
||||||
showApproveConfirmDialog() {
|
|
||||||
this.showingApproveConfirmDialog = true
|
|
||||||
},
|
|
||||||
hideApproveConfirmDialog() {
|
|
||||||
this.showingApproveConfirmDialog = false
|
|
||||||
},
|
|
||||||
showDenyConfirmDialog() {
|
|
||||||
this.showingDenyConfirmDialog = true
|
|
||||||
},
|
|
||||||
hideDenyConfirmDialog() {
|
|
||||||
this.showingDenyConfirmDialog = false
|
|
||||||
},
|
|
||||||
approveUser() {
|
|
||||||
if (this.shouldConfirmApprove) {
|
|
||||||
this.showApproveConfirmDialog()
|
|
||||||
} else {
|
|
||||||
this.doApprove()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doApprove() {
|
|
||||||
approveUser({
|
|
||||||
id: this.user.id,
|
|
||||||
credentials: useOAuthStore().token,
|
|
||||||
}).then(() => {
|
|
||||||
const notifId = this.findFollowRequestNotificationId()
|
|
||||||
useFollowRequestsStore().remove(this.user.id)
|
|
||||||
notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId)
|
|
||||||
})
|
|
||||||
this.hideApproveConfirmDialog()
|
|
||||||
},
|
|
||||||
denyUser() {
|
|
||||||
if (this.shouldConfirmDeny) {
|
|
||||||
this.showDenyConfirmDialog()
|
|
||||||
} else {
|
|
||||||
this.doDeny()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doDeny() {
|
|
||||||
denyUser({
|
|
||||||
id: this.user.id,
|
|
||||||
credentials: useOAuthStore().token,
|
|
||||||
}).then(() => {
|
|
||||||
const notifId = this.findFollowRequestNotificationId()
|
|
||||||
useFollowRequestsStore().remove(this.user.id)
|
|
||||||
notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId)
|
|
||||||
})
|
|
||||||
this.hideDenyConfirmDialog()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
mergedConfig() {
|
|
||||||
return useMergedConfigStore().mergedConfig
|
|
||||||
},
|
|
||||||
shouldConfirmApprove() {
|
|
||||||
return this.mergedConfig.modalOnApproveFollow
|
|
||||||
},
|
|
||||||
shouldConfirmDeny() {
|
|
||||||
return this.mergedConfig.modalOnDenyFollow
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,39 +3,17 @@
|
||||||
<div class="follow-request-card-content-container">
|
<div class="follow-request-card-content-container">
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="approveUser"
|
@click="() => approve(user.id)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.approve') }}
|
{{ $t('user_card.approve') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="denyUser"
|
@click="() => deny(user.id)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.deny') }}
|
{{ $t('user_card.deny') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<teleport to="#modal">
|
|
||||||
<ConfirmModal
|
|
||||||
v-if="showingApproveConfirmDialog"
|
|
||||||
:title="$t('user_card.approve_confirm_title')"
|
|
||||||
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
|
||||||
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
|
||||||
@accepted="doApprove"
|
|
||||||
@cancelled="hideApproveConfirmDialog"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
|
||||||
</ConfirmModal>
|
|
||||||
<ConfirmModal
|
|
||||||
v-if="showingDenyConfirmDialog"
|
|
||||||
:title="$t('user_card.deny_confirm_title')"
|
|
||||||
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
|
||||||
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
|
||||||
@accepted="doDeny"
|
|
||||||
@cancelled="hideDenyConfirmDialog"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
|
||||||
</ConfirmModal>
|
|
||||||
</teleport>
|
|
||||||
</basic-user-card>
|
</basic-user-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<teleport to="#modal">
|
||||||
|
<div>
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="store.showingApproveConfirmDialog"
|
||||||
|
:title="$t('user_card.approve_confirm_title')"
|
||||||
|
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
||||||
|
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
||||||
|
@accepted="store.doApprove"
|
||||||
|
@cancelled="store.hideApproveConfirmDialog"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
||||||
|
</ConfirmModal>
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="store.showingDenyConfirmDialog"
|
||||||
|
:title="$t('user_card.deny_confirm_title')"
|
||||||
|
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
||||||
|
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
||||||
|
@accepted="store.doDeny"
|
||||||
|
@cancelled="store.hideDenyConfirmDialog"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
||||||
|
</ConfirmModal>
|
||||||
|
</div>
|
||||||
|
</teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||||
|
|
||||||
|
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const user = computed(() => useUsersStore().findUser(store.tempId))
|
||||||
|
</script>
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { set, sumBy } from 'lodash'
|
import { set, sumBy } from 'lodash-es'
|
||||||
|
|
||||||
import Attachment from 'src/components/attachment/attachment.vue'
|
import Attachment from 'src/components/attachment/attachment.vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { isEmpty } from 'lodash'
|
import { isEmpty } from 'lodash-es'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash-es'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { last } from 'lodash'
|
import { last } from 'lodash-es'
|
||||||
|
|
||||||
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||||
import Popover from 'src/components/popover/popover.vue'
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { mapState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
import Report from 'src/components/report/report.vue'
|
import Report from 'src/components/report/report.vue'
|
||||||
import StatusContent from 'src/components/status_content/status_content.vue'
|
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||||
|
|
@ -16,13 +15,10 @@ import {
|
||||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||||
import { useUsersStore } from 'src/stores/users.js'
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { approveUser, denyUser } from 'src/api/user.js'
|
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
|
@ -58,8 +54,6 @@ const Notification = {
|
||||||
selecting: false,
|
selecting: false,
|
||||||
statusExpanded: false,
|
statusExpanded: false,
|
||||||
unmuted: false,
|
unmuted: false,
|
||||||
showingApproveConfirmDialog: false,
|
|
||||||
showingDenyConfirmDialog: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: ['notification'],
|
props: ['notification'],
|
||||||
|
|
@ -73,9 +67,6 @@ const Notification = {
|
||||||
|
|
||||||
UserPopover,
|
UserPopover,
|
||||||
UserLink,
|
UserLink,
|
||||||
ConfirmModal: defineAsyncComponent(
|
|
||||||
() => import('src/components/confirm_modal/confirm_modal.vue'),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('selectionchange', this.onContentSelect)
|
document.addEventListener('selectionchange', this.onContentSelect)
|
||||||
|
|
@ -125,51 +116,7 @@ const Notification = {
|
||||||
toggleMute() {
|
toggleMute() {
|
||||||
this.unmuted = !this.unmuted
|
this.unmuted = !this.unmuted
|
||||||
},
|
},
|
||||||
showApproveConfirmDialog() {
|
...mapActions(useFollowRequestsStore, ['approve', 'deny']),
|
||||||
this.showingApproveConfirmDialog = true
|
|
||||||
},
|
|
||||||
hideApproveConfirmDialog() {
|
|
||||||
this.showingApproveConfirmDialog = false
|
|
||||||
},
|
|
||||||
showDenyConfirmDialog() {
|
|
||||||
this.showingDenyConfirmDialog = true
|
|
||||||
},
|
|
||||||
hideDenyConfirmDialog() {
|
|
||||||
this.showingDenyConfirmDialog = false
|
|
||||||
},
|
|
||||||
approveUser() {
|
|
||||||
if (this.shouldConfirmApprove) {
|
|
||||||
this.showApproveConfirmDialog()
|
|
||||||
} else {
|
|
||||||
this.doApprove()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doApprove() {
|
|
||||||
approveUser({
|
|
||||||
id: this.user.id,
|
|
||||||
credentials: useOAuthStore().token,
|
|
||||||
})
|
|
||||||
useFollowRequestsStore().remove(this.user.id)
|
|
||||||
useNotificationsStore().markSingleNotificationAsSeen(this.notification.id)
|
|
||||||
this.hideApproveConfirmDialog()
|
|
||||||
},
|
|
||||||
denyUser() {
|
|
||||||
if (this.shouldConfirmDeny) {
|
|
||||||
this.showDenyConfirmDialog()
|
|
||||||
} else {
|
|
||||||
this.doDeny()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doDeny() {
|
|
||||||
denyUser({
|
|
||||||
id: this.user.id,
|
|
||||||
credentials: useOAuthStore().token,
|
|
||||||
}).then(() => {
|
|
||||||
useNotificationsStore().dismissNotificationLocal(this.notification.id)
|
|
||||||
useFollowRequestsStore().remove(this.user.id)
|
|
||||||
})
|
|
||||||
this.hideDenyConfirmDialog()
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
status() {
|
status() {
|
||||||
|
|
@ -220,12 +167,6 @@ const Notification = {
|
||||||
scaleMfm() {
|
scaleMfm() {
|
||||||
return this.mergedConfig.scaleMfm
|
return this.mergedConfig.scaleMfm
|
||||||
},
|
},
|
||||||
shouldConfirmApprove() {
|
|
||||||
return this.mergedConfig.modalOnApproveFollow
|
|
||||||
},
|
|
||||||
shouldConfirmDeny() {
|
|
||||||
return this.mergedConfig.modalOnDenyFollow
|
|
||||||
},
|
|
||||||
...mapState(useUsersStore, ['currentUser']),
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@
|
||||||
<button
|
<button
|
||||||
class="button-unstyled"
|
class="button-unstyled"
|
||||||
:title="$t('tool_tip.accept_follow_request')"
|
:title="$t('tool_tip.accept_follow_request')"
|
||||||
@click="approveUser()"
|
@click="() => approve(user.id)"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="check"
|
icon="check"
|
||||||
|
|
@ -236,7 +236,7 @@
|
||||||
<button
|
<button
|
||||||
class="button-unstyled"
|
class="button-unstyled"
|
||||||
:title="$t('tool_tip.reject_follow_request')"
|
:title="$t('tool_tip.reject_follow_request')"
|
||||||
@click="denyUser()"
|
@click="() => deny(user.id)"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="times"
|
icon="times"
|
||||||
|
|
@ -268,28 +268,6 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<teleport to="#modal">
|
|
||||||
<ConfirmModal
|
|
||||||
v-if="showingApproveConfirmDialog"
|
|
||||||
:title="$t('user_card.approve_confirm_title')"
|
|
||||||
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
|
||||||
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
|
||||||
@accepted="doApprove"
|
|
||||||
@cancelled="hideApproveConfirmDialog"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
|
||||||
</ConfirmModal>
|
|
||||||
<ConfirmModal
|
|
||||||
v-if="showingDenyConfirmDialog"
|
|
||||||
:title="$t('user_card.deny_confirm_title')"
|
|
||||||
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
|
||||||
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
|
||||||
@accepted="doDeny"
|
|
||||||
@cancelled="hideDenyConfirmDialog"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
|
||||||
</ConfirmModal>
|
|
||||||
</teleport>
|
|
||||||
</article>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
unescape as ldUnescape,
|
unescape as ldUnescape,
|
||||||
reject,
|
reject,
|
||||||
uniqBy,
|
uniqBy,
|
||||||
} from 'lodash'
|
} from 'lodash-es'
|
||||||
import { mapActions, mapState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import Modal from 'src/components/modal/modal.vue'
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash-es'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import Quote from './quote.vue'
|
import Quote from './quote.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { flattenDeep, unescape as ldUnescape } from 'lodash'
|
import { flattenDeep, unescape as ldUnescape } from 'lodash-es'
|
||||||
|
|
||||||
import HashtagLink from 'src/components/hashtag_link/hashtag_link.vue'
|
import HashtagLink from 'src/components/hashtag_link/hashtag_link.vue'
|
||||||
import { MENTIONS_LIMIT } from 'src/components/mentions_line/mentions_line.js'
|
import { MENTIONS_LIMIT } from 'src/components/mentions_line/mentions_line.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { map, uniqBy } from 'lodash'
|
import { map, uniqBy } from 'lodash-es'
|
||||||
|
|
||||||
import Conversation from 'src/components/conversation/conversation.vue'
|
import Conversation from 'src/components/conversation/conversation.vue'
|
||||||
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import Checkbox from 'components/checkbox/checkbox.vue'
|
||||||
import Popover from 'components/popover/popover.vue'
|
import Popover from 'components/popover/popover.vue'
|
||||||
import Select from 'components/select/select.vue'
|
import Select from 'components/select/select.vue'
|
||||||
import StillImage from 'components/still-image/still-image.vue'
|
import StillImage from 'components/still-image/still-image.vue'
|
||||||
import { clone } from 'lodash'
|
import { clone } from 'lodash-es'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
|
|
||||||
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
||||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
|
|
||||||
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
||||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export default {
|
||||||
...Setting.methods,
|
...Setting.methods,
|
||||||
getValue(e) {
|
getValue(e) {
|
||||||
// Basic tri-state toggle implementation
|
// Basic tri-state toggle implementation
|
||||||
if (!!this.indeterminateState && !e && this.visibleState === true) {
|
if (this.indeterminateState && !e && this.visibleState === true) {
|
||||||
// If we have indeterminate state, switching from true to false first goes through indeterminate
|
// If we have indeterminate state, switching from true to false first goes through indeterminate
|
||||||
return this.indeterminateState
|
return this.indeterminateState
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { clone } from 'lodash'
|
import { clone } from 'lodash-es'
|
||||||
|
|
||||||
import Attachment from 'src/components/attachment/attachment.vue'
|
import Attachment from 'src/components/attachment/attachment.vue'
|
||||||
import MediaUpload from 'src/components/media_upload/media_upload.vue'
|
import MediaUpload from 'src/components/media_upload/media_upload.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cloneDeep, get, isEqual, set } from 'lodash'
|
import { cloneDeep, get, isEqual, set } from 'lodash-es'
|
||||||
|
|
||||||
import DraftButtons from './draft_buttons.vue'
|
import DraftButtons from './draft_buttons.vue'
|
||||||
import LocalSettingIndicator from './local_setting_indicator.vue'
|
import LocalSettingIndicator from './local_setting_indicator.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cloneDeep, isEqual } from 'lodash'
|
import { cloneDeep, isEqual } from 'lodash-es'
|
||||||
import { mapActions, mapState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import { mapActions, mapState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get, map, reject } from 'lodash'
|
import { get, map, reject } from 'lodash-es'
|
||||||
|
|
||||||
import Autosuggest from 'src/components/autosuggest/autosuggest.vue'
|
import Autosuggest from 'src/components/autosuggest/autosuggest.vue'
|
||||||
import BlockCard from 'src/components/block_card/block_card.vue'
|
import BlockCard from 'src/components/block_card/block_card.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get, set, throttle, unset } from 'lodash'
|
import { get, set, throttle, unset } from 'lodash-es'
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
|
|
@ -846,7 +846,7 @@ export default {
|
||||||
exports.previewClass = computed(() => {
|
exports.previewClass = computed(() => {
|
||||||
const selectors = []
|
const selectors = []
|
||||||
if (
|
if (
|
||||||
!!selectedComponent.value.variants?.normal ||
|
selectedComponent.value.variants?.normal ||
|
||||||
selectedVariant.value !== 'normal'
|
selectedVariant.value !== 'normal'
|
||||||
) {
|
) {
|
||||||
selectors.push(selectedComponent.value.variants[selectedVariant.value])
|
selectors.push(selectedComponent.value.variants[selectedVariant.value])
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { flattenDeep, throttle } from 'lodash'
|
import { flattenDeep, throttle } from 'lodash-es'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import ColorInput from 'src/components/color_input/color_input.vue'
|
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ const SideDrawer = {
|
||||||
hideSitename: (store) => store.instanceIdentity.hideSitename,
|
hideSitename: (store) => store.instanceIdentity.hideSitename,
|
||||||
}),
|
}),
|
||||||
...mapState(useChatsStore, ['unreadChatsCount']),
|
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||||
...mapState(useDraftsStore, ['draftCount']),
|
...mapState(useDraftsStore, ['draftsCount']),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleDrawer() {
|
toggleDrawer() {
|
||||||
|
|
|
||||||
|
|
@ -269,10 +269,10 @@
|
||||||
icon="file-pen"
|
icon="file-pen"
|
||||||
/> {{ $t('nav.drafts') }}
|
/> {{ $t('nav.drafts') }}
|
||||||
<span
|
<span
|
||||||
v-if="draftCount"
|
v-if="draftsCount"
|
||||||
class="badge -neutral"
|
class="badge -neutral"
|
||||||
>
|
>
|
||||||
{{ draftCount }}
|
{{ draftsCount }}
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { groupBy, map } from 'lodash'
|
import { groupBy, map } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { debounce, throttle } from 'lodash'
|
import { debounce, throttle } from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import Conversation from 'src/components/conversation/conversation.vue'
|
import Conversation from 'src/components/conversation/conversation.vue'
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import {
|
||||||
escape as ldEscape,
|
escape as ldEscape,
|
||||||
unescape as ldUnescape,
|
unescape as ldUnescape,
|
||||||
merge,
|
merge,
|
||||||
} from 'lodash'
|
} from 'lodash-es'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
|
|
||||||
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
||||||
import List from 'src/components/list/list.vue'
|
import List from 'src/components/list/list.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { shuffle } from 'lodash'
|
import { shuffle } from 'lodash-es'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
|
||||||
|
|
@ -1742,6 +1742,7 @@
|
||||||
"approve_confirm_accept_button": "Approve",
|
"approve_confirm_accept_button": "Approve",
|
||||||
"approve_confirm_cancel_button": "Do not approve",
|
"approve_confirm_cancel_button": "Do not approve",
|
||||||
"approve_confirm": "Do you want to approve {user}'s follow request?",
|
"approve_confirm": "Do you want to approve {user}'s follow request?",
|
||||||
|
"approve_error": "Failure approving follow request: {error}",
|
||||||
"block": "Block",
|
"block": "Block",
|
||||||
"blocked": "Blocked!",
|
"blocked": "Blocked!",
|
||||||
"block_confirm_title": "Block confirmation",
|
"block_confirm_title": "Block confirmation",
|
||||||
|
|
@ -1754,6 +1755,7 @@
|
||||||
"deny_confirm_accept_button": "Deny",
|
"deny_confirm_accept_button": "Deny",
|
||||||
"deny_confirm_cancel_button": "Do not deny",
|
"deny_confirm_cancel_button": "Do not deny",
|
||||||
"deny_confirm": "Do you want to deny {user}'s follow request?",
|
"deny_confirm": "Do you want to deny {user}'s follow request?",
|
||||||
|
"deny_error": "Failure denying follow request: {error}",
|
||||||
"edit_profile": "Edit profile",
|
"edit_profile": "Edit profile",
|
||||||
"favorites": "Favorites",
|
"favorites": "Favorites",
|
||||||
"follow": "Follow",
|
"follow": "Follow",
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ const ensureFinalFallback = (codes) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
languages,
|
|
||||||
langCodeToJsonName,
|
|
||||||
langCodeToCldrName,
|
|
||||||
ensureFinalFallback,
|
ensureFinalFallback,
|
||||||
|
langCodeToCldrName,
|
||||||
|
langCodeToJsonName,
|
||||||
|
languages,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
// sed -i -e "s/'//gm" -e 's/"/\\"/gm' -re 's/^( +)(.+?): ((.+?))?(,?)(\{?)$/\1"\2": "\4"/gm' -e 's/\"\{\"/{/g' -e 's/,"$/",/g' file.json
|
// sed -i -e "s/'//gm" -e 's/"/\\"/gm' -re 's/^( +)(.+?): ((.+?))?(,?)(\{?)$/\1"\2": "\4"/gm' -e 's/\"\{\"/{/g' -e 's/,"$/",/g' file.json
|
||||||
// There's only problem that apostrophe character ' gets replaced by \\ so you have to fix it manually, sorry.
|
// There's only problem that apostrophe character ' gets replaced by \\ so you have to fix it manually, sorry.
|
||||||
|
|
||||||
import { isEqual } from 'lodash'
|
import { isEqual } from 'lodash-es'
|
||||||
|
|
||||||
import enMessages from './en.json'
|
import enMessages from './en.json'
|
||||||
import { langCodeToJsonName, languages } from './languages.js'
|
import { langCodeToJsonName, languages } from './languages.js'
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import EventTargetPolyfill from '@ungap/event-target'
|
import EventTargetPolyfill from '@ungap/event-target'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// for some reason eslint both likes and dislikes
|
|
||||||
// no-new here so we just call something useless
|
|
||||||
// so it stops reporting this file
|
|
||||||
const et = new EventTarget()
|
const et = new EventTarget()
|
||||||
et.dispatchEvent()
|
et.dispatchEvent()
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cloneDeep, get, set } from 'lodash'
|
import { cloneDeep, get, set } from 'lodash-es'
|
||||||
|
|
||||||
import { storage } from './storage.js'
|
import { storage } from './storage.js'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash-es'
|
||||||
|
|
||||||
const browserLocale = (navigator.language || 'en').split('-')[0]
|
const browserLocale = (navigator.language || 'en').split('-')[0]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { kebabCase } from 'lodash'
|
import { kebabCase } from 'lodash-es'
|
||||||
|
|
||||||
const propsToNative = (props) =>
|
const propsToNative = (props) =>
|
||||||
Object.keys(props).reduce((acc, cur) => {
|
Object.keys(props).reduce((acc, cur) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { find, reduce } from 'lodash'
|
import { find, reduce } from 'lodash-es'
|
||||||
|
|
||||||
export const replaceWord = (str, toReplace, replacement) => {
|
export const replaceWord = (str, toReplace, replacement) => {
|
||||||
return str.slice(0, toReplace.start) + replacement + str.slice(toReplace.end)
|
return str.slice(0, toReplace.start) + replacement + str.slice(toReplace.end)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { isFunction } from 'lodash'
|
import { isFunction } from 'lodash-es'
|
||||||
|
|
||||||
const getComponentOptions = (Component) =>
|
const getComponentOptions = (Component) =>
|
||||||
isFunction(Component) ? Component.options : Component
|
isFunction(Component) ? Component.options : Component
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { parseLinkHeader } from '@web3-storage/parse-link-header'
|
import { parseLinkHeader } from '@web3-storage/parse-link-header'
|
||||||
import escapeHtml from 'escape-html'
|
import escapeHtml from 'escape-html'
|
||||||
import { unescape as lodashUnescape } from 'lodash'
|
import { unescape as lodashUnescape } from 'lodash-es'
|
||||||
import punycode from 'punycode.js'
|
import punycode from 'punycode.js'
|
||||||
|
|
||||||
import { fileType } from '../file_type/file_type.service.js'
|
import { fileType } from '../file_type/file_type.service.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { capitalize } from 'lodash'
|
import { capitalize } from 'lodash-es'
|
||||||
|
|
||||||
function humanizeErrors(errors) {
|
function humanizeErrors(errors) {
|
||||||
return Object.entries(errors).reduce((errs, [k, val]) => {
|
return Object.entries(errors).reduce((errs, [k, val]) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { unescape as ldUnescape } from 'lodash'
|
import { unescape as ldUnescape } from 'lodash-es'
|
||||||
|
|
||||||
import { getTagName } from './utility.service.js'
|
import { getTagName } from './utility.service.js'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import ISO6391 from 'iso-639-1'
|
import ISO6391 from 'iso-639-1'
|
||||||
import { map } from 'lodash'
|
import { map } from 'lodash-es'
|
||||||
|
|
||||||
import languagesObject from '../../i18n/messages'
|
import languagesObject from '../../i18n/messages'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { map } from 'lodash'
|
import { map } from 'lodash-es'
|
||||||
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import sum from 'hash-sum'
|
import sum from 'hash-sum'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { chunk, throttle } from 'lodash'
|
import { chunk, throttle } from 'lodash-es'
|
||||||
|
|
||||||
import { getCssRules } from '../theme_data/css_utils.js'
|
import { getCssRules } from '../theme_data/css_utils.js'
|
||||||
import { getEngineChecksum, init } from '../theme_data/theme_data_3.service.js'
|
import { getEngineChecksum, init } from '../theme_data/theme_data_3.service.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { flattenDeep } from 'lodash'
|
import { flattenDeep } from 'lodash-es'
|
||||||
|
|
||||||
export const deserializeShadow = (string) => {
|
export const deserializeShadow = (string) => {
|
||||||
const modes = [
|
const modes = [
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { sortBy } from 'lodash'
|
import { sortBy } from 'lodash-es'
|
||||||
|
|
||||||
// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
|
// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
|
||||||
// into an array [item2, item3] for iterating
|
// into an array [item2, item3] for iterating
|
||||||
|
|
@ -186,7 +186,7 @@ export const combinationsMatch = (criteria, subject, strict) => {
|
||||||
*/
|
*/
|
||||||
export const findRules = (criteria, strict) => (subject) => {
|
export const findRules = (criteria, strict) => (subject) => {
|
||||||
// If we searching for "general" rules - ignore "specific" ones
|
// If we searching for "general" rules - ignore "specific" ones
|
||||||
if (criteria.parent === null && !!subject.parent) return false
|
if (criteria.parent === null && subject.parent) return false
|
||||||
if (!combinationsMatch(criteria, subject, strict)) return false
|
if (!combinationsMatch(criteria, subject, strict)) return false
|
||||||
|
|
||||||
if (criteria.parent !== undefined && criteria.parent !== null) {
|
if (criteria.parent !== undefined && criteria.parent !== null) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { brightness, convert } from 'chromatism'
|
import { brightness, convert } from 'chromatism'
|
||||||
import sum from 'hash-sum'
|
import sum from 'hash-sum'
|
||||||
import { flattenDeep, sortBy } from 'lodash'
|
import { flattenDeep, sortBy } from 'lodash-es'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
alphaBlend,
|
alphaBlend,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { includes } from 'lodash'
|
import { includes } from 'lodash-es'
|
||||||
|
|
||||||
const generateProfileLink = (id, screenName, restrictedNicknames) => {
|
const generateProfileLink = (id, screenName, restrictedNicknames) => {
|
||||||
const complicated =
|
const complicated =
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cloneDeep, differenceWith, get, isEqual, set } from 'lodash'
|
import { cloneDeep, differenceWith, get, isEqual, set } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { find, remove } from 'lodash'
|
import { find, remove } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { orderBy, sumBy } from 'lodash'
|
import { orderBy, sumBy } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
||||||
|
|
|
||||||
|
|
@ -52,12 +52,8 @@ export const useDraftsStore = defineStore('drafts', {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async abandonDraft(id) {
|
|
||||||
this.drafts.delete(id)
|
|
||||||
await deleteDraftFromStorage([id])
|
|
||||||
},
|
|
||||||
async loadDrafts() {
|
async loadDrafts() {
|
||||||
const currentData = await getStorageData()
|
const currentData = (await getStorageData()) ?? {}
|
||||||
this.drafts = new Map(Object.entries(currentData))
|
this.drafts = new Map(Object.entries(currentData))
|
||||||
},
|
},
|
||||||
async addOrSaveDraft(draft) {
|
async addOrSaveDraft(draft) {
|
||||||
|
|
@ -67,9 +63,13 @@ export const useDraftsStore = defineStore('drafts', {
|
||||||
await saveDraftToStorage(draftWithId)
|
await saveDraftToStorage(draftWithId)
|
||||||
return id
|
return id
|
||||||
},
|
},
|
||||||
async abandonAllDrafts(store) {
|
async abandonDraft(id) {
|
||||||
|
this.drafts.delete(id)
|
||||||
|
await deleteDraftFromStorage([id])
|
||||||
|
},
|
||||||
|
async abandonAllDrafts() {
|
||||||
const ids = [...this.drafts.keys()]
|
const ids = [...this.drafts.keys()]
|
||||||
ids.forEach((id) => this.abandonDraft(id))
|
ids.forEach((id) => this.drafts.delete(id))
|
||||||
await deleteDraftFromStorage(ids)
|
await deleteDraftFromStorage(ids)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { merge } from 'lodash'
|
import { merge } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,6 @@ const followRequestFetcher = ({ credentials }) => {
|
||||||
const startFetching = () => {
|
const startFetching = () => {
|
||||||
if (interval.value) throw new Error('Interval already exists!')
|
if (interval.value) throw new Error('Interval already exists!')
|
||||||
|
|
||||||
fetchAndUpdate()
|
|
||||||
|
|
||||||
interval.value = promiseInterval(fetchAndUpdate, 10000)
|
interval.value = promiseInterval(fetchAndUpdate, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,6 @@ const notificationsFetcher = (credentials) => {
|
||||||
const startFetching = () => {
|
const startFetching = () => {
|
||||||
if (interval.value) throw new Error('Interval already exists!')
|
if (interval.value) throw new Error('Interval already exists!')
|
||||||
|
|
||||||
fetchAndUpdate()
|
|
||||||
|
|
||||||
interval.value = promiseInterval(fetchAndUpdate, 10000)
|
interval.value = promiseInterval(fetchAndUpdate, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import followRequestFetcher from 'src/stores/fetchers/follow_requests.js'
|
import followRequestFetcher from 'src/stores/fetchers/follow_requests.js'
|
||||||
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { approveUser, denyUser } from 'src/api/user.js'
|
||||||
|
|
||||||
export const useFollowRequestsStore = defineStore('followRequests', {
|
export const useFollowRequestsStore = defineStore('followRequests', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
fetcher: null,
|
fetcher: null,
|
||||||
requests: new Map(),
|
requests: new Map(),
|
||||||
|
showingApproveConfirmDialog: false,
|
||||||
|
showingDenyConfirmDialog: false,
|
||||||
|
tempId: null,
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
followRequestsCount(state) {
|
followRequestsCount(state) {
|
||||||
|
|
@ -14,8 +22,9 @@ export const useFollowRequestsStore = defineStore('followRequests', {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
// Fetcher stuff
|
||||||
startFetching() {
|
startFetching() {
|
||||||
if (this.fetcher) throw 'Fetcher already exists!'
|
if (this.fetcher) throw new Error('Fetcher already exists!')
|
||||||
|
|
||||||
this.fetcher = followRequestFetcher({
|
this.fetcher = followRequestFetcher({
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
|
|
@ -24,14 +33,99 @@ export const useFollowRequestsStore = defineStore('followRequests', {
|
||||||
this.fetcher.startFetching()
|
this.fetcher.startFetching()
|
||||||
},
|
},
|
||||||
stopFetching() {
|
stopFetching() {
|
||||||
if (!this.fetcher) throw "Fetcher doesn't exists!"
|
if (!this.fetcher) throw new Error("Fetcher doesn't exists!")
|
||||||
this.fetcher.stopFetching(), (this.fetcher = null)
|
this.fetcher.stopFetching()
|
||||||
|
this.fetcher = null
|
||||||
},
|
},
|
||||||
setFollowRequests(requests) {
|
setFollowRequests(requests) {
|
||||||
this.requests = new Map(requests.map((user) => [user.id, user]))
|
this.requests = new Map(requests.map((user) => [user.id, user]))
|
||||||
},
|
},
|
||||||
remove(id) {
|
|
||||||
this.requests.delete(id)
|
// Confirm dialogs
|
||||||
|
showApproveConfirmDialog(id) {
|
||||||
|
this.showingApproveConfirmDialog = true
|
||||||
|
this.tempId = id
|
||||||
|
},
|
||||||
|
showDenyConfirmDialog(id) {
|
||||||
|
this.showingDenyConfirmDialog = true
|
||||||
|
this.tempId = id
|
||||||
|
},
|
||||||
|
hideApproveConfirmDialog() {
|
||||||
|
this.showingApproveConfirmDialog = false
|
||||||
|
this.tempId = null
|
||||||
|
},
|
||||||
|
hideDenyConfirmDialog() {
|
||||||
|
this.showingDenyConfirmDialog = false
|
||||||
|
this.tempId = null
|
||||||
|
},
|
||||||
|
|
||||||
|
// Dialog/Instant fork
|
||||||
|
approve(id) {
|
||||||
|
if (useMergedConfigStore().mergedConfig.modalOnApproveFollow) {
|
||||||
|
this.showApproveConfirmDialog(id)
|
||||||
|
} else {
|
||||||
|
this.doApprove(id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deny(id) {
|
||||||
|
if (useMergedConfigStore().mergedConfig.modalOnDenyFollow) {
|
||||||
|
this.showDenyConfirmDialog(id)
|
||||||
|
} else {
|
||||||
|
this.doDeny(id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Actual calls
|
||||||
|
async doApprove(userId) {
|
||||||
|
const id = userId ?? this.tempId
|
||||||
|
this.hideApproveConfirmDialog()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await approveUser({
|
||||||
|
id,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
|
||||||
|
const notifId = this.findFollowRequestNotificationId(id)
|
||||||
|
notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId)
|
||||||
|
this.requests.delete(id)
|
||||||
|
} catch (error) {
|
||||||
|
useInterfaceStore().pushGlobalNotice({
|
||||||
|
messageKey: 'user_card.approve_error',
|
||||||
|
messageArgs: { error },
|
||||||
|
level: 'error',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async doDeny(userId) {
|
||||||
|
const id = userId ?? this.tempId
|
||||||
|
this.hideDenyConfirmDialog()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await denyUser({
|
||||||
|
id,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
|
||||||
|
const notifId = this.findFollowRequestNotificationId(id)
|
||||||
|
notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId)
|
||||||
|
this.requests.delete(id)
|
||||||
|
} catch (error) {
|
||||||
|
useInterfaceStore().pushGlobalNotice({
|
||||||
|
messageKey: 'user_card.deny_error',
|
||||||
|
messageArgs: { error },
|
||||||
|
level: 'error',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
findFollowRequestNotificationId(userId) {
|
||||||
|
const notif = useNotificationsStore().data.find(
|
||||||
|
(notif) =>
|
||||||
|
notif.from_profile.id === userId && notif.type === 'follow_request',
|
||||||
|
)
|
||||||
|
return notif?.id
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { set } from 'lodash'
|
import { set } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
import { useShoutStore } from 'src/stores/shout.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
postFormats: [],
|
postFormats: [],
|
||||||
mailerEnabled: false,
|
mailerEnabled: false,
|
||||||
|
|
@ -38,6 +41,13 @@ export const useInstanceCapabilitiesStore = defineStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
this[capability] = value
|
this[capability] = value
|
||||||
|
|
||||||
|
if (
|
||||||
|
capability === 'shoutAvailable' &&
|
||||||
|
useUsersStore().currentUser?.token
|
||||||
|
) {
|
||||||
|
useShoutStore().initializeSocket()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { find, remove } from 'lodash'
|
import { find, remove } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cloneDeep, set } from 'lodash'
|
import { cloneDeep, set } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { merge } from 'lodash'
|
import { merge } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get, set } from 'lodash'
|
import { get, set } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ export const useShoutStore = defineStore('shout', {
|
||||||
messages: [],
|
messages: [],
|
||||||
channel: { state: '' },
|
channel: { state: '' },
|
||||||
joined: false,
|
joined: false,
|
||||||
token: null,
|
|
||||||
socket: null,
|
socket: null,
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
|
|
@ -20,7 +19,7 @@ export const useShoutStore = defineStore('shout', {
|
||||||
initializeSocket() {
|
initializeSocket() {
|
||||||
if (this.token === null) return
|
if (this.token === null) return
|
||||||
if (!useInstanceCapabilitiesStore().shoutAvailable) return
|
if (!useInstanceCapabilitiesStore().shoutAvailable) return
|
||||||
if (this.socket !== null) throw new Error('Shout socket already exist!')
|
if (this.socket !== null) return
|
||||||
|
|
||||||
this.socket = new Socket('/socket', { params: { token: this.token } })
|
this.socket = new Socket('/socket', { params: { token: this.token } })
|
||||||
this.socket.connect()
|
this.socket.connect()
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
take,
|
take,
|
||||||
uniqWith,
|
uniqWith,
|
||||||
unset,
|
unset,
|
||||||
} from 'lodash'
|
} from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { toRaw } from 'vue'
|
import { toRaw } from 'vue'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { first, last } from 'lodash'
|
import { first, last } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import timelineFetcher from 'src/stores/fetchers/timeline_fetcher.js'
|
import timelineFetcher from 'src/stores/fetchers/timeline_fetcher.js'
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {
|
||||||
groupBy,
|
groupBy,
|
||||||
isEqual,
|
isEqual,
|
||||||
last,
|
last,
|
||||||
} from 'lodash'
|
} from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { toRaw } from 'vue'
|
import { toRaw } from 'vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
import { last } from 'lodash'
|
import { last } from 'lodash-es'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
|
|
@ -706,7 +706,7 @@ export const useUsersStore = defineStore('users', {
|
||||||
useOAuthStore().clearToken()
|
useOAuthStore().clearToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.tatusCode === 401) {
|
if (error.statusCode === 401) {
|
||||||
throw new Error('Wrong username or password', error)
|
throw new Error('Wrong username or password', error)
|
||||||
} else {
|
} else {
|
||||||
throw new Error('An error occurred, please try again', error)
|
throw new Error('An error occurred, please try again', error)
|
||||||
|
|
@ -717,6 +717,7 @@ export const useUsersStore = defineStore('users', {
|
||||||
},
|
},
|
||||||
logout() {
|
logout() {
|
||||||
const oauth = useOAuthStore()
|
const oauth = useOAuthStore()
|
||||||
|
const locked = this.currentUser.locked
|
||||||
|
|
||||||
// Pause fetching
|
// Pause fetching
|
||||||
useNotificationsStore().pause()
|
useNotificationsStore().pause()
|
||||||
|
|
@ -727,7 +728,7 @@ export const useUsersStore = defineStore('users', {
|
||||||
useListsStore().stopFetching()
|
useListsStore().stopFetching()
|
||||||
useBookmarkFoldersStore().stopFetching()
|
useBookmarkFoldersStore().stopFetching()
|
||||||
useChatsStore().stopFetching()
|
useChatsStore().stopFetching()
|
||||||
if (this.currentUser.locked) {
|
if (locked) {
|
||||||
useFollowRequestsStore().stopFetching()
|
useFollowRequestsStore().stopFetching()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -788,7 +789,9 @@ export const useUsersStore = defineStore('users', {
|
||||||
useListsStore().startFetching()
|
useListsStore().startFetching()
|
||||||
useBookmarkFoldersStore().startFetching()
|
useBookmarkFoldersStore().startFetching()
|
||||||
useChatsStore().startFetching()
|
useChatsStore().startFetching()
|
||||||
useFollowRequestsStore().startFetching()
|
if (locked) {
|
||||||
|
useFollowRequestsStore().startFetching()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
useNotificationsStore().resume()
|
useNotificationsStore().resume()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-env serviceworker */
|
|
||||||
|
|
||||||
// biome-ignore: side effect import of assets list
|
// biome-ignore: side effect import of assets list
|
||||||
import 'virtual:pleroma-fe/service_worker_env'
|
import 'virtual:pleroma-fe/service_worker_env'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
// A custom Nightwatch assertion.
|
|
||||||
// the name of the method is the filename.
|
|
||||||
// can be used in tests like this:
|
|
||||||
//
|
|
||||||
// browser.assert.elementCount(selector, count)
|
|
||||||
//
|
|
||||||
// for how to write custom assertions see
|
|
||||||
// http://nightwatchjs.org/guide#writing-custom-assertions
|
|
||||||
exports.assertion = function (selector, count) {
|
|
||||||
this.message = 'Testing if element <' + selector + '> has count: ' + count
|
|
||||||
this.expected = count
|
|
||||||
this.pass = function (val) {
|
|
||||||
return val === this.expected
|
|
||||||
}
|
|
||||||
this.value = function (res) {
|
|
||||||
return res.value
|
|
||||||
}
|
|
||||||
this.command = function (cb) {
|
|
||||||
const self = this
|
|
||||||
return this.api.execute(
|
|
||||||
function (selector) {
|
|
||||||
return document.querySelectorAll(selector).length
|
|
||||||
},
|
|
||||||
[selector],
|
|
||||||
function (res) {
|
|
||||||
cb.call(self, res)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
require('@babel/register')
|
|
||||||
const config = require('../../config')
|
|
||||||
|
|
||||||
// http://nightwatchjs.org/guide#settings-file
|
|
||||||
module.exports = {
|
|
||||||
src_folders: ['test/e2e/specs'],
|
|
||||||
output_folder: 'test/e2e/reports',
|
|
||||||
custom_assertions_path: ['test/e2e/custom-assertions'],
|
|
||||||
|
|
||||||
selenium: {
|
|
||||||
start_process: true,
|
|
||||||
server_path: require('selenium-server').path,
|
|
||||||
host: '127.0.0.1',
|
|
||||||
port: 4444,
|
|
||||||
cli_args: {
|
|
||||||
'webdriver.chrome.driver': require('chromedriver').path,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
test_settings: {
|
|
||||||
default: {
|
|
||||||
selenium_port: 4444,
|
|
||||||
selenium_host: 'localhost',
|
|
||||||
silent: true,
|
|
||||||
globals: {
|
|
||||||
devServerURL:
|
|
||||||
'http://localhost:' + (process.env.PORT || config.dev.port),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
chrome: {
|
|
||||||
desiredCapabilities: {
|
|
||||||
browserName: 'chrome',
|
|
||||||
javascriptEnabled: true,
|
|
||||||
acceptSslCerts: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
firefox: {
|
|
||||||
desiredCapabilities: {
|
|
||||||
browserName: 'firefox',
|
|
||||||
javascriptEnabled: true,
|
|
||||||
acceptSslCerts: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
// 1. start the dev server using production config
|
|
||||||
process.env.NODE_ENV = 'testing'
|
|
||||||
const server = require('../../build/dev-server.js')
|
|
||||||
|
|
||||||
// 2. run the nightwatch test suite against it
|
|
||||||
// to run in additional browsers:
|
|
||||||
// 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
|
|
||||||
// 2. add it to the --env flag below
|
|
||||||
// or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
|
|
||||||
// For more information on Nightwatch's config file, see
|
|
||||||
// http://nightwatchjs.org/guide#settings-file
|
|
||||||
let opts = process.argv.slice(2)
|
|
||||||
if (!opts.includes('--config')) {
|
|
||||||
opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
|
|
||||||
}
|
|
||||||
if (!opts.includes('--env')) {
|
|
||||||
opts = opts.concat(['--env', 'chrome'])
|
|
||||||
}
|
|
||||||
|
|
||||||
const spawn = require('cross-spawn')
|
|
||||||
const runner = spawn('./node_modules/.bin/nightwatch', opts, {
|
|
||||||
stdio: 'inherit',
|
|
||||||
})
|
|
||||||
|
|
||||||
runner.on('exit', function (code) {
|
|
||||||
server.close()
|
|
||||||
process.exit(code)
|
|
||||||
})
|
|
||||||
|
|
||||||
runner.on('error', function (err) {
|
|
||||||
server.close()
|
|
||||||
throw err
|
|
||||||
})
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
// For authoring Nightwatch tests, see
|
|
||||||
// http://nightwatchjs.org/guide#usage
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
'default e2e tests': function (browser) {
|
|
||||||
// automatically uses dev Server port from /config.index.js
|
|
||||||
// default: http://localhost:8099
|
|
||||||
// see nightwatch.conf.js
|
|
||||||
const devServer = browser.globals.devServerURL
|
|
||||||
|
|
||||||
browser
|
|
||||||
.url(devServer)
|
|
||||||
.waitForElementVisible('#app', 5000)
|
|
||||||
.assert.elementPresent('.hello')
|
|
||||||
.assert.containsText('h1', 'Welcome to Your Vue.js App')
|
|
||||||
.assert.elementCount('img', 1)
|
|
||||||
.end()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
220
test/unit/specs/stores/drafts.spec.js
Normal file
220
test/unit/specs/stores/drafts.spec.js
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
import { createTestingPinia } from '@pinia/testing'
|
||||||
|
import { setActivePinia } from 'pinia'
|
||||||
|
|
||||||
|
import { useDraftsStore } from 'src/stores/drafts.js'
|
||||||
|
|
||||||
|
import { storage } from 'src/lib/storage.js'
|
||||||
|
|
||||||
|
describe('Drafts store', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||||
|
|
||||||
|
// Localforage does something weird that resets get/setItem and breaks
|
||||||
|
// mocking if we just spy one them without overriding implementation
|
||||||
|
vi.spyOn(storage, 'setItem').mockImplementation(() => ({}))
|
||||||
|
vi.spyOn(storage, 'getItem').mockImplementation(() => ({}))
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.resetAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Getters', () => {
|
||||||
|
it('draftsCount returns total number of drafts', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
|
||||||
|
await store.addOrSaveDraft({ id: 1, status: 'draft' })
|
||||||
|
await store.addOrSaveDraft({ id: 2, status: 'draft' })
|
||||||
|
await store.addOrSaveDraft({ id: 3, status: 'draft' })
|
||||||
|
|
||||||
|
expect(store).to.have.property('draftsCount', 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('draftsArray returns array of drafts', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
|
||||||
|
await store.addOrSaveDraft({ id: 1, status: 'draft' })
|
||||||
|
await store.addOrSaveDraft({ id: 2, status: 'draft' })
|
||||||
|
await store.addOrSaveDraft({ id: 3, status: 'draft' })
|
||||||
|
|
||||||
|
expect(store.draftsArray).to.have.length(3)
|
||||||
|
expect(store.draftsArray).to.be.an('Array')
|
||||||
|
expect(store.draftsArray).to.have.deep.members([
|
||||||
|
{ id: 1, status: 'draft' },
|
||||||
|
{ id: 2, status: 'draft' },
|
||||||
|
{ id: 3, status: 'draft' },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('draftsByTypeAndRefId', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
|
||||||
|
await store.addOrSaveDraft({
|
||||||
|
id: 1,
|
||||||
|
type: 'edit',
|
||||||
|
refId: 'e1',
|
||||||
|
status: 'draft',
|
||||||
|
})
|
||||||
|
await store.addOrSaveDraft({
|
||||||
|
id: 2,
|
||||||
|
type: 'reply',
|
||||||
|
refId: 'r1',
|
||||||
|
status: 'draft',
|
||||||
|
})
|
||||||
|
await store.addOrSaveDraft({ id: 3, status: 'draft' })
|
||||||
|
await store.addOrSaveDraft({
|
||||||
|
id: 4,
|
||||||
|
type: 'edit',
|
||||||
|
refId: 'e2',
|
||||||
|
status: 'draft',
|
||||||
|
})
|
||||||
|
await store.addOrSaveDraft({
|
||||||
|
id: 5,
|
||||||
|
type: 'reply',
|
||||||
|
refId: 'r2',
|
||||||
|
status: 'draft',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(store.draftsByTypeAndRefId).to.be.a('function')
|
||||||
|
expect(store.draftsByTypeAndRefId('edit', 'e1')).to.eql([
|
||||||
|
{ id: 1, type: 'edit', refId: 'e1', status: 'draft' },
|
||||||
|
])
|
||||||
|
expect(store.draftsByTypeAndRefId('reply', 'r1')).to.eql([
|
||||||
|
{ id: 2, type: 'reply', refId: 'r1', status: 'draft' },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Actions', () => {
|
||||||
|
describe('loadDrafts', () => {
|
||||||
|
it('should load drafts from storage and populate cache', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
storage.getItem.mockResolvedValueOnce({
|
||||||
|
a: { id: 'a', status: 'draft' },
|
||||||
|
b: { id: 'b', status: 'draft' },
|
||||||
|
c: { id: 'c', status: 'draft' },
|
||||||
|
})
|
||||||
|
|
||||||
|
await store.loadDrafts()
|
||||||
|
|
||||||
|
expect(store.drafts.get('a')).to.have.property('status', 'draft')
|
||||||
|
expect(store.drafts.get('b')).to.have.property('status', 'draft')
|
||||||
|
expect(store.drafts.get('c')).to.have.property('status', 'draft')
|
||||||
|
|
||||||
|
expect(storage.getItem).to.have.been.calledOnce
|
||||||
|
expect(storage.getItem).to.have.been.calledWith('pleroma-fe-drafts')
|
||||||
|
expect(storage.setItem).to.have.not.been.called
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle case where there is no local draft storage yet', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
storage.getItem.mockResolvedValueOnce(null)
|
||||||
|
|
||||||
|
await store.loadDrafts()
|
||||||
|
|
||||||
|
expect(store.drafts).to.have.property('size', 0)
|
||||||
|
|
||||||
|
expect(storage.getItem).to.have.been.calledOnce
|
||||||
|
expect(storage.getItem).to.have.been.calledWith('pleroma-fe-drafts')
|
||||||
|
expect(storage.setItem).to.have.not.been.called
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addOrSaveDraft', () => {
|
||||||
|
it('create draft', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
vi.setSystemTime(new Date(859586400000))
|
||||||
|
|
||||||
|
const id = await store.addOrSaveDraft({ status: 'draft' })
|
||||||
|
|
||||||
|
expect(store.drafts).to.have.property('size', 1)
|
||||||
|
expect(id).to.eql('859586400000')
|
||||||
|
expect(store.drafts.get(id)).to.have.property('status', 'draft')
|
||||||
|
expect(storage.getItem).to.have.been.calledOnce
|
||||||
|
expect(storage.setItem).to.have.been.calledOnce
|
||||||
|
expect(storage.setItem).to.have.been.calledWith('pleroma-fe-drafts', {
|
||||||
|
[id]: {
|
||||||
|
id,
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('update draft', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
await store.addOrSaveDraft({ id: '1', status: 'draft' })
|
||||||
|
|
||||||
|
expect(store.drafts.get('1')).to.have.property('status', 'draft')
|
||||||
|
|
||||||
|
await store.addOrSaveDraft({ id: '1', status: 'updated' })
|
||||||
|
|
||||||
|
expect(store.drafts.get('1')).to.have.property('status', 'updated')
|
||||||
|
expect(storage.getItem).to.have.been.calledTwice
|
||||||
|
expect(storage.setItem).to.have.been.calledTwice
|
||||||
|
expect(storage.setItem).to.have.been.calledWith('pleroma-fe-drafts', {
|
||||||
|
1: {
|
||||||
|
id: '1',
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('abandonDraft', () => {
|
||||||
|
it('should remove draft from storage and cache', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
store.drafts.set('a', { id: 'a', status: 'draft' })
|
||||||
|
store.drafts.set('b', { id: 'b', status: 'draft' })
|
||||||
|
store.drafts.set('c', { id: 'c', status: 'draft' })
|
||||||
|
storage.getItem.mockResolvedValueOnce({
|
||||||
|
a: { id: 'a', status: 'draft' },
|
||||||
|
b: { id: 'b', status: 'draft' },
|
||||||
|
c: { id: 'c', status: 'draft' },
|
||||||
|
})
|
||||||
|
|
||||||
|
await store.abandonDraft('b')
|
||||||
|
|
||||||
|
expect(store.drafts.get('a')).to.have.property('status', 'draft')
|
||||||
|
expect(store.drafts.get('b')).to.be.undefined
|
||||||
|
expect(store.drafts.get('c')).to.have.property('status', 'draft')
|
||||||
|
|
||||||
|
expect(storage.getItem).to.have.been.calledOnce
|
||||||
|
expect(storage.setItem).to.have.been.calledOnce
|
||||||
|
expect(storage.setItem).to.have.been.calledWith('pleroma-fe-drafts', {
|
||||||
|
a: {
|
||||||
|
id: 'a',
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
id: 'c',
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('abandonAllDrafts', () => {
|
||||||
|
it('should remove draft from storage and cache', async () => {
|
||||||
|
const store = useDraftsStore()
|
||||||
|
store.drafts.set('a', { id: 'a', status: 'draft' })
|
||||||
|
store.drafts.set('b', { id: 'b', status: 'draft' })
|
||||||
|
store.drafts.set('c', { id: 'c', status: 'draft' })
|
||||||
|
storage.getItem.mockResolvedValueOnce({
|
||||||
|
a: { id: 'a', status: 'draft' },
|
||||||
|
b: { id: 'b', status: 'draft' },
|
||||||
|
c: { id: 'c', status: 'draft' },
|
||||||
|
})
|
||||||
|
|
||||||
|
await store.abandonAllDrafts()
|
||||||
|
|
||||||
|
expect(store.drafts.get('a')).to.be.undefined
|
||||||
|
expect(store.drafts.get('b')).to.be.undefined
|
||||||
|
expect(store.drafts.get('c')).to.be.undefined
|
||||||
|
|
||||||
|
expect(storage.getItem).to.have.been.calledOnce
|
||||||
|
expect(storage.setItem).to.have.been.calledOnce
|
||||||
|
expect(storage.setItem).to.have.been.calledWith('pleroma-fe-drafts', {})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
350
test/unit/specs/stores/follow_requests.spec.js
Normal file
350
test/unit/specs/stores/follow_requests.spec.js
Normal file
|
|
@ -0,0 +1,350 @@
|
||||||
|
import { createTestingPinia } from '@pinia/testing'
|
||||||
|
import { setActivePinia } from 'pinia'
|
||||||
|
|
||||||
|
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
|
import * as USER_API from 'src/api/user.js'
|
||||||
|
|
||||||
|
const mockMastoAPIUser = ({
|
||||||
|
screen_name = 'u1',
|
||||||
|
name = 'user1',
|
||||||
|
url = 'http://localhost/u1',
|
||||||
|
id = 'u1',
|
||||||
|
} = {}) => ({
|
||||||
|
id,
|
||||||
|
acct: screen_name,
|
||||||
|
display_name: name,
|
||||||
|
fields: [],
|
||||||
|
avatar: '',
|
||||||
|
url,
|
||||||
|
pleroma: {
|
||||||
|
emoji_reactions: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Follow Requests store', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||||
|
vi.useFakeTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers()
|
||||||
|
vi.resetAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Getters', () => {
|
||||||
|
it('followRequestsCount returns total number of follow requests', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
|
||||||
|
store.requests = new Map([
|
||||||
|
['1', {}],
|
||||||
|
['2', {}],
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(store).to.have.property('followRequestsCount', 2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Actions', () => {
|
||||||
|
describe('Fetcher stuff', () => {
|
||||||
|
it('startFetching should initialize fetcher and fetch some data', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify([mockMastoAPIUser()]), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify([mockMastoAPIUser()]), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
store.startFetching()
|
||||||
|
expect(store.fetcher).to.not.be.null
|
||||||
|
|
||||||
|
await vi.advanceTimersToNextTimerAsync()
|
||||||
|
expect(mockFetch).to.have.been.calledOnce
|
||||||
|
|
||||||
|
await vi.advanceTimersToNextTimerAsync()
|
||||||
|
expect(mockFetch).to.have.been.calledTwice
|
||||||
|
expect(useUsersStore().findUser('u1')).to.not.be.undefined
|
||||||
|
expect(store.requests.get('u1')).to.not.be.undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('stopFetching should stop and remove the fetcher', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify([mockMastoAPIUser()]), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify([mockMastoAPIUser()]), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
store.startFetching()
|
||||||
|
expect(store.fetcher).to.not.be.null
|
||||||
|
|
||||||
|
store.stopFetching()
|
||||||
|
expect(store.fetcher).to.be.null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe.each(['Approve', 'Deny'])('%s', (intent) => {
|
||||||
|
const doCall = `do${intent}`
|
||||||
|
const apiCall = USER_API[`MASTODON_${intent.toUpperCase()}_USER_URL`]
|
||||||
|
const forkCall = intent.toLowerCase()
|
||||||
|
const forkProperty = `modalOn${intent}Follow`
|
||||||
|
const modalProperty = `showing${intent}ConfirmDialog`
|
||||||
|
const modalCalls = ['show', 'hide'].map(
|
||||||
|
(vis) => `${vis}${intent}ConfirmDialog`,
|
||||||
|
)
|
||||||
|
|
||||||
|
describe('Dialog calls', () => {
|
||||||
|
it(`${modalCalls[0]} should show dialog and set tempId`, async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
|
||||||
|
await store[modalCalls[0]]('u13')
|
||||||
|
|
||||||
|
expect(store).to.have.property(modalProperty, true)
|
||||||
|
expect(store).to.have.property('tempId', 'u13')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`${modalCalls[1]} should hide dialog and clear tempId`, async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
|
||||||
|
await store[modalCalls[1]]()
|
||||||
|
|
||||||
|
expect(store).to.have.property(modalProperty, false)
|
||||||
|
expect(store).to.have.property('tempId', null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Fork calls', () => {
|
||||||
|
it(`Should call ${doCall} if confirmations are disabled (${forkProperty} = false)`, async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const modalSpy = vi
|
||||||
|
.spyOn(store, modalCalls[0])
|
||||||
|
.mockImplementation(() => ({}))
|
||||||
|
const apiSpy = vi.spyOn(store, doCall).mockImplementation(() => ({}))
|
||||||
|
useMergedConfigStore().mergedConfig = { [forkProperty]: false }
|
||||||
|
|
||||||
|
await store[forkCall]('u23')
|
||||||
|
|
||||||
|
expect(modalSpy).to.not.have.been.called
|
||||||
|
expect(apiSpy).to.have.been.calledOnce
|
||||||
|
expect(apiSpy).to.have.been.calledWith('u23')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`Should call ${modalCalls[0]} if confirmations are enabled (${forkProperty} = true)`, async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const modalSpy = vi
|
||||||
|
.spyOn(store, modalCalls[0])
|
||||||
|
.mockImplementation(() => ({}))
|
||||||
|
const apiSpy = vi.spyOn(store, doCall).mockImplementation(() => ({}))
|
||||||
|
useMergedConfigStore().mergedConfig = { [forkProperty]: true }
|
||||||
|
|
||||||
|
await store[forkCall]('u23')
|
||||||
|
|
||||||
|
expect(modalSpy).to.have.been.called
|
||||||
|
expect(apiSpy).to.not.have.been.calledOnce
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Actual call', () => {
|
||||||
|
it('Should hide popover', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const spy = vi.spyOn(store, modalCalls[1])
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify('ok'), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
await store[doCall]()
|
||||||
|
|
||||||
|
expect(spy).to.have.been.calledOnce
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should call API', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify('ok'), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
await store[doCall]('u99')
|
||||||
|
|
||||||
|
expect(mockFetch).to.have.been.calledOnce
|
||||||
|
expect(mockFetch.mock.calls[0][0]).to.eql(apiCall('u99'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should mark notification as seen', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
store.findFollowRequestNotificationId = vi.fn()
|
||||||
|
store.findFollowRequestNotificationId.mockReturnValue('n91')
|
||||||
|
|
||||||
|
const spy = vi.spyOn(
|
||||||
|
useNotificationsStore(),
|
||||||
|
'markSingleNotificationAsSeen',
|
||||||
|
)
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify('ok'), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify('ok'), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
await store[doCall]('u99')
|
||||||
|
|
||||||
|
expect(spy).to.have.been.calledOnce
|
||||||
|
expect(spy).to.have.been.calledWith('n91')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fallback to tempId if no id is provided', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify('ok'), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
store.tempId = 'u80'
|
||||||
|
await store[doCall]()
|
||||||
|
|
||||||
|
expect(mockFetch).to.have.been.calledOnce
|
||||||
|
expect(mockFetch.mock.calls[0][0]).to.eql(apiCall('u80'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should remove request from cache', async () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
|
||||||
|
store.requests.set('u95', { id: 'u95' })
|
||||||
|
store.requests.set('u96', { id: 'u96' })
|
||||||
|
store.requests.set('u97', { id: 'u97' })
|
||||||
|
store.requests.set('u98', { id: 'u98' })
|
||||||
|
store.requests.set('u99', { id: 'u99' })
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify('ok'), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
await store[doCall]('u99')
|
||||||
|
|
||||||
|
expect(store.requests).to.have.length(4)
|
||||||
|
expect(store.requests.get('u99')).to.be.undefined
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Utility', () => {
|
||||||
|
describe('findFollowRequestNotificationId', () => {
|
||||||
|
it('should search notifications store for relevant notification', () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
|
||||||
|
useNotificationsStore().data = [
|
||||||
|
{
|
||||||
|
id: 'n4',
|
||||||
|
from_profile: { id: 'u3' },
|
||||||
|
type: 'follow_request',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'n3',
|
||||||
|
from_profile: { id: 'u2' },
|
||||||
|
type: 'repeat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'n2',
|
||||||
|
from_profile: { id: 'u1' },
|
||||||
|
type: 'follow_request',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'n1',
|
||||||
|
from_profile: { id: 'u1' },
|
||||||
|
type: 'favorite',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = store.findFollowRequestNotificationId('u1')
|
||||||
|
|
||||||
|
expect(result).to.have.eql('n2')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("shouldn't crash if there is no notification available", () => {
|
||||||
|
const store = useFollowRequestsStore()
|
||||||
|
|
||||||
|
useNotificationsStore().data = [
|
||||||
|
{
|
||||||
|
id: 'n4',
|
||||||
|
from_profile: { id: 'u3' },
|
||||||
|
type: 'follow_request',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'n3',
|
||||||
|
from_profile: { id: 'u2' },
|
||||||
|
type: 'repeat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'n2',
|
||||||
|
from_profile: { id: 'u1' },
|
||||||
|
type: 'follow_request',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'n1',
|
||||||
|
from_profile: { id: 'u1' },
|
||||||
|
type: 'favorite',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = store.findFollowRequestNotificationId('u5')
|
||||||
|
|
||||||
|
expect(result).to.have.eql(undefined)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue