Merge remote-tracking branch 'origin/develop' into more-fixes
This commit is contained in:
commit
6810d0fb9f
127 changed files with 4929 additions and 5175 deletions
|
|
@ -73,7 +73,7 @@ test:
|
||||||
|
|
||||||
e2e-pleroma:
|
e2e-pleroma:
|
||||||
stage: test
|
stage: test
|
||||||
image: mcr.microsoft.com/playwright:v1.57.0-jammy
|
image: mcr.microsoft.com/playwright:v1.61.0-jammy
|
||||||
services:
|
services:
|
||||||
- name: postgres:15-alpine
|
- name: postgres:15-alpine
|
||||||
alias: db
|
alias: db
|
||||||
|
|
@ -175,7 +175,7 @@ e2e-pleroma:
|
||||||
NOTIFY_EMAIL: $E2E_ADMIN_EMAIL
|
NOTIFY_EMAIL: $E2E_ADMIN_EMAIL
|
||||||
VITE_PROXY_TARGET: http://pleroma:4000
|
VITE_PROXY_TARGET: http://pleroma:4000
|
||||||
VITE_PROXY_ORIGIN: http://localhost:4000
|
VITE_PROXY_ORIGIN: http://localhost:4000
|
||||||
E2E_BASE_URL: http://localhost:8080
|
E2E_BASE_URL: http://localhost:8099
|
||||||
script:
|
script:
|
||||||
- npm install -g yarn@1.22.22
|
- npm install -g yarn@1.22.22
|
||||||
- yarn --frozen-lockfile
|
- yarn --frozen-lockfile
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ variables:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
test:
|
test:
|
||||||
image: mcr.microsoft.com/playwright:v1.57.0-jammy
|
image: mcr.microsoft.com/playwright:v1.61.0-jammy
|
||||||
entrypoint: *script_file_entrypoint
|
entrypoint: *script_file_entrypoint
|
||||||
environment:
|
environment:
|
||||||
APT_CACHE_DIR: apt-cache
|
APT_CACHE_DIR: apt-cache
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
E2E_BASE_URL: http://localhost:8080
|
E2E_BASE_URL: http://localhost:8099
|
||||||
FF_NETWORK_PER_BUILD: "true"
|
FF_NETWORK_PER_BUILD: "true"
|
||||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
||||||
VITE_PROXY_ORIGIN: "http://pleroma:4000"
|
VITE_PROXY_ORIGIN: "http://pleroma:4000"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ variables:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
test:
|
test:
|
||||||
image: mcr.microsoft.com/playwright:v1.57.0-jammy
|
image: mcr.microsoft.com/playwright:v1.61.0-jammy
|
||||||
environment:
|
environment:
|
||||||
APT_CACHE_DIR: apt-cache
|
APT_CACHE_DIR: apt-cache
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
"noUnusedLabels": "error",
|
"noUnusedLabels": "error",
|
||||||
"noUnusedPrivateClassMembers": "error",
|
"noUnusedPrivateClassMembers": "error",
|
||||||
"noUnusedVariables": "error",
|
"noUnusedVariables": "error",
|
||||||
|
"noUnusedImports": "error",
|
||||||
"useIsNan": "error",
|
"useIsNan": "error",
|
||||||
"useValidForDirection": "error",
|
"useValidForDirection": "error",
|
||||||
"useValidTypeof": "error",
|
"useValidTypeof": "error",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { readFile } from 'node:fs/promises'
|
|
||||||
import { dirname, resolve } from 'node:path'
|
import { dirname, resolve } from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { exactRegex } from '@rolldown/pluginutils'
|
import { exactRegex } from '@rolldown/pluginutils'
|
||||||
|
|
@ -102,8 +101,12 @@ export const buildSwPlugin = ({ swSrc, swDest }) => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const swBundle = await build(config)
|
try {
|
||||||
return swBundle.output[0]
|
const swBundle = await build(config)
|
||||||
|
return swBundle.output[0]
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error building ServiceWorker:', e)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
closeBundle: {
|
closeBundle: {
|
||||||
|
|
@ -112,7 +115,11 @@ export const buildSwPlugin = ({ swSrc, swDest }) => {
|
||||||
async handler() {
|
async handler() {
|
||||||
if (process.env.VITEST) return
|
if (process.env.VITEST) return
|
||||||
console.info('Building service worker for production')
|
console.info('Building service worker for production')
|
||||||
await build(config)
|
try {
|
||||||
|
await build(config)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error building ServiceWorker:', e)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
changelog.d/api-refactor.skip
Normal file
0
changelog.d/api-refactor.skip
Normal file
|
|
@ -39,6 +39,8 @@ services:
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 60
|
retries: 60
|
||||||
|
ports:
|
||||||
|
- 4000:4000
|
||||||
|
|
||||||
e2e:
|
e2e:
|
||||||
build:
|
build:
|
||||||
|
|
@ -51,7 +53,7 @@ services:
|
||||||
CI: "1"
|
CI: "1"
|
||||||
VITE_PROXY_TARGET: http://pleroma:4000
|
VITE_PROXY_TARGET: http://pleroma:4000
|
||||||
VITE_PROXY_ORIGIN: http://localhost:4000
|
VITE_PROXY_ORIGIN: http://localhost:4000
|
||||||
E2E_BASE_URL: http://localhost:8080
|
E2E_BASE_URL: http://localhost:8099
|
||||||
E2E_ADMIN_USERNAME: ${E2E_ADMIN_USERNAME:-admin}
|
E2E_ADMIN_USERNAME: ${E2E_ADMIN_USERNAME:-admin}
|
||||||
E2E_ADMIN_PASSWORD: ${E2E_ADMIN_PASSWORD:-adminadmin}
|
E2E_ADMIN_PASSWORD: ${E2E_ADMIN_PASSWORD:-adminadmin}
|
||||||
command: ["yarn", "e2e:pw"]
|
command: ["yarn", "e2e:pw"]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/playwright:v1.57.0-jammy
|
FROM mcr.microsoft.com/playwright:v1.61.0-jammy
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
|
||||||
11
package.json
11
package.json
|
|
@ -68,8 +68,9 @@
|
||||||
"@vitejs/devtools": "^0.3.1",
|
"@vitejs/devtools": "^0.3.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.7",
|
"@vitejs/plugin-vue": "^6.0.7",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||||
"@vitest/browser": "^3.0.7",
|
"@vitest/browser-playwright": "^4.1.7",
|
||||||
"@vitest/ui": "^3.0.7",
|
"@vitest/browser": "^4.1.7",
|
||||||
|
"@vitest/ui": "^4.1.7",
|
||||||
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
||||||
"@vue/babel-plugin-jsx": "1.5.0",
|
"@vue/babel-plugin-jsx": "1.5.0",
|
||||||
"@vue/compiler-sfc": "3.5.22",
|
"@vue/compiler-sfc": "3.5.22",
|
||||||
|
|
@ -95,10 +96,10 @@
|
||||||
"http-proxy-middleware": "3.0.5",
|
"http-proxy-middleware": "3.0.5",
|
||||||
"iso-639-1": "3.1.5",
|
"iso-639-1": "3.1.5",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"msw": "2.10.5",
|
"msw": "2.14.6",
|
||||||
"nightwatch": "3.12.2",
|
"nightwatch": "3.12.2",
|
||||||
"oxc": "^1.0.1",
|
"oxc": "^1.0.1",
|
||||||
"playwright": "1.57.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",
|
||||||
|
|
@ -118,7 +119,7 @@
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.0",
|
||||||
"vite-plugin-eslint2": "^5.1.0",
|
"vite-plugin-eslint2": "^5.1.0",
|
||||||
"vite-plugin-stylelint": "^6.1.0",
|
"vite-plugin-stylelint": "^6.1.0",
|
||||||
"vitest": "^3.0.7",
|
"vitest": "^4.1.7",
|
||||||
"vue-eslint-parser": "10.2.0"
|
"vue-eslint-parser": "10.2.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,6 @@ import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useShoutStore } from 'src/stores/shout.js'
|
import { useShoutStore } from 'src/stores/shout.js'
|
||||||
|
|
||||||
import messages from 'src/i18n/messages'
|
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
|
||||||
|
|
||||||
// Helper to unwrap reactive proxies
|
// Helper to unwrap reactive proxies
|
||||||
window.toValue = (x) => JSON.parse(JSON.stringify(x))
|
window.toValue = (x) => JSON.parse(JSON.stringify(x))
|
||||||
|
|
||||||
|
|
|
||||||
491
src/api/admin.js
Normal file
491
src/api/admin.js
Normal file
|
|
@ -0,0 +1,491 @@
|
||||||
|
import { promisedRequest } from './helpers.js'
|
||||||
|
|
||||||
|
const REPORTS = '/api/v1/pleroma/admin/reports'
|
||||||
|
const CONFIG_URL = '/api/v1/pleroma/admin/config'
|
||||||
|
const DESCRIPTIONS_URL = '/api/v1/pleroma/admin/config/descriptions'
|
||||||
|
|
||||||
|
const ANNOUNCEMENTS_URL = (id = '') =>
|
||||||
|
`/api/v1/pleroma/admin/announcements/${id}`
|
||||||
|
|
||||||
|
const FRONTENDS_URL = '/api/v1/pleroma/admin/frontends'
|
||||||
|
const FRONTENDS_INSTALL_URL = '/api/v1/pleroma/admin/frontends/install'
|
||||||
|
|
||||||
|
const USERS_URL = (nickname = '') => `/api/v1/pleroma/admin/users/${nickname}`
|
||||||
|
const USERS_URL_LIST = ({
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
filters = {},
|
||||||
|
query = '',
|
||||||
|
name = '',
|
||||||
|
email = '',
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
local = false,
|
||||||
|
external = false,
|
||||||
|
active = false,
|
||||||
|
needApproval = false,
|
||||||
|
unconfirmed = false,
|
||||||
|
deactivated = false,
|
||||||
|
isAdmin = true,
|
||||||
|
isModerator = true,
|
||||||
|
} = filters
|
||||||
|
const filters_str = [
|
||||||
|
local && 'local',
|
||||||
|
external && 'external',
|
||||||
|
active && 'active',
|
||||||
|
needApproval && 'need_approval',
|
||||||
|
unconfirmed && 'unconfirmed',
|
||||||
|
deactivated && 'deactivated',
|
||||||
|
isAdmin && 'is_admin',
|
||||||
|
isModerator && 'is_moderator',
|
||||||
|
]
|
||||||
|
.filter((x) => x)
|
||||||
|
.join(',')
|
||||||
|
return `/api/v1/pleroma/admin/users?page=${page}&page_size=${pageSize}&filters=${filters_str}&query=${query}&name=${name}&email=${email}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const TAG_USER_URL = '/api/pleroma/admin/users/tag'
|
||||||
|
|
||||||
|
const PERMISSION_GROUP_URL = (right) =>
|
||||||
|
`/api/pleroma/admin/users/permission_group/${right}`
|
||||||
|
const ACTIVATE_USERS_URL = '/api/pleroma/admin/users/activate'
|
||||||
|
const DEACTIVATE_USERS_URL = '/api/pleroma/admin/users/deactivate'
|
||||||
|
const SUGGEST_USERS_URL = '/api/pleroma/admin/users/suggest'
|
||||||
|
const UNSUGGEST_USERS_URL = '/api/pleroma/admin/users/unsuggest'
|
||||||
|
const APPROVE_USERS_URL = '/api/v1/pleroma/admin/users/approve'
|
||||||
|
const CONFIRM_USERS_URL = '/api/v1/pleroma/admin/users/confirm_email'
|
||||||
|
const RESEND_CONFIRMATION_EMAIL_URL =
|
||||||
|
'/api/v1/pleroma/admin/users/resend_confirmation_email'
|
||||||
|
const LIST_STATUSES_URL = ({ id, page, pageSize, godmode, withReblogs }) =>
|
||||||
|
`/api/v1/pleroma/admin/users/${id}/statuses?page_size=${pageSize}&page=${page}&godmode=${godmode}&with_reblogs=${withReblogs}`
|
||||||
|
const CHANGE_STATUS_SCOPE_URL = (id) => `/api/v1/pleroma/admin/statuses/${id}`
|
||||||
|
const REQUIRE_PASSWORD_CHANGE_URL =
|
||||||
|
'/api/v1/pleroma/admin/users/force_password_reset'
|
||||||
|
|
||||||
|
const DISABLE_MFA_URL = '/api/v1/pleroma/admin/users/disable_mfa'
|
||||||
|
const EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji'
|
||||||
|
const EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import'
|
||||||
|
const EMOJI_PACK_URL = (name) => `/api/v1/pleroma/emoji/pack?name=${name}`
|
||||||
|
const EMOJI_PACKS_DL_REMOTE_URL = '/api/v1/pleroma/emoji/packs/download'
|
||||||
|
const EMOJI_PACKS_DL_REMOTE_ZIP_URL = '/api/v1/pleroma/emoji/packs/download_zip'
|
||||||
|
const EMOJI_PACKS_LS_REMOTE_URL = (url, page, pageSize) =>
|
||||||
|
`/api/v1/pleroma/emoji/packs/remote?url=${url}&page=${page}&page_size=${pageSize}`
|
||||||
|
const EMOJI_UPDATE_FILE_URL = (name) =>
|
||||||
|
`/api/v1/pleroma/emoji/packs/files?name=${name}`
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
export const setUsersTags = ({
|
||||||
|
tags,
|
||||||
|
credentials,
|
||||||
|
value,
|
||||||
|
screen_names: nicknames,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: TAG_USER_URL,
|
||||||
|
method: value ? 'PUT' : 'DELETE',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
tags,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setUsersRight = ({
|
||||||
|
right,
|
||||||
|
credentials,
|
||||||
|
value,
|
||||||
|
screen_names: nicknames,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PERMISSION_GROUP_URL(right),
|
||||||
|
method: value ? 'POST' : 'DELETE',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setUsersActivationStatus = ({
|
||||||
|
credentials,
|
||||||
|
screen_names: nicknames,
|
||||||
|
value,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: value ? ACTIVATE_USERS_URL : DEACTIVATE_USERS_URL,
|
||||||
|
method: 'PATCH',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
}).then((response) => response.users)
|
||||||
|
|
||||||
|
export const setUsersApprovalStatus = ({
|
||||||
|
credentials,
|
||||||
|
screen_names: nicknames,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: APPROVE_USERS_URL,
|
||||||
|
method: 'PATCH',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
}).then((response) => response.users)
|
||||||
|
|
||||||
|
export const setUsersConfirmationStatus = ({
|
||||||
|
credentials,
|
||||||
|
screen_names: nicknames,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: CONFIRM_USERS_URL,
|
||||||
|
method: 'PATCH',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
}).then((response) => response.users)
|
||||||
|
|
||||||
|
export const setUsersSuggestionStatus = ({
|
||||||
|
credentials,
|
||||||
|
screen_names: nicknames,
|
||||||
|
value,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: value ? SUGGEST_USERS_URL : UNSUGGEST_USERS_URL,
|
||||||
|
method: 'PATCH',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
}).then((response) => response.users)
|
||||||
|
|
||||||
|
export const getUserData = ({ credentials, screen_name: nickname }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: USERS_URL(nickname),
|
||||||
|
method: 'GET',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteAccounts = ({ credentials, screen_names: nicknames }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: USERS_URL(),
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getAnnouncements = ({ id, credentials }) =>
|
||||||
|
promisedRequest({ url: ANNOUNCEMENTS_URL(id), credentials })
|
||||||
|
|
||||||
|
// the reported list is hardly useful because standards are for dating i guess,
|
||||||
|
// so make sure to fetchIfMissing right afterward using this call
|
||||||
|
export const listUsers = ({ opts, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: USERS_URL_LIST(opts),
|
||||||
|
credentials,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const resendConfirmationEmail = ({
|
||||||
|
screen_names: nicknames,
|
||||||
|
credentials,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: RESEND_CONFIRMATION_EMAIL_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const requirePasswordChange = ({
|
||||||
|
screen_names: nicknames,
|
||||||
|
credentials,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: REQUIRE_PASSWORD_CHANGE_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
payload: {
|
||||||
|
nicknames,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const disableMFA = ({ screen_name: nickname, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: DISABLE_MFA_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'PUT',
|
||||||
|
payload: {
|
||||||
|
nickname,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const listStatuses = ({ opts, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: LIST_STATUSES_URL(opts),
|
||||||
|
credentials,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const changeStatusScope = ({
|
||||||
|
opts: { id, sensitive, visibility },
|
||||||
|
credentials,
|
||||||
|
}) => {
|
||||||
|
var payload = {}
|
||||||
|
if (typeof sensitive !== 'undefined') {
|
||||||
|
payload['sensitive'] = sensitive
|
||||||
|
}
|
||||||
|
if (typeof visibility !== 'undefined') {
|
||||||
|
payload['visibility'] = visibility
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: CHANGE_STATUS_SCOPE_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'PUT',
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const announcementToPayload = ({
|
||||||
|
content,
|
||||||
|
startsAt,
|
||||||
|
endsAt,
|
||||||
|
allDay,
|
||||||
|
}) => {
|
||||||
|
const payload = { content }
|
||||||
|
|
||||||
|
if (typeof startsAt !== 'undefined') {
|
||||||
|
payload.starts_at = startsAt ? new Date(startsAt).toISOString() : null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof endsAt !== 'undefined') {
|
||||||
|
payload.ends_at = endsAt ? new Date(endsAt).toISOString() : null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof allDay !== 'undefined') {
|
||||||
|
payload.all_day = allDay
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
export const postAnnouncement = ({
|
||||||
|
credentials,
|
||||||
|
content,
|
||||||
|
startsAt,
|
||||||
|
endsAt,
|
||||||
|
allDay,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: ANNOUNCEMENTS_URL(),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload: announcementToPayload({ content, startsAt, endsAt, allDay }),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const editAnnouncement = ({
|
||||||
|
id,
|
||||||
|
credentials,
|
||||||
|
content,
|
||||||
|
startsAt,
|
||||||
|
endsAt,
|
||||||
|
allDay,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: ANNOUNCEMENTS_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
payload: announcementToPayload({ content, startsAt, endsAt, allDay }),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteAnnouncement = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: ANNOUNCEMENTS_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setReportState = ({ id, state, credentials }) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: REPORTS,
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
payload: {
|
||||||
|
reports: [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
state,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInstanceDBConfig = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: CONFIG_URL,
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getInstanceConfigDescriptions = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: DESCRIPTIONS_URL,
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getAvailableFrontends = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: FRONTENDS_URL,
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const pushInstanceDBConfig = ({ credentials, payload }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: CONFIG_URL,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const installFrontend = ({ credentials, payload }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: FRONTENDS_INSTALL_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Emoji packs
|
||||||
|
export const deleteEmojiPack = ({ name }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_PACK_URL(name),
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const reloadEmoji = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_RELOAD_URL,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const importEmojiFromFS = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_IMPORT_FS_URL,
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createEmojiPack = ({ name, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_PACK_URL(name),
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const listRemoteEmojiPacks = ({
|
||||||
|
instance,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
credentials,
|
||||||
|
}) => {
|
||||||
|
if (!instance.startsWith('http')) {
|
||||||
|
instance = 'https://' + instance
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: EMOJI_PACKS_LS_REMOTE_URL(instance, page, pageSize),
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const downloadRemoteEmojiPack = ({
|
||||||
|
instance,
|
||||||
|
packName,
|
||||||
|
as,
|
||||||
|
credentials,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_PACKS_DL_REMOTE_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload: {
|
||||||
|
url: instance,
|
||||||
|
name: packName,
|
||||||
|
as,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const downloadRemoteEmojiPackZIP = ({
|
||||||
|
url,
|
||||||
|
packName,
|
||||||
|
file,
|
||||||
|
credentials,
|
||||||
|
}) => {
|
||||||
|
const data = new FormData()
|
||||||
|
if (file) data.set('file', file)
|
||||||
|
if (url) data.set('url', url)
|
||||||
|
data.set('name', packName)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: EMOJI_PACKS_DL_REMOTE_ZIP_URL,
|
||||||
|
method: 'POST',
|
||||||
|
payload: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveEmojiPackMetadata = ({ name, newData, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_PACK_URL(name),
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
payload: { metadata: newData },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const addNewEmojiFile = ({ packName, file, shortcode, filename }) => {
|
||||||
|
const data = new FormData()
|
||||||
|
if (filename.trim() !== '') {
|
||||||
|
data.set('filename', filename)
|
||||||
|
}
|
||||||
|
if (shortcode.trim() !== '') {
|
||||||
|
data.set('shortcode', shortcode)
|
||||||
|
}
|
||||||
|
data.set('file', file)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: EMOJI_UPDATE_FILE_URL(packName),
|
||||||
|
method: 'POST',
|
||||||
|
payload: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateEmojiFile = ({
|
||||||
|
packName,
|
||||||
|
shortcode,
|
||||||
|
newShortcode,
|
||||||
|
newFilename,
|
||||||
|
credentials,
|
||||||
|
force,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_UPDATE_FILE_URL(packName),
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
payload: {
|
||||||
|
shortcode,
|
||||||
|
new_shortcode: newShortcode,
|
||||||
|
new_filename: newFilename,
|
||||||
|
force,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteEmojiFile = ({ packName, shortcode }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: `${EMOJI_UPDATE_FILE_URL(packName)}&shortcode=${shortcode}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
87
src/api/chats.js
Normal file
87
src/api/chats.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { paramsString, promisedRequest } from './helpers.js'
|
||||||
|
|
||||||
|
import { parseChat } from 'src/services/entity_normalizer/entity_normalizer.service.js'
|
||||||
|
|
||||||
|
const PLEROMA_CHATS_URL = '/api/v1/pleroma/chats'
|
||||||
|
const PLEROMA_CHAT_URL = (id) => `/api/v1/pleroma/chats/by-account-id/${id}`
|
||||||
|
const PLEROMA_CHAT_MESSAGES_URL = (id, { maxId, sinceId, limit }) =>
|
||||||
|
`/api/v1/pleroma/chats/${id}/messages${paramsString({ maxId, sinceId, limit })}`
|
||||||
|
const PLEROMA_CHAT_READ_URL = (id) => `/api/v1/pleroma/chats/${id}/read`
|
||||||
|
const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) =>
|
||||||
|
`/api/v1/pleroma/chats/${chatId}/messages/${messageId}`
|
||||||
|
|
||||||
|
export const chats = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_CHATS_URL,
|
||||||
|
credentials,
|
||||||
|
}).then(({ data }) => ({
|
||||||
|
chatList: data.map(parseChat).filter((c) => c),
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const getOrCreateChat = ({ accountId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_CHAT_URL(accountId),
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const chatMessages = ({
|
||||||
|
id,
|
||||||
|
credentials,
|
||||||
|
maxId,
|
||||||
|
sinceId,
|
||||||
|
limit = 20,
|
||||||
|
}) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: PLEROMA_CHAT_MESSAGES_URL(id, { maxId, sinceId, limit }),
|
||||||
|
method: 'GET',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendChatMessage = ({
|
||||||
|
id,
|
||||||
|
content,
|
||||||
|
mediaId = null,
|
||||||
|
idempotencyKey,
|
||||||
|
credentials,
|
||||||
|
}) => {
|
||||||
|
const payload = {
|
||||||
|
content,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mediaId) {
|
||||||
|
payload.media_id = mediaId
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = {}
|
||||||
|
|
||||||
|
if (idempotencyKey) {
|
||||||
|
headers['idempotency-key'] = idempotencyKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: PLEROMA_CHAT_MESSAGES_URL(id),
|
||||||
|
method: 'POST',
|
||||||
|
payload,
|
||||||
|
credentials,
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const readChat = ({ id, lastReadId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_CHAT_READ_URL(id),
|
||||||
|
method: 'POST',
|
||||||
|
payload: {
|
||||||
|
last_read_id: lastReadId,
|
||||||
|
},
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteChatMessage = ({ chatId, messageId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_DELETE_CHAT_MESSAGE_URL(chatId, messageId),
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
135
src/api/helpers.js
Normal file
135
src/api/helpers.js
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import { snakeCase } from 'lodash'
|
||||||
|
|
||||||
|
import { StatusCodeError } from 'src/services/errors/errors'
|
||||||
|
|
||||||
|
export const paramsString = (params = {}) => {
|
||||||
|
if (params == null || params === undefined) return ''
|
||||||
|
|
||||||
|
if (typeof params !== 'object' || Array.isArray(params)) {
|
||||||
|
throw new Error('Params are not an object!')
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = (() => {
|
||||||
|
if (params instanceof Map) {
|
||||||
|
return params.entries()
|
||||||
|
} else {
|
||||||
|
return Object.entries(params)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
if (entries.length === 0) return ''
|
||||||
|
|
||||||
|
const arrays = []
|
||||||
|
const nonArrays = []
|
||||||
|
|
||||||
|
entries.forEach(([k, v]) => {
|
||||||
|
if (v == null) return // Drop nulls
|
||||||
|
if (
|
||||||
|
(typeof v === 'object' && !Array.isArray(v)) ||
|
||||||
|
typeof v === 'function'
|
||||||
|
) {
|
||||||
|
throw new Error('Param cannot be non-primitive!')
|
||||||
|
}
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
arrays.push([k, v])
|
||||||
|
} else {
|
||||||
|
nonArrays.push([k, v])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
arrays.forEach(([k, array]) => {
|
||||||
|
array.forEach((v) => {
|
||||||
|
if (
|
||||||
|
typeof v === 'object' ||
|
||||||
|
typeof v === 'function' ||
|
||||||
|
typeof v === 'undefined'
|
||||||
|
)
|
||||||
|
throw new Error('Array param cannot contain non-primitives!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
'?' +
|
||||||
|
[
|
||||||
|
...nonArrays.map(([k, v]) => [snakeCase(k), v]),
|
||||||
|
// turning [a,[1,2,3]] into [[a[],1],[a[],2],[a[],3]]
|
||||||
|
...arrays.reduce(
|
||||||
|
(acc, [k, arrayValue]) => [
|
||||||
|
...acc,
|
||||||
|
...arrayValue.map((v) => [snakeCase(k) + '[]', v]),
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.map(([k, v]) => `${k}=${window.encodeURIComponent(v)}`)
|
||||||
|
.join('&')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const promisedRequest = async ({
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
payload,
|
||||||
|
formData,
|
||||||
|
credentials,
|
||||||
|
headers = {},
|
||||||
|
}) => {
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData) {
|
||||||
|
options.headers['Content-Type'] = 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData || payload) {
|
||||||
|
options.body = formData || JSON.stringify(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (credentials) {
|
||||||
|
options.headers = {
|
||||||
|
...options.headers,
|
||||||
|
...authHeaders(credentials),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, options)
|
||||||
|
const data = await (async () => {
|
||||||
|
const [contentType] = response.headers
|
||||||
|
.get('content-type')
|
||||||
|
.split(';')
|
||||||
|
.map((x) => x.toLowerCase().trim())
|
||||||
|
const contentLength = parseInt(response.headers.get('content-length'))
|
||||||
|
if (contentLength === 0) return null
|
||||||
|
|
||||||
|
switch (contentType) {
|
||||||
|
case 'text/plain':
|
||||||
|
return await response.text()
|
||||||
|
case 'application/json':
|
||||||
|
return await response.json()
|
||||||
|
default:
|
||||||
|
return await response.bytes()
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
const { ok, status } = response
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
return { response, status, data }
|
||||||
|
} else {
|
||||||
|
throw new StatusCodeError(response.status, data, { url, options }, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const authHeaders = (accessToken) => {
|
||||||
|
if (accessToken) {
|
||||||
|
return { Authorization: `Bearer ${accessToken}` }
|
||||||
|
} else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/api/mfa.js
Normal file
45
src/api/mfa.js
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { promisedRequest } from './helpers.js'
|
||||||
|
|
||||||
|
export const verifyOTPCode = ({
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
instance,
|
||||||
|
mfaToken,
|
||||||
|
code,
|
||||||
|
}) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', clientId)
|
||||||
|
formData.append('client_secret', clientSecret)
|
||||||
|
formData.append('mfa_token', mfaToken)
|
||||||
|
formData.append('code', code)
|
||||||
|
formData.append('challenge_type', 'totp')
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: '/oauth/mfa/challenge',
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const verifyRecoveryCode = ({
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
instance,
|
||||||
|
mfaToken,
|
||||||
|
code,
|
||||||
|
}) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', clientId)
|
||||||
|
formData.append('client_secret', clientSecret)
|
||||||
|
formData.append('mfa_token', mfaToken)
|
||||||
|
formData.append('code', code)
|
||||||
|
formData.append('challenge_type', 'recovery')
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: `${instance}/oauth/mfa/challenge`,
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
146
src/api/oauth.js
Normal file
146
src/api/oauth.js
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
import { paramsString, promisedRequest } from './helpers.js'
|
||||||
|
|
||||||
|
const REDIRECT_URI = `${window.location.origin}/oauth-callback`
|
||||||
|
|
||||||
|
export const MASTODON_APP_VERIFY_URL = '/api/v1/apps/verify_credentials'
|
||||||
|
export const MASTODON_APP_URL = '/api/v1/apps'
|
||||||
|
export const OAUTH_TOKEN_URL = '/oauth/token'
|
||||||
|
export const OAUTH_MFA_CHALLENGE_URL = '/oauth/mfa/challenge'
|
||||||
|
export const OAUTH_REVOKE_URL = '/oauth/revoke'
|
||||||
|
|
||||||
|
export const createApp = () => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_name', 'PleromaFE')
|
||||||
|
formData.append('website', 'https://pleroma.social')
|
||||||
|
formData.append('redirect_uris', REDIRECT_URI)
|
||||||
|
formData.append('scopes', 'read write follow push admin')
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
method: 'POST',
|
||||||
|
url: MASTODON_APP_URL,
|
||||||
|
formData,
|
||||||
|
}).then(({ data, ...rest }) => ({
|
||||||
|
...rest,
|
||||||
|
data: {
|
||||||
|
...data,
|
||||||
|
clientId: data.client_id,
|
||||||
|
clientSecret: data.client_secret,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const verifyAppToken = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_APP_VERIFY_URL,
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getLoginUrl = ({ instance, clientId }) => {
|
||||||
|
const data = {
|
||||||
|
responseType: 'code',
|
||||||
|
clientId,
|
||||||
|
redirectUri: REDIRECT_URI,
|
||||||
|
scope: 'read write follow push admin',
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${instance}/oauth/authorize${paramsString(data)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTokenWithCredentials = ({
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
}) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', clientId)
|
||||||
|
formData.append('client_secret', clientSecret)
|
||||||
|
formData.append('grant_type', 'password')
|
||||||
|
formData.append('username', username)
|
||||||
|
formData.append('password', password)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: OAUTH_TOKEN_URL,
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getToken = ({ clientId, clientSecret, code }) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', clientId)
|
||||||
|
formData.append('client_secret', clientSecret)
|
||||||
|
formData.append('grant_type', 'authorization_code')
|
||||||
|
formData.append('code', code)
|
||||||
|
formData.append('redirect_uri', `${window.location.origin}/oauth-callback`)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: OAUTH_TOKEN_URL,
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getClientToken = ({ clientId, clientSecret }) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', clientId)
|
||||||
|
formData.append('client_secret', clientSecret)
|
||||||
|
formData.append('grant_type', 'client_credentials')
|
||||||
|
formData.append('redirect_uri', `${window.location.origin}/oauth-callback`)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: OAUTH_TOKEN_URL,
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const verifyOTPCode = ({ app, mfaToken, code }) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', app.client_id)
|
||||||
|
formData.append('client_secret', app.client_secret)
|
||||||
|
formData.append('mfa_token', mfaToken)
|
||||||
|
formData.append('code', code)
|
||||||
|
formData.append('challenge_type', 'totp')
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: OAUTH_MFA_CHALLENGE_URL,
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const verifyRecoveryCode = ({ app, mfaToken, code }) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', app.client_id)
|
||||||
|
formData.append('client_secret', app.client_secret)
|
||||||
|
formData.append('mfa_token', mfaToken)
|
||||||
|
formData.append('code', code)
|
||||||
|
formData.append('challenge_type', 'recovery')
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: OAUTH_MFA_CHALLENGE_URL,
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const revokeToken = ({ app, token }) => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
|
||||||
|
formData.append('client_id', app.clientId)
|
||||||
|
formData.append('client_secret', app.clientSecret)
|
||||||
|
formData.append('token', token)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: OAUTH_REVOKE_URL,
|
||||||
|
method: 'POST',
|
||||||
|
formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
279
src/api/public.js
Normal file
279
src/api/public.js
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
import { paramsString, promisedRequest } from './helpers.js'
|
||||||
|
import { MASTODON_USER_TIMELINE_URL } from './timelines.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseSource,
|
||||||
|
parseStatus,
|
||||||
|
parseUser,
|
||||||
|
} from 'src/services/entity_normalizer/entity_normalizer.service.js'
|
||||||
|
|
||||||
|
const MASTODON_SUGGESTIONS_URL = '/api/v1/suggestions'
|
||||||
|
const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
|
||||||
|
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
|
||||||
|
const MASTODON_PASSWORD_RESET_URL = ({ email }) =>
|
||||||
|
`/auth/password${paramsString({ email })}`
|
||||||
|
|
||||||
|
const MASTODON_FOLLOWING_URL = (
|
||||||
|
id,
|
||||||
|
{ minId, maxId, sinceId, limit, withRelationships },
|
||||||
|
) =>
|
||||||
|
`/api/v1/accounts/${id}/following${paramsString({ minId, maxId, sinceId, limit, withRelationships })}`
|
||||||
|
const MASTODON_FOLLOWERS_URL = (
|
||||||
|
id,
|
||||||
|
{ minId, maxId, sinceId, limit, withRelationships },
|
||||||
|
) =>
|
||||||
|
`/api/v1/accounts/${id}/followers${paramsString({ minId, maxId, sinceId, limit, withRelationships })}`
|
||||||
|
|
||||||
|
export const MASTODON_STATUS_URL = (id) => `/api/v1/statuses/${id}`
|
||||||
|
const MASTODON_STATUS_CONTEXT_URL = (id) => `/api/v1/statuses/${id}/context`
|
||||||
|
const MASTODON_STATUS_SOURCE_URL = (id) => `/api/v1/statuses/${id}/source`
|
||||||
|
const MASTODON_STATUS_HISTORY_URL = (id) => `/api/v1/statuses/${id}/history`
|
||||||
|
const MASTODON_USER_URL = '/api/v1/accounts'
|
||||||
|
const MASTODON_USER_LOOKUP_URL = ({ acct }) =>
|
||||||
|
`/api/v1/accounts/lookup${paramsString({ acct })}`
|
||||||
|
const MASTODON_POLL_URL = (id = '') => `/api/v1/polls/${id}`
|
||||||
|
const MASTODON_STATUS_FAVORITEDBY_URL = (id) =>
|
||||||
|
`/api/v1/statuses/${id}/favourited_by`
|
||||||
|
const MASTODON_STATUS_REBLOGGEDBY_URL = (id) =>
|
||||||
|
`/api/v1/statuses/${id}/reblogged_by`
|
||||||
|
const MASTODON_SEARCH_2 = ({
|
||||||
|
q,
|
||||||
|
resolve,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
following,
|
||||||
|
type,
|
||||||
|
withRelationships,
|
||||||
|
accountId,
|
||||||
|
excludeUnreviewed,
|
||||||
|
}) =>
|
||||||
|
`/api/v2/search${paramsString({ q, resolve, limit, offset, following, type, withRelationships, accountId, excludeUnreviewed })}`
|
||||||
|
const MASTODON_USER_SEARCH_URL = ({ q, resolve }) =>
|
||||||
|
`/api/v1/accounts/search${paramsString({ q, resolve })}`
|
||||||
|
const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers'
|
||||||
|
const PLEROMA_EMOJI_REACTIONS_URL = (id) =>
|
||||||
|
`/api/v1/pleroma/statuses/${id}/reactions`
|
||||||
|
const PLEROMA_SCROBBLES_URL = (id, { maxId, sinceId, minId, limit, offset }) =>
|
||||||
|
`/api/v1/pleroma/accounts/${id}/scrobbles${paramsString({ maxId, sinceId, minId, limit, offset })}`
|
||||||
|
|
||||||
|
const EMOJI_PACKS_URL = (page, pageSize) =>
|
||||||
|
`/api/v1/pleroma/emoji/packs${paramsString({ page, pageSize })}`
|
||||||
|
|
||||||
|
// Params needed:
|
||||||
|
// nickname
|
||||||
|
// email
|
||||||
|
// fullname
|
||||||
|
// password
|
||||||
|
// password_confirm
|
||||||
|
//
|
||||||
|
// Optional
|
||||||
|
// bio
|
||||||
|
// homepage
|
||||||
|
// location
|
||||||
|
// token
|
||||||
|
// language
|
||||||
|
export const register = ({ params, credentials }) => {
|
||||||
|
const { nickname, ...rest } = params
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_REGISTRATION_URL,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nickname,
|
||||||
|
locale: 'en_US',
|
||||||
|
agreement: true,
|
||||||
|
...rest,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCaptcha = () =>
|
||||||
|
promisedRequest({
|
||||||
|
url: '/api/pleroma/captcha',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchUser = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: `${MASTODON_USER_URL}/${id}`,
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseUser(data) }))
|
||||||
|
|
||||||
|
export const fetchUserByName = ({ name, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_LOOKUP_URL({ acct: name }),
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
.then(({ data }) => data.id)
|
||||||
|
.catch((error) => {
|
||||||
|
if (error && error.statusCode === 404) {
|
||||||
|
// Either the backend does not support lookup endpoint,
|
||||||
|
// or there is no user with such name. Fallback and treat name as id.
|
||||||
|
return name
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((id) => fetchUser({ id, credentials }))
|
||||||
|
|
||||||
|
export const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_FOLLOWING_URL(id, { maxId, sinceId, limit }),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const fetchFollowers = ({
|
||||||
|
id,
|
||||||
|
maxId,
|
||||||
|
sinceId,
|
||||||
|
limit = 20,
|
||||||
|
credentials,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_FOLLOWERS_URL(id, {
|
||||||
|
maxId,
|
||||||
|
sinceId,
|
||||||
|
limit,
|
||||||
|
withRelationships: true,
|
||||||
|
}),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const fetchConversation = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_STATUS_CONTEXT_URL(id),
|
||||||
|
credentials,
|
||||||
|
}).then((result) => ({
|
||||||
|
...result,
|
||||||
|
data: {
|
||||||
|
...result.data,
|
||||||
|
ancestors: result.data.ancestors.map(parseStatus),
|
||||||
|
descendants: result.data.descendants.map(parseStatus),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const fetchStatus = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_STATUS_URL(id),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const fetchStatusSource = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_STATUS_SOURCE_URL(id),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseSource(data) }))
|
||||||
|
|
||||||
|
export const fetchStatusHistory = ({ status, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_STATUS_HISTORY_URL(status.id),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => {
|
||||||
|
return [...data].reverse().map((item) => {
|
||||||
|
item.originalStatus = status
|
||||||
|
return { ...rest, data: parseStatus(item) }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export const listEmojiPacks = ({ page, pageSize, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: EMOJI_PACKS_URL(page, pageSize),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchPinnedStatuses = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_TIMELINE_URL(id, { pinned: true }),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseStatus) }))
|
||||||
|
|
||||||
|
export const verifyCredentials = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LOGIN_URL,
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseUser(data) }))
|
||||||
|
|
||||||
|
export const resetPassword = ({ email }) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_PASSWORD_RESET_URL({ email }),
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const suggestions = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_SUGGESTIONS_URL,
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchPoll = ({ pollId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_POLL_URL(encodeURIComponent(pollId)),
|
||||||
|
method: 'GET',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchFavoritedByUsers = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_STATUS_FAVORITEDBY_URL(id),
|
||||||
|
method: 'GET',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const fetchRebloggedByUsers = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_STATUS_REBLOGGEDBY_URL(id),
|
||||||
|
method: 'GET',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const fetchEmojiReactions = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_EMOJI_REACTIONS_URL(id),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({
|
||||||
|
...rest,
|
||||||
|
data: data.map((r) => {
|
||||||
|
r.accounts = r.accounts.map(parseUser)
|
||||||
|
return r
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const searchUsers = ({ credentials, query }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_SEARCH_URL({ q: query, resolve: true }),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const search2 = ({
|
||||||
|
credentials,
|
||||||
|
q,
|
||||||
|
resolve,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
following,
|
||||||
|
type,
|
||||||
|
}) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_SEARCH_2({
|
||||||
|
q,
|
||||||
|
resolve,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
following,
|
||||||
|
type,
|
||||||
|
withRelationships: true,
|
||||||
|
}),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => {
|
||||||
|
data.accounts = data.accounts.slice(0, limit).map((u) => parseUser(u))
|
||||||
|
data.statuses = data.statuses.slice(0, limit).map((s) => parseStatus(s))
|
||||||
|
return { ...rest, data }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchKnownDomains = ({ credentials }) =>
|
||||||
|
promisedRequest({ url: MASTODON_KNOWN_DOMAIN_LIST_URL, credentials })
|
||||||
|
|
||||||
|
export const fetchScrobbles = ({ accountId, limit = 1 }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_SCROBBLES_URL(accountId, { limit }),
|
||||||
|
})
|
||||||
198
src/api/timelines.js
Normal file
198
src/api/timelines.js
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
import { paramsString, promisedRequest } from './helpers.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseLinkHeaderPagination,
|
||||||
|
parseNotification,
|
||||||
|
parseStatus,
|
||||||
|
} from 'src/services/entity_normalizer/entity_normalizer.service.js'
|
||||||
|
|
||||||
|
const MASTODON_USER_HOME_TIMELINE_URL = ({
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit,
|
||||||
|
replyVisibility,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/timelines/home${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
const MASTODON_LIST_TIMELINE_URL = (
|
||||||
|
id,
|
||||||
|
{ minId, sinceId, maxId, limit, replyVisibility },
|
||||||
|
) =>
|
||||||
|
`/api/v1/timelines/list/${id}${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
const MASTODON_DIRECT_MESSAGES_TIMELINE_URL = ({
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit,
|
||||||
|
replyVisibility,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/timelines/direct${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
const MASTODON_PUBLIC_TIMELINE = ({
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit,
|
||||||
|
replyVisibility,
|
||||||
|
local,
|
||||||
|
remote,
|
||||||
|
onlyMedia,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/timelines/public${paramsString({ minId, sinceId, maxId, limit, replyVisibility, local, remote, onlyMedia })}`
|
||||||
|
const MASTODON_TAG_TIMELINE_URL = (
|
||||||
|
tag,
|
||||||
|
{ minId, sinceId, maxId, limit, replyVisibility },
|
||||||
|
) =>
|
||||||
|
`/api/v1/timelines/tag/${tag}${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
export const MASTODON_USER_TIMELINE_URL = (
|
||||||
|
id,
|
||||||
|
{ minId, sinceId, maxId, limit, replyVisibility, pinned, onlyMedia },
|
||||||
|
) =>
|
||||||
|
`/api/v1/accounts/${id}/statuses${paramsString({ minId, sinceId, maxId, limit, replyVisibility, pinned, onlyMedia })}`
|
||||||
|
const MASTODON_USER_FAVORITES_TIMELINE_URL = ({
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit,
|
||||||
|
replyVisibility,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/favourites${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
const MASTODON_BOOKMARK_TIMELINE_URL = ({
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit,
|
||||||
|
replyVisibility,
|
||||||
|
folderId,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/bookmarks${paramsString({ minId, sinceId, maxId, limit, replyVisibility, folderId })}`
|
||||||
|
const PLEROMA_STATUS_QUOTES_URL = (
|
||||||
|
id,
|
||||||
|
{ minId, sinceId, maxId, limit, replyVisibility },
|
||||||
|
) =>
|
||||||
|
`/api/v1/pleroma/statuses/${id}/quotes${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
const PLEROMA_USER_FAVORITES_TIMELINE_URL = (
|
||||||
|
id,
|
||||||
|
{ minId, sinceId, maxId, limit, replyVisibility },
|
||||||
|
) =>
|
||||||
|
`/api/v1/pleroma/accounts/${id}/favourites${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
const AKKOMA_BUBBLE_TIMELINE_URL = ({
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit,
|
||||||
|
replyVisibility,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/timelines/bubble${paramsString({ minId, sinceId, maxId, limit, replyVisibility })}`
|
||||||
|
|
||||||
|
const MASTODON_USER_NOTIFICATIONS_URL = ({
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit,
|
||||||
|
includeTypes,
|
||||||
|
replyVisibility,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/notifications${paramsString({ minId, sinceId, maxId, limit, includeTypes, replyVisibility })}`
|
||||||
|
|
||||||
|
export const fetchTimeline = ({
|
||||||
|
timeline,
|
||||||
|
credentials,
|
||||||
|
sinceId,
|
||||||
|
minId,
|
||||||
|
maxId,
|
||||||
|
userId,
|
||||||
|
listId,
|
||||||
|
statusId,
|
||||||
|
tag,
|
||||||
|
withMuted,
|
||||||
|
replyVisibility = 'all',
|
||||||
|
includeTypes = [],
|
||||||
|
bookmarkFolderId,
|
||||||
|
}) => {
|
||||||
|
const timelineUrls = {
|
||||||
|
friends: MASTODON_USER_HOME_TIMELINE_URL,
|
||||||
|
public: MASTODON_PUBLIC_TIMELINE,
|
||||||
|
publicAndExternal: MASTODON_PUBLIC_TIMELINE,
|
||||||
|
dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
|
||||||
|
user: MASTODON_USER_TIMELINE_URL,
|
||||||
|
media: MASTODON_USER_TIMELINE_URL,
|
||||||
|
list: MASTODON_LIST_TIMELINE_URL,
|
||||||
|
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
|
||||||
|
publicFavorites: PLEROMA_USER_FAVORITES_TIMELINE_URL,
|
||||||
|
bookmarks: MASTODON_BOOKMARK_TIMELINE_URL,
|
||||||
|
bubble: AKKOMA_BUBBLE_TIMELINE_URL,
|
||||||
|
tag: MASTODON_TAG_TIMELINE_URL,
|
||||||
|
quotes: PLEROMA_STATUS_QUOTES_URL,
|
||||||
|
|
||||||
|
notifications: MASTODON_USER_NOTIFICATIONS_URL,
|
||||||
|
}
|
||||||
|
const urlFunc = timelineUrls[timeline]
|
||||||
|
|
||||||
|
const twoArgs = new Set([
|
||||||
|
'user',
|
||||||
|
'media',
|
||||||
|
'list',
|
||||||
|
'publicFavorites',
|
||||||
|
'tag',
|
||||||
|
'quotes',
|
||||||
|
])
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
minId,
|
||||||
|
sinceId,
|
||||||
|
maxId,
|
||||||
|
limit: 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = (() => {
|
||||||
|
switch (timeline) {
|
||||||
|
case 'user':
|
||||||
|
case 'media':
|
||||||
|
return userId
|
||||||
|
case 'list':
|
||||||
|
return listId
|
||||||
|
case 'quotes':
|
||||||
|
return statusId
|
||||||
|
case 'tag':
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
const isNotifications = timeline === 'notifications'
|
||||||
|
|
||||||
|
if (timeline === 'media') {
|
||||||
|
params.onlyMedia = true
|
||||||
|
}
|
||||||
|
if (timeline === 'public') {
|
||||||
|
params.local = true
|
||||||
|
}
|
||||||
|
if (timeline !== 'favorites' && timeline !== 'bookmarks') {
|
||||||
|
params.withMuted = withMuted
|
||||||
|
}
|
||||||
|
if (replyVisibility !== 'all') {
|
||||||
|
params.replyVisibility = replyVisibility
|
||||||
|
}
|
||||||
|
if (timeline === 'bookmarks' && bookmarkFolderId) {
|
||||||
|
params.folderId = bookmarkFolderId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotifications && includeTypes.length > 0) {
|
||||||
|
params.includeTypes = includeTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = twoArgs.has(timeline) ? urlFunc(id, params) : urlFunc(params)
|
||||||
|
return promisedRequest({ url, credentials }).then((result) => {
|
||||||
|
const pagination = parseLinkHeaderPagination(
|
||||||
|
result.response.headers.get('Link'),
|
||||||
|
{
|
||||||
|
flakeId: timeline !== 'bookmarks' && timeline !== 'notifications',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: result.data.map(isNotifications ? parseNotification : parseStatus),
|
||||||
|
pagination,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
924
src/api/user.js
Normal file
924
src/api/user.js
Normal file
|
|
@ -0,0 +1,924 @@
|
||||||
|
import { concat, last } from 'lodash'
|
||||||
|
|
||||||
|
import { paramsString, promisedRequest } from './helpers.js'
|
||||||
|
import { fetchFriends, MASTODON_STATUS_URL } from './public.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseAttachment,
|
||||||
|
parseStatus,
|
||||||
|
parseUser,
|
||||||
|
} from 'src/services/entity_normalizer/entity_normalizer.service.js'
|
||||||
|
|
||||||
|
const MUTES_IMPORT_URL = '/api/pleroma/mutes_import'
|
||||||
|
const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import'
|
||||||
|
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
|
||||||
|
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
|
||||||
|
const CHANGE_EMAIL_URL = '/api/pleroma/change_email'
|
||||||
|
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||||
|
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
|
||||||
|
const ALIASES_URL = '/api/pleroma/aliases'
|
||||||
|
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
||||||
|
const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
|
||||||
|
|
||||||
|
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
||||||
|
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
||||||
|
|
||||||
|
const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp'
|
||||||
|
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
||||||
|
const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/totp'
|
||||||
|
|
||||||
|
const MASTODON_DISMISS_NOTIFICATION_URL = (id) =>
|
||||||
|
`/api/v1/notifications/${id}/dismiss`
|
||||||
|
const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite`
|
||||||
|
const MASTODON_UNFAVORITE_URL = (id) => `/api/v1/statuses/${id}/unfavourite`
|
||||||
|
const MASTODON_RETWEET_URL = (id) => `/api/v1/statuses/${id}/reblog`
|
||||||
|
const MASTODON_UNRETWEET_URL = (id) => `/api/v1/statuses/${id}/unreblog`
|
||||||
|
const MASTODON_DELETE_URL = (id) => `/api/v1/statuses/${id}`
|
||||||
|
const MASTODON_FOLLOW_URL = (id) => `/api/v1/accounts/${id}/follow`
|
||||||
|
const MASTODON_UNFOLLOW_URL = (id) => `/api/v1/accounts/${id}/unfollow`
|
||||||
|
|
||||||
|
const MASTODON_FOLLOW_REQUESTS_URL = '/api/v1/follow_requests'
|
||||||
|
const MASTODON_APPROVE_USER_URL = (id) =>
|
||||||
|
`/api/v1/follow_requests/${id}/authorize`
|
||||||
|
const MASTODON_DENY_USER_URL = (id) => `/api/v1/follow_requests/${id}/reject`
|
||||||
|
const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) =>
|
||||||
|
`/api/v1/accounts/relationships/${paramsString({ id, withSuspended })}`
|
||||||
|
const MASTODON_USER_IN_LISTS = (id) => `/api/v1/accounts/${id}/lists`
|
||||||
|
export const MASTODON_LIST_URL = (id = '') => `/api/v1/lists/${id}`
|
||||||
|
export const MASTODON_LIST_ACCOUNTS_URL = (id) => `/api/v1/lists/${id}/accounts`
|
||||||
|
const MASTODON_USER_BLOCKS_URL = ({
|
||||||
|
maxId,
|
||||||
|
sinceId,
|
||||||
|
limit,
|
||||||
|
withRelationships,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/blocks/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
||||||
|
const MASTODON_USER_MUTES_URL = ({
|
||||||
|
maxId,
|
||||||
|
sinceId,
|
||||||
|
limit,
|
||||||
|
withRelationships,
|
||||||
|
}) =>
|
||||||
|
`/api/v1/mutes/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
||||||
|
const MASTODON_BLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/block`
|
||||||
|
const MASTODON_UNBLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/unblock`
|
||||||
|
const MASTODON_MUTE_USER_URL = (id) => `/api/v1/accounts/${id}/mute`
|
||||||
|
const MASTODON_UNMUTE_USER_URL = (id) => `/api/v1/accounts/${id}/unmute`
|
||||||
|
const MASTODON_REMOVE_USER_FROM_FOLLOWERS = (id) =>
|
||||||
|
`/api/v1/accounts/${id}/remove_from_followers`
|
||||||
|
const MASTODON_USER_NOTE_URL = (id) => `/api/v1/accounts/${id}/note`
|
||||||
|
const MASTODON_BOOKMARK_STATUS_URL = (id) => `/api/v1/statuses/${id}/bookmark`
|
||||||
|
const MASTODON_UNBOOKMARK_STATUS_URL = (id) =>
|
||||||
|
`/api/v1/statuses/${id}/unbookmark`
|
||||||
|
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
||||||
|
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
||||||
|
const MASTODON_VOTE_URL = (id) => `/api/v1/polls/${id}/votes`
|
||||||
|
const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
|
||||||
|
const MASTODON_REPORT_USER_URL = '/api/v1/reports'
|
||||||
|
const MASTODON_PIN_OWN_STATUS = (id) => `/api/v1/statuses/${id}/pin`
|
||||||
|
const MASTODON_UNPIN_OWN_STATUS = (id) => `/api/v1/statuses/${id}/unpin`
|
||||||
|
const MASTODON_MUTE_CONVERSATION = (id) => `/api/v1/statuses/${id}/mute`
|
||||||
|
const MASTODON_UNMUTE_CONVERSATION = (id) => `/api/v1/statuses/${id}/unmute`
|
||||||
|
const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks'
|
||||||
|
const MASTODON_ANNOUNCEMENTS_URL = '/api/v1/announcements'
|
||||||
|
const MASTODON_ANNOUNCEMENTS_DISMISS_URL = (id) =>
|
||||||
|
`/api/v1/announcements/${id}/dismiss`
|
||||||
|
const PLEROMA_EMOJI_REACT_URL = (id, emoji) =>
|
||||||
|
`/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
|
||||||
|
const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) =>
|
||||||
|
`/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
|
||||||
|
const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
|
||||||
|
const PLEROMA_BOOKMARK_FOLDERS_URL = '/api/v1/pleroma/bookmark_folders'
|
||||||
|
const PLEROMA_BOOKMARK_FOLDER_URL = (id) =>
|
||||||
|
`/api/v1/pleroma/bookmark_folders/${id}`
|
||||||
|
|
||||||
|
// #Posts
|
||||||
|
export const favorite = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_FAVORITE_URL(id),
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const unfavorite = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNFAVORITE_URL(id),
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const retweet = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_RETWEET_URL(id),
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const unretweet = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNRETWEET_URL(id),
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const reactWithEmoji = ({ id, emoji, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_EMOJI_REACT_URL(id, emoji),
|
||||||
|
method: 'PUT',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const unreactWithEmoji = ({ id, emoji, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_EMOJI_UNREACT_URL(id, emoji),
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const bookmarkStatus = ({ id, credentials, ...options }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_BOOKMARK_STATUS_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload: {
|
||||||
|
folder_id: options.folder_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const unbookmarkStatus = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNBOOKMARK_STATUS_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const pinOwnStatus = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_PIN_OWN_STATUS(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const unpinOwnStatus = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNPIN_OWN_STATUS(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const muteConversation = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_MUTE_CONVERSATION(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const unmuteConversation = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNMUTE_CONVERSATION(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
|
export const vote = ({ pollId, choices, credentials }) => {
|
||||||
|
const form = new FormData()
|
||||||
|
form.append('choices', choices)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_VOTE_URL(encodeURIComponent(pollId)),
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
choices,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #Posting
|
||||||
|
export const postStatus = ({
|
||||||
|
credentials,
|
||||||
|
status,
|
||||||
|
spoilerText,
|
||||||
|
visibility,
|
||||||
|
sensitive,
|
||||||
|
poll,
|
||||||
|
mediaIds = [],
|
||||||
|
inReplyToStatusId,
|
||||||
|
quoteId,
|
||||||
|
contentType,
|
||||||
|
preview,
|
||||||
|
idempotencyKey,
|
||||||
|
}) => {
|
||||||
|
const form = new FormData()
|
||||||
|
const pollOptions = poll.options || []
|
||||||
|
|
||||||
|
form.append('status', status)
|
||||||
|
form.append('source', 'Pleroma FE')
|
||||||
|
if (spoilerText) form.append('spoiler_text', spoilerText)
|
||||||
|
if (visibility) form.append('visibility', visibility)
|
||||||
|
if (sensitive) form.append('sensitive', sensitive)
|
||||||
|
if (contentType) form.append('content_type', contentType)
|
||||||
|
mediaIds.forEach((val) => {
|
||||||
|
form.append('media_ids[]', val)
|
||||||
|
})
|
||||||
|
if (pollOptions.some((option) => option !== '')) {
|
||||||
|
const normalizedPoll = {
|
||||||
|
expires_in: parseInt(poll.expiresIn, 10),
|
||||||
|
multiple: poll.multiple,
|
||||||
|
}
|
||||||
|
Object.keys(normalizedPoll).forEach((key) => {
|
||||||
|
form.append(`poll[${key}]`, normalizedPoll[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
pollOptions.forEach((option) => {
|
||||||
|
form.append('poll[options][]', option)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (inReplyToStatusId) {
|
||||||
|
form.append('in_reply_to_id', inReplyToStatusId)
|
||||||
|
}
|
||||||
|
if (quoteId) {
|
||||||
|
form.append('quote_id', quoteId)
|
||||||
|
}
|
||||||
|
if (preview) {
|
||||||
|
form.append('preview', 'true')
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = {}
|
||||||
|
if (idempotencyKey) {
|
||||||
|
headers['idempotency-key'] = idempotencyKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_POST_STATUS_URL,
|
||||||
|
formData: form,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
headers,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const editStatus = ({
|
||||||
|
id,
|
||||||
|
credentials,
|
||||||
|
status,
|
||||||
|
spoilerText,
|
||||||
|
sensitive,
|
||||||
|
poll,
|
||||||
|
mediaIds = [],
|
||||||
|
contentType,
|
||||||
|
}) => {
|
||||||
|
const form = new FormData()
|
||||||
|
const pollOptions = poll.options || []
|
||||||
|
|
||||||
|
form.append('status', status)
|
||||||
|
if (spoilerText) form.append('spoiler_text', spoilerText)
|
||||||
|
if (sensitive) form.append('sensitive', sensitive)
|
||||||
|
if (contentType) form.append('content_type', contentType)
|
||||||
|
mediaIds.forEach((val) => {
|
||||||
|
form.append('media_ids[]', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (pollOptions.some((option) => option !== '')) {
|
||||||
|
const normalizedPoll = {
|
||||||
|
expires_in: parseInt(poll.expiresIn, 10),
|
||||||
|
multiple: poll.multiple,
|
||||||
|
}
|
||||||
|
Object.keys(normalizedPoll).forEach((key) => {
|
||||||
|
form.append(`poll[${key}]`, normalizedPoll[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
pollOptions.forEach((option) => {
|
||||||
|
form.append('poll[options][]', option)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_STATUS_URL(id),
|
||||||
|
formData: form,
|
||||||
|
method: 'PUT',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteStatus = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_DELETE_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const uploadMedia = ({ formData, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_MEDIA_UPLOAD_URL,
|
||||||
|
formData,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseAttachment(data) }))
|
||||||
|
|
||||||
|
export const setMediaDescription = ({ id, description, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: `${MASTODON_MEDIA_UPLOAD_URL}/${id}`,
|
||||||
|
method: 'PUT',
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
description,
|
||||||
|
},
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseAttachment(data) }))
|
||||||
|
|
||||||
|
// #Notifications
|
||||||
|
export const dismissNotification = ({ credentials, id }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_DISMISS_NOTIFICATION_URL(id),
|
||||||
|
method: 'POST',
|
||||||
|
payload: { id },
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const markNotificationsAsSeen = ({
|
||||||
|
id,
|
||||||
|
credentials,
|
||||||
|
single = false,
|
||||||
|
}) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
if (single) {
|
||||||
|
formData.append('id', id)
|
||||||
|
} else {
|
||||||
|
formData.append('max_id', id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: NOTIFICATION_READ_URL,
|
||||||
|
formData,
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #Announcements
|
||||||
|
export const getAnnouncements = ({ credentials }) =>
|
||||||
|
promisedRequest({ url: MASTODON_ANNOUNCEMENTS_URL, credentials })
|
||||||
|
|
||||||
|
export const dismissAnnouncement = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_ANNOUNCEMENTS_DISMISS_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
// #Imports
|
||||||
|
export const importMutes = ({ file, credentials }) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('list', file)
|
||||||
|
return promisedRequest({
|
||||||
|
url: MUTES_IMPORT_URL,
|
||||||
|
formData,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then((response) => response.ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const importBlocks = ({ file, credentials }) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('list', file)
|
||||||
|
return promisedRequest({
|
||||||
|
url: BLOCKS_IMPORT_URL,
|
||||||
|
formData,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then((response) => response.ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const importFollows = ({ file, credentials }) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('list', file)
|
||||||
|
return promisedRequest({
|
||||||
|
url: FOLLOW_IMPORT_URL,
|
||||||
|
formData,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
}).then((response) => response.ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const exportFriends = ({ id, credentials }) => {
|
||||||
|
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO refactor this
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let friends = []
|
||||||
|
let more = true
|
||||||
|
while (more) {
|
||||||
|
const maxId = friends.length > 0 ? last(friends).id : undefined
|
||||||
|
const users = await fetchFriends({
|
||||||
|
id,
|
||||||
|
maxId,
|
||||||
|
credentials,
|
||||||
|
withRelationships: true,
|
||||||
|
})
|
||||||
|
friends = concat(friends, users)
|
||||||
|
if (users.length === 0) {
|
||||||
|
more = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(friends)
|
||||||
|
} catch (err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #Profile settings
|
||||||
|
export const updateNotificationSettings = ({ credentials, settings }) => {
|
||||||
|
return promisedRequest({
|
||||||
|
url: NOTIFICATION_SETTINGS_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'PUT',
|
||||||
|
payload: settings,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateProfileImages = ({
|
||||||
|
credentials,
|
||||||
|
avatar = null,
|
||||||
|
avatarName = null,
|
||||||
|
banner = null,
|
||||||
|
background = null,
|
||||||
|
}) => {
|
||||||
|
const form = new FormData()
|
||||||
|
if (avatar !== null) {
|
||||||
|
if (avatarName !== null) {
|
||||||
|
form.append('avatar', avatar, avatarName)
|
||||||
|
} else {
|
||||||
|
form.append('avatar', avatar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (banner !== null) form.append('header', banner)
|
||||||
|
if (background !== null) form.append('pleroma_background_image', background)
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_PROFILE_UPDATE_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
formData: form,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseUser(data) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateProfile = ({ credentials, params }) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
for (const name in params) {
|
||||||
|
if (name === 'fields_attributes') {
|
||||||
|
params[name].forEach((param, i) => {
|
||||||
|
formData.append(name + `[${i}][name]`, param.name)
|
||||||
|
formData.append(name + `[${i}][value]`, param.value)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (typeof params[name] === 'object') {
|
||||||
|
console.warn(
|
||||||
|
'Object detected in updateProfile API call. This will not work, use updateProfileJSON instead.',
|
||||||
|
)
|
||||||
|
console.warn('Object:\n' + JSON.stringify(params[name], null, 2))
|
||||||
|
}
|
||||||
|
formData.append(name, params[name])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_PROFILE_UPDATE_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
formData,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseUser(data) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateProfileJSON = ({ credentials, params }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_PROFILE_UPDATE_URL,
|
||||||
|
credentials,
|
||||||
|
payload: params,
|
||||||
|
method: 'PATCH',
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseUser(data) }))
|
||||||
|
|
||||||
|
export const changeEmail = ({ credentials, email, password }) => {
|
||||||
|
const form = new FormData()
|
||||||
|
|
||||||
|
form.append('email', email)
|
||||||
|
form.append('password', password)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: CHANGE_EMAIL_URL,
|
||||||
|
formData: form,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const moveAccount = ({ credentials, password, targetAccount }) => {
|
||||||
|
const form = new FormData()
|
||||||
|
|
||||||
|
form.append('password', password)
|
||||||
|
form.append('target_account', targetAccount)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MOVE_ACCOUNT_URL,
|
||||||
|
formData: form,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const changePassword = ({
|
||||||
|
credentials,
|
||||||
|
password,
|
||||||
|
newPassword,
|
||||||
|
newPasswordConfirmation,
|
||||||
|
}) => {
|
||||||
|
const form = new FormData()
|
||||||
|
|
||||||
|
form.append('password', password)
|
||||||
|
form.append('new_password', newPassword)
|
||||||
|
form.append('new_password_confirmation', newPasswordConfirmation)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: CHANGE_PASSWORD_URL,
|
||||||
|
formData: form,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #MFA
|
||||||
|
export const settingsMFA = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MFA_SETTINGS_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const mfaDisableOTP = ({ credentials, password }) => {
|
||||||
|
const form = new FormData()
|
||||||
|
|
||||||
|
form.append('password', password)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MFA_DISABLE_OTP_URL,
|
||||||
|
formData: form,
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mfaConfirmOTP = ({ credentials, password, token }) => {
|
||||||
|
const form = new FormData()
|
||||||
|
|
||||||
|
form.append('password', password)
|
||||||
|
form.append('code', token)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MFA_CONFIRM_OTP_URL,
|
||||||
|
formData: form,
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export const mfaSetupOTP = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MFA_SETUP_OTP_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
export const generateMfaBackupCodes = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MFA_BACKUP_CODES_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
// #Aliases
|
||||||
|
export const addAlias = ({ credentials, alias }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: ALIASES_URL,
|
||||||
|
method: 'PUT',
|
||||||
|
credentials,
|
||||||
|
payload: { alias },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteAlias = ({ credentials, alias }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: ALIASES_URL,
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials,
|
||||||
|
payload: { alias },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const listAliases = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: ALIASES_URL,
|
||||||
|
method: 'GET',
|
||||||
|
credentials,
|
||||||
|
params: {
|
||||||
|
_cacheBooster: new Date().getTime(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// User manipulation
|
||||||
|
export const fetchUserRelationship = ({ id, withSuspended, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_RELATIONSHIPS_URL({ id, withSuspended }),
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const followUser = ({ id, credentials, ...options }) => {
|
||||||
|
const payload = {}
|
||||||
|
|
||||||
|
if (options.reblogs !== undefined) {
|
||||||
|
payload.reblogs = options.reblogs
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.notify !== undefined) {
|
||||||
|
payload.notify = options.notify
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_FOLLOW_URL(id),
|
||||||
|
payload,
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unfollowUser = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNFOLLOW_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
export const fetchUserInLists = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_IN_LISTS(id),
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const removeUserFromFollowers = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_REMOVE_USER_FROM_FOLLOWERS(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchFollowRequests = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_FOLLOW_REQUESTS_URL,
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const approveUser = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_APPROVE_USER_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const denyUser = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_DENY_USER_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const editUserNote = ({ id, credentials, comment }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_NOTE_URL(id),
|
||||||
|
credentials,
|
||||||
|
payload: {
|
||||||
|
comment,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchMutes = ({ maxId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_MUTES_URL({ maxId, withRelationships: true }),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const muteUser = ({ id, expiresIn, credentials }) => {
|
||||||
|
const payload = {}
|
||||||
|
if (expiresIn) {
|
||||||
|
payload.expires_in = expiresIn
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_MUTE_USER_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unmuteUser = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNMUTE_USER_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchBlocks = ({ maxId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_USER_BLOCKS_URL({ maxId, withRelationships: true }),
|
||||||
|
credentials,
|
||||||
|
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||||
|
|
||||||
|
export const blockUser = ({ id, expiresIn, credentials }) => {
|
||||||
|
const payload = {}
|
||||||
|
if (expiresIn) {
|
||||||
|
payload.duration = expiresIn
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: MASTODON_BLOCK_USER_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unblockUser = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_UNBLOCK_USER_URL(id),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const reportUser = ({
|
||||||
|
credentials,
|
||||||
|
userId,
|
||||||
|
statusIds,
|
||||||
|
comment,
|
||||||
|
forward,
|
||||||
|
}) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_REPORT_USER_URL,
|
||||||
|
method: 'POST',
|
||||||
|
payload: {
|
||||||
|
account_id: userId,
|
||||||
|
status_ids: statusIds,
|
||||||
|
comment,
|
||||||
|
forward,
|
||||||
|
},
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
// #Domain mutes
|
||||||
|
export const fetchDomainMutes = ({ credentials }) =>
|
||||||
|
promisedRequest({ url: MASTODON_DOMAIN_BLOCKS_URL, credentials })
|
||||||
|
|
||||||
|
export const muteDomain = ({ domain, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_DOMAIN_BLOCKS_URL,
|
||||||
|
method: 'POST',
|
||||||
|
payload: { domain },
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const unmuteDomain = ({ domain, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_DOMAIN_BLOCKS_URL,
|
||||||
|
method: 'DELETE',
|
||||||
|
payload: { domain },
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
// #Backups
|
||||||
|
export const addBackup = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_BACKUP_URL,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const listBackups = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_BACKUP_URL,
|
||||||
|
method: 'GET',
|
||||||
|
credentials,
|
||||||
|
params: {
|
||||||
|
_cacheBooster: new Date().getTime(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// #OAuth
|
||||||
|
export const fetchOAuthTokens = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: '/api/oauth_tokens.json',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const revokeOAuthToken = ({ id, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: `/api/oauth_tokens/${id}`,
|
||||||
|
credentials,
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
|
||||||
|
// #Lists
|
||||||
|
export const fetchLists = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_URL(),
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createList = ({ title, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_URL(),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload: { title },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getList = ({ listId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_URL(listId),
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateList = ({ listId, title, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_URL(listId),
|
||||||
|
|
||||||
|
credentials,
|
||||||
|
method: 'PUT',
|
||||||
|
payload: { title },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getListAccounts = ({ listId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_ACCOUNTS_URL(listId),
|
||||||
|
credentials,
|
||||||
|
}).then((data) => data.map(({ id }) => id))
|
||||||
|
|
||||||
|
export const addAccountsToList = ({ listId, accountIds, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_ACCOUNTS_URL(listId),
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload: { account_ids: accountIds },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const removeAccountsFromList = ({ listId, accountIds, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_ACCOUNTS_URL(listId),
|
||||||
|
credentials,
|
||||||
|
method: 'DELETE',
|
||||||
|
payload: { account_ids: accountIds },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteList = ({ listId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: MASTODON_LIST_URL(listId),
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
// #Bookmarks
|
||||||
|
export const fetchBookmarkFolders = ({ credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_BOOKMARK_FOLDERS_URL,
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createBookmarkFolder = ({ name, emoji, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_BOOKMARK_FOLDERS_URL,
|
||||||
|
credentials,
|
||||||
|
method: 'POST',
|
||||||
|
payload: { name, emoji },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateBookmarkFolder = ({ folderId, name, emoji, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_BOOKMARK_FOLDER_URL(folderId),
|
||||||
|
credentials,
|
||||||
|
method: 'PATCH',
|
||||||
|
payload: { name, emoji },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteBookmarkFolder = ({ folderId, credentials }) =>
|
||||||
|
promisedRequest({
|
||||||
|
url: PLEROMA_BOOKMARK_FOLDER_URL(folderId),
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
|
||||||
|
// #So long and thanks for all the fish
|
||||||
|
export const deleteAccount = ({ credentials, password }) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
formData.append('password', password)
|
||||||
|
|
||||||
|
return promisedRequest({
|
||||||
|
url: DELETE_ACCOUNT_URL,
|
||||||
|
formData,
|
||||||
|
method: 'POST',
|
||||||
|
credentials,
|
||||||
|
})
|
||||||
|
}
|
||||||
176
src/api/websocket.js
Normal file
176
src/api/websocket.js
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
import { paramsString } from './helpers.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseChat,
|
||||||
|
parseNotification,
|
||||||
|
parseStatus,
|
||||||
|
} from 'src/services/entity_normalizer/entity_normalizer.service.js'
|
||||||
|
|
||||||
|
const MASTODON_STREAMING = ({ accessToken, stream }) =>
|
||||||
|
`/api/v1/streaming${paramsString({ accessToken, stream })}`
|
||||||
|
|
||||||
|
export const getMastodonSocketURI = ({ credentials, stream }) => {
|
||||||
|
return MASTODON_STREAMING({ accessToken: credentials, stream })
|
||||||
|
}
|
||||||
|
|
||||||
|
const MASTODON_STREAMING_EVENTS = new Set([
|
||||||
|
'update',
|
||||||
|
'notification',
|
||||||
|
'delete',
|
||||||
|
'filters_changed',
|
||||||
|
'status.update',
|
||||||
|
])
|
||||||
|
|
||||||
|
const PLEROMA_STREAMING_EVENTS = new Set([
|
||||||
|
'pleroma:chat_update',
|
||||||
|
'pleroma:respond',
|
||||||
|
])
|
||||||
|
|
||||||
|
// A thin wrapper around WebSocket API that allows adding a pre-processor to it
|
||||||
|
// Uses EventTarget and a CustomEvent to proxy events
|
||||||
|
export const ProcessedWS = ({
|
||||||
|
url,
|
||||||
|
preprocessor = handleMastoWS,
|
||||||
|
id = 'Unknown',
|
||||||
|
credentials,
|
||||||
|
}) => {
|
||||||
|
const eventTarget = new EventTarget()
|
||||||
|
const socket = new WebSocket(url)
|
||||||
|
if (!socket) throw new Error(`Failed to create socket ${id}`)
|
||||||
|
const proxy = (original, eventName, processor = (a) => a) => {
|
||||||
|
original.addEventListener(eventName, (eventData) => {
|
||||||
|
eventTarget.dispatchEvent(
|
||||||
|
new CustomEvent(eventName, { detail: processor(eventData) }),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
socket.addEventListener('open', (wsEvent) => {
|
||||||
|
console.debug(`[WS][${id}] Socket connected`, wsEvent)
|
||||||
|
if (credentials) {
|
||||||
|
socket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'pleroma:authenticate',
|
||||||
|
token: credentials,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
socket.addEventListener('error', (wsEvent) => {
|
||||||
|
console.debug(`[WS][${id}] Socket errored`, wsEvent)
|
||||||
|
})
|
||||||
|
socket.addEventListener('close', (wsEvent) => {
|
||||||
|
console.debug(
|
||||||
|
`[WS][${id}] Socket disconnected with code ${wsEvent.code}`,
|
||||||
|
wsEvent,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// Commented code reason: very spammy, uncomment to enable message debug logging
|
||||||
|
/*
|
||||||
|
socket.addEventListener('message', (wsEvent) => {
|
||||||
|
console.debug(
|
||||||
|
`[WS][${id}] Message received`,
|
||||||
|
wsEvent
|
||||||
|
)
|
||||||
|
})
|
||||||
|
/**/
|
||||||
|
|
||||||
|
const onAuthenticated = () => {
|
||||||
|
eventTarget.dispatchEvent(new CustomEvent('pleroma:authenticated'))
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy(socket, 'open')
|
||||||
|
proxy(socket, 'close')
|
||||||
|
proxy(socket, 'message', (event) => preprocessor(event, { onAuthenticated }))
|
||||||
|
proxy(socket, 'error')
|
||||||
|
|
||||||
|
// 1000 = Normal Closure
|
||||||
|
eventTarget.close = () => {
|
||||||
|
socket.close(1000, 'Shutting down socket')
|
||||||
|
}
|
||||||
|
eventTarget.getState = () => socket.readyState
|
||||||
|
eventTarget.subscribe = (stream, args = {}) => {
|
||||||
|
console.debug(`[WS][${id}] Subscribing to stream ${stream} with args`, args)
|
||||||
|
socket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'subscribe',
|
||||||
|
stream,
|
||||||
|
...args,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
eventTarget.unsubscribe = (stream, args = {}) => {
|
||||||
|
console.debug(
|
||||||
|
`[WS][${id}] Unsubscribing from stream ${stream} with args`,
|
||||||
|
args,
|
||||||
|
)
|
||||||
|
socket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'unsubscribe',
|
||||||
|
stream,
|
||||||
|
...args,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleMastoWS = (
|
||||||
|
wsEvent,
|
||||||
|
{
|
||||||
|
onAuthenticated = () => {
|
||||||
|
/* no-op */
|
||||||
|
},
|
||||||
|
} = {},
|
||||||
|
) => {
|
||||||
|
const { data } = wsEvent
|
||||||
|
if (!data) return
|
||||||
|
const parsedEvent = JSON.parse(data)
|
||||||
|
const { event, payload } = parsedEvent
|
||||||
|
if (
|
||||||
|
MASTODON_STREAMING_EVENTS.has(event) ||
|
||||||
|
PLEROMA_STREAMING_EVENTS.has(event)
|
||||||
|
) {
|
||||||
|
// MastoBE and PleromaBE both send payload for delete as a PLAIN string
|
||||||
|
if (event === 'delete') {
|
||||||
|
return { event, id: payload }
|
||||||
|
}
|
||||||
|
const data = payload ? JSON.parse(payload) : null
|
||||||
|
if (event === 'pleroma:respond') {
|
||||||
|
if (data.type === 'pleroma:authenticate') {
|
||||||
|
if (data.result === 'success') {
|
||||||
|
console.debug('[WS] Successfully authenticated')
|
||||||
|
onAuthenticated()
|
||||||
|
} else {
|
||||||
|
if (data.error === 'already_authenticated') {
|
||||||
|
onAuthenticated()
|
||||||
|
} else {
|
||||||
|
console.error('[WS] Unable to authenticate:', data.error)
|
||||||
|
wsEvent.target.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
} else if (event === 'update') {
|
||||||
|
return { event, status: parseStatus(data) }
|
||||||
|
} else if (event === 'status.update') {
|
||||||
|
return { event, status: parseStatus(data) }
|
||||||
|
} else if (event === 'notification') {
|
||||||
|
return { event, notification: parseNotification(data) }
|
||||||
|
} else if (event === 'pleroma:chat_update') {
|
||||||
|
return { event, chatUpdate: parseChat(data) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('Unknown event', wsEvent)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WSConnectionStatus = Object.freeze({
|
||||||
|
JOINED: 1,
|
||||||
|
CLOSED: 2,
|
||||||
|
ERROR: 3,
|
||||||
|
DISABLED: 4,
|
||||||
|
STARTING: 5,
|
||||||
|
STARTING_INITIAL: 6,
|
||||||
|
})
|
||||||
|
|
@ -19,7 +19,6 @@ import {
|
||||||
config.autoAddCss = false
|
config.autoAddCss = false
|
||||||
|
|
||||||
import App from '../App.vue'
|
import App from '../App.vue'
|
||||||
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
|
||||||
import FaviconService from '../services/favicon_service/favicon_service.js'
|
import FaviconService from '../services/favicon_service/favicon_service.js'
|
||||||
import { applyStyleConfig } from '../services/style_setter/style_setter.js'
|
import { applyStyleConfig } from '../services/style_setter/style_setter.js'
|
||||||
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
|
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
|
||||||
|
|
@ -29,7 +28,6 @@ import {
|
||||||
} from '../services/window_utils/window_utils'
|
} from '../services/window_utils/window_utils'
|
||||||
import routes from './routes'
|
import routes from './routes'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
|
||||||
import { useAuthFlowStore } from 'src/stores/auth_flow'
|
import { useAuthFlowStore } from 'src/stores/auth_flow'
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||||
import { useI18nStore } from 'src/stores/i18n'
|
import { useI18nStore } from 'src/stores/i18n'
|
||||||
|
|
@ -38,7 +36,7 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||||
|
|
||||||
|
|
@ -261,16 +259,6 @@ const getStickers = async ({ store }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAppSecret = async ({ store }) => {
|
|
||||||
const oauth = useOAuthStore()
|
|
||||||
if (oauth.userToken) {
|
|
||||||
store.commit(
|
|
||||||
'setBackendInteractor',
|
|
||||||
backendInteractorService(oauth.getToken),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolveStaffAccounts = ({ store, accounts }) => {
|
const resolveStaffAccounts = ({ store, accounts }) => {
|
||||||
const nicknames = accounts.map((uri) => uri.split('/').pop())
|
const nicknames = accounts.map((uri) => uri.split('/').pop())
|
||||||
useInstanceStore().set({
|
useInstanceStore().set({
|
||||||
|
|
@ -461,14 +449,13 @@ const setConfig = async ({ store }) => {
|
||||||
const apiConfig = configInfos[0]
|
const apiConfig = configInfos[0]
|
||||||
const staticConfig = configInfos[1]
|
const staticConfig = configInfos[1]
|
||||||
|
|
||||||
getAppSecret({ store })
|
|
||||||
await setSettings({ store, apiConfig, staticConfig })
|
await setSettings({ store, apiConfig, staticConfig })
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkOAuthToken = async ({ store }) => {
|
const checkOAuthToken = async ({ store }) => {
|
||||||
const oauth = useOAuthStore()
|
const oauth = useOAuthStore()
|
||||||
if (oauth.getUserToken) {
|
if (oauth.userToken) {
|
||||||
return store.dispatch('loginUser', oauth.getUserToken)
|
return store.dispatch('loginUser', oauth.userToken)
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
@ -578,10 +565,6 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
||||||
getInstanceConfig({ store }),
|
getInstanceConfig({ store }),
|
||||||
]).catch((e) => Promise.reject(e))
|
]).catch((e) => Promise.reject(e))
|
||||||
|
|
||||||
// Start fetching things that don't need to block the UI
|
|
||||||
store.dispatch('fetchMutes')
|
|
||||||
store.dispatch('loadDrafts')
|
|
||||||
useAnnouncementsStore().startFetchingAnnouncements()
|
|
||||||
getTOS({ store })
|
getTOS({ store })
|
||||||
getStickers({ store })
|
getStickers({ store })
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,7 @@ const AnnouncementsPage = {
|
||||||
canPostAnnouncement() {
|
canPostAnnouncement() {
|
||||||
return (
|
return (
|
||||||
this.currentUser &&
|
this.currentUser &&
|
||||||
this.currentUser.privileges.has(
|
this.currentUser.privileges.has('announcements_manage_announcements')
|
||||||
'announcements_manage_announcements',
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import Popover from 'src/components/popover/popover.vue'
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
import VideoAttachment from 'src/components/video_attachment/video_attachment.vue'
|
|
||||||
import nsfwImage from '../../assets/nsfw.png'
|
import nsfwImage from '../../assets/nsfw.png'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import EmojiPicker from 'src/components/emoji_picker/emoji_picker.vue'
|
import EmojiPicker from 'src/components/emoji_picker/emoji_picker.vue'
|
||||||
import apiService from '../../services/api/api.service'
|
|
||||||
|
|
||||||
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js'
|
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { fetchBookmarkFolders } from 'src/api/user.js'
|
||||||
|
|
||||||
const BookmarkFolderEdit = {
|
const BookmarkFolderEdit = {
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -22,8 +24,10 @@ const BookmarkFolderEdit = {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (!this.id) return
|
if (!this.id) return
|
||||||
const credentials = this.$store.state.users.currentUser.credentials
|
|
||||||
apiService.fetchBookmarkFolders({ credentials }).then((folders) => {
|
fetchBookmarkFolders({
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: folders }) => {
|
||||||
const folder = folders.find((folder) => folder.id === this.id)
|
const folder = folders.find((folder) => folder.id === this.id)
|
||||||
if (!folder) return
|
if (!folder) return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { mapGetters, mapState } from 'vuex'
|
||||||
import ChatMessage from 'src/components/chat_message/chat_message.vue'
|
import ChatMessage from 'src/components/chat_message/chat_message.vue'
|
||||||
import ChatTitle from 'src/components/chat_title/chat_title.vue'
|
import ChatTitle from 'src/components/chat_title/chat_title.vue'
|
||||||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||||
import { WSConnectionStatus } from '../../services/api/api.service.js'
|
|
||||||
import chatService from '../../services/chat_service/chat_service.js'
|
import chatService from '../../services/chat_service/chat_service.js'
|
||||||
import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js'
|
import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js'
|
||||||
import { promiseInterval } from '../../services/promise_interval/promise_interval.js'
|
import { promiseInterval } from '../../services/promise_interval/promise_interval.js'
|
||||||
|
|
@ -17,6 +16,14 @@ import {
|
||||||
} from './chat_layout_utils.js'
|
} from './chat_layout_utils.js'
|
||||||
|
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
chatMessages,
|
||||||
|
getOrCreateChat,
|
||||||
|
sendChatMessage,
|
||||||
|
} from 'src/api/chats.js'
|
||||||
|
import { WSConnectionStatus } from 'src/api/websocket.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faChevronDown, faChevronLeft } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronDown, faChevronLeft } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -115,7 +122,6 @@ const Chat = {
|
||||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||||
}),
|
}),
|
||||||
...mapState({
|
...mapState({
|
||||||
backendInteractor: (state) => state.api.backendInteractor,
|
|
||||||
mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus,
|
mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus,
|
||||||
currentUser: (state) => state.users.currentUser,
|
currentUser: (state) => state.users.currentUser,
|
||||||
}),
|
}),
|
||||||
|
|
@ -267,43 +273,48 @@ const Chat = {
|
||||||
const fetchOlderMessages = !!maxId
|
const fetchOlderMessages = !!maxId
|
||||||
const sinceId = fetchLatest && chatMessageService.maxId
|
const sinceId = fetchLatest && chatMessageService.maxId
|
||||||
|
|
||||||
return this.backendInteractor
|
return chatMessages({
|
||||||
.chatMessages({ id: chatId, maxId, sinceId })
|
id: chatId,
|
||||||
.then((messages) => {
|
maxId,
|
||||||
// Clear the current chat in case we're recovering from a ws connection loss.
|
sinceId,
|
||||||
if (isFirstFetch) {
|
credentials: useOAuthStore().token,
|
||||||
chatService.clear(chatMessageService)
|
}).then(({ data: messages }) => {
|
||||||
}
|
// Clear the current chat in case we're recovering from a ws connection loss.
|
||||||
|
if (isFirstFetch) {
|
||||||
|
chatService.clear(chatMessageService)
|
||||||
|
}
|
||||||
|
|
||||||
const positionBeforeUpdate = getScrollPosition()
|
const positionBeforeUpdate = getScrollPosition()
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch('addChatMessages', { chatId, messages })
|
.dispatch('addChatMessages', { chatId, messages })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (fetchOlderMessages) {
|
if (fetchOlderMessages) {
|
||||||
this.handleScrollUp(positionBeforeUpdate)
|
this.handleScrollUp(positionBeforeUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// In vertical screens, the first batch of fetched messages may not always take the
|
// In vertical screens, the first batch of fetched messages may not always take the
|
||||||
// full height of the scrollable container.
|
// full height of the scrollable container.
|
||||||
// If this is the case, we want to fetch the messages until the scrollable container
|
// If this is the case, we want to fetch the messages until the scrollable container
|
||||||
// is fully populated so that the user has the ability to scroll up and load the history.
|
// is fully populated so that the user has the ability to scroll up and load the history.
|
||||||
if (!isScrollable() && messages.length > 0) {
|
if (!isScrollable() && messages.length > 0) {
|
||||||
this.fetchChat({
|
this.fetchChat({
|
||||||
maxId: this.currentChatMessageService.minId,
|
maxId: this.currentChatMessageService.minId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
async startFetching() {
|
async startFetching() {
|
||||||
let chat = this.findOpenedChatByRecipientId(this.recipientId)
|
let chat = this.findOpenedChatByRecipientId(this.recipientId)
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
try {
|
try {
|
||||||
chat = await this.backendInteractor.getOrCreateChat({
|
const { data } = await getOrCreateChat({
|
||||||
accountId: this.recipientId,
|
accountId: this.recipientId,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
|
chat = data
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error creating or getting a chat', e)
|
console.error('Error creating or getting a chat', e)
|
||||||
this.errorLoadingChat = true
|
this.errorLoadingChat = true
|
||||||
|
|
@ -369,9 +380,11 @@ const Chat = {
|
||||||
doSendMessage({ params, fakeMessage, retriesLeft = MAX_RETRIES }) {
|
doSendMessage({ params, fakeMessage, retriesLeft = MAX_RETRIES }) {
|
||||||
if (retriesLeft <= 0) return
|
if (retriesLeft <= 0) return
|
||||||
|
|
||||||
this.backendInteractor
|
sendChatMessage({
|
||||||
.sendChatMessage(params)
|
params,
|
||||||
.then((data) => {
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
this.$store.dispatch('addChatMessages', {
|
this.$store.dispatch('addChatMessages', {
|
||||||
chatId: this.currentChat.id,
|
chatId: this.currentChat.id,
|
||||||
updateMaxId: false,
|
updateMaxId: false,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState as mapPiniaState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { mapState } from 'vuex'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Attachment from 'src/components/attachment/attachment.vue'
|
import Attachment from 'src/components/attachment/attachment.vue'
|
||||||
import ChatMessageDate from 'src/components/chat_message_date/chat_message_date.vue'
|
import ChatMessageDate from 'src/components/chat_message_date/chat_message_date.vue'
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { mapGetters, mapState } from 'vuex'
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -22,7 +24,9 @@ const chatNew = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
const { chats } = await this.backendInteractor.chats()
|
const { chats } = await chats({
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
chats.forEach((chat) => this.suggestions.push(chat.account))
|
chats.forEach((chat) => this.suggestions.push(chat.account))
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -38,7 +42,6 @@ const chatNew = {
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapState({
|
||||||
currentUser: (state) => state.users.currentUser,
|
currentUser: (state) => state.users.currentUser,
|
||||||
backendInteractor: (state) => state.api.backendInteractor,
|
|
||||||
}),
|
}),
|
||||||
...mapGetters(['findUser']),
|
...mapGetters(['findUser']),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
import UserPopover from 'src/components/user_popover/user_popover.vue'
|
import UserPopover from 'src/components/user_popover/user_popover.vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,13 @@ import { mapState } from 'vuex'
|
||||||
import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filter_settings.vue'
|
import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filter_settings.vue'
|
||||||
import QuickViewSettings from 'src/components/quick_view_settings/quick_view_settings.vue'
|
import QuickViewSettings from 'src/components/quick_view_settings/quick_view_settings.vue'
|
||||||
import ThreadTree from 'src/components/thread_tree/thread_tree.vue'
|
import ThreadTree from 'src/components/thread_tree/thread_tree.vue'
|
||||||
import { WSConnectionStatus } from '../../services/api/api.service.js'
|
|
||||||
|
|
||||||
import { useInterfaceStore } from 'src/stores/interface'
|
import { useInterfaceStore } from 'src/stores/interface'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { fetchConversation, fetchStatus } from 'src/api/public.js'
|
||||||
|
import { WSConnectionStatus } from 'src/api/websocket.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -436,22 +439,26 @@ const conversation = {
|
||||||
methods: {
|
methods: {
|
||||||
fetchConversation() {
|
fetchConversation() {
|
||||||
if (this.status) {
|
if (this.status) {
|
||||||
this.$store.state.api.backendInteractor
|
fetchConversation({
|
||||||
.fetchConversation({ id: this.statusId })
|
id: this.statusId,
|
||||||
.then(({ ancestors, descendants }) => {
|
credentials: useOAuthStore().token,
|
||||||
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
|
}).then(({ data: { ancestors, descendants } }) => {
|
||||||
this.$store.dispatch('addNewStatuses', { statuses: descendants })
|
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
|
||||||
this.setHighlight(this.originalStatusId)
|
this.$store.dispatch('addNewStatuses', { statuses: descendants })
|
||||||
})
|
this.setHighlight(this.originalStatusId)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this.loadStatusError = null
|
this.loadStatusError = null
|
||||||
this.$store.state.api.backendInteractor
|
fetchStatus({
|
||||||
.fetchStatus({ id: this.statusId })
|
id: this.statusId,
|
||||||
.then((status) => {
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
.then(({ data: status }) => {
|
||||||
this.$store.dispatch('addNewStatuses', { statuses: [status] })
|
this.$store.dispatch('addNewStatuses', { statuses: [status] })
|
||||||
this.fetchConversation()
|
this.fetchConversation()
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
this.loadStatusError = error
|
this.loadStatusError = error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* suggest - generates a suggestor function to be used by emoji-input
|
* suggest - generates a suggestor function to be used by emoji-input
|
||||||
* data: object providing source information for specific types of suggestions:
|
* data: object providing source information for specific types of suggestions:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import Popover from 'src/components/popover/popover.vue'
|
||||||
import { ensureFinalFallback } from '../../i18n/languages.js'
|
import { ensureFinalFallback } from '../../i18n/languages.js'
|
||||||
|
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
import { useEmojiStore } from 'src/stores/emoji.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 { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ import { notificationsFromStore } from '../../services/notification_utils/notifi
|
||||||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { approveUser, denyUser } from 'src/api/user.js'
|
||||||
|
|
||||||
const FollowRequestCard = {
|
const FollowRequestCard = {
|
||||||
props: ['user'],
|
props: ['user'],
|
||||||
|
|
@ -48,7 +51,10 @@ const FollowRequestCard = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doApprove() {
|
doApprove() {
|
||||||
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
|
approveUser({
|
||||||
|
id: this.user.id,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
|
|
||||||
const notifId = this.findFollowRequestNotificationId()
|
const notifId = this.findFollowRequestNotificationId()
|
||||||
|
|
@ -70,12 +76,14 @@ const FollowRequestCard = {
|
||||||
},
|
},
|
||||||
doDeny() {
|
doDeny() {
|
||||||
const notifId = this.findFollowRequestNotificationId()
|
const notifId = this.findFollowRequestNotificationId()
|
||||||
this.$store.state.api.backendInteractor
|
|
||||||
.denyUser({ id: this.user.id })
|
denyUser({
|
||||||
.then(() => {
|
id: this.user.id,
|
||||||
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
|
credentials: useOAuthStore().token,
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
}).then(() => {
|
||||||
})
|
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
|
||||||
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
|
})
|
||||||
this.hideDenyConfirmDialog()
|
this.hideDenyConfirmDialog()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { mapState } from 'vuex'
|
||||||
import { getListEntries } from 'src/components/navigation/filter.js'
|
import { getListEntries } from 'src/components/navigation/filter.js'
|
||||||
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useListsStore } from 'src/stores/lists.js'
|
import { useListsStore } from 'src/stores/lists.js'
|
||||||
|
|
||||||
export const ListsMenuContent = {
|
export const ListsMenuContent = {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { mapActions, mapState as mapPiniaState, mapStores } from 'pinia'
|
import { mapActions, mapState as mapPiniaState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
import oauthApi from '../../services/new_api/oauth.js'
|
|
||||||
|
|
||||||
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
||||||
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'
|
||||||
|
|
||||||
|
import { getLoginUrl, getTokenWithCredentials } from 'src/api/oauth.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -35,19 +35,13 @@ const LoginForm = {
|
||||||
this.isTokenAuth ? this.submitToken() : this.submitPassword()
|
this.isTokenAuth ? this.submitToken() : this.submitPassword()
|
||||||
},
|
},
|
||||||
submitToken() {
|
submitToken() {
|
||||||
const data = {
|
|
||||||
instance: this.server,
|
|
||||||
commit: this.$store.commit,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: we do not really need the app token, but obtaining a token and
|
// NOTE: we do not really need the app token, but obtaining a token and
|
||||||
// calling verify_credentials is the only way to ensure the app still works.
|
// calling verify_credentials is the only way to ensure the app still works.
|
||||||
this.ensureAppToken().then(() => {
|
this.ensureAppToken().then(() => {
|
||||||
const app = {
|
window.location.href = getLoginUrl({
|
||||||
clientId: this.clientId,
|
clientId: this.clientId,
|
||||||
clientSecret: this.clientSecret,
|
instance: this.server,
|
||||||
}
|
})
|
||||||
oauthApi.login({ ...app, ...data })
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
submitPassword() {
|
submitPassword() {
|
||||||
|
|
@ -56,37 +50,32 @@ const LoginForm = {
|
||||||
// NOTE: we do not really need the app token, but obtaining a token and
|
// NOTE: we do not really need the app token, but obtaining a token and
|
||||||
// calling verify_credentials is the only way to ensure the app still works.
|
// calling verify_credentials is the only way to ensure the app still works.
|
||||||
this.ensureAppToken().then(() => {
|
this.ensureAppToken().then(() => {
|
||||||
const app = {
|
getTokenWithCredentials({
|
||||||
clientId: this.clientId,
|
clientId: this.clientId,
|
||||||
clientSecret: this.clientSecret,
|
clientSecret: this.clientSecret,
|
||||||
}
|
instance: this.server,
|
||||||
|
username: this.user.username,
|
||||||
oauthApi
|
password: this.user.password,
|
||||||
.getTokenWithCredentials({
|
})
|
||||||
...app,
|
.then(({ data: result }) => {
|
||||||
instance: this.server,
|
|
||||||
username: this.user.username,
|
|
||||||
password: this.user.password,
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (result.error) {
|
|
||||||
if (result.error === 'mfa_required') {
|
|
||||||
this.requireMFA({ settings: result })
|
|
||||||
} else if (result.identifier === 'password_reset_required') {
|
|
||||||
this.$router.push({
|
|
||||||
name: 'password-reset',
|
|
||||||
params: { passwordResetRequested: true },
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.error = result.error
|
|
||||||
this.focusOnPasswordInput()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.login(result).then(() => {
|
this.login(result).then(() => {
|
||||||
this.$router.push({ name: 'friends' })
|
this.$router.push({ name: 'friends' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.errorData?.error === 'mfa_required') {
|
||||||
|
this.requireMFA({ settings: error })
|
||||||
|
} else if (error.identifier === 'password_reset_required') {
|
||||||
|
this.$router.push({
|
||||||
|
name: 'password-reset',
|
||||||
|
params: { passwordResetRequested: true },
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.error = error
|
||||||
|
this.focusOnPasswordInput()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
clearError() {
|
clearError() {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import Modal from 'src/components/modal/modal.vue'
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
import StillImage from 'src/components/still-image/still-image.vue'
|
|
||||||
import GestureService from '../../services/gesture_service/gesture_service'
|
import GestureService from '../../services/gesture_service/gesture_service'
|
||||||
|
|
||||||
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState as mapPiniaState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { mapState } from 'vuex'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
|
||||||
|
|
||||||
import UnicodeDomainIndicator from 'src/components/unicode_domain_indicator/unicode_domain_indicator.vue'
|
import UnicodeDomainIndicator from 'src/components/unicode_domain_indicator/unicode_domain_indicator.vue'
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { mapActions, mapState, mapStores } from 'pinia'
|
import { mapActions, mapState, mapStores } from 'pinia'
|
||||||
|
|
||||||
import mfaApi from '../../services/new_api/mfa.js'
|
|
||||||
|
|
||||||
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
||||||
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'
|
||||||
|
|
||||||
|
import { verifyRecoveryCode } from 'src/api/mfa.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -43,18 +43,18 @@ export default {
|
||||||
code: this.code,
|
code: this.code,
|
||||||
}
|
}
|
||||||
|
|
||||||
mfaApi.verifyRecoveryCode(data).then((result) => {
|
verifyRecoveryCode(data)
|
||||||
if (result.error) {
|
.then((result) => {
|
||||||
this.error = result.error
|
this.login(result).then(() => {
|
||||||
|
this.$router.push({ name: 'friends' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.error = error
|
||||||
this.code = null
|
this.code = null
|
||||||
this.focusOnCodeInput()
|
this.focusOnCodeInput()
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
this.login(result).then(() => {
|
|
||||||
this.$router.push({ name: 'friends' })
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { mapActions, mapState, mapStores } from 'pinia'
|
import { mapActions, mapState, mapStores } from 'pinia'
|
||||||
|
|
||||||
import mfaApi from '../../services/new_api/mfa.js'
|
|
||||||
|
|
||||||
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
||||||
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'
|
||||||
|
|
||||||
|
import { verifyOTPCode } from 'src/api/mfa.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -46,18 +46,18 @@ export default {
|
||||||
code: this.code,
|
code: this.code,
|
||||||
}
|
}
|
||||||
|
|
||||||
mfaApi.verifyOTPCode(data).then((result) => {
|
verifyOTPCode(data)
|
||||||
if (result.error) {
|
.then(({ data: result }) => {
|
||||||
this.error = result.error
|
this.login(result).then(() => {
|
||||||
|
this.$router.push({ name: 'friends' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.error = error
|
||||||
this.code = null
|
this.code = null
|
||||||
this.focusOnCodeInput()
|
this.focusOnCodeInput()
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
this.login(result).then(() => {
|
|
||||||
this.$router.push({ name: 'friends' })
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ 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'
|
||||||
|
|
||||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,10 @@ import {
|
||||||
|
|
||||||
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 { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
import { useUserHighlightStore } from 'src/stores/user_highlight.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'
|
||||||
|
|
@ -142,7 +144,10 @@ const Notification = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doApprove() {
|
doApprove() {
|
||||||
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
|
approveUser({
|
||||||
|
id: this.user.id,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
this.$store.dispatch('markSingleNotificationAsSeen', {
|
this.$store.dispatch('markSingleNotificationAsSeen', {
|
||||||
id: this.notification.id,
|
id: this.notification.id,
|
||||||
|
|
@ -163,14 +168,15 @@ const Notification = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doDeny() {
|
doDeny() {
|
||||||
this.$store.state.api.backendInteractor
|
denyUser({
|
||||||
.denyUser({ id: this.user.id })
|
id: this.user.id,
|
||||||
.then(() => {
|
credentials: useOAuthStore().token,
|
||||||
this.$store.dispatch('dismissNotificationLocal', {
|
}).then(() => {
|
||||||
id: this.notification.id,
|
this.$store.dispatch('dismissNotificationLocal', {
|
||||||
})
|
id: this.notification.id,
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
|
||||||
})
|
})
|
||||||
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
|
})
|
||||||
this.hideDenyConfirmDialog()
|
this.hideDenyConfirmDialog()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import oauth from '../../services/new_api/oauth.js'
|
|
||||||
|
|
||||||
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'
|
||||||
|
|
||||||
|
import { getToken } from 'src/api/oauth.js'
|
||||||
|
|
||||||
const oac = {
|
const oac = {
|
||||||
props: ['code'],
|
props: ['code'],
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -10,18 +10,16 @@ const oac = {
|
||||||
const oauthStore = useOAuthStore()
|
const oauthStore = useOAuthStore()
|
||||||
const { clientId, clientSecret } = oauthStore
|
const { clientId, clientSecret } = oauthStore
|
||||||
|
|
||||||
oauth
|
getToken({
|
||||||
.getToken({
|
clientId,
|
||||||
clientId,
|
clientSecret,
|
||||||
clientSecret,
|
instance: useInstanceStore().server,
|
||||||
instance: useInstanceStore().server,
|
code: this.code,
|
||||||
code: this.code,
|
}).then(({ data: result }) => {
|
||||||
})
|
oauthStore.setToken(result.access_token)
|
||||||
.then((result) => {
|
this.$store.dispatch('loginUser', result.access_token)
|
||||||
oauthStore.setToken(result.access_token)
|
this.$router.push({ name: 'friends' })
|
||||||
this.$store.dispatch('loginUser', result.access_token)
|
})
|
||||||
this.$router.push({ name: 'friends' })
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState as mapPiniaState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
import passwordResetApi from '../../services/new_api/password_reset.js'
|
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
|
||||||
|
import { resetPassword } from 'src/api/public.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
import { faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ const passwordReset = {
|
||||||
...mapState({
|
...mapState({
|
||||||
signedIn: (state) => !!state.users.currentUser,
|
signedIn: (state) => !!state.users.currentUser,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useInstanceStore, ['server', 'mailerEnabled']),
|
...mapPiniaState(useInstanceStore, ['mailerEnabled']),
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.signedIn) {
|
if (this.signedIn) {
|
||||||
|
|
@ -44,9 +44,8 @@ const passwordReset = {
|
||||||
submit() {
|
submit() {
|
||||||
this.isPending = true
|
this.isPending = true
|
||||||
const email = this.user.email
|
const email = this.user.email
|
||||||
const server = this.server
|
|
||||||
|
|
||||||
passwordResetApi({ server, email })
|
resetPassword({ email })
|
||||||
.then(({ status }) => {
|
.then(({ status }) => {
|
||||||
this.isPending = false
|
this.isPending = false
|
||||||
this.user.email = ''
|
this.user.email = ''
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { debounce, map, reject, uniqBy } from 'lodash'
|
import { debounce, map, reject, uniqBy } from 'lodash'
|
||||||
import { mapActions, mapState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
import { mapGetters } from 'vuex'
|
|
||||||
|
|
||||||
import Attachment from 'src/components/attachment/attachment.vue'
|
import Attachment from 'src/components/attachment/attachment.vue'
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
|
@ -635,11 +634,7 @@ const PostStatusForm = {
|
||||||
// Don't apply preview if not loading, because it means
|
// Don't apply preview if not loading, because it means
|
||||||
// user has closed the preview manually.
|
// user has closed the preview manually.
|
||||||
if (!this.previewLoading) return
|
if (!this.previewLoading) return
|
||||||
if (!data.error) {
|
this.preview = data
|
||||||
this.preview = data
|
|
||||||
} else {
|
|
||||||
this.preview = { error: data.error }
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.preview = { error }
|
this.preview = { error }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash'
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
import Modal from 'src/components/modal/modal.vue'
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { fetchUser } from 'src/api/public.js'
|
||||||
|
|
||||||
const RemoteUserResolver = {
|
const RemoteUserResolver = {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
error: false,
|
error: false,
|
||||||
|
|
@ -7,11 +11,12 @@ const RemoteUserResolver = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
redirect() {
|
redirect() {
|
||||||
const acct =
|
const id = this.$route.params.username + '@' + this.$route.params.hostname
|
||||||
this.$route.params.username + '@' + this.$route.params.hostname
|
fetchUser({
|
||||||
this.$store.state.api.backendInteractor
|
id,
|
||||||
.fetchUser({ id: acct })
|
credentials: useOAuthStore().token,
|
||||||
.then((externalUser) => {
|
})
|
||||||
|
.then(({ data: externalUser }) => {
|
||||||
if (externalUser.error) {
|
if (externalUser.error) {
|
||||||
this.error = true
|
this.error = true
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||||
import ModerationTools from 'src/components/moderation_tools/moderation_tools.vue'
|
import ModerationTools from 'src/components/moderation_tools/moderation_tools.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 { assign, clone } from 'lodash'
|
import { clone } from 'lodash'
|
||||||
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'
|
||||||
|
|
@ -11,6 +11,7 @@ import ModifiedIndicator from '../helpers/modified_indicator.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
import StringSetting from '../helpers/string_setting.vue'
|
import StringSetting from '../helpers/string_setting.vue'
|
||||||
|
|
||||||
|
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
|
@ -98,10 +99,10 @@ const EmojiTab = {
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
reloadEmoji() {
|
reloadEmoji() {
|
||||||
this.$store.state.api.backendInteractor.reloadEmoji()
|
useAdminSettingsStore().reloadEmoji()
|
||||||
},
|
},
|
||||||
importFromFS() {
|
importFromFS() {
|
||||||
this.$store.state.api.backendInteractor.importEmojiFromFS()
|
useAdminSettingsStore().importEmojiFromFS()
|
||||||
},
|
},
|
||||||
emojiAddr(name) {
|
emojiAddr(name) {
|
||||||
if (this.pack.remote !== undefined) {
|
if (this.pack.remote !== undefined) {
|
||||||
|
|
@ -113,7 +114,7 @@ const EmojiTab = {
|
||||||
},
|
},
|
||||||
|
|
||||||
createEmojiPack() {
|
createEmojiPack() {
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.createEmojiPack({ name: this.newPackName })
|
.createEmojiPack({ name: this.newPackName })
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
|
@ -130,7 +131,7 @@ const EmojiTab = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deleteEmojiPack() {
|
deleteEmojiPack() {
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.deleteEmojiPack({ name: this.packName })
|
.deleteEmojiPack({ name: this.packName })
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
|
@ -157,7 +158,7 @@ const EmojiTab = {
|
||||||
return edited !== def
|
return edited !== def
|
||||||
},
|
},
|
||||||
savePackMetadata() {
|
savePackMetadata() {
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta })
|
.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta })
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
|
@ -182,7 +183,7 @@ const EmojiTab = {
|
||||||
useEmojiStore()
|
useEmojiStore()
|
||||||
.getAdminPacks(
|
.getAdminPacks(
|
||||||
this.remotePackInstance,
|
this.remotePackInstance,
|
||||||
this.$store.state.api.backendInteractor.listEmojiPacks,
|
useAdminSettingsStore().listEmojiPacks,
|
||||||
)
|
)
|
||||||
.then((allPacks) => {
|
.then((allPacks) => {
|
||||||
this.knownLocalPacks = allPacks
|
this.knownLocalPacks = allPacks
|
||||||
|
|
@ -195,7 +196,7 @@ const EmojiTab = {
|
||||||
useEmojiStore()
|
useEmojiStore()
|
||||||
.getAdminPacks(
|
.getAdminPacks(
|
||||||
this.remotePackInstance,
|
this.remotePackInstance,
|
||||||
this.$store.state.api.backendInteractor.listRemoteEmojiPacks,
|
useAdminSettingsStore().listRemoteEmojiPacks,
|
||||||
)
|
)
|
||||||
.then((allPacks) => {
|
.then((allPacks) => {
|
||||||
let inst = this.remotePackInstance
|
let inst = this.remotePackInstance
|
||||||
|
|
@ -226,7 +227,7 @@ const EmojiTab = {
|
||||||
this.remotePackDownloadAs = this.pack.remote.baseName
|
this.remotePackDownloadAs = this.pack.remote.baseName
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.downloadRemoteEmojiPack({
|
.downloadRemoteEmojiPack({
|
||||||
instance: this.pack.remote.instance,
|
instance: this.pack.remote.instance,
|
||||||
packName: this.pack.remote.baseName,
|
packName: this.pack.remote.baseName,
|
||||||
|
|
@ -247,7 +248,7 @@ const EmojiTab = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
downloadRemoteURLPack() {
|
downloadRemoteURLPack() {
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.downloadRemoteEmojiPackZIP({
|
.downloadRemoteEmojiPackZIP({
|
||||||
url: this.remotePackURL,
|
url: this.remotePackURL,
|
||||||
packName: this.newPackName,
|
packName: this.newPackName,
|
||||||
|
|
@ -268,7 +269,7 @@ const EmojiTab = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
downloadRemoteFilePack() {
|
downloadRemoteFilePack() {
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.downloadRemoteEmojiPackZIP({
|
.downloadRemoteEmojiPackZIP({
|
||||||
file: this.remotePackFile[0],
|
file: this.remotePackFile[0],
|
||||||
packName: this.newPackName,
|
packName: this.newPackName,
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ const FrontendsTab = {
|
||||||
const payload = { name, ref }
|
const payload = { name, ref }
|
||||||
|
|
||||||
this.working = true
|
this.working = true
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.installFrontend({ payload })
|
.installFrontend({ payload })
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.working = false
|
this.working = false
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { isEmpty } from 'lodash'
|
|
||||||
|
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import List from 'src/components/list/list.vue'
|
import List from 'src/components/list/list.vue'
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,14 @@ import Popover from 'components/popover/popover.vue'
|
||||||
import SelectComponent from 'components/select/select.vue'
|
import SelectComponent from 'components/select/select.vue'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
addNewEmojiFile,
|
||||||
|
deleteEmojiFile,
|
||||||
|
updateEmojiFile,
|
||||||
|
} from 'src/api/admin.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Popover,
|
Popover,
|
||||||
|
|
@ -243,14 +251,14 @@ export default {
|
||||||
saveEditedEmoji() {
|
saveEditedEmoji() {
|
||||||
if (!this.isEdited) return
|
if (!this.isEdited) return
|
||||||
|
|
||||||
this.$store.state.api.backendInteractor
|
updateEmojiFile({
|
||||||
.updateEmojiFile({
|
packName: this.packName,
|
||||||
packName: this.packName,
|
shortcode: this.shortcode,
|
||||||
shortcode: this.shortcode,
|
newShortcode: this.editedShortcode,
|
||||||
newShortcode: this.editedShortcode,
|
newFilename: this.editedFile,
|
||||||
newFilename: this.editedFile,
|
force: false,
|
||||||
force: false,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.error !== undefined) {
|
if (resp.error !== undefined) {
|
||||||
this.$emit('displayError', resp.error)
|
this.$emit('displayError', resp.error)
|
||||||
|
|
@ -263,18 +271,18 @@ export default {
|
||||||
},
|
},
|
||||||
uploadEmoji() {
|
uploadEmoji() {
|
||||||
let packName = this.remote === undefined ? this.packName : this.copyToPack
|
let packName = this.remote === undefined ? this.packName : this.copyToPack
|
||||||
this.$store.state.api.backendInteractor
|
addNewEmojiFile({
|
||||||
.addNewEmojiFile({
|
packName: packName,
|
||||||
packName: packName,
|
file:
|
||||||
file:
|
this.remote === undefined
|
||||||
this.remote === undefined
|
? this.uploadURL !== ''
|
||||||
? this.uploadURL !== ''
|
? this.uploadURL
|
||||||
? this.uploadURL
|
: this.uploadFile[0]
|
||||||
: this.uploadFile[0]
|
: this.emojiAddr(this.file),
|
||||||
: this.emojiAddr(this.file),
|
shortcode: this.editedShortcode,
|
||||||
shortcode: this.editedShortcode,
|
filename: this.editedFile,
|
||||||
filename: this.editedFile,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.error !== undefined) {
|
if (resp.error !== undefined) {
|
||||||
|
|
@ -297,8 +305,11 @@ export default {
|
||||||
deleteEmoji() {
|
deleteEmoji() {
|
||||||
this.deleteModalVisible = false
|
this.deleteModalVisible = false
|
||||||
|
|
||||||
this.$store.state.api.backendInteractor
|
deleteEmojiFile({
|
||||||
.deleteEmojiFile({ packName: this.packName, shortcode: this.shortcode })
|
packName: this.packName,
|
||||||
|
shortcode: this.shortcode,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.error !== undefined) {
|
if (resp.error !== undefined) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import LocalSettingIndicator from './local_setting_indicator.vue'
|
||||||
import ModifiedIndicator from './modified_indicator.vue'
|
import ModifiedIndicator from './modified_indicator.vue'
|
||||||
|
|
||||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// eslint-disable-next-line no-unused
|
// eslint-disable-next-line no-unused
|
||||||
|
|
||||||
import { throttle } from 'lodash'
|
import { mapState as mapPiniaState } from 'pinia'
|
||||||
import { mapState as mapPiniaState, mapState } from 'pinia'
|
import { Fragment } from 'vue'
|
||||||
import { Fragment, h } from 'vue'
|
|
||||||
|
|
||||||
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
|
||||||
import AuthTab from './admin_tabs/auth_tab.vue'
|
import AuthTab from './admin_tabs/auth_tab.vue'
|
||||||
import EmojiTab from './admin_tabs/emoji_tab.vue'
|
import EmojiTab from './admin_tabs/emoji_tab.vue'
|
||||||
import FederationTab from './admin_tabs/federation_tab.vue'
|
import FederationTab from './admin_tabs/federation_tab.vue'
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ import Preview from './old_theme_tab/theme_preview.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { normalizeThemeData, useInterfaceStore } from 'src/stores/interface.js'
|
import { normalizeThemeData, useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { updateProfileImages } from 'src/api/user.js'
|
||||||
import { newImporter } from 'src/services/export_import/export_import.js'
|
import { newImporter } from 'src/services/export_import/export_import.js'
|
||||||
import {
|
import {
|
||||||
adoptStyleSheets,
|
adoptStyleSheets,
|
||||||
|
|
@ -484,9 +486,11 @@ const AppearanceTab = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.backgroundUploading = true
|
this.backgroundUploading = true
|
||||||
this.$store.state.api.backendInteractor
|
updateProfileImages({
|
||||||
.updateProfileImages({ background })
|
background,
|
||||||
.then((data) => {
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
this.$store.commit('addNewUsers', [data])
|
this.$store.commit('addNewUsers', [data])
|
||||||
this.$store.commit('setCurrentUser', data)
|
this.$store.commit('setCurrentUser', data)
|
||||||
this.backgroundPreview = null
|
this.backgroundPreview = null
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,10 @@ import UnitSetting from '../helpers/unit_setting.vue'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
|
|
||||||
|
import { updateProfile } from 'src/api/user.js'
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
import localeService from 'src/services/locale/locale.service.js'
|
||||||
import { cacheKey, clearCache, emojiCacheKey } from 'src/services/sw/sw.js'
|
import { cacheKey, clearCache, emojiCacheKey } from 'src/services/sw/sw.js'
|
||||||
|
|
||||||
|
|
@ -164,12 +166,13 @@ const ComposingTab = {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.state.api.backendInteractor
|
updateProfile({
|
||||||
.updateProfile({ params })
|
params,
|
||||||
.then((user) => {
|
credentials: useOAuthStore().token,
|
||||||
this.$store.commit('addNewUsers', [user])
|
}).then(({ data: user }) => {
|
||||||
this.$store.commit('setCurrentUser', user)
|
this.$store.commit('addNewUsers', [user])
|
||||||
})
|
this.$store.commit('setCurrentUser', user)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
updateFont(key, value) {
|
updateFont(key, value) {
|
||||||
useSyncConfigStore().setSimplePrefAndSave({
|
useSyncConfigStore().setSimplePrefAndSave({
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,20 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import Exporter from 'src/components/exporter/exporter.vue'
|
import Exporter from 'src/components/exporter/exporter.vue'
|
||||||
import Importer from 'src/components/importer/importer.vue'
|
import Importer from 'src/components/importer/importer.vue'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens.js'
|
import { useOAuthTokensStore } from 'src/stores/oauth_tokens.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
addBackup,
|
||||||
|
exportFriends,
|
||||||
|
fetchBlocks,
|
||||||
|
fetchMutes,
|
||||||
|
importBlocks,
|
||||||
|
importFollows,
|
||||||
|
importMutes,
|
||||||
|
listBackups,
|
||||||
|
} from 'src/api/user.js'
|
||||||
|
|
||||||
const DataImportExportTab = {
|
const DataImportExportTab = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -28,48 +40,57 @@ const DataImportExportTab = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
backendInteractor: (state) => state.api.backendInteractor,
|
|
||||||
user: (state) => state.users.currentUser,
|
user: (state) => state.users.currentUser,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getFollowsContent() {
|
getFollowsContent() {
|
||||||
return this.backendInteractor
|
return exportFriends({
|
||||||
.exportFriends({ id: this.user.id })
|
id: this.user.id,
|
||||||
.then(this.generateExportableUsersContent)
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(this.generateExportableUsersContent)
|
||||||
},
|
},
|
||||||
getBlocksContent() {
|
getBlocksContent() {
|
||||||
return this.backendInteractor
|
return fetchBlocks({
|
||||||
.fetchBlocks()
|
credentials: useOAuthStore().token,
|
||||||
.then(this.generateExportableUsersContent)
|
}).then(this.generateExportableUsersContent)
|
||||||
},
|
},
|
||||||
getMutesContent() {
|
getMutesContent() {
|
||||||
return this.backendInteractor
|
return fetchMutes({
|
||||||
.fetchMutes()
|
credentials: useOAuthStore().token,
|
||||||
.then(this.generateExportableUsersContent)
|
}).then(this.generateExportableUsersContent)
|
||||||
},
|
},
|
||||||
importFollows(file) {
|
importFollows(file) {
|
||||||
return this.backendInteractor.importFollows({ file }).then((status) => {
|
return importFollows({
|
||||||
|
file,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: status }) => {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
throw new Error('failed')
|
throw new Error('failed')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
importBlocks(file) {
|
importBlocks(file) {
|
||||||
return this.backendInteractor.importBlocks({ file }).then((status) => {
|
return importBlocks({
|
||||||
|
file,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: status }) => {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
throw new Error('failed')
|
throw new Error('failed')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
importMutes(file) {
|
importMutes(file) {
|
||||||
return this.backendInteractor.importMutes({ file }).then((status) => {
|
return importMutes({
|
||||||
|
file,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: status }) => {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
throw new Error('failed')
|
throw new Error('failed')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
generateExportableUsersContent(users) {
|
generateExportableUsersContent({ data: users }) {
|
||||||
// Get addresses
|
// Get addresses
|
||||||
return users
|
return users
|
||||||
.map((user) => {
|
.map((user) => {
|
||||||
|
|
@ -83,8 +104,9 @@ const DataImportExportTab = {
|
||||||
.join('\n')
|
.join('\n')
|
||||||
},
|
},
|
||||||
addBackup() {
|
addBackup() {
|
||||||
this.$store.state.api.backendInteractor
|
addBackup({
|
||||||
.addBackup()
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.addedBackup = true
|
this.addedBackup = true
|
||||||
this.addBackupError = false
|
this.addBackupError = false
|
||||||
|
|
@ -96,9 +118,10 @@ const DataImportExportTab = {
|
||||||
.then(() => this.fetchBackups())
|
.then(() => this.fetchBackups())
|
||||||
},
|
},
|
||||||
fetchBackups() {
|
fetchBackups() {
|
||||||
this.$store.state.api.backendInteractor
|
listBackups({
|
||||||
.listBackups()
|
credentials: useOAuthStore().token,
|
||||||
.then((res) => {
|
})
|
||||||
|
.then(({ data: res }) => {
|
||||||
this.backups = res
|
this.backups = res
|
||||||
this.listBackupsError = false
|
this.listBackupsError = false
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,13 @@ import FloatSetting from '../helpers/float_setting.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
import UnitSetting from '../helpers/unit_setting.vue'
|
import UnitSetting from '../helpers/unit_setting.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
|
|
||||||
|
import { updateProfile } from 'src/api/user.js'
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
import localeService from 'src/services/locale/locale.service.js'
|
||||||
|
|
||||||
const GeneralTab = {
|
const GeneralTab = {
|
||||||
|
|
@ -58,12 +59,13 @@ const GeneralTab = {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.state.api.backendInteractor
|
updateProfile({
|
||||||
.updateProfile({ params })
|
params,
|
||||||
.then((user) => {
|
credentials: useOAuthStore().token,
|
||||||
this.$store.commit('addNewUsers', [user])
|
}).then(({ data: user }) => {
|
||||||
this.$store.commit('setCurrentUser', user)
|
this.$store.commit('addNewUsers', [user])
|
||||||
})
|
this.$store.commit('setCurrentUser', user)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
updateFont(path, value) {
|
updateFont(path, value) {
|
||||||
useLocalConfigStore().set({ path, value })
|
useLocalConfigStore().set({ path, value })
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get, isEmpty, map, reject } from 'lodash'
|
import { get, map, reject } from 'lodash'
|
||||||
|
|
||||||
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'
|
||||||
|
|
@ -10,8 +10,11 @@ import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens.js'
|
import { useOAuthTokensStore } from 'src/stores/oauth_tokens.js'
|
||||||
|
|
||||||
|
import { importBlocks, importFollows } from 'src/api/user.js'
|
||||||
|
|
||||||
const MutesAndBlocks = {
|
const MutesAndBlocks = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -54,22 +57,24 @@ const MutesAndBlocks = {
|
||||||
return () => this.$store.dispatch('fetch' + group, this.userId)
|
return () => this.$store.dispatch('fetch' + group, this.userId)
|
||||||
},
|
},
|
||||||
importFollows(file) {
|
importFollows(file) {
|
||||||
return this.$store.state.api.backendInteractor
|
return importFollows({
|
||||||
.importFollows({ file })
|
file,
|
||||||
.then((status) => {
|
credentials: useOAuthStore().token,
|
||||||
if (!status) {
|
}).then(({ data: status }) => {
|
||||||
throw new Error('failed')
|
if (!status) {
|
||||||
}
|
throw new Error('failed')
|
||||||
})
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
importBlocks(file) {
|
importBlocks(file) {
|
||||||
return this.$store.state.api.backendInteractor
|
return importBlocks({
|
||||||
.importBlocks({ file })
|
file,
|
||||||
.then((status) => {
|
credentials: useOAuthStore().token,
|
||||||
if (!status) {
|
}).then(({ data: status }) => {
|
||||||
throw new Error('failed')
|
if (!status) {
|
||||||
}
|
throw new Error('failed')
|
||||||
})
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
generateExportableUsersContent(users) {
|
generateExportableUsersContent(users) {
|
||||||
// Get addresses
|
// Get addresses
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { updateNotificationSettings } from 'src/api/user.js'
|
||||||
|
|
||||||
const NotificationsTab = {
|
const NotificationsTab = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -27,7 +31,8 @@ const NotificationsTab = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateNotificationSettings() {
|
updateNotificationSettings() {
|
||||||
this.$store.state.api.backendInteractor.updateNotificationSettings({
|
updateNotificationSettings({
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
settings: this.notificationSettings,
|
settings: this.notificationSettings,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
import UnitSetting from '../helpers/unit_setting.vue'
|
import UnitSetting from '../helpers/unit_setting.vue'
|
||||||
|
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
|
||||||
|
|
||||||
const PostsTab = {
|
const PostsTab = {
|
||||||
data() {
|
data() {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@ import UserCard from 'src/components/user_card/user_card.vue'
|
||||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { updateProfile } from 'src/api/user.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faCircleNotch,
|
faCircleNotch,
|
||||||
|
|
@ -35,10 +39,11 @@ const ProfileTab = {
|
||||||
const params = {
|
const params = {
|
||||||
locked: this.locked,
|
locked: this.locked,
|
||||||
}
|
}
|
||||||
|
updateProfile({
|
||||||
this.$store.state.api.backendInteractor
|
params,
|
||||||
.updateProfile({ params })
|
credentials: useOAuthStore().token,
|
||||||
.then((user) => {
|
})
|
||||||
|
.then(({ data: user }) => {
|
||||||
this.$store.commit('addNewUsers', [user])
|
this.$store.commit('addNewUsers', [user])
|
||||||
this.$store.commit('setCurrentUser', user)
|
this.$store.commit('setCurrentUser', user)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,18 @@
|
||||||
import VueQrcode from '@chenfengyuan/vue-qrcode'
|
import VueQrcode from '@chenfengyuan/vue-qrcode'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Confirm from './confirm.vue'
|
import Confirm from './confirm.vue'
|
||||||
import RecoveryCodes from './mfa_backup_codes.vue'
|
import RecoveryCodes from './mfa_backup_codes.vue'
|
||||||
import TOTP from './mfa_totp.vue'
|
import TOTP from './mfa_totp.vue'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
generateMfaBackupCodes,
|
||||||
|
mfaConfirmOTP,
|
||||||
|
mfaSetupOTP,
|
||||||
|
settingsMFA,
|
||||||
|
} from 'src/api/user.js'
|
||||||
|
|
||||||
const Mfa = {
|
const Mfa = {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
settings: {
|
settings: {
|
||||||
|
|
@ -71,9 +79,6 @@ const Mfa = {
|
||||||
confirmNewBackupCodes() {
|
confirmNewBackupCodes() {
|
||||||
return this.backupCodes.getNewCodes
|
return this.backupCodes.getNewCodes
|
||||||
},
|
},
|
||||||
...mapState({
|
|
||||||
backendInteractor: (state) => state.api.backendInteractor,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -87,7 +92,9 @@ const Mfa = {
|
||||||
this.backupCodes.inProgress = true
|
this.backupCodes.inProgress = true
|
||||||
this.backupCodes.codes = []
|
this.backupCodes.codes = []
|
||||||
|
|
||||||
return this.backendInteractor.generateMfaBackupCodes().then((res) => {
|
return generateMfaBackupCodes({
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: res }) => {
|
||||||
this.backupCodes.codes = res.codes
|
this.backupCodes.codes = res.codes
|
||||||
this.backupCodes.inProgress = false
|
this.backupCodes.inProgress = false
|
||||||
})
|
})
|
||||||
|
|
@ -112,7 +119,9 @@ const Mfa = {
|
||||||
// prepare setup OTP
|
// prepare setup OTP
|
||||||
this.setupState.state = 'setupOTP'
|
this.setupState.state = 'setupOTP'
|
||||||
this.setupState.setupOTPState = 'prepare'
|
this.setupState.setupOTPState = 'prepare'
|
||||||
this.backendInteractor.mfaSetupOTP().then((res) => {
|
mfaSetupOTP({
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: res }) => {
|
||||||
this.otpSettings = res
|
this.otpSettings = res
|
||||||
this.setupState.setupOTPState = 'confirm'
|
this.setupState.setupOTPState = 'confirm'
|
||||||
})
|
})
|
||||||
|
|
@ -120,18 +129,17 @@ const Mfa = {
|
||||||
doConfirmOTP() {
|
doConfirmOTP() {
|
||||||
// handler confirm enable OTP
|
// handler confirm enable OTP
|
||||||
this.error = null
|
this.error = null
|
||||||
this.backendInteractor
|
mfaConfirmOTP({
|
||||||
.mfaConfirmOTP({
|
token: this.otpConfirmToken,
|
||||||
token: this.otpConfirmToken,
|
password: this.currentPassword,
|
||||||
password: this.currentPassword,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
if (res.error) {
|
|
||||||
this.error = res.error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.completeSetup()
|
this.completeSetup()
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.error = error
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
completeSetup() {
|
completeSetup() {
|
||||||
|
|
@ -152,7 +160,9 @@ const Mfa = {
|
||||||
|
|
||||||
// fetch settings from server
|
// fetch settings from server
|
||||||
async fetchSettings() {
|
async fetchSettings() {
|
||||||
const result = await this.backendInteractor.settingsMFA()
|
const { data: result } = await settingsMFA({
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
if (result.error) return
|
if (result.error) return
|
||||||
this.settings = result.settings
|
this.settings = result.settings
|
||||||
this.settings.available = true
|
this.settings.available = true
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Confirm from './confirm.vue'
|
import Confirm from './confirm.vue'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { mfaDisableOTP } from 'src/api/user.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['settings'],
|
props: ['settings'],
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
|
@ -17,9 +19,6 @@ export default {
|
||||||
isActivated() {
|
isActivated() {
|
||||||
return this.settings.totp
|
return this.settings.totp
|
||||||
},
|
},
|
||||||
...mapState({
|
|
||||||
backendInteractor: (state) => state.api.backendInteractor,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
doActivate() {
|
doActivate() {
|
||||||
|
|
@ -36,19 +35,18 @@ export default {
|
||||||
// confirm deactivate TOTP method
|
// confirm deactivate TOTP method
|
||||||
this.error = null
|
this.error = null
|
||||||
this.inProgress = true
|
this.inProgress = true
|
||||||
this.backendInteractor
|
mfaDisableOTP({
|
||||||
.mfaDisableOTP({
|
password: this.currentPassword,
|
||||||
password: this.currentPassword,
|
credentials: useOAuthStore().token,
|
||||||
})
|
}).then(({ data: res }) => {
|
||||||
.then((res) => {
|
this.inProgress = false
|
||||||
this.inProgress = false
|
if (res.error) {
|
||||||
if (res.error) {
|
this.error = res.error
|
||||||
this.error = res.error
|
return
|
||||||
return
|
}
|
||||||
}
|
this.deactivate = false
|
||||||
this.deactivate = false
|
this.$emit('deactivate')
|
||||||
this.$emit('deactivate')
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,19 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
||||||
import Mfa from './mfa.vue'
|
import Mfa from './mfa.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
||||||
|
|
||||||
|
import {
|
||||||
|
addAlias,
|
||||||
|
changeEmail,
|
||||||
|
changePassword,
|
||||||
|
deleteAccount,
|
||||||
|
deleteAlias,
|
||||||
|
listAliases,
|
||||||
|
moveAccount,
|
||||||
|
} from 'src/api/user.js'
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
import localeService from 'src/services/locale/locale.service.js'
|
||||||
|
|
||||||
const SecurityTab = {
|
const SecurityTab = {
|
||||||
|
|
@ -65,78 +74,79 @@ const SecurityTab = {
|
||||||
this.deletingAccount = true
|
this.deletingAccount = true
|
||||||
},
|
},
|
||||||
deleteAccount() {
|
deleteAccount() {
|
||||||
this.$store.state.api.backendInteractor
|
deleteAccount({
|
||||||
.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })
|
credentials: useOAuthStore().token,
|
||||||
.then((res) => {
|
password: this.deleteAccountConfirmPasswordInput,
|
||||||
if (res.status === 'success') {
|
}).then(({ data: res }) => {
|
||||||
this.$store.dispatch('logout')
|
if (res.status === 'success') {
|
||||||
this.$router.push({ name: 'root' })
|
this.$store.dispatch('logout')
|
||||||
} else {
|
this.$router.push({ name: 'root' })
|
||||||
this.deleteAccountError = res.error
|
} else {
|
||||||
}
|
this.deleteAccountError = res.error
|
||||||
})
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
changePassword() {
|
changePassword() {
|
||||||
const params = {
|
const params = {
|
||||||
password: this.changePasswordInputs[0],
|
password: this.changePasswordInputs[0],
|
||||||
newPassword: this.changePasswordInputs[1],
|
newPassword: this.changePasswordInputs[1],
|
||||||
newPasswordConfirmation: this.changePasswordInputs[2],
|
newPasswordConfirmation: this.changePasswordInputs[2],
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
}
|
}
|
||||||
this.$store.state.api.backendInteractor
|
changePassword(params).then(({ data: res }) => {
|
||||||
.changePassword(params)
|
if (res.status === 'success') {
|
||||||
.then((res) => {
|
this.changedPassword = true
|
||||||
if (res.status === 'success') {
|
this.changePasswordError = false
|
||||||
this.changedPassword = true
|
this.logout()
|
||||||
this.changePasswordError = false
|
} else {
|
||||||
this.logout()
|
this.changedPassword = false
|
||||||
} else {
|
this.changePasswordError = res.error
|
||||||
this.changedPassword = false
|
}
|
||||||
this.changePasswordError = res.error
|
})
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
changeEmail() {
|
changeEmail() {
|
||||||
const params = {
|
const params = {
|
||||||
email: this.newEmail,
|
email: this.newEmail,
|
||||||
password: this.changeEmailPassword,
|
password: this.changeEmailPassword,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
}
|
}
|
||||||
this.$store.state.api.backendInteractor
|
changeEmail(params).then(({ data: res }) => {
|
||||||
.changeEmail(params)
|
if (res.status === 'success') {
|
||||||
.then((res) => {
|
this.changedEmail = true
|
||||||
if (res.status === 'success') {
|
this.changeEmailError = false
|
||||||
this.changedEmail = true
|
} else {
|
||||||
this.changeEmailError = false
|
this.changedEmail = false
|
||||||
} else {
|
this.changeEmailError = res.error
|
||||||
this.changedEmail = false
|
}
|
||||||
this.changeEmailError = res.error
|
})
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
moveAccount() {
|
moveAccount() {
|
||||||
const params = {
|
const params = {
|
||||||
targetAccount: this.moveAccountTarget,
|
targetAccount: this.moveAccountTarget,
|
||||||
password: this.moveAccountPassword,
|
password: this.moveAccountPassword,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
}
|
}
|
||||||
this.$store.state.api.backendInteractor
|
moveAccount(params).then(({ data: res }) => {
|
||||||
.moveAccount(params)
|
if (res.status === 'success') {
|
||||||
.then((res) => {
|
this.movedAccount = true
|
||||||
if (res.status === 'success') {
|
this.moveAccountError = false
|
||||||
this.movedAccount = true
|
} else {
|
||||||
this.moveAccountError = false
|
this.movedAccount = false
|
||||||
} else {
|
this.moveAccountError = res.error
|
||||||
this.movedAccount = false
|
}
|
||||||
this.moveAccountError = res.error
|
})
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
removeAlias(alias) {
|
removeAlias(alias) {
|
||||||
this.$store.state.api.backendInteractor
|
deleteAlias({
|
||||||
.deleteAlias({ alias })
|
alias,
|
||||||
.then(() => this.fetchAliases())
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(() => this.fetchAliases())
|
||||||
},
|
},
|
||||||
addAlias() {
|
addAlias() {
|
||||||
this.$store.state.api.backendInteractor
|
addAlias({
|
||||||
.addAlias({ alias: this.addAliasTarget })
|
alias: this.addAliasTarget,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.addedAlias = true
|
this.addedAlias = true
|
||||||
this.addAliasError = false
|
this.addAliasError = false
|
||||||
|
|
@ -149,9 +159,10 @@ const SecurityTab = {
|
||||||
.then(() => this.fetchAliases())
|
.then(() => this.fetchAliases())
|
||||||
},
|
},
|
||||||
fetchAliases() {
|
fetchAliases() {
|
||||||
this.$store.state.api.backendInteractor
|
listAliases({
|
||||||
.listAliases()
|
credentials: useOAuthStore().token,
|
||||||
.then((res) => {
|
})
|
||||||
|
.then(({ data: res }) => {
|
||||||
this.aliases = res.aliases
|
this.aliases = res.aliases
|
||||||
this.listAliasesError = false
|
this.listAliasesError = false
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { mapActions, mapState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js'
|
import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js'
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ const StatusActionButtons = {
|
||||||
button
|
button
|
||||||
.action?.(this.funcArg)
|
.action?.(this.funcArg)
|
||||||
.then(() => this.$emit('onSuccess'))
|
.then(() => this.$emit('onSuccess'))
|
||||||
.catch((err) => this.$emit('onError', err.error.error))
|
.catch((err) => this.$emit('onError', err))
|
||||||
},
|
},
|
||||||
onExtraClose() {
|
onExtraClose() {
|
||||||
this.showPin = false
|
this.showPin = false
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState as mapPiniaState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { mapState } from 'vuex'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Attachment from 'src/components/attachment/attachment.vue'
|
import Attachment from 'src/components/attachment/attachment.vue'
|
||||||
import Gallery from 'src/components/gallery/gallery.vue'
|
import Gallery from 'src/components/gallery/gallery.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { find } from 'lodash'
|
import { find } from 'lodash'
|
||||||
|
|
||||||
import Popover from 'src/components/popover/popover.vue'
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
import Status from 'src/components/status/status.vue'
|
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import statusPosterService from '../../services/status_poster/status_poster.serv
|
||||||
import TabSwitcher from '../tab_switcher/tab_switcher.jsx'
|
import TabSwitcher from '../tab_switcher/tab_switcher.jsx'
|
||||||
|
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
|
|
||||||
const StickerPicker = {
|
const StickerPicker = {
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import Popover from 'components/popover/popover.vue'
|
||||||
import SelectComponent from 'components/select/select.vue'
|
import SelectComponent from 'components/select/select.vue'
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
|
import { useAdminSettingsStore } from 'src/stores/admin_settings'
|
||||||
import { useEmojiStore } from 'src/stores/emoji'
|
import { useEmojiStore } from 'src/stores/emoji'
|
||||||
import { useInterfaceStore } from 'src/stores/interface'
|
import { useInterfaceStore } from 'src/stores/interface'
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
copyToLocalPack() {
|
copyToLocalPack() {
|
||||||
this.$store.state.api.backendInteractor
|
useAdminSettingsStore()
|
||||||
.addNewEmojiFile({
|
.addNewEmojiFile({
|
||||||
packName: this.packName,
|
packName: this.packName,
|
||||||
file: this.$attrs.src,
|
file: this.$attrs.src,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
// eslint-disable-next-line no-unused
|
// eslint-disable-next-line no-unused
|
||||||
|
|
||||||
import { mapState } from 'pinia'
|
import { Fragment } from 'vue'
|
||||||
import { Fragment, h } from 'vue'
|
|
||||||
|
|
||||||
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
||||||
|
|
||||||
import './tab_switcher.scss'
|
import './tab_switcher.scss'
|
||||||
|
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
|
||||||
|
|
||||||
const findFirstUsable = (slots) => slots.findIndex((_) => _.props)
|
const findFirstUsable = (slots) => slots.findIndex((_) => _.props)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { usePostStatusStore } from 'src/stores/post_status'
|
import { usePostStatusStore } from 'src/stores/post_status'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||||
|
|
||||||
|
import { updateProfile } from 'src/api/user.js'
|
||||||
import { propsToNative } from 'src/services/attributes_helper/attributes_helper.service.js'
|
import { propsToNative } from 'src/services/attributes_helper/attributes_helper.service.js'
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
import localeService from 'src/services/locale/locale.service.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'
|
||||||
|
|
@ -597,9 +598,8 @@ export default {
|
||||||
params.header = this.newBannerFile
|
params.header = this.newBannerFile
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.state.api.backendInteractor
|
updateProfile({ params })
|
||||||
.updateProfile({ params })
|
.then(({ data: user }) => {
|
||||||
.then((user) => {
|
|
||||||
this.newFields.splice(this.newFields.length)
|
this.newFields.splice(this.newFields.length)
|
||||||
merge(this.newFields, user.fields)
|
merge(this.newFields, user.fields)
|
||||||
this.$store.commit('addNewUsers', [user])
|
this.$store.commit('addNewUsers', [user])
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
import Popover from 'src/components/popover/popover.vue'
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
import UnicodeDomainIndicator from 'src/components/unicode_domain_indicator/unicode_domain_indicator.vue'
|
import UnicodeDomainIndicator from 'src/components/unicode_domain_indicator/unicode_domain_indicator.vue'
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
|
|
||||||
import Popover from 'src/components/popover/popover.vue'
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
import UserCard from 'src/components/user_card/user_card.vue'
|
import UserCard from 'src/components/user_card/user_card.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash'
|
||||||
import { mapState } from 'pinia'
|
|
||||||
|
|
||||||
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'
|
||||||
|
|
@ -7,7 +6,6 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
import Timeline from 'src/components/timeline/timeline.vue'
|
||||||
import UserCard from 'src/components/user_card/user_card.vue'
|
import UserCard from 'src/components/user_card/user_card.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
import { get } from 'lodash'
|
|
||||||
import { mapState } from 'pinia'
|
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import List from 'src/components/list/list.vue'
|
import List from 'src/components/list/list.vue'
|
||||||
import Status from 'src/components/status/status.vue'
|
import Status from 'src/components/status/status.vue'
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,11 @@ import List from 'src/components/list/list.vue'
|
||||||
import Modal from 'src/components/modal/modal.vue'
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
import UserLink from 'src/components/user_link/user_link.vue'
|
import UserLink from 'src/components/user_link/user_link.vue'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useReportsStore } from 'src/stores/reports.js'
|
import { useReportsStore } from 'src/stores/reports.js'
|
||||||
|
|
||||||
|
import { reportUser } from 'src/api/user.js'
|
||||||
|
|
||||||
const UserReportingModal = {
|
const UserReportingModal = {
|
||||||
components: {
|
components: {
|
||||||
List,
|
List,
|
||||||
|
|
@ -70,9 +73,9 @@ const UserReportingModal = {
|
||||||
comment: this.comment,
|
comment: this.comment,
|
||||||
forward: this.forward,
|
forward: this.forward,
|
||||||
statusIds: [...this.statusIdsToReport],
|
statusIds: [...this.statusIdsToReport],
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
}
|
}
|
||||||
this.$store.state.api.backendInteractor
|
reportUser({ ...params })
|
||||||
.reportUser({ ...params })
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.processing = false
|
this.processing = false
|
||||||
this.resetState()
|
this.resetState()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
||||||
import apiService from '../../services/api/api.service.js'
|
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { fetchUser, suggestions } from 'src/api/public.js'
|
||||||
|
|
||||||
const WhoToFollow = {
|
const WhoToFollow = {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -17,21 +18,22 @@ const WhoToFollow = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showWhoToFollow(reply) {
|
showWhoToFollow(reply) {
|
||||||
reply.forEach((i) => {
|
reply.forEach(({ id }) => {
|
||||||
this.$store.state.api.backendInteractor
|
fetchUser({
|
||||||
.fetchUser({ id: i.acct })
|
id,
|
||||||
.then((externalUser) => {
|
credentials: useOAuthStore().token,
|
||||||
if (!externalUser.error) {
|
}).then(({ data: externalUser }) => {
|
||||||
this.$store.commit('addNewUsers', [externalUser])
|
if (!externalUser.error) {
|
||||||
this.users.push(externalUser)
|
this.$store.commit('addNewUsers', [externalUser])
|
||||||
}
|
this.users.push(externalUser)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getWhoToFollow() {
|
getWhoToFollow() {
|
||||||
const credentials = this.$store.state.users.currentUser.credentials
|
const credentials = useOAuthStore().token
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
apiService.suggestions({ credentials }).then((reply) => {
|
suggestions({ credentials }).then(({ data: reply }) => {
|
||||||
this.showWhoToFollow(reply)
|
this.showWhoToFollow(reply)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { shuffle } from 'lodash'
|
import { shuffle } from 'lodash'
|
||||||
|
|
||||||
import apiService from '../../services/api/api.service.js'
|
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { fetchUser, suggestions } from 'src/api/public.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'
|
||||||
|
|
||||||
function showWhoToFollow(panel, reply) {
|
function showWhoToFollow(panel, reply) {
|
||||||
|
|
@ -18,14 +18,15 @@ function showWhoToFollow(panel, reply) {
|
||||||
toFollow.img = img
|
toFollow.img = img
|
||||||
toFollow.name = name
|
toFollow.name = name
|
||||||
|
|
||||||
panel.$store.state.api.backendInteractor
|
fetchUser({
|
||||||
.fetchUser({ id: name })
|
id: name,
|
||||||
.then((externalUser) => {
|
credentials: useOAuthStore().token,
|
||||||
if (!externalUser.error) {
|
}).then(({ data: externalUser }) => {
|
||||||
panel.$store.commit('addNewUsers', [externalUser])
|
if (!externalUser.error) {
|
||||||
toFollow.id = externalUser.id
|
panel.$store.commit('addNewUsers', [externalUser])
|
||||||
}
|
toFollow.id = externalUser.id
|
||||||
})
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ function getWhoToFollow(panel) {
|
||||||
panel.usersToFollow.forEach((toFollow) => {
|
panel.usersToFollow.forEach((toFollow) => {
|
||||||
toFollow.name = 'Loading...'
|
toFollow.name = 'Loading...'
|
||||||
})
|
})
|
||||||
apiService.suggestions({ credentials }).then((reply) => {
|
suggestions({ credentials }).then(({ data: reply }) => {
|
||||||
showWhoToFollow(panel, reply)
|
showWhoToFollow(panel, reply)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import Cookies from 'js-cookie'
|
|
||||||
|
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||||
import { useI18nStore } from 'src/stores/i18n.js'
|
import { useI18nStore } from 'src/stores/i18n.js'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,27 @@
|
||||||
import { Socket } from 'phoenix'
|
import { Socket } from 'phoenix'
|
||||||
|
|
||||||
import { WSConnectionStatus } from '../services/api/api.service.js'
|
|
||||||
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
|
||||||
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useShoutStore } from 'src/stores/shout.js'
|
import { useShoutStore } from 'src/stores/shout.js'
|
||||||
|
|
||||||
|
import { fetchTimeline } from 'src/api/timelines.js'
|
||||||
|
import {
|
||||||
|
getMastodonSocketURI,
|
||||||
|
ProcessedWS,
|
||||||
|
WSConnectionStatus,
|
||||||
|
} from 'src/api/websocket.js'
|
||||||
|
import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service'
|
||||||
|
import notificationsFetcher from 'src/services/notifications_fetcher/notifications_fetcher.service.js'
|
||||||
|
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
|
||||||
|
|
||||||
const retryTimeout = (multiplier) => 1000 * multiplier
|
const retryTimeout = (multiplier) => 1000 * multiplier
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
state: {
|
state: {
|
||||||
retryMultiplier: 1,
|
retryMultiplier: 1,
|
||||||
backendInteractor: backendInteractorService(),
|
|
||||||
fetchers: {},
|
fetchers: {},
|
||||||
socket: null,
|
socket: null,
|
||||||
mastoUserSocket: null,
|
mastoUserSocket: null,
|
||||||
|
|
@ -25,9 +32,6 @@ const api = {
|
||||||
followRequestCount: (state) => state.followRequests.length,
|
followRequestCount: (state) => state.followRequests.length,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setBackendInteractor(state, backendInteractor) {
|
|
||||||
state.backendInteractor = backendInteractor
|
|
||||||
},
|
|
||||||
addFetcher(state, { fetcherName, fetcher }) {
|
addFetcher(state, { fetcherName, fetcher }) {
|
||||||
state.fetchers[fetcherName] = fetcher
|
state.fetchers[fetcherName] = fetcher
|
||||||
},
|
},
|
||||||
|
|
@ -91,9 +95,16 @@ const api = {
|
||||||
try {
|
try {
|
||||||
const { state, commit, dispatch, rootState } = store
|
const { state, commit, dispatch, rootState } = store
|
||||||
const timelineData = rootState.statuses.timelines.friends
|
const timelineData = rootState.statuses.timelines.friends
|
||||||
state.mastoUserSocket = state.backendInteractor.startUserSocket({
|
|
||||||
store,
|
const credentials = useOAuthStore().token
|
||||||
|
const url = getMastodonSocketURI({ credentials })
|
||||||
|
|
||||||
|
state.mastoUserSocket = ProcessedWS({
|
||||||
|
url,
|
||||||
|
id: 'Unified',
|
||||||
|
credentials,
|
||||||
})
|
})
|
||||||
|
|
||||||
state.mastoUserSocket.addEventListener(
|
state.mastoUserSocket.addEventListener(
|
||||||
'pleroma:authenticated',
|
'pleroma:authenticated',
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -245,7 +256,7 @@ const api = {
|
||||||
return
|
return
|
||||||
if (store.state.fetchers[timeline]) return
|
if (store.state.fetchers[timeline]) return
|
||||||
|
|
||||||
const fetcher = store.state.backendInteractor.startFetchingTimeline({
|
const fetcher = timelineFetcher.startFetching({
|
||||||
timeline,
|
timeline,
|
||||||
store,
|
store,
|
||||||
userId,
|
userId,
|
||||||
|
|
@ -253,7 +264,9 @@ const api = {
|
||||||
statusId,
|
statusId,
|
||||||
bookmarkFolderId,
|
bookmarkFolderId,
|
||||||
tag,
|
tag,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
|
|
||||||
store.commit('addFetcher', { fetcherName: timeline, fetcher })
|
store.commit('addFetcher', { fetcherName: timeline, fetcher })
|
||||||
},
|
},
|
||||||
stopFetchingTimeline(store, timeline) {
|
stopFetchingTimeline(store, timeline) {
|
||||||
|
|
@ -261,19 +274,22 @@ const api = {
|
||||||
if (!fetcher) return
|
if (!fetcher) return
|
||||||
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
|
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchTimeline(store, { timeline, ...rest }) {
|
fetchTimeline(store, { timeline, ...rest }) {
|
||||||
store.state.backendInteractor.fetchTimeline({
|
fetchTimeline({
|
||||||
store,
|
store,
|
||||||
timeline,
|
timeline,
|
||||||
...rest,
|
...rest,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
startFetchingNotifications(store) {
|
startFetchingNotifications(store) {
|
||||||
if (store.state.fetchers.notifications) return
|
if (store.state.fetchers.notifications) return
|
||||||
const fetcher = store.state.backendInteractor.startFetchingNotifications({
|
const fetcher = notificationsFetcher.startFetching({
|
||||||
store,
|
store,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
store.commit('addFetcher', { fetcherName: 'notifications', fetcher })
|
store.commit('addFetcher', { fetcherName: 'notifications', fetcher })
|
||||||
},
|
},
|
||||||
|
|
@ -282,19 +298,14 @@ const api = {
|
||||||
if (!fetcher) return
|
if (!fetcher) return
|
||||||
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
|
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
|
||||||
},
|
},
|
||||||
fetchNotifications(store, { ...rest }) {
|
|
||||||
store.state.backendInteractor.fetchNotifications({
|
|
||||||
store,
|
|
||||||
...rest,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Follow requests
|
// Follow requests
|
||||||
startFetchingFollowRequests(store) {
|
startFetchingFollowRequests(store) {
|
||||||
if (store.state.fetchers.followRequests) return
|
if (store.state.fetchers.followRequests) return
|
||||||
const fetcher = store.state.backendInteractor.startFetchingFollowRequests(
|
const fetcher = followRequestFetcher.startFetchingFollowRequests({
|
||||||
{ store },
|
store,
|
||||||
)
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
|
||||||
store.commit('addFetcher', { fetcherName: 'followRequests', fetcher })
|
store.commit('addFetcher', { fetcherName: 'followRequests', fetcher })
|
||||||
},
|
},
|
||||||
|
|
@ -303,39 +314,6 @@ const api = {
|
||||||
if (!fetcher) return
|
if (!fetcher) return
|
||||||
store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher })
|
store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher })
|
||||||
},
|
},
|
||||||
removeFollowRequest(store, request) {
|
|
||||||
const requests = store.state.followRequests.filter((it) => it !== request)
|
|
||||||
store.commit('setFollowRequests', requests)
|
|
||||||
},
|
|
||||||
|
|
||||||
// Lists
|
|
||||||
startFetchingLists(store) {
|
|
||||||
if (store.state.fetchers.lists) return
|
|
||||||
const fetcher = store.state.backendInteractor.startFetchingLists({
|
|
||||||
store,
|
|
||||||
})
|
|
||||||
store.commit('addFetcher', { fetcherName: 'lists', fetcher })
|
|
||||||
},
|
|
||||||
stopFetchingLists(store) {
|
|
||||||
const fetcher = store.state.fetchers.lists
|
|
||||||
if (!fetcher) return
|
|
||||||
store.commit('removeFetcher', { fetcherName: 'lists', fetcher })
|
|
||||||
},
|
|
||||||
|
|
||||||
// Bookmark folders
|
|
||||||
startFetchingBookmarkFolders(store) {
|
|
||||||
if (store.state.fetchers.bookmarkFolders) return
|
|
||||||
if (!useInstanceCapabilitiesStore().pleromaBookmarkFoldersAvailable)
|
|
||||||
return
|
|
||||||
const fetcher =
|
|
||||||
store.state.backendInteractor.startFetchingBookmarkFolders({ store })
|
|
||||||
store.commit('addFetcher', { fetcherName: 'bookmarkFolders', fetcher })
|
|
||||||
},
|
|
||||||
stopFetchingBookmarkFolders(store) {
|
|
||||||
const fetcher = store.state.fetchers.bookmarkFolders
|
|
||||||
if (!fetcher) return
|
|
||||||
store.commit('removeFetcher', { fetcherName: 'bookmarkFolders', fetcher })
|
|
||||||
},
|
|
||||||
|
|
||||||
// Pleroma websocket
|
// Pleroma websocket
|
||||||
setWsToken(store, token) {
|
setWsToken(store, token) {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ import {
|
||||||
} from '../services/entity_normalizer/entity_normalizer.service.js'
|
} from '../services/entity_normalizer/entity_normalizer.service.js'
|
||||||
import { promiseInterval } from '../services/promise_interval/promise_interval.js'
|
import { promiseInterval } from '../services/promise_interval/promise_interval.js'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { chats, deleteChatMessage, readChat } from 'src/api/chats.js'
|
||||||
|
|
||||||
const emptyChatList = () => ({
|
const emptyChatList = () => ({
|
||||||
data: [],
|
data: [],
|
||||||
idStore: {},
|
idStore: {},
|
||||||
|
|
@ -36,7 +40,7 @@ const unreadChatCount = (state) => {
|
||||||
return sumBy(state.chatList.data, 'unread')
|
return sumBy(state.chatList.data, 'unread')
|
||||||
}
|
}
|
||||||
|
|
||||||
const chats = {
|
const chatsModule = {
|
||||||
state: { ...defaultState },
|
state: { ...defaultState },
|
||||||
getters: {
|
getters: {
|
||||||
currentChat: (state) => state.openedChats[state.currentChatId],
|
currentChat: (state) => state.openedChats[state.currentChatId],
|
||||||
|
|
@ -51,7 +55,6 @@ const chats = {
|
||||||
// Chat list
|
// Chat list
|
||||||
startFetchingChats({ dispatch, commit }) {
|
startFetchingChats({ dispatch, commit }) {
|
||||||
const fetcher = () => dispatch('fetchChats', { latest: true })
|
const fetcher = () => dispatch('fetchChats', { latest: true })
|
||||||
fetcher()
|
|
||||||
commit('setChatListFetcher', {
|
commit('setChatListFetcher', {
|
||||||
fetcher: () => promiseInterval(fetcher, 5000),
|
fetcher: () => promiseInterval(fetcher, 5000),
|
||||||
})
|
})
|
||||||
|
|
@ -60,8 +63,10 @@ const chats = {
|
||||||
commit('setChatListFetcher', { fetcher: undefined })
|
commit('setChatListFetcher', { fetcher: undefined })
|
||||||
},
|
},
|
||||||
fetchChats({ dispatch, rootState }) {
|
fetchChats({ dispatch, rootState }) {
|
||||||
return rootState.api.backendInteractor.chats().then(({ chats }) => {
|
return chats({
|
||||||
dispatch('addNewChats', { chats })
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ chatList }) => {
|
||||||
|
dispatch('addNewChats', { chats: chatList })
|
||||||
return chats
|
return chats
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -113,11 +118,18 @@ const chats = {
|
||||||
commit('readChat', { id, lastReadId })
|
commit('readChat', { id, lastReadId })
|
||||||
|
|
||||||
if (isNewMessage) {
|
if (isNewMessage) {
|
||||||
rootState.api.backendInteractor.readChat({ id, lastReadId })
|
readChat({
|
||||||
|
id,
|
||||||
|
lastReadId,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteChatMessage({ rootState, commit }, value) {
|
deleteChatMessage({ rootState, commit }, value) {
|
||||||
rootState.api.backendInteractor.deleteChatMessage(value)
|
deleteChatMessage({
|
||||||
|
...value,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
commit('deleteChatMessage', { commit, ...value })
|
commit('deleteChatMessage', { commit, ...value })
|
||||||
},
|
},
|
||||||
resetChats({ commit, dispatch }) {
|
resetChats({ commit, dispatch }) {
|
||||||
|
|
@ -262,4 +274,4 @@ const chats = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default chats
|
export default chatsModule
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { get, set } from 'lodash'
|
import { get } from 'lodash'
|
||||||
|
|
||||||
const browserLocale = (navigator.language || 'en').split('-')[0]
|
const browserLocale = (navigator.language || 'en').split('-')[0]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import apiService from '../services/api/api.service.js'
|
|
||||||
import {
|
import {
|
||||||
closeAllDesktopNotifications,
|
closeAllDesktopNotifications,
|
||||||
closeDesktopNotification,
|
closeDesktopNotification,
|
||||||
|
|
@ -11,9 +10,12 @@ import {
|
||||||
|
|
||||||
import { useI18nStore } from 'src/stores/i18n.js'
|
import { useI18nStore } from 'src/stores/i18n.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useReportsStore } from 'src/stores/reports.js'
|
import { useReportsStore } from 'src/stores/reports.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
|
|
||||||
|
import { dismissNotification, markNotificationsAsSeen } from 'src/api/user.js'
|
||||||
|
|
||||||
const emptyNotifications = () => ({
|
const emptyNotifications = () => ({
|
||||||
desktopNotificationSilence: true,
|
desktopNotificationSilence: true,
|
||||||
maxId: 0,
|
maxId: 0,
|
||||||
|
|
@ -154,33 +156,32 @@ export const notifications = {
|
||||||
},
|
},
|
||||||
markNotificationsAsSeen({ rootState, state, commit }) {
|
markNotificationsAsSeen({ rootState, state, commit }) {
|
||||||
commit('markNotificationsAsSeen')
|
commit('markNotificationsAsSeen')
|
||||||
apiService
|
markNotificationsAsSeen({
|
||||||
.markNotificationsAsSeen({
|
id: state.maxId,
|
||||||
id: state.maxId,
|
credentials: rootState.users.currentUser.credentials,
|
||||||
credentials: rootState.users.currentUser.credentials,
|
}).then(() => {
|
||||||
})
|
closeAllDesktopNotifications(rootState)
|
||||||
.then(() => {
|
})
|
||||||
closeAllDesktopNotifications(rootState)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
markSingleNotificationAsSeen({ rootState, commit }, { id }) {
|
markSingleNotificationAsSeen({ rootState, commit }, { id }) {
|
||||||
commit('markSingleNotificationAsSeen', { id })
|
commit('markSingleNotificationAsSeen', { id })
|
||||||
apiService
|
markNotificationsAsSeen({
|
||||||
.markNotificationsAsSeen({
|
single: true,
|
||||||
single: true,
|
id,
|
||||||
id,
|
credentials: rootState.users.currentUser.credentials,
|
||||||
credentials: rootState.users.currentUser.credentials,
|
}).then(() => {
|
||||||
})
|
closeDesktopNotification(rootState, { id })
|
||||||
.then(() => {
|
})
|
||||||
closeDesktopNotification(rootState, { id })
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
dismissNotificationLocal({ commit }, { id }) {
|
dismissNotificationLocal({ commit }, { id }) {
|
||||||
commit('dismissNotification', { id })
|
commit('dismissNotification', { id })
|
||||||
},
|
},
|
||||||
dismissNotification({ rootState, commit }, { id }) {
|
dismissNotification({ rootState, commit }, { id }) {
|
||||||
commit('dismissNotification', { id })
|
commit('dismissNotification', { id })
|
||||||
rootState.api.backendInteractor.dismissNotification({ id })
|
dismissNotification({
|
||||||
|
id,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
updateNotification({ commit }, { id, updater }) {
|
updateNotification({ commit }, { id, updater }) {
|
||||||
commit('updateNotification', { id, updater })
|
commit('updateNotification', { id, updater })
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,34 @@
|
||||||
import { get, set } from 'lodash'
|
import { get, set } from 'lodash'
|
||||||
|
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import { updateNotificationSettings, updateProfile } from 'src/api/user.js'
|
||||||
|
|
||||||
const defaultApi = ({ rootState, commit }, { path, value }) => {
|
const defaultApi = ({ rootState, commit }, { path, value }) => {
|
||||||
const params = {}
|
const params = {}
|
||||||
set(params, path, value)
|
set(params, path, value)
|
||||||
return rootState.api.backendInteractor
|
return updateProfile({
|
||||||
.updateProfile({ params })
|
params,
|
||||||
.then((result) => {
|
credentials: useOAuthStore().token,
|
||||||
commit('addNewUsers', [result])
|
}).then(({ data: result }) => {
|
||||||
commit('setCurrentUser', result)
|
commit('addNewUsers', [result])
|
||||||
})
|
commit('setCurrentUser', result)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => {
|
const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => {
|
||||||
const settings = {}
|
const settings = {}
|
||||||
set(settings, path, value)
|
set(settings, path, value)
|
||||||
return rootState.api.backendInteractor
|
return updateNotificationSettings({
|
||||||
.updateNotificationSettings({ settings })
|
settings,
|
||||||
.then((result) => {
|
credentials: useOAuthStore().token,
|
||||||
if (result.status === 'success') {
|
}).then(({ data: result }) => {
|
||||||
commit('confirmProfileOption', { name, value })
|
if (result.status === 'success') {
|
||||||
} else {
|
commit('confirmProfileOption', { name, value })
|
||||||
commit('confirmProfileOption', { name, value: oldValue })
|
} else {
|
||||||
}
|
commit('confirmProfileOption', { name, value: oldValue })
|
||||||
})
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,36 @@ import {
|
||||||
slice,
|
slice,
|
||||||
} from 'lodash'
|
} from 'lodash'
|
||||||
|
|
||||||
import apiService from '../services/api/api.service.js'
|
|
||||||
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchEmojiReactions,
|
||||||
|
fetchFavoritedByUsers,
|
||||||
|
fetchPinnedStatuses,
|
||||||
|
fetchRebloggedByUsers,
|
||||||
|
fetchScrobbles,
|
||||||
|
fetchStatus,
|
||||||
|
fetchStatusHistory,
|
||||||
|
fetchStatusSource,
|
||||||
|
search2,
|
||||||
|
} from 'src/api/public.js'
|
||||||
|
import {
|
||||||
|
bookmarkStatus,
|
||||||
|
deleteStatus,
|
||||||
|
favorite,
|
||||||
|
muteConversation,
|
||||||
|
pinOwnStatus,
|
||||||
|
reactWithEmoji,
|
||||||
|
retweet,
|
||||||
|
unbookmarkStatus,
|
||||||
|
unfavorite,
|
||||||
|
unmuteConversation,
|
||||||
|
unpinOwnStatus,
|
||||||
|
unreactWithEmoji,
|
||||||
|
unretweet,
|
||||||
|
} from 'src/api/user.js'
|
||||||
|
|
||||||
const emptyTl = (userId = 0) => ({
|
const emptyTl = (userId = 0) => ({
|
||||||
statuses: [],
|
statuses: [],
|
||||||
|
|
@ -131,9 +157,8 @@ const getLatestScrobble = (state, user) => {
|
||||||
|
|
||||||
state.scrobblesNextFetch[user.id] = Date.now() + 24 * 60 * 60 * 1000
|
state.scrobblesNextFetch[user.id] = Date.now() + 24 * 60 * 60 * 1000
|
||||||
if (!scrobblesSupport) return
|
if (!scrobblesSupport) return
|
||||||
apiService
|
fetchScrobbles({ accountId: user.id })
|
||||||
.fetchScrobbles({ accountId: user.id })
|
.then(({ data: scrobbles }) => {
|
||||||
.then((scrobbles) => {
|
|
||||||
if (scrobbles?.error) {
|
if (scrobbles?.error) {
|
||||||
useInstanceCapabilitiesStore().set('pleromaScrobblesAvailable', false)
|
useInstanceCapabilitiesStore().set('pleromaScrobblesAvailable', false)
|
||||||
return
|
return
|
||||||
|
|
@ -602,25 +627,24 @@ const statuses = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchStatus({ rootState, dispatch }, id) {
|
fetchStatus({ rootState, dispatch }, id) {
|
||||||
return rootState.api.backendInteractor
|
return fetchStatus({ id }).then(({ data: status }) =>
|
||||||
.fetchStatus({ id })
|
dispatch('addNewStatuses', { statuses: [status] }),
|
||||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
)
|
||||||
},
|
},
|
||||||
fetchStatusSource({ rootState }, status) {
|
fetchStatusSource({ rootState }, status) {
|
||||||
return apiService.fetchStatusSource({
|
return fetchStatusSource({
|
||||||
id: status.id,
|
id: status.id,
|
||||||
credentials: rootState.users.currentUser.credentials,
|
credentials: useOAuthStore().token,
|
||||||
})
|
}).then(({ data }) => data)
|
||||||
},
|
},
|
||||||
fetchStatusHistory(_, status) {
|
fetchStatusHistory(_, status) {
|
||||||
return apiService.fetchStatusHistory({ status })
|
return fetchStatusHistory({ status }).then(({ data }) => data)
|
||||||
},
|
},
|
||||||
deleteStatus({ rootState, commit }, status) {
|
deleteStatus({ rootState, commit }, status) {
|
||||||
apiService
|
deleteStatus({
|
||||||
.deleteStatus({
|
id: status.id,
|
||||||
id: status.id,
|
credentials: useOAuthStore().token,
|
||||||
credentials: rootState.users.currentUser.credentials,
|
})
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
commit('setDeleted', { status })
|
commit('setDeleted', { status })
|
||||||
})
|
})
|
||||||
|
|
@ -643,99 +667,115 @@ const statuses = {
|
||||||
favorite({ rootState, commit }, status) {
|
favorite({ rootState, commit }, status) {
|
||||||
// Optimistic favoriting...
|
// Optimistic favoriting...
|
||||||
commit('setFavorited', { status, value: true })
|
commit('setFavorited', { status, value: true })
|
||||||
rootState.api.backendInteractor
|
favorite({
|
||||||
.favorite({ id: status.id })
|
id: status.id,
|
||||||
.then((status) =>
|
credentials: useOAuthStore().token,
|
||||||
commit('setFavoritedConfirm', {
|
}).then(({ data: status }) =>
|
||||||
status,
|
commit('setFavoritedConfirm', {
|
||||||
user: rootState.users.currentUser,
|
status,
|
||||||
}),
|
user: rootState.users.currentUser,
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
unfavorite({ rootState, commit }, status) {
|
unfavorite({ rootState, commit }, status) {
|
||||||
// Optimistic unfavoriting...
|
// Optimistic unfavoriting...
|
||||||
commit('setFavorited', { status, value: false })
|
commit('setFavorited', { status, value: false })
|
||||||
rootState.api.backendInteractor
|
unfavorite({
|
||||||
.unfavorite({ id: status.id })
|
id: status.id,
|
||||||
.then((status) =>
|
credentials: useOAuthStore().token,
|
||||||
commit('setFavoritedConfirm', {
|
}).then(({ data: status }) =>
|
||||||
status,
|
commit('setFavoritedConfirm', {
|
||||||
user: rootState.users.currentUser,
|
status,
|
||||||
}),
|
user: rootState.users.currentUser,
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
fetchPinnedStatuses({ rootState, dispatch }, userId) {
|
fetchPinnedStatuses({ rootState, dispatch }, userId) {
|
||||||
rootState.api.backendInteractor
|
fetchPinnedStatuses({
|
||||||
.fetchPinnedStatuses({ id: userId })
|
id: userId,
|
||||||
.then((statuses) =>
|
credentials: useOAuthStore().token,
|
||||||
dispatch('addNewStatuses', {
|
}).then(({ data: statuses }) =>
|
||||||
statuses,
|
dispatch('addNewStatuses', {
|
||||||
timeline: 'user',
|
statuses,
|
||||||
userId,
|
timeline: 'user',
|
||||||
showImmediately: true,
|
userId,
|
||||||
noIdUpdate: true,
|
showImmediately: true,
|
||||||
}),
|
noIdUpdate: true,
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
pinStatus({ rootState, dispatch }, statusId) {
|
pinStatus({ rootState, dispatch }, statusId) {
|
||||||
return rootState.api.backendInteractor
|
return pinOwnStatus({
|
||||||
.pinOwnStatus({ id: statusId })
|
id: statusId,
|
||||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: status }) =>
|
||||||
|
dispatch('addNewStatuses', { statuses: [status] }),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
unpinStatus({ rootState, dispatch }, statusId) {
|
unpinStatus({ rootState, dispatch }, statusId) {
|
||||||
return rootState.api.backendInteractor
|
return unpinOwnStatus({
|
||||||
.unpinOwnStatus({ id: statusId })
|
id: statusId,
|
||||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: status }) =>
|
||||||
|
dispatch('addNewStatuses', { statuses: [status] }),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
muteConversation({ rootState, commit }, { id: statusId }) {
|
muteConversation({ rootState, commit }, { id: statusId }) {
|
||||||
return rootState.api.backendInteractor
|
return muteConversation({
|
||||||
.muteConversation({ id: statusId })
|
id: statusId,
|
||||||
.then((status) => commit('setMutedStatus', status))
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: status }) => commit('setMutedStatus', status))
|
||||||
},
|
},
|
||||||
unmuteConversation({ rootState, commit }, { id: statusId }) {
|
unmuteConversation({ rootState, commit }, { id: statusId }) {
|
||||||
return rootState.api.backendInteractor
|
return unmuteConversation({
|
||||||
.unmuteConversation({ id: statusId })
|
id: statusId,
|
||||||
.then((status) => commit('setMutedStatus', status))
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: status }) => commit('setMutedStatus', status))
|
||||||
},
|
},
|
||||||
retweet({ rootState, commit }, status) {
|
retweet({ rootState, commit }, status) {
|
||||||
// Optimistic retweeting...
|
// Optimistic retweeting...
|
||||||
commit('setRetweeted', { status, value: true })
|
commit('setRetweeted', { status, value: true })
|
||||||
rootState.api.backendInteractor
|
retweet({
|
||||||
.retweet({ id: status.id })
|
id: status.id,
|
||||||
.then((status) =>
|
credentials: useOAuthStore().token,
|
||||||
commit('setRetweetedConfirm', {
|
}).then(({ data: status }) =>
|
||||||
status: status.retweeted_status,
|
commit('setRetweetedConfirm', {
|
||||||
user: rootState.users.currentUser,
|
status: status.retweeted_status,
|
||||||
}),
|
user: rootState.users.currentUser,
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
unretweet({ rootState, commit }, status) {
|
unretweet({ rootState, commit }, status) {
|
||||||
// Optimistic unretweeting...
|
// Optimistic unretweeting...
|
||||||
commit('setRetweeted', { status, value: false })
|
commit('setRetweeted', { status, value: false })
|
||||||
rootState.api.backendInteractor
|
unretweet({
|
||||||
.unretweet({ id: status.id })
|
id: status.id,
|
||||||
.then((status) =>
|
credentials: useOAuthStore().token,
|
||||||
commit('setRetweetedConfirm', {
|
}).then(({ data: status }) =>
|
||||||
status,
|
commit('setRetweetedConfirm', {
|
||||||
user: rootState.users.currentUser,
|
status,
|
||||||
}),
|
user: rootState.users.currentUser,
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
bookmark({ rootState, commit }, status) {
|
bookmark({ rootState, commit }, status) {
|
||||||
commit('setBookmarked', { status, value: true })
|
commit('setBookmarked', { status, value: true })
|
||||||
rootState.api.backendInteractor
|
bookmarkStatus({
|
||||||
.bookmarkStatus({ id: status.id, folder_id: status.bookmark_folder_id })
|
id: status.id,
|
||||||
.then((status) => {
|
folder_id: status.bookmark_folder_id,
|
||||||
commit('setBookmarkedConfirm', { status })
|
credentials: useOAuthStore().token,
|
||||||
})
|
}).then(({ data: status }) => {
|
||||||
|
commit('setBookmarkedConfirm', { status })
|
||||||
|
})
|
||||||
},
|
},
|
||||||
unbookmark({ rootState, commit }, status) {
|
unbookmark({ rootState, commit }, status) {
|
||||||
commit('setBookmarked', { status, value: false })
|
commit('setBookmarked', { status, value: false })
|
||||||
rootState.api.backendInteractor
|
unbookmarkStatus({
|
||||||
.unbookmarkStatus({ id: status.id })
|
id: status.id,
|
||||||
.then((status) => {
|
credentials: useOAuthStore().token,
|
||||||
commit('setBookmarkedConfirm', { status })
|
}).then(({ data: status }) => {
|
||||||
})
|
commit('setBookmarkedConfirm', { status })
|
||||||
|
})
|
||||||
},
|
},
|
||||||
queueFlush({ commit }, { timeline, id }) {
|
queueFlush({ commit }, { timeline, id }) {
|
||||||
commit('queueFlush', { timeline, id })
|
commit('queueFlush', { timeline, id })
|
||||||
|
|
@ -745,8 +785,14 @@ const statuses = {
|
||||||
},
|
},
|
||||||
fetchFavsAndRepeats({ rootState, commit }, id) {
|
fetchFavsAndRepeats({ rootState, commit }, id) {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
rootState.api.backendInteractor.fetchFavoritedByUsers({ id }),
|
fetchFavoritedByUsers({
|
||||||
rootState.api.backendInteractor.fetchRebloggedByUsers({ id }),
|
id,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data }) => data),
|
||||||
|
fetchRebloggedByUsers({
|
||||||
|
id,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data }) => data),
|
||||||
]).then(([favoritedByUsers, rebloggedByUsers]) => {
|
]).then(([favoritedByUsers, rebloggedByUsers]) => {
|
||||||
commit('addFavs', {
|
commit('addFavs', {
|
||||||
id,
|
id,
|
||||||
|
|
@ -765,7 +811,11 @@ const statuses = {
|
||||||
if (!currentUser) return
|
if (!currentUser) return
|
||||||
|
|
||||||
commit('addOwnReaction', { id, emoji, currentUser })
|
commit('addOwnReaction', { id, emoji, currentUser })
|
||||||
rootState.api.backendInteractor.reactWithEmoji({ id, emoji }).then(() => {
|
reactWithEmoji({
|
||||||
|
id,
|
||||||
|
emoji,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(() => {
|
||||||
dispatch('fetchEmojiReactionsBy', id)
|
dispatch('fetchEmojiReactionsBy', id)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -774,59 +824,70 @@ const statuses = {
|
||||||
if (!currentUser) return
|
if (!currentUser) return
|
||||||
|
|
||||||
commit('removeOwnReaction', { id, emoji, currentUser })
|
commit('removeOwnReaction', { id, emoji, currentUser })
|
||||||
rootState.api.backendInteractor
|
unreactWithEmoji({
|
||||||
.unreactWithEmoji({ id, emoji })
|
id,
|
||||||
.then(() => {
|
emoji,
|
||||||
dispatch('fetchEmojiReactionsBy', id)
|
currentUser: rootState.users.currentUser,
|
||||||
})
|
}).then(() => {
|
||||||
|
dispatch('fetchEmojiReactionsBy', id)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
fetchEmojiReactionsBy({ rootState, commit }, id) {
|
fetchEmojiReactionsBy({ rootState, commit }, id) {
|
||||||
return rootState.api.backendInteractor
|
return fetchEmojiReactions({
|
||||||
.fetchEmojiReactions({ id })
|
id,
|
||||||
.then((emojiReactions) => {
|
credentials: useOAuthStore().token,
|
||||||
commit('addEmojiReactionsBy', {
|
}).then(({ data: emojiReactions }) => {
|
||||||
id,
|
commit('addEmojiReactionsBy', {
|
||||||
emojiReactions,
|
id,
|
||||||
currentUser: rootState.users.currentUser,
|
emojiReactions,
|
||||||
})
|
currentUser: rootState.users.currentUser,
|
||||||
})
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
fetchFavs({ rootState, commit }, id) {
|
fetchFavs({ rootState, commit }, id) {
|
||||||
rootState.api.backendInteractor
|
fetchFavoritedByUsers({
|
||||||
.fetchFavoritedByUsers({ id })
|
id,
|
||||||
.then((favoritedByUsers) =>
|
credentials: useOAuthStore().token,
|
||||||
commit('addFavs', {
|
}).then(({ data: favoritedByUsers }) =>
|
||||||
id,
|
commit('addFavs', {
|
||||||
favoritedByUsers,
|
id,
|
||||||
currentUser: rootState.users.currentUser,
|
favoritedByUsers,
|
||||||
}),
|
currentUser: rootState.users.currentUser,
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
fetchRepeats({ rootState, commit }, id) {
|
fetchRepeats({ rootState, commit }, id) {
|
||||||
rootState.api.backendInteractor
|
fetchRebloggedByUsers({
|
||||||
.fetchRebloggedByUsers({ id })
|
id,
|
||||||
.then((rebloggedByUsers) =>
|
credentials: useOAuthStore().token,
|
||||||
commit('addRepeats', {
|
}).then(({ data: rebloggedByUsers }) =>
|
||||||
id,
|
commit('addRepeats', {
|
||||||
rebloggedByUsers,
|
id,
|
||||||
currentUser: rootState.users.currentUser,
|
rebloggedByUsers,
|
||||||
}),
|
currentUser: rootState.users.currentUser,
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
search(store, { q, resolve, limit, offset, following, type }) {
|
search(store, { q, resolve, limit, offset, following, type }) {
|
||||||
return store.rootState.api.backendInteractor
|
return search2({
|
||||||
.search2({ q, resolve, limit, offset, following, type })
|
q,
|
||||||
.then((data) => {
|
resolve,
|
||||||
store.commit('addNewUsers', data.accounts)
|
limit,
|
||||||
store.commit(
|
offset,
|
||||||
'addNewUsers',
|
following,
|
||||||
data.statuses.map((s) => s.user).filter((u) => u),
|
type,
|
||||||
)
|
credentials: useOAuthStore().token,
|
||||||
data.statuses = store.commit('addNewStatuses', {
|
}).then(({ data }) => {
|
||||||
statuses: data.statuses,
|
store.commit('addNewUsers', data.accounts)
|
||||||
})
|
store.commit(
|
||||||
return data
|
'addNewUsers',
|
||||||
|
data.statuses.map((s) => s.user).filter((u) => u),
|
||||||
|
)
|
||||||
|
data.statuses = store.commit('addNewStatuses', {
|
||||||
|
statuses: data.statuses,
|
||||||
})
|
})
|
||||||
|
return data
|
||||||
|
})
|
||||||
},
|
},
|
||||||
setVirtualHeight({ commit }, { statusId, height }) {
|
setVirtualHeight({ commit }, { statusId, height }) {
|
||||||
commit('setVirtualHeight', { statusId, height })
|
commit('setVirtualHeight', { statusId, height })
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import Cookies from 'js-cookie'
|
||||||
import {
|
import {
|
||||||
compact,
|
compact,
|
||||||
concat,
|
concat,
|
||||||
|
|
@ -9,9 +10,6 @@ import {
|
||||||
uniq,
|
uniq,
|
||||||
} from 'lodash'
|
} from 'lodash'
|
||||||
|
|
||||||
import apiService from '../services/api/api.service.js'
|
|
||||||
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
|
||||||
import oauthApi from '../services/new_api/oauth.js'
|
|
||||||
import {
|
import {
|
||||||
registerPushNotifications,
|
registerPushNotifications,
|
||||||
unregisterPushNotifications,
|
unregisterPushNotifications,
|
||||||
|
|
@ -21,15 +19,42 @@ import {
|
||||||
windowWidth,
|
windowWidth,
|
||||||
} from '../services/window_utils/window_utils'
|
} from '../services/window_utils/window_utils'
|
||||||
|
|
||||||
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
|
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js'
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useListsStore } from 'src/stores/lists.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||||
|
|
||||||
|
import { revokeToken } from 'src/api/oauth.js'
|
||||||
|
import {
|
||||||
|
fetchFollowers,
|
||||||
|
fetchFriends,
|
||||||
|
fetchUser,
|
||||||
|
fetchUserByName,
|
||||||
|
getCaptcha,
|
||||||
|
register,
|
||||||
|
searchUsers,
|
||||||
|
verifyCredentials,
|
||||||
|
} from 'src/api/public.js'
|
||||||
|
import {
|
||||||
|
blockUser as apiBlockUser,
|
||||||
|
muteUser as apiMuteUser,
|
||||||
|
unblockUser as apiUnblockUser,
|
||||||
|
unmuteUser as apiUnmuteUser,
|
||||||
|
fetchBlocks,
|
||||||
|
fetchDomainMutes,
|
||||||
|
fetchMutes,
|
||||||
|
fetchUserInLists,
|
||||||
|
fetchUserRelationship,
|
||||||
|
followUser,
|
||||||
|
} from 'src/api/user.js'
|
||||||
|
|
||||||
// TODO: Unify with mergeOrAdd in statuses.js
|
// TODO: Unify with mergeOrAdd in statuses.js
|
||||||
export const mergeOrAdd = (arr, obj, item) => {
|
export const mergeOrAdd = (arr, obj, item) => {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
|
@ -72,43 +97,35 @@ const blockUser = (store, args) => {
|
||||||
store.commit('updateUserRelationship', [predictedRelationship])
|
store.commit('updateUserRelationship', [predictedRelationship])
|
||||||
store.commit('addBlockId', id)
|
store.commit('addBlockId', id)
|
||||||
|
|
||||||
return store.rootState.api.backendInteractor
|
return apiBlockUser({ id, expiresIn }).then(({ data: relationship }) => {
|
||||||
.blockUser({ id, expiresIn })
|
store.commit('updateUserRelationship', [relationship])
|
||||||
.then((relationship) => {
|
store.commit('addBlockId', id)
|
||||||
store.commit('updateUserRelationship', [relationship])
|
|
||||||
store.commit('addBlockId', id)
|
|
||||||
|
|
||||||
store.commit('removeStatus', { timeline: 'friends', userId: id })
|
store.commit('removeStatus', { timeline: 'friends', userId: id })
|
||||||
store.commit('removeStatus', { timeline: 'public', userId: id })
|
store.commit('removeStatus', { timeline: 'public', userId: id })
|
||||||
store.commit('removeStatus', {
|
store.commit('removeStatus', {
|
||||||
timeline: 'publicAndExternal',
|
timeline: 'publicAndExternal',
|
||||||
userId: id,
|
userId: id,
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const unblockUser = (store, id) => {
|
const unblockUser = (store, id) => {
|
||||||
return store.rootState.api.backendInteractor
|
return apiUnblockUser({ id }).then(({ data: relationship }) =>
|
||||||
.unblockUser({ id })
|
store.commit('updateUserRelationship', [relationship]),
|
||||||
.then((relationship) =>
|
)
|
||||||
store.commit('updateUserRelationship', [relationship]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeUserFromFollowers = (store, id) => {
|
const removeUserFromFollowers = (store, id) => {
|
||||||
return store.rootState.api.backendInteractor
|
return removeUserFromFollowers({ id }).then((relationship) =>
|
||||||
.removeUserFromFollowers({ id })
|
store.commit('updateUserRelationship', [relationship]),
|
||||||
.then((relationship) =>
|
)
|
||||||
store.commit('updateUserRelationship', [relationship]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const editUserNote = (store, { id, comment }) => {
|
const editUserNote = (store, { id, comment }) => {
|
||||||
return store.rootState.api.backendInteractor
|
return editUserNote({ id, comment }).then((relationship) =>
|
||||||
.editUserNote({ id, comment })
|
store.commit('updateUserRelationship', [relationship]),
|
||||||
.then((relationship) =>
|
)
|
||||||
store.commit('updateUserRelationship', [relationship]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const muteUser = (store, args) => {
|
const muteUser = (store, args) => {
|
||||||
|
|
@ -119,12 +136,14 @@ const muteUser = (store, args) => {
|
||||||
store.commit('updateUserRelationship', [predictedRelationship])
|
store.commit('updateUserRelationship', [predictedRelationship])
|
||||||
store.commit('addMuteId', id)
|
store.commit('addMuteId', id)
|
||||||
|
|
||||||
return store.rootState.api.backendInteractor
|
return apiMuteUser({
|
||||||
.muteUser({ id, expiresIn })
|
id,
|
||||||
.then((relationship) => {
|
expiresIn,
|
||||||
store.commit('updateUserRelationship', [relationship])
|
credentials: useOAuthStore().token,
|
||||||
store.commit('addMuteId', id)
|
}).then(({ data: relationship }) => {
|
||||||
})
|
store.commit('updateUserRelationship', [relationship])
|
||||||
|
store.commit('addMuteId', id)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const unmuteUser = (store, id) => {
|
const unmuteUser = (store, id) => {
|
||||||
|
|
@ -132,39 +151,43 @@ const unmuteUser = (store, id) => {
|
||||||
predictedRelationship.muting = false
|
predictedRelationship.muting = false
|
||||||
store.commit('updateUserRelationship', [predictedRelationship])
|
store.commit('updateUserRelationship', [predictedRelationship])
|
||||||
|
|
||||||
return store.rootState.api.backendInteractor
|
return apiUnmuteUser({ id }).then(({ data: relationship }) =>
|
||||||
.unmuteUser({ id })
|
store.commit('updateUserRelationship', [relationship]),
|
||||||
.then((relationship) =>
|
)
|
||||||
store.commit('updateUserRelationship', [relationship]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideReblogs = (store, userId) => {
|
const hideReblogs = (store, userId) => {
|
||||||
return store.rootState.api.backendInteractor
|
return followUser({
|
||||||
.followUser({ id: userId, reblogs: false })
|
id: userId,
|
||||||
.then((relationship) => {
|
reblogs: false,
|
||||||
store.commit('updateUserRelationship', [relationship])
|
credentials: useOAuthStore().token,
|
||||||
})
|
}).then(({ data: relationship }) =>
|
||||||
|
store.commit('updateUserRelationship', [relationship]),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const showReblogs = (store, userId) => {
|
const showReblogs = (store, userId) => {
|
||||||
return store.rootState.api.backendInteractor
|
return followUser({
|
||||||
.followUser({ id: userId, reblogs: true })
|
id: userId,
|
||||||
.then((relationship) =>
|
reblogs: true,
|
||||||
store.commit('updateUserRelationship', [relationship]),
|
credentials: useOAuthStore().token,
|
||||||
)
|
}).then(({ data: relationship }) =>
|
||||||
|
store.commit('updateUserRelationship', [relationship]),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const muteDomain = (store, domain) => {
|
const muteDomain = (store, domain) => {
|
||||||
return store.rootState.api.backendInteractor
|
return muteDomain({
|
||||||
.muteDomain({ domain })
|
domain,
|
||||||
.then(() => store.commit('addDomainMute', domain))
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(() => store.commit('addDomainMute', domain))
|
||||||
}
|
}
|
||||||
|
|
||||||
const unmuteDomain = (store, domain) => {
|
const unmuteDomain = (store, domain) => {
|
||||||
return store.rootState.api.backendInteractor
|
return unmuteDomain({
|
||||||
.unmuteDomain({ domain })
|
domain,
|
||||||
.then(() => store.commit('removeDomainMute', domain))
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(() => store.commit('removeDomainMute', domain))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
|
|
@ -385,55 +408,70 @@ const users = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchUser(store, id) {
|
fetchUser(store, id) {
|
||||||
return store.rootState.api.backendInteractor
|
return fetchUser({
|
||||||
.fetchUser({ id })
|
id,
|
||||||
.then((user) => {
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
.then(({ data: user }) => {
|
||||||
store.commit('addNewUsers', [user])
|
store.commit('addNewUsers', [user])
|
||||||
return user
|
return user
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.statusCode === 404) {
|
||||||
|
console.warn(`User ${id} not found`)
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
fetchUserByName(store, name) {
|
fetchUserByName(store, name) {
|
||||||
return store.rootState.api.backendInteractor
|
return fetchUserByName({
|
||||||
.fetchUserByName({ name })
|
name,
|
||||||
.then((user) => {
|
credentials: useOAuthStore().token,
|
||||||
store.commit('addNewUsers', [user])
|
}).then(({ data: user }) => {
|
||||||
return user
|
store.commit('addNewUsers', [user])
|
||||||
})
|
return user
|
||||||
|
})
|
||||||
},
|
},
|
||||||
fetchUserRelationship(store, id) {
|
fetchUserRelationship(store, id) {
|
||||||
if (store.state.currentUser) {
|
if (store.state.currentUser) {
|
||||||
store.rootState.api.backendInteractor
|
fetchUserRelationship({
|
||||||
.fetchUserRelationship({ id })
|
id,
|
||||||
.then((relationships) =>
|
credentials: useOAuthStore().token,
|
||||||
store.commit('updateUserRelationship', relationships),
|
}).then(({ data: relationships }) =>
|
||||||
)
|
store.commit('updateUserRelationship', relationships),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchUserInLists(store, id) {
|
fetchUserInLists(store, id) {
|
||||||
if (store.state.currentUser) {
|
if (store.state.currentUser) {
|
||||||
store.rootState.api.backendInteractor
|
fetchUserInLists({
|
||||||
.fetchUserInLists({ id })
|
id,
|
||||||
.then((inLists) => store.commit('updateUserInLists', { id, inLists }))
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data: inLists }) =>
|
||||||
|
store.commit('updateUserInLists', { id, inLists }),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchBlocks(store, args) {
|
fetchBlocks(store, args) {
|
||||||
const { reset } = args || {}
|
const { reset } = args || {}
|
||||||
|
|
||||||
const maxId = store.state.currentUser.blockIdsMaxId
|
const maxId = store.state.currentUser.blockIdsMaxId
|
||||||
return store.rootState.api.backendInteractor
|
return fetchBlocks({
|
||||||
.fetchBlocks({ maxId })
|
maxId,
|
||||||
.then((blocks) => {
|
credentials: useOAuthStore().token,
|
||||||
if (reset) {
|
}).then(({ data: blocks }) => {
|
||||||
store.commit('saveBlockIds', map(blocks, 'id'))
|
if (reset) {
|
||||||
} else {
|
store.commit('saveBlockIds', map(blocks, 'id'))
|
||||||
map(blocks, 'id').map((id) => store.commit('addBlockId', id))
|
} else {
|
||||||
}
|
map(blocks, 'id').map((id) => store.commit('addBlockId', id))
|
||||||
if (blocks.length) {
|
}
|
||||||
store.commit('setBlockIdsMaxId', last(blocks).id)
|
if (blocks.length) {
|
||||||
}
|
store.commit('setBlockIdsMaxId', last(blocks).id)
|
||||||
store.commit('addNewUsers', blocks)
|
}
|
||||||
return blocks
|
store.commit('addNewUsers', blocks)
|
||||||
})
|
return blocks
|
||||||
|
})
|
||||||
},
|
},
|
||||||
blockUser(store, data) {
|
blockUser(store, data) {
|
||||||
return blockUser(store, data)
|
return blockUser(store, data)
|
||||||
|
|
@ -457,20 +495,21 @@ const users = {
|
||||||
const { reset } = args || {}
|
const { reset } = args || {}
|
||||||
|
|
||||||
const maxId = store.state.currentUser.muteIdsMaxId
|
const maxId = store.state.currentUser.muteIdsMaxId
|
||||||
return store.rootState.api.backendInteractor
|
return fetchMutes({
|
||||||
.fetchMutes({ maxId })
|
maxId,
|
||||||
.then((mutes) => {
|
credentials: useOAuthStore().token,
|
||||||
if (reset) {
|
}).then(({ data: mutes }) => {
|
||||||
store.commit('saveMuteIds', map(mutes, 'id'))
|
if (reset) {
|
||||||
} else {
|
store.commit('saveMuteIds', map(mutes, 'id'))
|
||||||
map(mutes, 'id').map((id) => store.commit('addMuteId', id))
|
} else {
|
||||||
}
|
map(mutes, 'id').map((id) => store.commit('addMuteId', id))
|
||||||
if (mutes.length) {
|
}
|
||||||
store.commit('setMuteIdsMaxId', last(mutes).id)
|
if (mutes.length) {
|
||||||
}
|
store.commit('setMuteIdsMaxId', last(mutes).id)
|
||||||
store.commit('addNewUsers', mutes)
|
}
|
||||||
return mutes
|
store.commit('addNewUsers', mutes)
|
||||||
})
|
return mutes
|
||||||
|
})
|
||||||
},
|
},
|
||||||
muteUser(store, data) {
|
muteUser(store, data) {
|
||||||
return muteUser(store, data)
|
return muteUser(store, data)
|
||||||
|
|
@ -491,12 +530,12 @@ const users = {
|
||||||
return Promise.all(ids.map((d) => unmuteUser(store, d)))
|
return Promise.all(ids.map((d) => unmuteUser(store, d)))
|
||||||
},
|
},
|
||||||
fetchDomainMutes(store) {
|
fetchDomainMutes(store) {
|
||||||
return store.rootState.api.backendInteractor
|
return fetchDomainMutes({
|
||||||
.fetchDomainMutes()
|
credentials: useOAuthStore().token,
|
||||||
.then((domainMutes) => {
|
}).then(({ data: domainMutes }) => {
|
||||||
store.commit('saveDomainMutes', domainMutes)
|
store.commit('saveDomainMutes', domainMutes)
|
||||||
return domainMutes
|
return domainMutes
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
muteDomain(store, domain) {
|
muteDomain(store, domain) {
|
||||||
return muteDomain(store, domain)
|
return muteDomain(store, domain)
|
||||||
|
|
@ -513,24 +552,28 @@ const users = {
|
||||||
fetchFriends({ rootState, commit }, id) {
|
fetchFriends({ rootState, commit }, id) {
|
||||||
const user = rootState.users.usersObject[id]
|
const user = rootState.users.usersObject[id]
|
||||||
const maxId = last(user.friendIds)
|
const maxId = last(user.friendIds)
|
||||||
return rootState.api.backendInteractor
|
return fetchFriends({
|
||||||
.fetchFriends({ id, maxId })
|
id,
|
||||||
.then((friends) => {
|
maxId,
|
||||||
commit('addNewUsers', friends)
|
credentials: useOAuthStore().token,
|
||||||
commit('saveFriendIds', { id, friendIds: map(friends, 'id') })
|
}).then(({ data: friends }) => {
|
||||||
return friends
|
commit('addNewUsers', friends)
|
||||||
})
|
commit('saveFriendIds', { id, friendIds: map(friends, 'id') })
|
||||||
|
return friends
|
||||||
|
})
|
||||||
},
|
},
|
||||||
fetchFollowers({ rootState, commit }, id) {
|
fetchFollowers({ rootState, commit }, id) {
|
||||||
const user = rootState.users.usersObject[id]
|
const user = rootState.users.usersObject[id]
|
||||||
const maxId = last(user.followerIds)
|
const maxId = last(user.followerIds)
|
||||||
return rootState.api.backendInteractor
|
return fetchFollowers({
|
||||||
.fetchFollowers({ id, maxId })
|
id,
|
||||||
.then((followers) => {
|
maxId,
|
||||||
commit('addNewUsers', followers)
|
credentials: useOAuthStore().token,
|
||||||
commit('saveFollowerIds', { id, followerIds: map(followers, 'id') })
|
}).then(({ data: followers }) => {
|
||||||
return followers
|
commit('addNewUsers', followers)
|
||||||
})
|
commit('saveFollowerIds', { id, followerIds: map(followers, 'id') })
|
||||||
|
return followers
|
||||||
|
})
|
||||||
},
|
},
|
||||||
clearFriends({ commit }, userId) {
|
clearFriends({ commit }, userId) {
|
||||||
commit('clearFriends', userId)
|
commit('clearFriends', userId)
|
||||||
|
|
@ -539,18 +582,22 @@ const users = {
|
||||||
commit('clearFollowers', userId)
|
commit('clearFollowers', userId)
|
||||||
},
|
},
|
||||||
subscribeUser({ rootState, commit }, id) {
|
subscribeUser({ rootState, commit }, id) {
|
||||||
return rootState.api.backendInteractor
|
return followUser({
|
||||||
.followUser({ id, notify: true })
|
id,
|
||||||
.then((relationship) =>
|
notify: true,
|
||||||
commit('updateUserRelationship', [relationship]),
|
credentials: useOAuthStore().token,
|
||||||
)
|
}).then(({ data: relationship }) =>
|
||||||
|
commit('updateUserRelationship', [relationship]),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
unsubscribeUser({ rootState, commit }, id) {
|
unsubscribeUser({ rootState, commit }, id) {
|
||||||
return rootState.api.backendInteractor
|
return followUser({
|
||||||
.followUser({ id, notify: false })
|
id,
|
||||||
.then((relationship) =>
|
notify: false,
|
||||||
commit('updateUserRelationship', [relationship]),
|
credentials: useOAuthStore().token,
|
||||||
)
|
}).then(({ data: relationship }) =>
|
||||||
|
commit('updateUserRelationship', [relationship]),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
registerPushNotifications(store) {
|
registerPushNotifications(store) {
|
||||||
const token = store.state.currentUser.credentials
|
const token = store.state.currentUser.credentials
|
||||||
|
|
@ -611,12 +658,13 @@ const users = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
searchUsers({ rootState, commit }, { query }) {
|
searchUsers({ rootState, commit }, { query }) {
|
||||||
return rootState.api.backendInteractor
|
return searchUsers({
|
||||||
.searchUsers({ query })
|
query,
|
||||||
.then((users) => {
|
credentials: useOAuthStore().token,
|
||||||
commit('addNewUsers', users)
|
}).then(({ data: users }) => {
|
||||||
return users
|
commit('addNewUsers', users)
|
||||||
})
|
return users
|
||||||
|
})
|
||||||
},
|
},
|
||||||
async signUp(store, userInfo) {
|
async signUp(store, userInfo) {
|
||||||
const oauthStore = useOAuthStore()
|
const oauthStore = useOAuthStore()
|
||||||
|
|
@ -624,7 +672,7 @@ const users = {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = await oauthStore.ensureAppToken()
|
const token = await oauthStore.ensureAppToken()
|
||||||
const data = await apiService.register({
|
const { data } = await register({
|
||||||
credentials: token,
|
credentials: token,
|
||||||
params: { ...userInfo },
|
params: { ...userInfo },
|
||||||
})
|
})
|
||||||
|
|
@ -645,8 +693,10 @@ const users = {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getCaptcha(store) {
|
getCaptcha(store) {
|
||||||
return store.rootState.api.backendInteractor.getCaptcha()
|
return getCaptcha({
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
}).then(({ data }) => data)
|
||||||
},
|
},
|
||||||
|
|
||||||
logout(store) {
|
logout(store) {
|
||||||
|
|
@ -663,24 +713,21 @@ const users = {
|
||||||
token: oauth.userToken,
|
token: oauth.userToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
return oauthApi.revokeToken(params)
|
return revokeToken(params)
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
store.commit('clearCurrentUser')
|
store.commit('clearCurrentUser')
|
||||||
store.dispatch('disconnectFromSocket')
|
store.dispatch('disconnectFromSocket')
|
||||||
oauth.clearToken()
|
|
||||||
store.dispatch('stopFetchingTimeline', 'friends')
|
store.dispatch('stopFetchingTimeline', 'friends')
|
||||||
store.commit(
|
|
||||||
'setBackendInteractor',
|
|
||||||
backendInteractorService(oauth.getToken),
|
|
||||||
)
|
|
||||||
store.dispatch('stopFetchingNotifications')
|
store.dispatch('stopFetchingNotifications')
|
||||||
store.dispatch('stopFetchingLists')
|
useListsStore().stopFetching()
|
||||||
store.dispatch('stopFetchingBookmarkFolders')
|
useBookmarkFoldersStore().stopFetching()
|
||||||
store.dispatch('stopFetchingFollowRequests')
|
store.dispatch('stopFetchingFollowRequests')
|
||||||
store.commit('clearNotifications')
|
store.commit('clearNotifications')
|
||||||
store.commit('resetStatuses')
|
store.commit('resetStatuses')
|
||||||
store.dispatch('resetChats')
|
store.dispatch('resetChats')
|
||||||
|
oauth.clearToken()
|
||||||
|
Cookies.remove('__Host-pleroma_key', { path: '/' })
|
||||||
useInterfaceStore().setLastTimeline('public-timeline')
|
useInterfaceStore().setLastTimeline('public-timeline')
|
||||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||||
|
|
@ -690,141 +737,137 @@ const users = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const commit = store.commit
|
const commit = store.commit
|
||||||
const dispatch = store.dispatch
|
const dispatch = store.dispatch
|
||||||
|
|
||||||
commit('beginLogin')
|
commit('beginLogin')
|
||||||
store.rootState.api.backendInteractor
|
|
||||||
.verifyCredentials(accessToken)
|
|
||||||
.then((data) => {
|
|
||||||
if (!data.error) {
|
|
||||||
const user = data
|
|
||||||
// user.credentials = userCredentials
|
|
||||||
user.credentials = accessToken
|
|
||||||
user.blockIds = []
|
|
||||||
user.muteIds = []
|
|
||||||
user.domainMutes = []
|
|
||||||
commit('setCurrentUser', user)
|
|
||||||
|
|
||||||
useSyncConfigStore()
|
verifyCredentials({
|
||||||
.initSyncConfig(user)
|
credentials: useOAuthStore().token,
|
||||||
.then(() => {
|
})
|
||||||
useInterfaceStore()
|
.then(({ data: user }) => {
|
||||||
.applyTheme()
|
// user.credentials = userCredentials
|
||||||
.catch((e) => {
|
user.credentials = accessToken
|
||||||
console.error('Error setting theme', e)
|
user.blockIds = []
|
||||||
})
|
user.muteIds = []
|
||||||
})
|
user.domainMutes = []
|
||||||
useUserHighlightStore().initUserHighlight(user)
|
commit('setCurrentUser', user)
|
||||||
commit('addNewUsers', [user])
|
|
||||||
|
|
||||||
useEmojiStore().fetchEmoji()
|
useSyncConfigStore()
|
||||||
|
.initSyncConfig(user)
|
||||||
getNotificationPermission().then((permission) =>
|
.then(() => {
|
||||||
useInterfaceStore().setNotificationPermission(permission),
|
useInterfaceStore()
|
||||||
)
|
.applyTheme()
|
||||||
|
.catch((e) => {
|
||||||
// Set our new backend interactor
|
console.error('Error setting theme', e)
|
||||||
commit(
|
})
|
||||||
'setBackendInteractor',
|
|
||||||
backendInteractorService(accessToken),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Do server-side storage migrations
|
|
||||||
|
|
||||||
// Debug snippet to clean up storage and reset migrations
|
|
||||||
/*
|
|
||||||
// Reset wordfilter
|
|
||||||
Object.keys(
|
|
||||||
useSyncConfigStore().prefsStorage.simple.muteFilters
|
|
||||||
).forEach(key => {
|
|
||||||
useSyncConfigStore().unsetSimplePrefAndSave({ path: 'muteFilters.' + key, value: null })
|
|
||||||
})
|
})
|
||||||
|
useUserHighlightStore().initUserHighlight(user)
|
||||||
|
commit('addNewUsers', [user])
|
||||||
|
|
||||||
// Reset flag to 0 to re-run migrations
|
useEmojiStore().fetchEmoji()
|
||||||
useSyncConfigStore().setFlag({ flag: 'configMigration', value: 0 })
|
|
||||||
/**/
|
|
||||||
|
|
||||||
if (user.token) {
|
getNotificationPermission().then((permission) =>
|
||||||
dispatch('setWsToken', user.token)
|
useInterfaceStore().setNotificationPermission(permission),
|
||||||
|
)
|
||||||
|
|
||||||
// Initialize the shout socket.
|
// Do server-side storage migrations
|
||||||
dispatch('initializeSocket')
|
|
||||||
}
|
|
||||||
|
|
||||||
const startPolling = () => {
|
// Debug snippet to clean up storage and reset migrations
|
||||||
// Start getting fresh posts.
|
/*
|
||||||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
// Reset wordfilter
|
||||||
|
Object.keys(
|
||||||
|
useSyncConfigStore().prefsStorage.simple.muteFilters
|
||||||
|
).forEach(key => {
|
||||||
|
useSyncConfigStore().unsetSimplePrefAndSave({ path: 'muteFilters.' + key, value: null })
|
||||||
|
})
|
||||||
|
|
||||||
// Start fetching notifications
|
// Reset flag to 0 to re-run migrations
|
||||||
dispatch('startFetchingNotifications')
|
useSyncConfigStore().setFlag({ flag: 'configMigration', value: 0 })
|
||||||
|
/**/
|
||||||
|
|
||||||
if (
|
if (user.token) {
|
||||||
useInstanceCapabilitiesStore().pleromaChatMessagesAvailable
|
dispatch('setWsToken', user.token)
|
||||||
) {
|
|
||||||
// Start fetching chats
|
|
||||||
dispatch('startFetchingChats')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch('startFetchingLists')
|
// Initialize the shout socket.
|
||||||
dispatch('startFetchingBookmarkFolders')
|
dispatch('initializeSocket')
|
||||||
|
}
|
||||||
|
|
||||||
if (user.locked) {
|
const startPolling = () => {
|
||||||
dispatch('startFetchingFollowRequests')
|
// Start getting fresh posts.
|
||||||
}
|
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||||
|
|
||||||
if (useMergedConfigStore().mergedConfig.useStreamingApi) {
|
// Start fetching notifications
|
||||||
dispatch('fetchTimeline', { timeline: 'friends', since: null })
|
dispatch('startFetchingNotifications')
|
||||||
dispatch('fetchNotifications', { since: null })
|
|
||||||
dispatch('enableMastoSockets', true)
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(
|
|
||||||
'Failed initializing MastoAPI Streaming socket',
|
|
||||||
error,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
dispatch('fetchChats', { latest: true })
|
|
||||||
setTimeout(
|
|
||||||
() => dispatch('setNotificationsSilence', false),
|
|
||||||
10000,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
startPolling()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user mutes
|
if (useInstanceCapabilitiesStore().pleromaChatMessagesAvailable) {
|
||||||
dispatch('fetchMutes')
|
// Start fetching chats
|
||||||
|
dispatch('startFetchingChats')
|
||||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
|
||||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
|
||||||
|
|
||||||
// Fetch our friends
|
|
||||||
store.rootState.api.backendInteractor
|
|
||||||
.fetchFriends({ id: user.id })
|
|
||||||
.then((friends) => commit('addNewUsers', friends))
|
|
||||||
} else {
|
|
||||||
const response = data.error
|
|
||||||
// Authentication failed
|
|
||||||
commit('endLogin')
|
|
||||||
|
|
||||||
// remove authentication token on client/authentication errors
|
|
||||||
if ([400, 401, 403, 422].includes(response.status)) {
|
|
||||||
useOAuthStore().clearToken()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
reject(new Error('Wrong username or password'))
|
|
||||||
} else {
|
|
||||||
reject(new Error('An error occurred, please try again'))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useListsStore().startFetching()
|
||||||
|
useBookmarkFoldersStore().startFetching()
|
||||||
|
|
||||||
|
if (user.locked) {
|
||||||
|
dispatch('startFetchingFollowRequests')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useMergedConfigStore().mergedConfig.useStreamingApi) {
|
||||||
|
dispatch('fetchTimeline', {
|
||||||
|
timeline: 'friends',
|
||||||
|
sinceId: null,
|
||||||
|
})
|
||||||
|
dispatch('fetchNotifications', { sinceId: null })
|
||||||
|
dispatch('enableMastoSockets', true)
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(
|
||||||
|
'Failed initializing MastoAPI Streaming socket',
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
dispatch('fetchChats', { latest: true })
|
||||||
|
setTimeout(
|
||||||
|
() => dispatch('setNotificationsSilence', false),
|
||||||
|
10000,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
startPolling()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start fetching things that don't need to block the UI
|
||||||
|
useAnnouncementsStore().startFetchingAnnouncements()
|
||||||
|
|
||||||
|
dispatch('fetchMutes')
|
||||||
|
dispatch('loadDrafts')
|
||||||
|
|
||||||
|
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||||
|
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||||
|
|
||||||
|
// Fetch our friends
|
||||||
|
fetchFriends({ id: user.id }).then(({ data: friends }) =>
|
||||||
|
commit('addNewUsers', friends),
|
||||||
|
)
|
||||||
commit('endLogin')
|
commit('endLogin')
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
|
// Authentication failed
|
||||||
commit('endLogin')
|
commit('endLogin')
|
||||||
reject(new Error('Failed to connect to server, try again'))
|
|
||||||
|
// remove authentication token on client/authentication errors
|
||||||
|
if ([400, 401, 403, 422].includes(error.statusCode)) {
|
||||||
|
useOAuthStore().clearToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
commit('endLogin')
|
||||||
|
if (error.tatusCode === 401) {
|
||||||
|
throw new Error('Wrong username or password', error)
|
||||||
|
} else {
|
||||||
|
throw new Error('An error occurred, please try again', error)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,75 +0,0 @@
|
||||||
import bookmarkFoldersFetcher from '../../services/bookmark_folders_fetcher/bookmark_folders_fetcher.service.js'
|
|
||||||
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
|
||||||
import listsFetcher from '../../services/lists_fetcher/lists_fetcher.service.js'
|
|
||||||
import apiService, {
|
|
||||||
getMastodonSocketURI,
|
|
||||||
ProcessedWS,
|
|
||||||
} from '../api/api.service.js'
|
|
||||||
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
|
|
||||||
import timelineFetcher from '../timeline_fetcher/timeline_fetcher.service.js'
|
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
|
|
||||||
const backendInteractorService = (credentials) => ({
|
|
||||||
startFetchingTimeline({
|
|
||||||
timeline,
|
|
||||||
store,
|
|
||||||
userId = false,
|
|
||||||
listId = false,
|
|
||||||
statusId = false,
|
|
||||||
bookmarkFolderId = false,
|
|
||||||
tag,
|
|
||||||
}) {
|
|
||||||
return timelineFetcher.startFetching({
|
|
||||||
timeline,
|
|
||||||
store,
|
|
||||||
credentials,
|
|
||||||
userId,
|
|
||||||
listId,
|
|
||||||
statusId,
|
|
||||||
bookmarkFolderId,
|
|
||||||
tag,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchTimeline(args) {
|
|
||||||
return timelineFetcher.fetchAndUpdate({ ...args, credentials })
|
|
||||||
},
|
|
||||||
|
|
||||||
startFetchingNotifications({ store }) {
|
|
||||||
return notificationsFetcher.startFetching({ store, credentials })
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchNotifications(args) {
|
|
||||||
return notificationsFetcher.fetchAndUpdate({ ...args, credentials })
|
|
||||||
},
|
|
||||||
|
|
||||||
startFetchingFollowRequests({ store }) {
|
|
||||||
return followRequestFetcher.startFetching({ store, credentials })
|
|
||||||
},
|
|
||||||
|
|
||||||
startFetchingLists({ store }) {
|
|
||||||
return listsFetcher.startFetching({ store, credentials })
|
|
||||||
},
|
|
||||||
|
|
||||||
startFetchingBookmarkFolders({ store }) {
|
|
||||||
return bookmarkFoldersFetcher.startFetching({ store, credentials })
|
|
||||||
},
|
|
||||||
|
|
||||||
startUserSocket({ store }) {
|
|
||||||
const serv = useInstanceStore().server.replace('http', 'ws')
|
|
||||||
const url = getMastodonSocketURI({}, serv)
|
|
||||||
return ProcessedWS({ url, id: 'Unified', credentials })
|
|
||||||
},
|
|
||||||
|
|
||||||
...Object.entries(apiService).reduce((acc, [key, func]) => {
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
[key]: (args) => func({ credentials, ...args }),
|
|
||||||
}
|
|
||||||
}, {}),
|
|
||||||
|
|
||||||
verifyCredentials: apiService.verifyCredentials,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default backendInteractorService
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
import apiService from '../api/api.service.js'
|
|
||||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
|
||||||
|
|
||||||
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js'
|
|
||||||
|
|
||||||
const fetchAndUpdate = ({ credentials }) => {
|
|
||||||
return apiService
|
|
||||||
.fetchBookmarkFolders({ credentials })
|
|
||||||
.then(
|
|
||||||
(bookmarkFolders) => {
|
|
||||||
useBookmarkFoldersStore().setBookmarkFolders(bookmarkFolders)
|
|
||||||
},
|
|
||||||
(rej) => {
|
|
||||||
console.error(rej)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const startFetching = ({ credentials, store }) => {
|
|
||||||
const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })
|
|
||||||
boundFetchAndUpdate()
|
|
||||||
return promiseInterval(boundFetchAndUpdate, 240000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const bookmarkFoldersFetcher = {
|
|
||||||
startFetching,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default bookmarkFoldersFetcher
|
|
||||||
|
|
@ -13,8 +13,10 @@ function humanizeErrors(errors) {
|
||||||
export function StatusCodeError(statusCode, body, options, response) {
|
export function StatusCodeError(statusCode, body, options, response) {
|
||||||
this.name = 'StatusCodeError'
|
this.name = 'StatusCodeError'
|
||||||
this.statusCode = statusCode
|
this.statusCode = statusCode
|
||||||
this.message =
|
this.statusText = body.error || body.errors || body
|
||||||
statusCode + ' - ' + (JSON && JSON.stringify ? JSON.stringify(body) : body)
|
this.details = JSON && JSON.stringify ? JSON.stringify(body) : body
|
||||||
|
this.errorData = body.error || body.errors
|
||||||
|
this.message = this.statusCode + ' - ' + this.statusText
|
||||||
this.error = body // legacy attribute
|
this.error = body // legacy attribute
|
||||||
this.options = options
|
this.options = options
|
||||||
this.response = response
|
this.response = response
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,19 @@
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchUserRelationship,
|
||||||
|
followUser,
|
||||||
|
unfollowUser,
|
||||||
|
} from 'src/api/user.js'
|
||||||
|
|
||||||
const fetchRelationship = (attempt, userId, store) =>
|
const fetchRelationship = (attempt, userId, store) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
store.state.api.backendInteractor
|
fetchUserRelationship({
|
||||||
.fetchUserRelationship({ id: userId })
|
id: userId,
|
||||||
.then((relationship) => {
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
.then(({ data: relationship }) => {
|
||||||
store.commit('updateUserRelationship', [relationship])
|
store.commit('updateUserRelationship', [relationship])
|
||||||
return relationship
|
return relationship
|
||||||
})
|
})
|
||||||
|
|
@ -25,40 +35,33 @@ const fetchRelationship = (attempt, userId, store) =>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const requestFollow = (userId, store) =>
|
export const requestFollow = async (userId, store) => {
|
||||||
new Promise((resolve) => {
|
const { data: updated } = await followUser({
|
||||||
store.state.api.backendInteractor
|
id: userId,
|
||||||
.followUser({ id: userId })
|
credentials: useOAuthStore().token,
|
||||||
.then((updated) => {
|
|
||||||
store.commit('updateUserRelationship', [updated])
|
|
||||||
|
|
||||||
if (updated.following || (updated.locked && updated.requested)) {
|
|
||||||
// If we get result immediately or the account is locked, just stop.
|
|
||||||
resolve()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// But usually we don't get result immediately, so we ask server
|
|
||||||
// for updated user profile to confirm if we are following them
|
|
||||||
// Sometimes it takes several tries. Sometimes we end up not following
|
|
||||||
// user anyway, probably because they locked themselves and we
|
|
||||||
// don't know that yet.
|
|
||||||
// Recursive Promise, it will call itself up to 3 times.
|
|
||||||
|
|
||||||
return fetchRelationship(1, updated, store).then(() => {
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const requestUnfollow = (userId, store) =>
|
store.commit('updateUserRelationship', [updated])
|
||||||
new Promise((resolve) => {
|
|
||||||
store.state.api.backendInteractor
|
if (updated.following || (updated.locked && updated.requested)) {
|
||||||
.unfollowUser({ id: userId })
|
// If we get result immediately or the account is locked, just stop.
|
||||||
.then((updated) => {
|
return
|
||||||
store.commit('updateUserRelationship', [updated])
|
}
|
||||||
resolve({
|
|
||||||
updated,
|
// But usually we don't get result immediately, so we ask server
|
||||||
})
|
// for updated user profile to confirm if we are following them
|
||||||
})
|
// Sometimes it takes several tries. Sometimes we end up not following
|
||||||
|
// user anyway, probably because they locked themselves and we
|
||||||
|
// don't know that yet.
|
||||||
|
// Recursive Promise, it will call itself up to 3 times.
|
||||||
|
return await fetchRelationship(1, updated, store)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requestUnfollow = async (userId, store) => {
|
||||||
|
const { data: updated } = await unfollowUser({
|
||||||
|
id: userId,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return await store.commit('updateUserRelationship', [updated])
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import apiService from '../api/api.service.js'
|
import { fetchFollowRequests } from 'src/api/user.js'
|
||||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
|
||||||
|
|
||||||
const fetchAndUpdate = ({ store, credentials }) => {
|
const fetchAndUpdate = ({ store, credentials }) => {
|
||||||
return apiService
|
return fetchFollowRequests({ credentials })
|
||||||
.fetchFollowRequests({ credentials })
|
|
||||||
.then(
|
.then(
|
||||||
(requests) => {
|
({ data: requests }) => {
|
||||||
store.commit('setFollowRequests', requests)
|
store.commit('setFollowRequests', requests)
|
||||||
store.commit('addNewUsers', requests)
|
store.commit('addNewUsers', requests)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
import apiService from '../api/api.service.js'
|
|
||||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
|
||||||
|
|
||||||
import { useListsStore } from 'src/stores/lists.js'
|
|
||||||
|
|
||||||
const fetchAndUpdate = ({ credentials }) => {
|
|
||||||
return apiService
|
|
||||||
.fetchLists({ credentials })
|
|
||||||
.then(
|
|
||||||
(lists) => {
|
|
||||||
useListsStore().setLists(lists)
|
|
||||||
},
|
|
||||||
(rej) => {
|
|
||||||
console.error(rej)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const startFetching = ({ credentials, store }) => {
|
|
||||||
const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })
|
|
||||||
boundFetchAndUpdate()
|
|
||||||
return promiseInterval(boundFetchAndUpdate, 240000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const listsFetcher = {
|
|
||||||
startFetching,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default listsFetcher
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
const verifyOTPCode = ({
|
|
||||||
clientId,
|
|
||||||
clientSecret,
|
|
||||||
instance,
|
|
||||||
mfaToken,
|
|
||||||
code,
|
|
||||||
}) => {
|
|
||||||
const url = `${instance}/oauth/mfa/challenge`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', clientId)
|
|
||||||
form.append('client_secret', clientSecret)
|
|
||||||
form.append('mfa_token', mfaToken)
|
|
||||||
form.append('code', code)
|
|
||||||
form.append('challenge_type', 'totp')
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
const verifyRecoveryCode = ({
|
|
||||||
clientId,
|
|
||||||
clientSecret,
|
|
||||||
instance,
|
|
||||||
mfaToken,
|
|
||||||
code,
|
|
||||||
}) => {
|
|
||||||
const url = `${instance}/oauth/mfa/challenge`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', clientId)
|
|
||||||
form.append('client_secret', clientSecret)
|
|
||||||
form.append('mfa_token', mfaToken)
|
|
||||||
form.append('code', code)
|
|
||||||
form.append('challenge_type', 'recovery')
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
const mfa = {
|
|
||||||
verifyOTPCode,
|
|
||||||
verifyRecoveryCode,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default mfa
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
import { reduce } from 'lodash'
|
|
||||||
|
|
||||||
import { StatusCodeError } from 'src/services/errors/errors.js'
|
|
||||||
|
|
||||||
const REDIRECT_URI = `${window.location.origin}/oauth-callback`
|
|
||||||
|
|
||||||
export const getJsonOrError = async (response) => {
|
|
||||||
if (response.ok) {
|
|
||||||
return response.json().catch((error) => {
|
|
||||||
throw new StatusCodeError(response.status, error, {}, response)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
throw new StatusCodeError(
|
|
||||||
response.status,
|
|
||||||
await response.text(),
|
|
||||||
{},
|
|
||||||
response,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createApp = (instance) => {
|
|
||||||
const url = `${instance}/api/v1/apps`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_name', 'PleromaFE')
|
|
||||||
form.append('website', 'https://pleroma.social')
|
|
||||||
form.append('redirect_uris', REDIRECT_URI)
|
|
||||||
form.append('scopes', 'read write follow push admin')
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then(getJsonOrError)
|
|
||||||
.then((app) => ({
|
|
||||||
clientId: app.client_id,
|
|
||||||
clientSecret: app.client_secret,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const verifyAppToken = ({ instance, appToken }) => {
|
|
||||||
return window
|
|
||||||
.fetch(`${instance}/api/v1/apps/verify_credentials`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: { Authorization: `Bearer ${appToken}` },
|
|
||||||
})
|
|
||||||
.then(getJsonOrError)
|
|
||||||
}
|
|
||||||
|
|
||||||
const login = ({ instance, clientId }) => {
|
|
||||||
const data = {
|
|
||||||
response_type: 'code',
|
|
||||||
client_id: clientId,
|
|
||||||
redirect_uri: REDIRECT_URI,
|
|
||||||
scope: 'read write follow push admin',
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataString = reduce(
|
|
||||||
data,
|
|
||||||
(acc, v, k) => {
|
|
||||||
const encoded = `${k}=${encodeURIComponent(v)}`
|
|
||||||
if (!acc) {
|
|
||||||
return encoded
|
|
||||||
} else {
|
|
||||||
return `${acc}&${encoded}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Do the redirect...
|
|
||||||
const url = `${instance}/oauth/authorize?${dataString}`
|
|
||||||
|
|
||||||
window.location.href = url
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTokenWithCredentials = ({
|
|
||||||
clientId,
|
|
||||||
clientSecret,
|
|
||||||
instance,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
}) => {
|
|
||||||
const url = `${instance}/oauth/token`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', clientId)
|
|
||||||
form.append('client_secret', clientSecret)
|
|
||||||
form.append('grant_type', 'password')
|
|
||||||
form.append('username', username)
|
|
||||||
form.append('password', password)
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
const getToken = ({ clientId, clientSecret, instance, code }) => {
|
|
||||||
const url = `${instance}/oauth/token`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', clientId)
|
|
||||||
form.append('client_secret', clientSecret)
|
|
||||||
form.append('grant_type', 'authorization_code')
|
|
||||||
form.append('code', code)
|
|
||||||
form.append('redirect_uri', `${window.location.origin}/oauth-callback`)
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getClientToken = ({ clientId, clientSecret, instance }) => {
|
|
||||||
const url = `${instance}/oauth/token`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', clientId)
|
|
||||||
form.append('client_secret', clientSecret)
|
|
||||||
form.append('grant_type', 'client_credentials')
|
|
||||||
form.append('redirect_uri', `${window.location.origin}/oauth-callback`)
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then(getJsonOrError)
|
|
||||||
}
|
|
||||||
const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
|
|
||||||
const url = `${instance}/oauth/mfa/challenge`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', app.client_id)
|
|
||||||
form.append('client_secret', app.client_secret)
|
|
||||||
form.append('mfa_token', mfaToken)
|
|
||||||
form.append('code', code)
|
|
||||||
form.append('challenge_type', 'totp')
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
const verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {
|
|
||||||
const url = `${instance}/oauth/mfa/challenge`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', app.client_id)
|
|
||||||
form.append('client_secret', app.client_secret)
|
|
||||||
form.append('mfa_token', mfaToken)
|
|
||||||
form.append('code', code)
|
|
||||||
form.append('challenge_type', 'recovery')
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
const revokeToken = ({ app, instance, token }) => {
|
|
||||||
const url = `${instance}/oauth/revoke`
|
|
||||||
const form = new window.FormData()
|
|
||||||
|
|
||||||
form.append('client_id', app.clientId)
|
|
||||||
form.append('client_secret', app.clientSecret)
|
|
||||||
form.append('token', token)
|
|
||||||
|
|
||||||
return window
|
|
||||||
.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form,
|
|
||||||
})
|
|
||||||
.then((data) => data.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
const oauth = {
|
|
||||||
login,
|
|
||||||
getToken,
|
|
||||||
getTokenWithCredentials,
|
|
||||||
verifyOTPCode,
|
|
||||||
verifyRecoveryCode,
|
|
||||||
revokeToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default oauth
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import { reduce } from 'lodash'
|
|
||||||
|
|
||||||
const MASTODON_PASSWORD_RESET_URL = '/auth/password'
|
|
||||||
|
|
||||||
const resetPassword = ({ instance, email }) => {
|
|
||||||
const params = { email }
|
|
||||||
const query = reduce(
|
|
||||||
params,
|
|
||||||
(acc, v, k) => {
|
|
||||||
const encoded = `${k}=${encodeURIComponent(v)}`
|
|
||||||
return `${acc}&${encoded}`
|
|
||||||
},
|
|
||||||
'',
|
|
||||||
)
|
|
||||||
const url = `${instance}${MASTODON_PASSWORD_RESET_URL}?${query}`
|
|
||||||
|
|
||||||
return window.fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default resetPassword
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
|
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
|
||||||
import { muteFilterHits } from '../status_parser/status_parser.js'
|
import { muteFilterHits } from '../status_parser/status_parser.js'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
|
||||||
|
|
||||||
import FaviconService from 'src/services/favicon_service/favicon_service.js'
|
import FaviconService from 'src/services/favicon_service/favicon_service.js'
|
||||||
|
|
||||||
export const ACTIONABLE_NOTIFICATION_TYPES = new Set([
|
export const ACTIONABLE_NOTIFICATION_TYPES = new Set([
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import apiService from '../api/api.service.js'
|
|
||||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
||||||
|
import { fetchTimeline } from 'src/api/timelines.js'
|
||||||
|
|
||||||
const update = ({ store, notifications, older }) => {
|
const update = ({ store, notifications, older }) => {
|
||||||
store.dispatch('addNewNotifications', { notifications, older })
|
store.dispatch('addNewNotifications', { notifications, older })
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ const mastoApiNotificationTypes = new Set([
|
||||||
'pleroma:report',
|
'pleroma:report',
|
||||||
])
|
])
|
||||||
|
|
||||||
const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
const fetchAndUpdate = ({ store, credentials, older = false, sinceId }) => {
|
||||||
const args = { credentials }
|
const args = { credentials }
|
||||||
const rootState = store.rootState || store.state
|
const rootState = store.rootState || store.state
|
||||||
const timelineData = rootState.notifications
|
const timelineData = rootState.notifications
|
||||||
|
|
@ -35,24 +35,24 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
||||||
mastoApiNotificationTypes.add('pleroma:chat_mention')
|
mastoApiNotificationTypes.add('pleroma:chat_mention')
|
||||||
}
|
}
|
||||||
|
|
||||||
args.includeTypes = mastoApiNotificationTypes
|
args.includeTypes = [...mastoApiNotificationTypes]
|
||||||
args.withMuted = !hideMutedPosts
|
args.withMuted = !hideMutedPosts
|
||||||
|
|
||||||
args.timeline = 'notifications'
|
args.timeline = 'notifications'
|
||||||
if (older) {
|
if (older) {
|
||||||
if (timelineData.minId !== Number.POSITIVE_INFINITY) {
|
if (timelineData.minId !== Number.POSITIVE_INFINITY) {
|
||||||
args.until = timelineData.minId
|
args.maxId = timelineData.minId
|
||||||
}
|
}
|
||||||
return fetchNotifications({ store, args, older })
|
return fetchNotifications({ store, args, older })
|
||||||
} else {
|
} else {
|
||||||
// fetch new notifications
|
// fetch new notifications
|
||||||
if (
|
if (
|
||||||
since === undefined &&
|
sinceId === undefined &&
|
||||||
timelineData.maxId !== Number.POSITIVE_INFINITY
|
timelineData.maxId !== Number.POSITIVE_INFINITY
|
||||||
) {
|
) {
|
||||||
args.since = timelineData.maxId
|
args.sinceId = timelineData.maxId
|
||||||
} else if (since !== null) {
|
} else if (sinceId !== null) {
|
||||||
args.since = since
|
args.sinceId = sinceId
|
||||||
}
|
}
|
||||||
const result = fetchNotifications({ store, args, older })
|
const result = fetchNotifications({ store, args, older })
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
||||||
if (readNotifsIds.length > 0 && readNotifsIds.length > 0) {
|
if (readNotifsIds.length > 0 && readNotifsIds.length > 0) {
|
||||||
const minId = Math.min(...unreadNotifsIds) // Oldest known unread notification
|
const minId = Math.min(...unreadNotifsIds) // Oldest known unread notification
|
||||||
if (minId !== Infinity) {
|
if (minId !== Infinity) {
|
||||||
args.since = false // Don't use since_id since it sorta conflicts with min_id
|
args.sinceId = null // Don't use since_id since it sorta conflicts with min_id
|
||||||
args.minId = minId - 1 // go beyond
|
args.minId = minId - 1 // go beyond
|
||||||
fetchNotifications({ store, args, older })
|
fetchNotifications({ store, args, older })
|
||||||
}
|
}
|
||||||
|
|
@ -80,29 +80,25 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchNotifications = ({ store, args, older }) => {
|
const fetchNotifications = ({ store, args, older }) => {
|
||||||
return apiService
|
return fetchTimeline(args)
|
||||||
.fetchTimeline(args)
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.errors) {
|
|
||||||
if (
|
|
||||||
response.status === 400 &&
|
|
||||||
response.statusText.includes('Invalid value for enum')
|
|
||||||
) {
|
|
||||||
response.statusText
|
|
||||||
.matchAll(/(\w+) - Invalid value for enum./g)
|
|
||||||
.toArray()
|
|
||||||
.map((x) => x[1])
|
|
||||||
.forEach((x) => mastoApiNotificationTypes.delete(x))
|
|
||||||
return fetchNotifications({ store, args, older })
|
|
||||||
} else {
|
|
||||||
throw new Error(`${response.status} ${response.statusText}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const notifications = response.data
|
const notifications = response.data
|
||||||
update({ store, notifications, older })
|
update({ store, notifications, older })
|
||||||
return notifications
|
return notifications
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
if (
|
||||||
|
error.statusCode === 400 &&
|
||||||
|
error.statusText.includes('Invalid value for enum')
|
||||||
|
) {
|
||||||
|
error.statusText
|
||||||
|
.matchAll(/(\w+) - Invalid value for enum./g)
|
||||||
|
.toArray()
|
||||||
|
.map((x) => x[1])
|
||||||
|
.forEach((x) => mastoApiNotificationTypes.delete(x))
|
||||||
|
return fetchNotifications({ store, args, older })
|
||||||
|
}
|
||||||
|
|
||||||
useInterfaceStore().pushGlobalNotice({
|
useInterfaceStore().pushGlobalNotice({
|
||||||
level: 'error',
|
level: 'error',
|
||||||
messageKey: 'notifications.error',
|
messageKey: 'notifications.error',
|
||||||
|
|
|
||||||
|
|
@ -4,32 +4,34 @@
|
||||||
// time after the first interval.
|
// time after the first interval.
|
||||||
// - interval is the interval delay in ms.
|
// - interval is the interval delay in ms.
|
||||||
|
|
||||||
|
const wait = (timeout) => {
|
||||||
|
let timeoutId
|
||||||
|
const promise = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
timeoutId = window.setTimeout(() => resolve(), timeout)
|
||||||
|
})
|
||||||
|
return { timeoutId, promise }
|
||||||
|
}
|
||||||
|
|
||||||
export const promiseInterval = (promiseCall, interval) => {
|
export const promiseInterval = (promiseCall, interval) => {
|
||||||
let stopped = false
|
let stopped = false
|
||||||
let timeout = null
|
let timeout = null
|
||||||
|
|
||||||
const func = () => {
|
|
||||||
const promise = promiseCall()
|
|
||||||
// something unexpected happened and promiseCall did not
|
|
||||||
// return a promise, abort the loop.
|
|
||||||
if (!(promise && promise.finally)) {
|
|
||||||
console.warn(
|
|
||||||
'promiseInterval: promise call did not return a promise, stopping interval.',
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
promise.finally(() => {
|
|
||||||
if (stopped) return
|
|
||||||
timeout = window.setTimeout(func, interval)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopFetcher = () => {
|
const stopFetcher = () => {
|
||||||
stopped = true
|
stopped = true
|
||||||
window.clearTimeout(timeout)
|
window.clearTimeout(timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout = window.setTimeout(func, interval)
|
const loop = async () => {
|
||||||
|
while (!stopped) {
|
||||||
|
await promiseCall()
|
||||||
|
const { timeoutId, promise } = wait(interval)
|
||||||
|
timeout = timeoutId
|
||||||
|
await promise()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop().then()
|
||||||
|
|
||||||
return { stop: stopFetcher }
|
return { stop: stopFetcher }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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