diff --git a/packages/pl-fe/.eslintignore b/packages/pl-fe/.eslintignore
deleted file mode 100644
index 256b5ff45..000000000
--- a/packages/pl-fe/.eslintignore
+++ /dev/null
@@ -1,7 +0,0 @@
-/node_modules/**
-/dist/**
-/static/**
-/public/**
-/tmp/**
-/coverage/**
-/custom/**
diff --git a/packages/pl-fe/.eslintrc.json b/packages/pl-fe/.eslintrc.json
deleted file mode 100644
index d8693cc09..000000000
--- a/packages/pl-fe/.eslintrc.json
+++ /dev/null
@@ -1,359 +0,0 @@
-{
- "root": true,
- "extends": [
- "eslint:recommended",
- "plugin:import/typescript",
- "plugin:compat/recommended",
- "plugin:tailwindcss/recommended"
- ],
- "env": {
- "browser": true,
- "node": true,
- "es6": true,
- "jest": true
- },
- "globals": {
- "ATTACHMENT_HOST": false
- },
- "plugins": [
- "@stylistic",
- "@typescript-eslint",
- "formatjs",
- "import",
- "jsdoc",
- "jsx-a11y",
- "promise",
- "react",
- "react-hooks"
- ],
- "parserOptions": {
- "sourceType": "module",
- "ecmaFeatures": {
- "experimentalObjectRestSpread": true,
- "jsx": true
- },
- "ecmaVersion": 2018
- },
- "settings": {
- "react": {
- "version": "detect"
- },
- "import/extensions": [
- ".js",
- ".jsx",
- ".cjs",
- ".mjs",
- ".ts",
- ".tsx"
- ],
- "import/ignore": [
- "node_modules",
- "\\.(css|scss|json)$"
- ],
- "import/resolver": {
- "typescript": true,
- "node": true
- },
- "polyfills": [
- "es:all",
- "fetch",
- "IntersectionObserver",
- "Promise",
- "ResizeObserver",
- "URL",
- "URLSearchParams"
- ],
- "tailwindcss": {
- "config": "tailwind.config.ts"
- }
- },
- "rules": {
- "brace-style": "error",
- "comma-dangle": [
- "error",
- "always-multiline"
- ],
- "comma-spacing": [
- "warn",
- {
- "before": false,
- "after": true
- }
- ],
- "comma-style": [
- "warn",
- "last"
- ],
- "import/no-duplicates": "error",
- "space-before-function-paren": [
- "error",
- "never"
- ],
- "space-infix-ops": "error",
- "space-in-parens": [
- "error",
- "never"
- ],
- "keyword-spacing": "error",
- "dot-notation": "error",
- "eol-last": ["error", "always"],
- "eqeqeq": "error",
- "indent": [
- "error",
- 2,
- {
- "SwitchCase": 1,
- "ignoredNodes": [
- "TemplateLiteral"
- ]
- }
- ],
- "jsx-quotes": [
- "error",
- "prefer-single"
- ],
- "key-spacing": [
- "error",
- {
- "mode": "minimum"
- }
- ],
- "no-catch-shadow": "error",
- "no-cond-assign": "error",
- "no-console": [
- "warn",
- {
- "allow": [
- "error",
- "warn"
- ]
- }
- ],
- "no-extra-semi": "error",
- "no-const-assign": "error",
- "no-fallthrough": "error",
- "no-irregular-whitespace": "error",
- "no-loop-func": "error",
- "no-mixed-spaces-and-tabs": "error",
- "no-nested-ternary": "warn",
- "no-restricted-imports": [
- "error",
- {
- "patterns": [
- {
- "group": [
- "react-inlinesvg"
- ],
- "message": "Use the SvgIcon component instead."
- }
- ]
- }
- ],
- "no-trailing-spaces": "error",
- "no-undef": "error",
- "no-unreachable": "error",
- "no-unused-expressions": "error",
- "no-unused-vars": "off",
- "@typescript-eslint/no-unused-vars": [
- "error",
- {
- "vars": "all",
- "args": "none",
- "ignoreRestSiblings": true,
- "caughtErrors": "none"
- }
- ],
- "no-useless-escape": "warn",
- "no-var": "error",
- "object-curly-spacing": [
- "error",
- "always"
- ],
- "padded-blocks": [
- "error",
- {
- "classes": "always"
- }
- ],
- "prefer-const": "error",
- "quotes": [
- "error",
- "single"
- ],
- "semi": "error",
- "space-unary-ops": [
- "error",
- {
- "words": true,
- "nonwords": false
- }
- ],
- "strict": "off",
- "valid-typeof": "error",
- "react/jsx-boolean-value": "error",
- "react/jsx-closing-bracket-location": [
- "error",
- "line-aligned"
- ],
- "react/jsx-curly-spacing": "error",
- "react/jsx-equals-spacing": "error",
- "react/jsx-first-prop-new-line": [
- "error",
- "multiline-multiprop"
- ],
- "react/jsx-indent": [
- "error",
- 2
- ],
- "react/jsx-no-comment-textnodes": "error",
- "react/jsx-no-duplicate-props": "error",
- "react/jsx-no-undef": "error",
- "react/jsx-tag-spacing": "error",
- "react/jsx-uses-react": "error",
- "react/jsx-uses-vars": "error",
- "react/jsx-wrap-multilines": "error",
- "react/no-multi-comp": "off",
- "react/no-string-refs": "error",
- "react/self-closing-comp": "error",
- "jsx-a11y/accessible-emoji": "warn",
- "jsx-a11y/alt-text": "warn",
- "jsx-a11y/anchor-has-content": "warn",
- "jsx-a11y/anchor-is-valid": [
- "warn",
- {
- "components": [
- "Link",
- "NavLink"
- ],
- "specialLink": [
- "to"
- ],
- "aspect": [
- "noHref",
- "invalidHref",
- "preferButton"
- ]
- }
- ],
- "jsx-a11y/aria-activedescendant-has-tabindex": "warn",
- "jsx-a11y/aria-props": "warn",
- "jsx-a11y/aria-proptypes": "warn",
- "jsx-a11y/aria-role": "warn",
- "jsx-a11y/aria-unsupported-elements": "warn",
- "jsx-a11y/heading-has-content": "warn",
- "jsx-a11y/html-has-lang": "warn",
- "jsx-a11y/iframe-has-title": "warn",
- "jsx-a11y/img-redundant-alt": "warn",
- "jsx-a11y/interactive-supports-focus": "warn",
- "jsx-a11y/label-has-for": "off",
- "jsx-a11y/mouse-events-have-key-events": "warn",
- "jsx-a11y/no-access-key": "warn",
- "jsx-a11y/no-distracting-elements": "warn",
- "jsx-a11y/no-noninteractive-element-interactions": [
- "warn",
- {
- "handlers": [
- "onClick"
- ]
- }
- ],
- "jsx-a11y/no-onchange": "warn",
- "jsx-a11y/no-redundant-roles": "warn",
- "jsx-a11y/no-static-element-interactions": [
- "warn",
- {
- "handlers": [
- "onClick"
- ]
- }
- ],
- "jsx-a11y/role-has-required-aria-props": "warn",
- "jsx-a11y/role-supports-aria-props": "off",
- "jsx-a11y/scope": "warn",
- "jsx-a11y/tabindex-no-positive": "warn",
- "import/extensions": [
- "error",
- "always",
- {
- "js": "never",
- "mjs": "ignorePackages",
- "jsx": "never",
- "ts": "never",
- "tsx": "never"
- }
- ],
- "import/newline-after-import": "error",
- "import/no-extraneous-dependencies": "error",
- "import/no-unresolved": "error",
- "import/no-webpack-loader-syntax": "error",
- "import/order": [
- "error",
- {
- "groups": [
- "builtin",
- "external",
- "internal",
- "parent",
- "sibling",
- "index",
- "object",
- "type"
- ],
- "newlines-between": "always",
- "alphabetize": {
- "order": "asc"
- }
- }
- ],
- "@stylistic/member-delimiter-style": "error",
- "promise/catch-or-return": "error",
- "react-hooks/rules-of-hooks": "error",
- "tailwindcss/classnames-order": [
- "error",
- {
- "classRegex": "^(base|container|icon|item|list|outer|wrapper)?[c|C]lass(Name)?$",
- "config": "tailwind.config.ts"
- }
- ],
- "tailwindcss/migration-from-tailwind-2": "error",
- "tailwindcss/no-custom-classname": "off",
-
- "formatjs/enforce-default-message": "error",
- "formatjs/enforce-id": "error",
- "formatjs/no-literal-string-in-jsx": "warn"
- },
- "overrides": [
- {
- "files": [
- "**/*.ts",
- "**/*.tsx"
- ],
- "rules": {
- "no-undef": "off",
- "space-before-function-paren": "off"
- },
- "parser": "@typescript-eslint/parser"
- },
- {
- "files": [
- "src/components/ui/**/*"
- ],
- "rules": {
- "jsdoc/require-jsdoc": [
- "error",
- {
- "publicOnly": true,
- "require": {
- "ArrowFunctionExpression": true,
- "ClassDeclaration": true,
- "ClassExpression": true,
- "FunctionDeclaration": true,
- "FunctionExpression": true,
- "MethodDefinition": true
- }
- }
- ]
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/pl-fe/.oxfmtrc.json b/packages/pl-fe/.oxfmtrc.json
new file mode 100644
index 000000000..3bd481651
--- /dev/null
+++ b/packages/pl-fe/.oxfmtrc.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
+ "ignorePatterns": [],
+ "printWidth": null,
+ "singleQuote": true,
+ "arrowParens": null,
+ "experimentalSortImports": {
+ "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"]
+ },
+ "tabWidth": 2,
+ "jsxSingleQuote": true,
+ "overrides": [
+ {
+ "files": ["**/*.tsx"],
+ "options": {
+ "experimentalTailwindcss": {
+ "tailwindConfig": "./tailwind.config.ts",
+ "attributes": ["className", "iconClassName", "itemClassName", "listClassName"],
+ "functions": ["clsx"]
+ }
+ }
+ }
+ ]
+}
diff --git a/packages/pl-fe/.stylelintrc.json b/packages/pl-fe/.stylelintrc.json
index f2e80958d..26faeab14 100644
--- a/packages/pl-fe/.stylelintrc.json
+++ b/packages/pl-fe/.stylelintrc.json
@@ -3,16 +3,33 @@
"rules": {
"alpha-value-notation": null,
"at-rule-no-unknown": null,
- "at-rule-empty-line-before": ["always", { "ignore": ["after-comment", "first-nested", "inside-block", "blockless-after-same-name-blockless", "blockless-after-blockless"] }],
+ "at-rule-empty-line-before": [
+ "always",
+ {
+ "ignore": [
+ "after-comment",
+ "first-nested",
+ "inside-block",
+ "blockless-after-same-name-blockless",
+ "blockless-after-blockless"
+ ]
+ }
+ ],
"color-function-notation": null,
"custom-property-pattern": null,
"declaration-block-no-redundant-longhand-properties": null,
"declaration-empty-line-before": "never",
- "font-family-no-missing-generic-family-keyword": [true, { "ignoreFontFamilies": ["ForkAwesome", "Font Awesome 5 Free"] }],
+ "font-family-no-missing-generic-family-keyword": [
+ true,
+ { "ignoreFontFamilies": ["ForkAwesome", "Font Awesome 5 Free"] }
+ ],
"no-descending-specificity": null,
"no-duplicate-selectors": null,
"no-invalid-position-at-import-rule": null,
- "scss/at-rule-no-unknown": [true, { "ignoreAtRules": ["tailwind", "apply", "layer", "config"]}],
+ "scss/at-rule-no-unknown": [
+ true,
+ { "ignoreAtRules": ["tailwind", "apply", "layer", "config"] }
+ ],
"scss/operator-no-unspaced": null,
"selector-class-pattern": null
}
diff --git a/packages/pl-fe/CHANGELOG.md b/packages/pl-fe/CHANGELOG.md
index 7bb1e0e96..a8c1ab547 100644
--- a/packages/pl-fe/CHANGELOG.md
+++ b/packages/pl-fe/CHANGELOG.md
@@ -13,6 +13,7 @@ Changes made since the project forked from Soapbox in April 2024.
- Cat ears
**Behavior:**
+
- Notifications of the same type and reposts of the same post are grouped client-side.
- Date is displayed for notifications that are not about new posts.
- Replies to your posts are displayed differently to other mentions in notification list.
@@ -23,6 +24,7 @@ Changes made since the project forked from Soapbox in April 2024.
- Poll results can be displayed before voting.
**Settings:**
+
- You can add image description to your avatar/backend, if supported by backend.
- GoToSocial users can manage post interaction policies.
- Users can set interface theme color.
@@ -30,6 +32,7 @@ Changes made since the project forked from Soapbox in April 2024.
- Users can use system font for emoji rendering.
**Composing posts:**
+
- WYSIWYG text formatting, available if Markdown is supported.
- When writing posts, links to statuses are added as quotes, when supported by backend.
- You can select post language manually, when composing.
@@ -44,11 +47,13 @@ Changes made since the project forked from Soapbox in April 2024.
- When entering a long, all-lowercase hashtag, a suggestion about hashtag accessibility is displayed.
**Dashboard:**
+
- Dashboard main page displays metrics included in Mastodon admin dashboard, if supported by backend.
**Features:**
+
- The most recent scrobble is displayed on user profile/card.
-- Users can generate *interaction circles* for their profiles.
+- Users can generate _interaction circles_ for their profiles.
- You can bite users, if supported by backend.
- You can browse Bubble timeline, if supported by backend.
- Mastodon displays trending articles on Search page.
@@ -73,6 +78,7 @@ Changes made since the project forked from Soapbox in April 2024.
### Changed
**Behavior:**
+
- Separated favourites from reaction emojis. Limit for one reaction per post is removed. Facebook-like emoji reaction bar is removed.
- Simplified sensitive text/media logic.
- Reposting user is mentioned, when replying to a reposted status.
@@ -85,17 +91,20 @@ Changes made since the project forked from Soapbox in April 2024.
- Various accessibility changes, focused on screen reader compatibility.
**Settings:**
+
- Moved missing description confirmation option back to Settings page.
- Profile fields can be reordered on the Edit profile page.
- Explicit addressing can be disabled on supported backends.
- Developers options are no longer hidden behind a challenge.
**Composing posts:**
+
- Custom emojis are now split into categories.
- GoToSocial users can post with date in the past.
- Post scopes were renamed to match wording used by Mastodon.
**UI changes:**
+
- Removed header. Search bar and profile dropdown are moved to the sidebar. Mobile sidebar button is moved to the thumb navigation.
- Floating action button for creating new posts is moved to the thumb navigation.
- Mobile sidebar UI is changed to look like a popover.
@@ -116,6 +125,7 @@ Changes made since the project forked from Soapbox in April 2024.
- Redesigned audio/video player controls.
**Internal:**
+
- Migrated some local stores from Redux to Zustand. Other stores have been migrated away from `immutable`, before moving them either to Zustand or TanStack Query.
- Posts are now emojified during render, instead of when inserting posts to the state.
- Barrel exports are no longer used.
@@ -126,6 +136,7 @@ Changes made since the project forked from Soapbox in April 2024.
- Default max image size is increased to match Mastodon limits.
**Dependencies:**
+
- Replaced `react-popper` and `react-overlays` with `@floating-ui/react`.
- `uuid` package is replaced by the `randomUUID()` method.
diff --git a/packages/pl-fe/index.html b/packages/pl-fe/index.html
index d9183f921..960eb6e91 100644
--- a/packages/pl-fe/index.html
+++ b/packages/pl-fe/index.html
@@ -1,12 +1,18 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
<%- snippets %>
diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json
index 4dbdc5103..b07ef8a0b 100644
--- a/packages/pl-fe/package.json
+++ b/packages/pl-fe/package.json
@@ -2,20 +2,21 @@
"name": "pl-fe",
"displayName": "Nicolium",
"version": "0.0.1",
- "type": "module",
"description": "Mastodon-compatible social media front-end",
- "homepage": "https://codeberg.org/mkljczk/nicolium",
- "repository": {
- "type": "git",
- "url": "https://codeberg.org/mkljczk/nicolium"
- },
"keywords": [
"fediverse",
"pleroma"
],
+ "homepage": "https://codeberg.org/mkljczk/nicolium",
"bugs": {
"url": "https://codeberg.org/mkljczk/nicolium/issues"
},
+ "license": "AGPL-3.0-or-later",
+ "repository": {
+ "type": "git",
+ "url": "https://codeberg.org/mkljczk/nicolium"
+ },
+ "type": "module",
"scripts": {
"start": "npx vite serve",
"dev": "${npm_execpath} run start",
@@ -24,16 +25,12 @@
"audit:fix": "npx yarn-audit-fix",
"i18n": "npx formatjs extract 'src/**/*.{ts,tsx}' --ignore '**/*.d.ts' --out-file build/messages.json && npx formatjs compile build/messages.json --out-file src/locales/en.json",
"lint": "${npm_execpath} run lint:js && ${npm_execpath} run lint:sass",
- "lint:js": "npx eslint --ext .js,.jsx,.cjs,.mjs,.ts,.tsx . --cache",
+ "lint:js": "oxlint",
"lint:sass": "npx stylelint src/styles/**/*.scss",
+ "fmt": "oxfmt",
+ "fmt:check": "oxfmt --check",
"precommit": "lint-staged"
},
- "license": "AGPL-3.0-or-later",
- "browserslist": [
- "> 0.5%",
- "last 2 versions",
- "not dead"
- ],
"dependencies": {
"@emoji-mart/data": "^1.2.1",
"@floating-ui/react": "^0.27.16",
@@ -137,7 +134,6 @@
"devDependencies": {
"@formatjs/cli": "^6.9.0",
"@sentry/types": "^8.47.0",
- "@stylistic/eslint-plugin": "^3.1.0",
"@types/dom-chromium-ai": "^0.0.11",
"@types/leaflet": "^1.9.15",
"@types/lodash": "^4.17.13",
@@ -148,20 +144,8 @@
"@types/react-router-dom": "^5.3.3",
"@types/react-sparklines": "^1.7.5",
"@types/react-swipeable-views": "^0.13.6",
- "@typescript-eslint/eslint-plugin": "^8.24.1",
- "@typescript-eslint/parser": "^8.24.0",
"@vitejs/plugin-react": "^5.1.3",
- "eslint": "^8.57.1",
- "eslint-import-resolver-typescript": "^4.0.0",
- "eslint-plugin-compat": "^6.0.2",
"eslint-plugin-formatjs": "^5.4.2",
- "eslint-plugin-import": "^2.31.0",
- "eslint-plugin-jsdoc": "^50.6.1",
- "eslint-plugin-jsx-a11y": "^6.10.2",
- "eslint-plugin-promise": "^7.2.1",
- "eslint-plugin-react": "^7.37.3",
- "eslint-plugin-react-hooks": "^4.6.2",
- "eslint-plugin-tailwindcss": "^3.17.5",
"globals": "^15.14.0",
"oxfmt": "^0.32.0",
"oxlint": "^1.47.0",
@@ -182,7 +166,15 @@
"vite-plugin-static-copy": "^3.2.0"
},
"lint-staged": {
- "*.{js,cjs,mjs,ts,tsx}": "eslint --cache",
+ "*.{js,cjs,mjs,ts,tsx}": [
+ "oxlint",
+ "oxfmt --check"
+ ],
"src/styles/**/*.scss": "stylelint"
- }
+ },
+ "browserslist": [
+ "> 0.5%",
+ "last 2 versions",
+ "not dead"
+ ]
}
diff --git a/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll-with-emojis.json b/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll-with-emojis.json
index 76c722a45..1e1ee1f61 100644
--- a/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll-with-emojis.json
+++ b/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll-with-emojis.json
@@ -56,9 +56,7 @@
"note": "I create Fediverse software that empowers people online. I'm vegan btw Note: If you have a question for me, please tag me publicly. This gives the opportunity for others to chime in, and bystanders to learn.",
"pleroma": {
"accepts_chat_messages": true,
- "also_known_as": [
- "https://mitra.social/users/alex"
- ],
+ "also_known_as": ["https://mitra.social/users/alex"],
"ap_id": "https://gleasonator.com/users/alex",
"background_image": null,
"birthday": "1993-07-03",
diff --git a/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll.json b/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll.json
index 452a5acb7..74e464df4 100644
--- a/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll.json
+++ b/packages/pl-fe/src/__fixtures__/pleroma-status-with-poll.json
@@ -56,9 +56,7 @@
"note": "I create Fediverse software that empowers people online. I'm vegan btw Note: If you have a question for me, please tag me publicly. This gives the opportunity for others to chime in, and bystanders to learn.",
"pleroma": {
"accepts_chat_messages": true,
- "also_known_as": [
- "https://mitra.social/users/alex"
- ],
+ "also_known_as": ["https://mitra.social/users/alex"],
"ap_id": "https://gleasonator.com/users/alex",
"background_image": null,
"birthday": "1993-07-03",
diff --git a/packages/pl-fe/src/__fixtures__/pleroma-status.json b/packages/pl-fe/src/__fixtures__/pleroma-status.json
index 69f84afab..bf795f5e4 100644
--- a/packages/pl-fe/src/__fixtures__/pleroma-status.json
+++ b/packages/pl-fe/src/__fixtures__/pleroma-status.json
@@ -56,9 +56,7 @@
"note": "I create Fediverse software that empowers people online. I'm vegan btw Note: If you have a question for me, please tag me publicly. This gives the opportunity for others to chime in, and bystanders to learn.",
"pleroma": {
"accepts_chat_messages": true,
- "also_known_as": [
- "https://mitra.social/users/alex"
- ],
+ "also_known_as": ["https://mitra.social/users/alex"],
"ap_id": "https://gleasonator.com/users/alex",
"background_image": null,
"birthday": "1993-07-03",
diff --git a/packages/pl-fe/src/actions/accounts.ts b/packages/pl-fe/src/actions/accounts.ts
index fcf68ab64..18d567fda 100644
--- a/packages/pl-fe/src/actions/accounts.ts
+++ b/packages/pl-fe/src/actions/accounts.ts
@@ -15,62 +15,67 @@ import type { AppDispatch, RootState } from '@/store';
const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS' as const;
const ACCOUNT_MUTE_SUCCESS = 'ACCOUNT_MUTE_SUCCESS' as const;
-const createAccount = (params: CreateAccountParams) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState()).settings.createAccount(params).then((response) =>
- ({ params, response }),
- );
+const createAccount =
+ (params: CreateAccountParams) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState())
+ .settings.createAccount(params)
+ .then((response) => ({ params, response }));
-const fetchAccount = (accountId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- dispatch(fetchRelationships([accountId]));
+const fetchAccount = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ dispatch(fetchRelationships([accountId]));
- const account = selectAccount(getState(), accountId);
+ const account = selectAccount(getState(), accountId);
- if (account) {
- return Promise.resolve(null);
- }
+ if (account) {
+ return Promise.resolve(null);
+ }
- return getClient(getState()).accounts.getAccount(accountId)
- .then(response => {
- dispatch(importEntities({ accounts: [response] }));
- })
- .catch(error => {
- });
- };
+ return getClient(getState())
+ .accounts.getAccount(accountId)
+ .then((response) => {
+ dispatch(importEntities({ accounts: [response] }));
+ })
+ .catch((error) => {});
+};
-const fetchAccountByUsername = (username: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const fetchAccountByUsername =
+ (username: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const { auth, me } = getState();
const features = auth.client.features;
if (features.accountByUsername && (me || !features.accountLookup)) {
- return getClient(getState()).accounts.getAccount(username).then(response => {
- dispatch(fetchRelationships([response.id]));
- dispatch(importEntities({ accounts: [response] }));
- });
+ return getClient(getState())
+ .accounts.getAccount(username)
+ .then((response) => {
+ dispatch(fetchRelationships([response.id]));
+ dispatch(importEntities({ accounts: [response] }));
+ });
} else if (features.accountLookup) {
- return dispatch(accountLookup(username)).then(account => {
+ return dispatch(accountLookup(username)).then((account) => {
dispatch(fetchRelationships([account.id]));
});
} else {
- return getClient(getState()).accounts.searchAccounts(username, { resolve: true, limit: 1 }).then(accounts => {
- const found = accounts.find((a) => a.acct === username);
+ return getClient(getState())
+ .accounts.searchAccounts(username, { resolve: true, limit: 1 })
+ .then((accounts) => {
+ const found = accounts.find((a) => a.acct === username);
- if (found) {
- dispatch(fetchRelationships([found.id]));
- } else {
- throw accounts;
- }
- });
+ if (found) {
+ dispatch(fetchRelationships([found.id]));
+ } else {
+ throw accounts;
+ }
+ });
}
};
-const fetchRelationships = (accountIds: string[]) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const fetchRelationships =
+ (accountIds: string[]) => (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return null;
- const newAccountIds = accountIds.filter(id => !queryClient.getQueryData(['accountRelationships', id]));
+ const newAccountIds = accountIds.filter(
+ (id) => !queryClient.getQueryData(['accountRelationships', id]),
+ );
if (newAccountIds.length === 0) {
return null;
@@ -78,24 +83,25 @@ const fetchRelationships = (accountIds: string[]) =>
const fetcher = batcher.relationships(getClient(getState())).fetch;
- return Promise.all(newAccountIds.map(fetcher))
- .then(response =>{
- dispatch(importEntities({ relationships: response }));
- });
+ return Promise.all(newAccountIds.map(fetcher)).then((response) => {
+ dispatch(importEntities({ relationships: response }));
+ });
};
-const accountLookup = (acct: string, signal?: AbortSignal) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState()).accounts.lookupAccount(acct, { signal }).then((account) => {
- if (account && account.id) dispatch(importEntities({ accounts: [account] }));
- return account;
- });
+const accountLookup =
+ (acct: string, signal?: AbortSignal) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState())
+ .accounts.lookupAccount(acct, { signal })
+ .then((account) => {
+ if (account && account.id) dispatch(importEntities({ accounts: [account] }));
+ return account;
+ });
type AccountsAction = {
- type: typeof ACCOUNT_BLOCK_SUCCESS | typeof ACCOUNT_MUTE_SUCCESS;
- relationship: Relationship;
- statuses: Record;
- };
+ type: typeof ACCOUNT_BLOCK_SUCCESS | typeof ACCOUNT_MUTE_SUCCESS;
+ relationship: Relationship;
+ statuses: Record;
+};
export {
ACCOUNT_BLOCK_SUCCESS,
diff --git a/packages/pl-fe/src/actions/admin.ts b/packages/pl-fe/src/actions/admin.ts
index 3c4808b2c..6c99d761d 100644
--- a/packages/pl-fe/src/actions/admin.ts
+++ b/packages/pl-fe/src/actions/admin.ts
@@ -17,72 +17,86 @@ const ADMIN_CONFIG_FETCH_SUCCESS = 'ADMIN_CONFIG_FETCH_SUCCESS' as const;
const ADMIN_CONFIG_UPDATE_REQUEST = 'ADMIN_CONFIG_UPDATE_REQUEST' as const;
const ADMIN_CONFIG_UPDATE_SUCCESS = 'ADMIN_CONFIG_UPDATE_SUCCESS' as const;
-const fetchConfig = () =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).admin.config.getPleromaConfig()
- .then((data) => {
- dispatch({ type: ADMIN_CONFIG_FETCH_SUCCESS, configs: data.configs, needsReboot: data.need_reboot });
+const fetchConfig = () => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState)
+ .admin.config.getPleromaConfig()
+ .then((data) => {
+ dispatch({
+ type: ADMIN_CONFIG_FETCH_SUCCESS,
+ configs: data.configs,
+ needsReboot: data.need_reboot,
});
+ });
-const updateConfig = (configs: PleromaConfig['configs']) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const updateConfig =
+ (configs: PleromaConfig['configs']) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: ADMIN_CONFIG_UPDATE_REQUEST, configs });
- return getClient(getState).admin.config.updatePleromaConfig(configs)
+ return getClient(getState)
+ .admin.config.updatePleromaConfig(configs)
.then((data) => {
- dispatch({ type: ADMIN_CONFIG_UPDATE_SUCCESS, configs: data.configs, needsReboot: data.need_reboot });
+ dispatch({
+ type: ADMIN_CONFIG_UPDATE_SUCCESS,
+ configs: data.configs,
+ needsReboot: data.need_reboot,
+ });
});
};
-const updateFrontendConfig = (data: Record) =>
- (dispatch: AppDispatch) => {
- const params = [{
+const updateFrontendConfig = (data: Record) => (dispatch: AppDispatch) => {
+ const params = [
+ {
group: ':pleroma',
key: ':frontend_configurations',
- value: [{
- tuple: [':pl_fe', data],
- }],
- }];
+ value: [
+ {
+ tuple: [':pl_fe', data],
+ },
+ ],
+ },
+ ];
- return dispatch(updateConfig(params));
- };
+ return dispatch(updateConfig(params));
+};
-const deactivateUser = (accountId: string, report_id?: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const deactivateUser =
+ (accountId: string, report_id?: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
- return getClient(state).admin.accounts.performAccountAction(accountId, 'suspend', { report_id });
+ return getClient(state).admin.accounts.performAccountAction(accountId, 'suspend', {
+ report_id,
+ });
};
-const deleteUser = (accountId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).admin.accounts.deleteAccount(accountId);
+const deleteUser = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState).admin.accounts.deleteAccount(accountId);
-const deleteStatus = (statusId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).admin.statuses.deleteStatus(statusId)
- .then(() => {
- dispatch(deleteFromTimelines(statusId));
- return ({ statusId });
- });
+const deleteStatus = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState)
+ .admin.statuses.deleteStatus(statusId)
+ .then(() => {
+ dispatch(deleteFromTimelines(statusId));
+ return { statusId };
+ });
-const toggleStatusSensitivity = (statusId: string, sensitive: boolean) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).admin.statuses.updateStatus(statusId, { sensitive: !sensitive })
+const toggleStatusSensitivity =
+ (statusId: string, sensitive: boolean) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState)
+ .admin.statuses.updateStatus(statusId, { sensitive: !sensitive })
.then((status) => {
dispatch(importEntities({ statuses: [status] }));
});
-const tagUser = (accountId: string, tags: string[]) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
+const tagUser =
+ (accountId: string, tags: string[]) => (dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).admin.accounts.tagUser(accountId, tags);
-const untagUser = (accountId: string, tags: string[]) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
+const untagUser =
+ (accountId: string, tags: string[]) => (dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).admin.accounts.untagUser(accountId, tags);
/** Synchronizes user tags to the backend. */
-const setTags = (accountId: string, oldTags: string[], newTags: string[]) =>
- async(dispatch: AppDispatch) => {
+const setTags =
+ (accountId: string, oldTags: string[], newTags: string[]) => async (dispatch: AppDispatch) => {
const diff = getTagDiff(oldTags, newTags);
if (diff.added.length) await dispatch(tagUser(accountId, diff.added));
@@ -90,28 +104,26 @@ const setTags = (accountId: string, oldTags: string[], newTags: string[]) =>
};
/** Synchronizes badges to the backend. */
-const setBadges = (accountId: string, oldTags: string[], newTags: string[]) =>
- (dispatch: AppDispatch) => {
+const setBadges =
+ (accountId: string, oldTags: string[], newTags: string[]) => (dispatch: AppDispatch) => {
const oldBadges = filterBadges(oldTags);
const newBadges = filterBadges(newTags);
return dispatch(setTags(accountId, oldBadges, newBadges));
};
-const promoteToAdmin = (accountId: string) =>
- (_dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).admin.accounts.promoteToAdmin(accountId);
+const promoteToAdmin = (accountId: string) => (_dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState).admin.accounts.promoteToAdmin(accountId);
-const promoteToModerator = (accountId: string) =>
- (_dispatch: AppDispatch, getState: () => RootState) =>
+const promoteToModerator =
+ (accountId: string) => (_dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).admin.accounts.promoteToModerator(accountId);
-const demoteToUser = (accountId: string) =>
- (_dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).admin.accounts.demoteToUser(accountId);
+const demoteToUser = (accountId: string) => (_dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState).admin.accounts.demoteToUser(accountId);
-const setRole = (accountId: string, role: 'user' | 'moderator' | 'admin') =>
- (dispatch: AppDispatch) => {
+const setRole =
+ (accountId: string, role: 'user' | 'moderator' | 'admin') => (dispatch: AppDispatch) => {
switch (role) {
case 'user':
return dispatch(demoteToUser(accountId));
@@ -126,20 +138,45 @@ const redactStatus = (statusId: string) => (dispatch: AppDispatch, getState: ()
const state = getState();
const status = state.statuses[statusId];
- const poll = status.poll_id ? queryClient.getQueryData(['statuses', 'polls', status.poll_id]) : undefined;
+ const poll = status.poll_id
+ ? queryClient.getQueryData(['statuses', 'polls', status.poll_id])
+ : undefined;
- return getClient(state).statuses.getStatusSource(statusId).then(response => {
- dispatch(setComposeToStatus(status, poll, response.text, response.spoiler_text, response.content_type, false, undefined, undefined, true));
- useModalsStore.getState().actions.openModal('COMPOSE');
- }).catch(error => {
- dispatch({ type: STATUS_FETCH_SOURCE_FAIL, error });
- });
+ return getClient(state)
+ .statuses.getStatusSource(statusId)
+ .then((response) => {
+ dispatch(
+ setComposeToStatus(
+ status,
+ poll,
+ response.text,
+ response.spoiler_text,
+ response.content_type,
+ false,
+ undefined,
+ undefined,
+ true,
+ ),
+ );
+ useModalsStore.getState().actions.openModal('COMPOSE');
+ })
+ .catch((error) => {
+ dispatch({ type: STATUS_FETCH_SOURCE_FAIL, error });
+ });
};
type AdminActions =
- | { type: typeof ADMIN_CONFIG_FETCH_SUCCESS; configs: PleromaConfig['configs']; needsReboot: boolean }
+ | {
+ type: typeof ADMIN_CONFIG_FETCH_SUCCESS;
+ configs: PleromaConfig['configs'];
+ needsReboot: boolean;
+ }
| { type: typeof ADMIN_CONFIG_UPDATE_REQUEST; configs: PleromaConfig['configs'] }
- | { type: typeof ADMIN_CONFIG_UPDATE_SUCCESS; configs: PleromaConfig['configs']; needsReboot: boolean };
+ | {
+ type: typeof ADMIN_CONFIG_UPDATE_SUCCESS;
+ configs: PleromaConfig['configs'];
+ needsReboot: boolean;
+ };
export {
ADMIN_CONFIG_FETCH_SUCCESS,
diff --git a/packages/pl-fe/src/actions/apps.ts b/packages/pl-fe/src/actions/apps.ts
index c8bf94aa4..8bdbd4f9a 100644
--- a/packages/pl-fe/src/actions/apps.ts
+++ b/packages/pl-fe/src/actions/apps.ts
@@ -16,6 +16,4 @@ const createApp = (params: CreateApplicationParams, baseURL?: string) => {
return client.apps.createApplication(params);
};
-export {
- createApp,
-};
+export { createApp };
diff --git a/packages/pl-fe/src/actions/auth.ts b/packages/pl-fe/src/actions/auth.ts
index 698dc7665..174347d24 100644
--- a/packages/pl-fe/src/actions/auth.ts
+++ b/packages/pl-fe/src/actions/auth.ts
@@ -5,7 +5,7 @@
* @see module:pl-fe/actions/apps
* @see module:pl-fe/actions/oauth
* @see module:pl-fe/actions/security
-*/
+ */
import {
credentialAccountSchema,
PlApiClient,
@@ -55,64 +55,66 @@ const AUTH_ACCOUNT_REMEMBER_SUCCESS = 'AUTH_ACCOUNT_REMEMBER_SUCCESS' as const;
const messages = defineMessages({
loggedOut: { id: 'auth.logged_out', defaultMessage: 'Logged out.' },
- awaitingApproval: { id: 'auth.awaiting_approval', defaultMessage: 'Your account is awaiting approval' },
- invalidCredentials: { id: 'auth.invalid_credentials', defaultMessage: 'Wrong username or password' },
+ awaitingApproval: {
+ id: 'auth.awaiting_approval',
+ defaultMessage: 'Your account is awaiting approval',
+ },
+ invalidCredentials: {
+ id: 'auth.invalid_credentials',
+ defaultMessage: 'Wrong username or password',
+ },
});
-const noOp = () => new Promise(f =>{
- f(undefined);
-});
+const noOp = () =>
+ new Promise((f) => {
+ f(undefined);
+ });
-const createAppAndToken = () =>
- (dispatch: AppDispatch) =>
- dispatch(createAuthApp()).then(() =>
- dispatch(createAppToken()),
- );
+const createAppAndToken = () => (dispatch: AppDispatch) =>
+ dispatch(createAuthApp()).then(() => dispatch(createAppToken()));
interface AuthAppCreatedAction {
type: typeof AUTH_APP_CREATED;
app: CredentialApplication;
}
-const createAuthApp = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const params = {
- client_name: `${sourceCode.displayName} (${new URL(window.origin).host})`,
- redirect_uris: 'urn:ietf:wg:oauth:2.0:oob',
- scopes: getScopes(getState()),
- website: sourceCode.homepage,
- };
-
- return createApp(params).then((app) =>
- dispatch({ type: AUTH_APP_CREATED, app }),
- );
+const createAuthApp = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const params = {
+ client_name: `${sourceCode.displayName} (${new URL(window.origin).host})`,
+ redirect_uris: 'urn:ietf:wg:oauth:2.0:oob',
+ scopes: getScopes(getState()),
+ website: sourceCode.homepage,
};
+ return createApp(params).then((app) =>
+ dispatch({ type: AUTH_APP_CREATED, app }),
+ );
+};
+
interface AuthAppAuthorizedAction {
type: typeof AUTH_APP_AUTHORIZED;
app: CredentialApplication;
token: Token;
}
-const createAppToken = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const app = getState().auth.app!;
+const createAppToken = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const app = getState().auth.app!;
- const params = {
- client_id: app.client_id,
- client_secret: app.client_secret,
- redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
- grant_type: 'client_credentials',
- scope: getScopes(getState()),
- };
-
- return obtainOAuthToken(params).then((token) =>
- dispatch({ type: AUTH_APP_AUTHORIZED, app, token }),
- );
+ const params = {
+ client_id: app.client_id,
+ client_secret: app.client_secret,
+ redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
+ grant_type: 'client_credentials',
+ scope: getScopes(getState()),
};
-const createUserToken = (username: string, password: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+ return obtainOAuthToken(params).then((token) =>
+ dispatch({ type: AUTH_APP_AUTHORIZED, app, token }),
+ );
+};
+
+const createUserToken =
+ (username: string, password: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const app = getState().auth.app;
const params = {
@@ -125,25 +127,26 @@ const createUserToken = (username: string, password: string) =>
scope: getScopes(getState()),
};
- return obtainOAuthToken(params)
- .then((token) => dispatch(authLoggedIn(token, app)));
+ return obtainOAuthToken(params).then((token) => dispatch(authLoggedIn(token, app)));
};
-const otpVerify = (code: string, mfa_token: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const otpVerify =
+ (code: string, mfa_token: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const app = state.auth.app;
const baseUrl = parseBaseURL(state.me) || BuildConfig.BACKEND_URL;
const client = new PlApiClient(baseUrl);
- return client.oauth.mfaChallenge({
- client_id: app?.client_id!,
- client_secret: app?.client_secret!,
- mfa_token: mfa_token,
- code: code,
- challenge_type: 'totp',
- // redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
- // scope: getScopes(getState()),
- }).then((token) => dispatch(authLoggedIn(token, app)));
+ return client.oauth
+ .mfaChallenge({
+ client_id: app?.client_id!,
+ client_secret: app?.client_secret!,
+ mfa_token: mfa_token,
+ code: code,
+ challenge_type: 'totp',
+ // redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
+ // scope: getScopes(getState()),
+ })
+ .then((token) => dispatch(authLoggedIn(token, app)));
};
interface VerifyCredentialsRequestAction {
@@ -163,7 +166,8 @@ interface VerifyCredentialsFailAction {
error: unknown;
}
-const verifyCredentials = (token: string, accountUrl?: string) =>
+const verifyCredentials =
+ (token: string, accountUrl?: string) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
const baseURL = parseBaseURL(accountUrl) || BuildConfig.BACKEND_URL;
@@ -173,26 +177,37 @@ const verifyCredentials = (token: string, accountUrl?: string) =>
await client.instance.getInstance();
- return client.settings.verifyCredentials().then((account) => {
- dispatch(importEntities({ accounts: [account] }));
- dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account });
- if (account.id === getState().me) dispatch(fetchMeSuccess(account));
- return account;
- }).catch(error => {
- if (error?.response?.status === 403 && error?.response?.json?.id) {
- // The user is waitlisted
- const account = error.response.json;
- const parsedAccount = v.parse(credentialAccountSchema, error.response.json);
- dispatch(importEntities({ accounts: [parsedAccount] }));
- dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account: parsedAccount });
- if (account.id === getState().me) dispatch(fetchMeSuccess(parsedAccount));
- return parsedAccount;
- } else {
- if (getState().me === null) dispatch(fetchMeFail(error));
- dispatch({ type: VERIFY_CREDENTIALS_FAIL, token, error });
- throw error;
- }
- });
+ return client.settings
+ .verifyCredentials()
+ .then((account) => {
+ dispatch(importEntities({ accounts: [account] }));
+ dispatch({
+ type: VERIFY_CREDENTIALS_SUCCESS,
+ token,
+ account,
+ });
+ if (account.id === getState().me) dispatch(fetchMeSuccess(account));
+ return account;
+ })
+ .catch((error) => {
+ if (error?.response?.status === 403 && error?.response?.json?.id) {
+ // The user is waitlisted
+ const account = error.response.json;
+ const parsedAccount = v.parse(credentialAccountSchema, error.response.json);
+ dispatch(importEntities({ accounts: [parsedAccount] }));
+ dispatch({
+ type: VERIFY_CREDENTIALS_SUCCESS,
+ token,
+ account: parsedAccount,
+ });
+ if (account.id === getState().me) dispatch(fetchMeSuccess(parsedAccount));
+ return parsedAccount;
+ } else {
+ if (getState().me === null) dispatch(fetchMeFail(error));
+ dispatch({ type: VERIFY_CREDENTIALS_FAIL, token, error });
+ throw error;
+ }
+ });
};
interface AuthAccountRememberSuccessAction {
@@ -201,34 +216,39 @@ interface AuthAccountRememberSuccessAction {
account: CredentialAccount;
}
-const rememberAuthAccount = (accountUrl: string) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- KVStore.getItemOrError(`authAccount:${accountUrl}`).then(account => {
+const rememberAuthAccount =
+ (accountUrl: string) => (dispatch: AppDispatch, getState: () => RootState) =>
+ KVStore.getItemOrError(`authAccount:${accountUrl}`).then((account) => {
dispatch(importEntities({ accounts: [account] }));
- dispatch({ type: AUTH_ACCOUNT_REMEMBER_SUCCESS, account, accountUrl });
+ dispatch({
+ type: AUTH_ACCOUNT_REMEMBER_SUCCESS,
+ account,
+ accountUrl,
+ });
if (account.id === getState().me) dispatch(fetchMeSuccess(account));
return account;
});
-const loadCredentials = (token: string, accountUrl: string) =>
- (dispatch: AppDispatch) => dispatch(rememberAuthAccount(accountUrl))
- .finally(() => dispatch(verifyCredentials(token, accountUrl)));
+const loadCredentials = (token: string, accountUrl: string) => (dispatch: AppDispatch) =>
+ dispatch(rememberAuthAccount(accountUrl)).finally(() =>
+ dispatch(verifyCredentials(token, accountUrl)),
+ );
-const logIn = (username: string, password: string) =>
- (dispatch: AppDispatch) => dispatch(createAuthApp()).then(() =>
- dispatch(createUserToken(normalizeUsername(username), password)),
- ).catch((error: { response: PlfeResponse }) => {
- if ((error.response?.json)?.error === 'mfa_required') {
- // If MFA is required, throw the error and handle it in the component.
+const logIn = (username: string, password: string) => (dispatch: AppDispatch) =>
+ dispatch(createAuthApp())
+ .then(() => dispatch(createUserToken(normalizeUsername(username), password)))
+ .catch((error: { response: PlfeResponse }) => {
+ if (error.response?.json?.error === 'mfa_required') {
+ // If MFA is required, throw the error and handle it in the component.
+ throw error;
+ } else if (error.response?.json?.identifier === 'awaiting_approval') {
+ toast.error(messages.awaitingApproval);
+ } else {
+ // Return "wrong password" message.
+ toast.error(messages.invalidCredentials);
+ }
throw error;
- } else if ((error.response?.json)?.identifier === 'awaiting_approval') {
- toast.error(messages.awaitingApproval);
- } else {
- // Return "wrong password" message.
- toast.error(messages.invalidCredentials);
- }
- throw error;
- });
+ });
interface AuthLoggedOutAction {
type: typeof AUTH_LOGGED_OUT;
@@ -236,86 +256,79 @@ interface AuthLoggedOutAction {
standalone: boolean;
}
-const logOut = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const state = getState();
- const account = getLoggedInAccount(state);
- const standalone = isStandalone(state);
+const logOut = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const state = getState();
+ const account = getLoggedInAccount(state);
+ const standalone = isStandalone(state);
- if (!account) return dispatch(noOp);
+ if (!account) return dispatch(noOp);
- const token = state.auth.users[account.url].access_token;
+ const token = state.auth.users[account.url].access_token;
- const params = {
- client_id: state.auth.tokens[token]?.client_id ?? state.auth.app?.client_id!,
- client_secret: state.auth.tokens[token]?.client_secret ?? state.auth.app?.client_secret!,
- token,
- };
-
- return dispatch(revokeOAuthToken(params))
- .finally(() => {
- // Clear all stored cache from React Query
- queryClient.invalidateQueries();
- queryClient.clear();
-
- // Clear the account from Sentry.
- unsetSentryAccount();
-
- dispatch({ type: AUTH_LOGGED_OUT, account, standalone });
-
- toast.success(messages.loggedOut);
- });
+ const params = {
+ client_id: state.auth.tokens[token]?.client_id ?? state.auth.app?.client_id!,
+ client_secret: state.auth.tokens[token]?.client_secret ?? state.auth.app?.client_secret!,
+ token,
};
+ return dispatch(revokeOAuthToken(params)).finally(() => {
+ // Clear all stored cache from React Query
+ queryClient.invalidateQueries();
+ queryClient.clear();
+
+ // Clear the account from Sentry.
+ unsetSentryAccount();
+
+ dispatch({ type: AUTH_LOGGED_OUT, account, standalone });
+
+ toast.success(messages.loggedOut);
+ });
+};
+
interface SwitchAccountAction {
type: typeof SWITCH_ACCOUNT;
account: Account;
}
-const switchAccount = (accountId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const account = selectAccount(getState(), accountId);
- if (!account) return;
+const switchAccount = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ const account = selectAccount(getState(), accountId);
+ if (!account) return;
- // Clear all stored cache from React Query
- queryClient.invalidateQueries();
- queryClient.clear();
+ // Clear all stored cache from React Query
+ queryClient.invalidateQueries();
+ queryClient.clear();
- return dispatch({ type: SWITCH_ACCOUNT, account });
- };
+ return dispatch({ type: SWITCH_ACCOUNT, account });
+};
-const fetchOwnAccounts = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const state = getState();
- Object.values(state.auth.users).forEach((user) => {
- const account = selectAccount(state, user.id);
- if (!account) {
- dispatch(verifyCredentials(user.access_token, user.url))
- .catch(() =>{
- console.warn(`Failed to load account: ${user.url}`);
- });
- }
- });
- };
-
-const register = (params: CreateAccountParams) =>
- async (dispatch: AppDispatch) => {
- params.fullname = params.username;
-
- const { app } = await dispatch(createAppAndToken());
-
- return dispatch(createAccount(params))
- .then(({ response }) => {
- if ('identifier' in response) {
- toast.info(response.message);
- } else {
- return dispatch(authLoggedIn(response, app));
- }
+const fetchOwnAccounts = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const state = getState();
+ Object.values(state.auth.users).forEach((user) => {
+ const account = selectAccount(state, user.id);
+ if (!account) {
+ dispatch(verifyCredentials(user.access_token, user.url)).catch(() => {
+ console.warn(`Failed to load account: ${user.url}`);
});
- };
+ }
+ });
+};
-const fetchCaptcha = () =>
- (_dispatch: AppDispatch, getState: () => RootState) => getClient(getState).oauth.getCaptcha();
+const register = (params: CreateAccountParams) => async (dispatch: AppDispatch) => {
+ params.fullname = params.username;
+
+ const { app } = await dispatch(createAppAndToken());
+
+ return dispatch(createAccount(params)).then(({ response }) => {
+ if ('identifier' in response) {
+ toast.info(response.message);
+ } else {
+ return dispatch(authLoggedIn(response, app));
+ }
+ });
+};
+
+const fetchCaptcha = () => (_dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState).oauth.getCaptcha();
interface AuthLoggedInAction {
type: typeof AUTH_LOGGED_IN;
@@ -323,8 +336,8 @@ interface AuthLoggedInAction {
app?: CredentialApplication;
}
-const authLoggedIn = (token: Token, app?: CredentialApplication | null) =>
- (dispatch: AppDispatch) => {
+const authLoggedIn =
+ (token: Token, app?: CredentialApplication | null) => (dispatch: AppDispatch) => {
dispatch({ type: AUTH_LOGGED_IN, token, app: app ?? undefined });
return token;
};
diff --git a/packages/pl-fe/src/actions/chats.ts b/packages/pl-fe/src/actions/chats.ts
index 7e1017ea2..fc3d784f4 100644
--- a/packages/pl-fe/src/actions/chats.ts
+++ b/packages/pl-fe/src/actions/chats.ts
@@ -3,13 +3,10 @@ import { useSettingsStore } from '@/stores/settings';
import type { AppDispatch, RootState } from '@/store';
-const toggleChatPane = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const main = useSettingsStore.getState().settings.chats.mainWindow;
- const state = main === 'minimized' ? 'open' : 'minimized';
- dispatch(changeSetting(['chats', 'mainWindow'], state));
- };
-
-export {
- toggleChatPane,
+const toggleChatPane = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const main = useSettingsStore.getState().settings.chats.mainWindow;
+ const state = main === 'minimized' ? 'open' : 'minimized';
+ dispatch(changeSetting(['chats', 'mainWindow'], state));
};
+
+export { toggleChatPane };
diff --git a/packages/pl-fe/src/actions/circle.ts b/packages/pl-fe/src/actions/circle.ts
index 480fa6104..47c94d9b6 100644
--- a/packages/pl-fe/src/actions/circle.ts
+++ b/packages/pl-fe/src/actions/circle.ts
@@ -14,10 +14,19 @@ interface Interaction {
favourites: number;
}
-const processCircle = (setProgress: (progress: {
- state: 'pending' | 'fetchingStatuses' | 'fetchingFavourites' | 'fetchingAvatars' | 'drawing' | 'done';
- progress: number;
-}) => void) =>
+const processCircle =
+ (
+ setProgress: (progress: {
+ state:
+ | 'pending'
+ | 'fetchingStatuses'
+ | 'fetchingFavourites'
+ | 'fetchingAvatars'
+ | 'drawing'
+ | 'done';
+ progress: number;
+ }) => void,
+ ) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
setProgress({ state: 'pending', progress: 0 });
@@ -63,7 +72,8 @@ const processCircle = (setProgress: (progress: {
return response.next;
};
- const fetchFavourites = async (next: (() => Promise>) | null) => { // limit 40
+ const fetchFavourites = async (next: (() => Promise>) | null) => {
+ // limit 40
const response = await (next?.() ?? client.myAccount.getFavourites({ limit: 40 }));
response.items.forEach((status) => {
@@ -93,26 +103,30 @@ const processCircle = (setProgress: (progress: {
if (!next) break;
}
- const result = await Promise.all(Object.entries(interactions).map(([id, { acct, avatar, avatar_description, favourites, reblogs, replies }]) => {
- const score = favourites + replies * 1.1 + reblogs * 1.3;
- return { id, acct, avatar, avatar_description, score };
- }).toSorted((a, b) => b.score - a.score).slice(0, 49).map(async (interaction, index, array) => {
- setProgress({ state: 'fetchingAvatars', progress: 80 + (index / array.length) * 10 });
+ const result = await Promise.all(
+ Object.entries(interactions)
+ .map(([id, { acct, avatar, avatar_description, favourites, reblogs, replies }]) => {
+ const score = favourites + replies * 1.1 + reblogs * 1.3;
+ return { id, acct, avatar, avatar_description, score };
+ })
+ .toSorted((a, b) => b.score - a.score)
+ .slice(0, 49)
+ .map(async (interaction, index, array) => {
+ setProgress({ state: 'fetchingAvatars', progress: 80 + (index / array.length) * 10 });
- if (interaction.acct) return interaction;
+ if (interaction.acct) return interaction;
- const account = await client.accounts.getAccount(interaction.id);
+ const account = await client.accounts.getAccount(interaction.id);
- interaction.acct = account.acct;
- interaction.avatar = account.avatar_static || account.avatar;
- interaction.avatar_description = account.avatar_description;
+ interaction.acct = account.acct;
+ interaction.avatar = account.avatar_static || account.avatar;
+ interaction.avatar_description = account.avatar_description;
- return interaction;
- }));
+ return interaction;
+ }),
+ );
return result;
};
-export {
- processCircle,
-};
+export { processCircle };
diff --git a/packages/pl-fe/src/actions/compose.ts b/packages/pl-fe/src/actions/compose.ts
index 568eb2180..babe042ec 100644
--- a/packages/pl-fe/src/actions/compose.ts
+++ b/packages/pl-fe/src/actions/compose.ts
@@ -186,27 +186,27 @@ const setComposeToStatus =
editorState?: string | null,
redacting?: boolean,
) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const { features } = getClient(getState);
- const explicitAddressing =
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ const { features } = getClient(getState);
+ const explicitAddressing =
features.createStatusExplicitAddressing &&
!useSettingsStore.getState().settings.forceImplicitAddressing;
- dispatch({
- type: COMPOSE_SET_STATUS,
- composeId: 'compose-modal',
- status,
- poll,
- rawText,
- explicitAddressing,
- spoilerText,
- contentType,
- withRedraft,
- draftId,
- editorState,
- redacting,
- });
- };
+ dispatch({
+ type: COMPOSE_SET_STATUS,
+ composeId: 'compose-modal',
+ status,
+ poll,
+ rawText,
+ explicitAddressing,
+ spoilerText,
+ contentType,
+ withRedraft,
+ draftId,
+ editorState,
+ redacting,
+ });
+ };
const changeCompose = (composeId: string, text: string) => ({
type: COMPOSE_CHANGE,
@@ -242,28 +242,28 @@ const replyCompose =
rebloggedBy?: ComposeReplyAction['rebloggedBy'],
approvalRequired?: ComposeReplyAction['approvalRequired'],
) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const state = getState();
- const { features } = getClient(getState);
- const { forceImplicitAddressing, preserveSpoilers } = useSettingsStore.getState().settings;
- const explicitAddressing = features.createStatusExplicitAddressing && !forceImplicitAddressing;
- const account = selectOwnAccount(state);
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ const state = getState();
+ const { features } = getClient(getState);
+ const { forceImplicitAddressing, preserveSpoilers } = useSettingsStore.getState().settings;
+ const explicitAddressing = features.createStatusExplicitAddressing && !forceImplicitAddressing;
+ const account = selectOwnAccount(state);
- if (!account) return;
+ if (!account) return;
- dispatch({
- type: COMPOSE_REPLY,
- composeId: 'compose-modal',
- status,
- account,
- explicitAddressing,
- preserveSpoilers,
- rebloggedBy,
- approvalRequired,
- conversationScope: features.createStatusConversationScope,
- });
- useModalsStore.getState().actions.openModal('COMPOSE');
- };
+ dispatch({
+ type: COMPOSE_REPLY,
+ composeId: 'compose-modal',
+ status,
+ account,
+ explicitAddressing,
+ preserveSpoilers,
+ rebloggedBy,
+ approvalRequired,
+ conversationScope: features.createStatusConversationScope,
+ });
+ useModalsStore.getState().actions.openModal('COMPOSE');
+ };
const cancelReplyCompose = () => ({
type: COMPOSE_REPLY_CANCEL,
@@ -323,16 +323,16 @@ interface ComposeMentionAction {
const mentionCompose =
(account: ComposeMentionAction['account']) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!getState().me) return;
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!getState().me) return;
- dispatch({
- type: COMPOSE_MENTION,
- composeId: 'compose-modal',
- account: account,
- });
- useModalsStore.getState().actions.openModal('COMPOSE');
- };
+ dispatch({
+ type: COMPOSE_MENTION,
+ composeId: 'compose-modal',
+ account: account,
+ });
+ useModalsStore.getState().actions.openModal('COMPOSE');
+ };
interface ComposeDirectAction {
type: typeof COMPOSE_DIRECT;
@@ -376,9 +376,9 @@ const handleComposeSubmit = (
data.visibility === 'direct' && getClient(getState()).features.conversations
? { to: '/conversations' }
: {
- to: '/@{$username}/posts/$statusId',
- params: { username: data.account.acct, statusId: data.id },
- };
+ to: '/@{$username}/posts/$statusId',
+ params: { username: data.account.acct, statusId: data.id },
+ };
toast.success(
redact ? messages.redactSuccess : edit ? messages.editSuccess : messages.success,
{
@@ -423,159 +423,159 @@ interface SubmitComposeOpts {
const submitCompose =
(composeId: string, opts: SubmitComposeOpts = {}, preview = false) =>
- async (dispatch: AppDispatch, getState: () => RootState) => {
- const { force = false, onSuccess } = opts;
+ async (dispatch: AppDispatch, getState: () => RootState) => {
+ const { force = false, onSuccess } = opts;
- if (!isLoggedIn(getState)) return;
- const state = getState();
+ if (!isLoggedIn(getState)) return;
+ const state = getState();
- const compose = state.compose[composeId];
+ const compose = state.compose[composeId];
- const status = compose.text;
- const media = compose.mediaAttachments;
- const editedId = compose.editedId;
- let to = compose.to;
- const { forceImplicitAddressing } = useSettingsStore.getState().settings;
- const explicitAddressing =
+ const status = compose.text;
+ const media = compose.mediaAttachments;
+ const editedId = compose.editedId;
+ let to = compose.to;
+ const { forceImplicitAddressing } = useSettingsStore.getState().settings;
+ const explicitAddressing =
state.auth.client.features.createStatusExplicitAddressing && !forceImplicitAddressing;
- if (!preview) {
- if (!validateSchedule(state, composeId)) {
- toast.error(messages.scheduleError);
- return;
- }
-
- if ((!status || !status.length) && media.length === 0) {
- return;
- }
-
- if (!force && needsDescriptions(state, composeId)) {
- useModalsStore.getState().actions.openModal('MISSING_DESCRIPTION', {
- onContinue: () => {
- useModalsStore.getState().actions.closeModal('MISSING_DESCRIPTION');
- dispatch(submitCompose(composeId, { force: true, onSuccess }));
- },
- });
- return;
- }
+ if (!preview) {
+ if (!validateSchedule(state, composeId)) {
+ toast.error(messages.scheduleError);
+ return;
}
- // https://stackoverflow.com/a/30007882 for domain regex
- const mentions: string[] | null = status.match(
- /(?:^|\s)@([a-z\d_-]+(?:@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]+)?)/gi,
- );
-
- if (mentions) {
- to = [
- ...new Set([
- ...to,
- ...mentions.map((mention) =>
- mention
- .replace(/ /g, '')
- .trim()
- .slice(1),
- ),
- ]),
- ];
+ if ((!status || !status.length) && media.length === 0) {
+ return;
}
- if (!preview) {
- dispatch(submitComposeRequest(composeId));
-
- useModalsStore.getState().actions.closeModal('COMPOSE');
-
- if (compose.language && !editedId && !preview) {
- useSettingsStore.getState().actions.rememberLanguageUse(compose.language);
- dispatch(saveSettings());
- }
+ if (!force && needsDescriptions(state, composeId)) {
+ useModalsStore.getState().actions.openModal('MISSING_DESCRIPTION', {
+ onContinue: () => {
+ useModalsStore.getState().actions.closeModal('MISSING_DESCRIPTION');
+ dispatch(submitCompose(composeId, { force: true, onSuccess }));
+ },
+ });
+ return;
}
+ }
- const idempotencyKey = compose.idempotencyKey;
- const contentType = compose.contentType === 'wysiwyg' ? 'text/markdown' : compose.contentType;
+ // https://stackoverflow.com/a/30007882 for domain regex
+ const mentions: string[] | null = status.match(
+ /(?:^|\s)@([a-z\d_-]+(?:@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]+)?)/gi,
+ );
- const params: CreateStatusParams = {
- status,
- in_reply_to_id: compose.inReplyToId ?? undefined,
- quote_id: compose.quoteId ?? undefined,
- media_ids: media.map((item) => item.id),
- sensitive: compose.sensitive,
- spoiler_text: compose.spoilerText,
- visibility: compose.visibility,
- content_type: contentType,
- scheduled_at: preview ? undefined : compose.scheduledAt?.toISOString(),
- language: (compose.language ?? compose.suggestedLanguage) ?? undefined,
- to: explicitAddressing && to.length ? to : undefined,
- local_only: compose.localOnly,
- interaction_policy:
+ if (mentions) {
+ to = [
+ ...new Set([
+ ...to,
+ ...mentions.map((mention) =>
+ mention
+ .replace(/ /g, '')
+ .trim()
+ .slice(1),
+ ),
+ ]),
+ ];
+ }
+
+ if (!preview) {
+ dispatch(submitComposeRequest(composeId));
+
+ useModalsStore.getState().actions.closeModal('COMPOSE');
+
+ if (compose.language && !editedId && !preview) {
+ useSettingsStore.getState().actions.rememberLanguageUse(compose.language);
+ dispatch(saveSettings());
+ }
+ }
+
+ const idempotencyKey = compose.idempotencyKey;
+ const contentType = compose.contentType === 'wysiwyg' ? 'text/markdown' : compose.contentType;
+
+ const params: CreateStatusParams = {
+ status,
+ in_reply_to_id: compose.inReplyToId ?? undefined,
+ quote_id: compose.quoteId ?? undefined,
+ media_ids: media.map((item) => item.id),
+ sensitive: compose.sensitive,
+ spoiler_text: compose.spoilerText,
+ visibility: compose.visibility,
+ content_type: contentType,
+ scheduled_at: preview ? undefined : compose.scheduledAt?.toISOString(),
+ language: compose.language ?? compose.suggestedLanguage ?? undefined,
+ to: explicitAddressing && to.length ? to : undefined,
+ local_only: compose.localOnly,
+ interaction_policy:
(['public', 'unlisted', 'private'].includes(compose.visibility) &&
compose.interactionPolicy) ??
undefined,
- quote_approval_policy: compose.quoteApprovalPolicy ?? undefined,
- location_id: compose.location?.origin_id ?? undefined,
- preview,
- };
-
- if (compose.poll) {
- params.poll = {
- options: compose.poll.options,
- expires_in: compose.poll.expires_in,
- multiple: compose.poll.multiple,
- hide_totals: compose.poll.hide_totals,
- options_map: compose.poll.options_map,
- };
- }
-
- if (compose.language && Object.keys(compose.textMap).length) {
- params.status_map = compose.textMap;
- params.status_map[compose.language] = status;
-
- if (params.spoiler_text) {
- params.spoiler_text_map = compose.spoilerTextMap;
- params.spoiler_text_map[compose.language] = compose.spoilerText;
- }
-
- const poll = params.poll;
- if (poll?.options_map) {
- poll.options.forEach(
- (option, index: number) => (poll.options_map![index][compose.language!] = option),
- );
- }
- }
-
- if (compose.visibility === 'group' && compose.groupId) {
- params.group_id = compose.groupId;
- }
-
- if (preview) {
- const data = await getClient(state).statuses.previewStatus(params);
- dispatch(previewComposeSuccess(composeId, data));
- onSuccess?.();
- } else {
- if (compose.redacting) {
- // @ts-ignore
- params.overwrite = compose.redactingOverwrite;
- }
-
- try {
- const data = await dispatch(
- createStatus(params, idempotencyKey, editedId, compose.redacting),
- );
- handleComposeSubmit(
- dispatch,
- getState,
- composeId,
- data,
- status,
- !!editedId,
- compose.redacting,
- );
- onSuccess?.();
- } catch (error) {
- dispatch(submitComposeFail(composeId, error));
- }
- }
+ quote_approval_policy: compose.quoteApprovalPolicy ?? undefined,
+ location_id: compose.location?.origin_id ?? undefined,
+ preview,
};
+ if (compose.poll) {
+ params.poll = {
+ options: compose.poll.options,
+ expires_in: compose.poll.expires_in,
+ multiple: compose.poll.multiple,
+ hide_totals: compose.poll.hide_totals,
+ options_map: compose.poll.options_map,
+ };
+ }
+
+ if (compose.language && Object.keys(compose.textMap).length) {
+ params.status_map = compose.textMap;
+ params.status_map[compose.language] = status;
+
+ if (params.spoiler_text) {
+ params.spoiler_text_map = compose.spoilerTextMap;
+ params.spoiler_text_map[compose.language] = compose.spoilerText;
+ }
+
+ const poll = params.poll;
+ if (poll?.options_map) {
+ poll.options.forEach(
+ (option, index: number) => (poll.options_map![index][compose.language!] = option),
+ );
+ }
+ }
+
+ if (compose.visibility === 'group' && compose.groupId) {
+ params.group_id = compose.groupId;
+ }
+
+ if (preview) {
+ const data = await getClient(state).statuses.previewStatus(params);
+ dispatch(previewComposeSuccess(composeId, data));
+ onSuccess?.();
+ } else {
+ if (compose.redacting) {
+ // @ts-ignore
+ params.overwrite = compose.redactingOverwrite;
+ }
+
+ try {
+ const data = await dispatch(
+ createStatus(params, idempotencyKey, editedId, compose.redacting),
+ );
+ handleComposeSubmit(
+ dispatch,
+ getState,
+ composeId,
+ data,
+ status,
+ !!editedId,
+ compose.redacting,
+ );
+ onSuccess?.();
+ } catch (error) {
+ dispatch(submitComposeFail(composeId, error));
+ }
+ }
+ };
+
const submitComposeRequest = (composeId: string) => ({
type: COMPOSE_SUBMIT_REQUEST,
composeId,
@@ -606,47 +606,47 @@ const cancelPreviewCompose = (composeId: string) => ({
const uploadCompose =
(composeId: string, files: FileList, intl: IntlShape) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
- const attachmentLimit = getState().instance.configuration.statuses.max_media_attachments;
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
+ const attachmentLimit = getState().instance.configuration.statuses.max_media_attachments;
- const media = getState().compose[composeId]?.mediaAttachments;
- const progress = new Array(files.length).fill(0);
- let total = Array.from(files).reduce((a, v) => a + v.size, 0);
+ const media = getState().compose[composeId]?.mediaAttachments;
+ const progress = new Array(files.length).fill(0);
+ let total = Array.from(files).reduce((a, v) => a + v.size, 0);
- const mediaCount = media ? media.length : 0;
+ const mediaCount = media ? media.length : 0;
- if (files.length + mediaCount > attachmentLimit) {
- toast.error(messages.uploadErrorLimit);
- return;
- }
+ if (files.length + mediaCount > attachmentLimit) {
+ toast.error(messages.uploadErrorLimit);
+ return;
+ }
- dispatch(uploadComposeRequest(composeId));
+ dispatch(uploadComposeRequest(composeId));
- Array.from(files).forEach((f, i) => {
- if (mediaCount + i > attachmentLimit - 1) return;
+ Array.from(files).forEach((f, i) => {
+ if (mediaCount + i > attachmentLimit - 1) return;
- dispatch(
- uploadFile(
- f,
- intl,
- (data) => dispatch(uploadComposeSuccess(composeId, data)),
- (error) => dispatch(uploadComposeFail(composeId, error)),
- ({ loaded }) => {
- progress[i] = loaded;
- dispatch(
- uploadComposeProgress(
- composeId,
- progress.reduce((a, v) => a + v, 0),
- total,
- ),
- );
- },
- (value) => (total += value),
- ),
- );
- });
- };
+ dispatch(
+ uploadFile(
+ f,
+ intl,
+ (data) => dispatch(uploadComposeSuccess(composeId, data)),
+ (error) => dispatch(uploadComposeFail(composeId, error)),
+ ({ loaded }) => {
+ progress[i] = loaded;
+ dispatch(
+ uploadComposeProgress(
+ composeId,
+ progress.reduce((a, v) => a + v, 0),
+ total,
+ ),
+ );
+ },
+ (value) => (total += value),
+ ),
+ );
+ });
+ };
const uploadComposeRequest = (composeId: string) => ({
type: COMPOSE_UPLOAD_REQUEST,
@@ -674,19 +674,19 @@ const uploadComposeFail = (composeId: string, error: unknown) => ({
const changeUploadCompose =
(composeId: string, mediaId: string, params: UpdateMediaParams) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return Promise.resolve();
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return Promise.resolve();
- dispatch(changeUploadComposeRequest(composeId));
+ dispatch(changeUploadComposeRequest(composeId));
- return dispatch(updateMedia(mediaId, params))
- .then((response) => {
- dispatch(changeUploadComposeSuccess(composeId, response));
- })
- .catch((error) => {
- dispatch(changeUploadComposeFail(composeId, mediaId, error));
- });
- };
+ return dispatch(updateMedia(mediaId, params))
+ .then((response) => {
+ dispatch(changeUploadComposeSuccess(composeId, response));
+ })
+ .catch((error) => {
+ dispatch(changeUploadComposeFail(composeId, mediaId, error));
+ });
+ };
const changeUploadComposeRequest = (composeId: string) => ({
type: COMPOSE_UPLOAD_CHANGE_REQUEST,
@@ -854,33 +854,33 @@ const selectComposeSuggestion =
suggestion: AutoSuggestion,
path: ComposeSuggestionSelectAction['path'],
) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- let completion = '',
- startPosition = position;
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ let completion = '',
+ startPosition = position;
- if (typeof suggestion === 'object' && 'id' in suggestion) {
- completion = isNativeEmoji(suggestion) ? suggestion.native : suggestion.colons;
- startPosition = position - 1;
+ if (typeof suggestion === 'object' && 'id' in suggestion) {
+ completion = isNativeEmoji(suggestion) ? suggestion.native : suggestion.colons;
+ startPosition = position - 1;
- useSettingsStore.getState().actions.rememberEmojiUse(suggestion);
- dispatch(saveSettings());
- } else if (typeof suggestion === 'string' && suggestion[0] === '#') {
- completion = suggestion;
- startPosition = position - 1;
- } else if (typeof suggestion === 'string') {
- completion = selectAccount(getState(), suggestion)!.acct;
- startPosition = position;
- }
+ useSettingsStore.getState().actions.rememberEmojiUse(suggestion);
+ dispatch(saveSettings());
+ } else if (typeof suggestion === 'string' && suggestion[0] === '#') {
+ completion = suggestion;
+ startPosition = position - 1;
+ } else if (typeof suggestion === 'string') {
+ completion = selectAccount(getState(), suggestion)!.acct;
+ startPosition = position;
+ }
- dispatch({
- type: COMPOSE_SUGGESTION_SELECT,
- composeId,
- position: startPosition,
- token,
- completion,
- path,
- });
- };
+ dispatch({
+ type: COMPOSE_SUGGESTION_SELECT,
+ composeId,
+ position: startPosition,
+ token,
+ completion,
+ path,
+ });
+ };
const updateSuggestionTags = (composeId: string, token: string, tags: Array) => ({
type: COMPOSE_SUGGESTION_TAGS_UPDATE,
@@ -990,11 +990,11 @@ const changePollSettings = (composeId: string, expiresIn?: number, isMultiple?:
const openComposeWithText =
(composeId: string, text = '') =>
- (dispatch: AppDispatch) => {
- dispatch(resetCompose(composeId));
- useModalsStore.getState().actions.openModal('COMPOSE');
- dispatch(changeCompose(composeId, text));
- };
+ (dispatch: AppDispatch) => {
+ dispatch(resetCompose(composeId));
+ useModalsStore.getState().actions.openModal('COMPOSE');
+ dispatch(changeCompose(composeId, text));
+ };
interface ComposeAddToMentionsAction {
type: typeof COMPOSE_ADD_TO_MENTIONS;
@@ -1044,20 +1044,20 @@ interface ComposeEventReplyAction {
const eventDiscussionCompose =
(composeId: string, status: ComposeEventReplyAction['status']) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const state = getState();
- const { forceImplicitAddressing } = useSettingsStore.getState().settings;
- const explicitAddressing =
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ const state = getState();
+ const { forceImplicitAddressing } = useSettingsStore.getState().settings;
+ const explicitAddressing =
state.auth.client.features.createStatusExplicitAddressing && !forceImplicitAddressing;
- return dispatch({
- type: COMPOSE_EVENT_REPLY,
- composeId,
- status,
- account: selectOwnAccount(state),
- explicitAddressing,
- });
- };
+ return dispatch({
+ type: COMPOSE_EVENT_REPLY,
+ composeId,
+ status,
+ account: selectOwnAccount(state),
+ explicitAddressing,
+ });
+ };
const setEditorState = (
composeId: string,
diff --git a/packages/pl-fe/src/actions/consumer-auth.ts b/packages/pl-fe/src/actions/consumer-auth.ts
index 226035eba..fecbea090 100644
--- a/packages/pl-fe/src/actions/consumer-auth.ts
+++ b/packages/pl-fe/src/actions/consumer-auth.ts
@@ -9,22 +9,21 @@ import { createApp } from './apps';
import type { AppDispatch, RootState } from '@/store';
-const createProviderApp = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const scopes = getScopes(getState(), undefined, true);
+const createProviderApp = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const scopes = getScopes(getState(), undefined, true);
- const params = {
- client_name: `${sourceCode.displayName} (${new URL(window.origin).host})`,
- redirect_uris: `${window.location.origin}/login/external`,
- website: sourceCode.homepage,
- scopes,
- };
-
- return createApp(params);
+ const params = {
+ client_name: `${sourceCode.displayName} (${new URL(window.origin).host})`,
+ redirect_uris: `${window.location.origin}/login/external`,
+ website: sourceCode.homepage,
+ scopes,
};
-const prepareRequest = (provider: string) =>
- async(dispatch: AppDispatch, getState: () => RootState) => {
+ return createApp(params);
+};
+
+const prepareRequest =
+ (provider: string) => async (dispatch: AppDispatch, getState: () => RootState) => {
const baseURL = isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : '';
const scopes = getScopes(getState(), undefined, true);
@@ -47,6 +46,4 @@ const prepareRequest = (provider: string) =>
location.href = `${baseURL}/oauth/prepare_request?${query.toString()}`;
};
-export {
- prepareRequest,
-};
+export { prepareRequest };
diff --git a/packages/pl-fe/src/actions/conversations.ts b/packages/pl-fe/src/actions/conversations.ts
index 7fe537784..f79b47ff3 100644
--- a/packages/pl-fe/src/actions/conversations.ts
+++ b/packages/pl-fe/src/actions/conversations.ts
@@ -26,37 +26,45 @@ interface ConversationsReadAction {
conversationId: string;
}
-const markConversationRead = (conversationId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
+const markConversationRead =
+ (conversationId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
- dispatch({
- type: CONVERSATIONS_READ,
- conversationId,
- });
+ dispatch({
+ type: CONVERSATIONS_READ,
+ conversationId,
+ });
- return getClient(getState).timelines.markConversationRead(conversationId);
-};
+ return getClient(getState).timelines.markConversationRead(conversationId);
+ };
-const expandConversations = (expand = true) => (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
- const state = getState();
- if (state.conversations.isLoading) return;
+const expandConversations =
+ (expand = true) =>
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
+ const state = getState();
+ if (state.conversations.isLoading) return;
- const hasMore = state.conversations.hasMore;
- if (expand && !hasMore) return;
+ const hasMore = state.conversations.hasMore;
+ if (expand && !hasMore) return;
- dispatch(expandConversationsRequest());
+ dispatch(expandConversationsRequest());
- return (state.conversations.next?.() ?? getClient(state).timelines.getConversations())
- .then(response => {
- dispatch(importEntities({
- accounts: response.items.reduce((aggr: Array, item) => aggr.concat(item.accounts), []),
- statuses: response.items.map((item) => item.last_status),
- }));
- dispatch(expandConversationsSuccess(response.items, response.next, expand));
- })
- .catch(err => dispatch(expandConversationsFail(err)));
-};
+ return (state.conversations.next?.() ?? getClient(state).timelines.getConversations())
+ .then((response) => {
+ dispatch(
+ importEntities({
+ accounts: response.items.reduce(
+ (aggr: Array, item) => aggr.concat(item.accounts),
+ [],
+ ),
+ statuses: response.items.map((item) => item.last_status),
+ }),
+ );
+ dispatch(expandConversationsSuccess(response.items, response.next, expand));
+ })
+ .catch((err) => dispatch(expandConversationsFail(err)));
+ };
const expandConversationsRequest = () => ({ type: CONVERSATIONS_FETCH_REQUEST });
@@ -82,10 +90,12 @@ interface ConversataionsUpdateAction {
}
const updateConversations = (conversation: Conversation) => (dispatch: AppDispatch) => {
- dispatch(importEntities({
- accounts: conversation.accounts,
- statuses: [conversation.last_status],
- }));
+ dispatch(
+ importEntities({
+ accounts: conversation.accounts,
+ statuses: [conversation.last_status],
+ }),
+ );
return dispatch({
type: CONVERSATIONS_UPDATE,
@@ -100,7 +110,7 @@ type ConversationsAction =
| ReturnType
| ReturnType
| ReturnType
- | ConversataionsUpdateAction
+ | ConversataionsUpdateAction;
export {
CONVERSATIONS_MOUNT,
diff --git a/packages/pl-fe/src/actions/emoji-reacts.ts b/packages/pl-fe/src/actions/emoji-reacts.ts
index 7320460ab..da80d722a 100644
--- a/packages/pl-fe/src/actions/emoji-reacts.ts
+++ b/packages/pl-fe/src/actions/emoji-reacts.ts
@@ -17,45 +17,61 @@ const EMOJI_REACT_FAIL = 'EMOJI_REACT_FAIL' as const;
const UNEMOJI_REACT_REQUEST = 'UNEMOJI_REACT_REQUEST' as const;
const messages = defineMessages({
- unsupported: { id: 'emoji_reactions.unsupported_by_remote', defaultMessage: '@{acct}’s instance most likely doesn’t understand emoji reactions. The user will not get notified of the reaction.' },
+ unsupported: {
+ id: 'emoji_reactions.unsupported_by_remote',
+ defaultMessage:
+ '@{acct}’s instance most likely doesn’t understand emoji reactions. The user will not get notified of the reaction.',
+ },
});
-const noOp = () => () => new Promise(f =>{
- f(undefined);
-});
+const noOp = () => () =>
+ new Promise((f) => {
+ f(undefined);
+ });
-const emojiReact = (statusId: string, emoji: string, custom: string | undefined = undefined, intl: IntlShape) =>
+const emojiReact =
+ (statusId: string, emoji: string, custom: string | undefined = undefined, intl: IntlShape) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return dispatch(noOp());
dispatch(emojiReactRequest(statusId, emoji, custom));
- return getClient(getState).statuses.createStatusReaction(statusId, emoji).then((response) => {
- dispatch(importEntities({ statuses: [response] }));
+ return getClient(getState)
+ .statuses.createStatusReaction(statusId, emoji)
+ .then((response) => {
+ dispatch(importEntities({ statuses: [response] }));
- const checkEmojiReactsSupport = !response.account.local && useSettingsStore.getState().settings.checkEmojiReactsSupport;
+ const checkEmojiReactsSupport =
+ !response.account.local && useSettingsStore.getState().settings.checkEmojiReactsSupport;
- if (checkEmojiReactsSupport) {
- supportsEmojiReacts(response.account.ap_id ?? response.account.url).then((result) => {
- if (result === 'false') {
- toast.info(intl.formatMessage(messages.unsupported, { acct: response.account.acct }));
- }
- }).catch((e) => {});
- }
- }).catch((error) => {
- dispatch(emojiReactFail(statusId, emoji, error));
- });
+ if (checkEmojiReactsSupport) {
+ supportsEmojiReacts(response.account.ap_id ?? response.account.url)
+ .then((result) => {
+ if (result === 'false') {
+ toast.info(
+ intl.formatMessage(messages.unsupported, { acct: response.account.acct }),
+ );
+ }
+ })
+ .catch((e) => {});
+ }
+ })
+ .catch((error) => {
+ dispatch(emojiReactFail(statusId, emoji, error));
+ });
};
-const unEmojiReact = (statusId: string, emoji: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const unEmojiReact =
+ (statusId: string, emoji: string) => (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return dispatch(noOp());
dispatch(unEmojiReactRequest(statusId, emoji));
- return getClient(getState).statuses.deleteStatusReaction(statusId, emoji).then(response => {
- dispatch(importEntities({ statuses: [response] }));
- });
+ return getClient(getState)
+ .statuses.deleteStatusReaction(statusId, emoji)
+ .then((response) => {
+ dispatch(importEntities({ statuses: [response] }));
+ });
};
const emojiReactRequest = (statusId: string, emoji: string, custom?: string) => ({
@@ -81,7 +97,7 @@ const unEmojiReactRequest = (statusId: string, emoji: string) => ({
type EmojiReactsAction =
| ReturnType
| ReturnType
- | ReturnType
+ | ReturnType;
export {
EMOJI_REACT_REQUEST,
diff --git a/packages/pl-fe/src/actions/events.ts b/packages/pl-fe/src/actions/events.ts
index 4c5560c74..103deffb5 100644
--- a/packages/pl-fe/src/actions/events.ts
+++ b/packages/pl-fe/src/actions/events.ts
@@ -4,7 +4,11 @@ import { getClient } from '@/api';
import toast from '@/toast';
import { importEntities } from './importer';
-import { STATUS_FETCH_SOURCE_FAIL, STATUS_FETCH_SOURCE_REQUEST, STATUS_FETCH_SOURCE_SUCCESS } from './statuses';
+import {
+ STATUS_FETCH_SOURCE_FAIL,
+ STATUS_FETCH_SOURCE_REQUEST,
+ STATUS_FETCH_SOURCE_SUCCESS,
+} from './statuses';
import type { AppDispatch, RootState } from '@/store';
import type { CreateEventParams, Location, MediaAttachment, Status } from 'pl-api';
@@ -20,33 +24,43 @@ const EVENT_COMPOSE_CANCEL = 'EVENT_COMPOSE_CANCEL' as const;
const EVENT_FORM_SET = 'EVENT_FORM_SET' as const;
const messages = defineMessages({
- exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
+ exceededImageSizeLimit: {
+ id: 'upload_error.image_size_limit',
+ defaultMessage: 'Image exceeds the current file size limit ({limit})',
+ },
success: { id: 'compose_event.submit_success', defaultMessage: 'Your event was created' },
editSuccess: { id: 'compose_event.edit_success', defaultMessage: 'Your event was edited' },
view: { id: 'toast.view', defaultMessage: 'View' },
- authorized: { id: 'compose_event.participation_requests.authorize_success', defaultMessage: 'User accepted' },
- rejected: { id: 'compose_event.participation_requests.reject_success', defaultMessage: 'User rejected' },
+ authorized: {
+ id: 'compose_event.participation_requests.authorize_success',
+ defaultMessage: 'User accepted',
+ },
+ rejected: {
+ id: 'compose_event.participation_requests.reject_success',
+ defaultMessage: 'User rejected',
+ },
});
-const submitEvent = ({
- statusId,
- name,
- status,
- banner,
- startTime,
- endTime,
- joinMode,
- location,
-}: {
- statusId: string | null;
- name: string;
- status: string;
- banner: MediaAttachment | null;
- startTime: Date;
- endTime: Date | null;
- joinMode: 'restricted' | 'free';
- location: Location | null;
-}) =>
+const submitEvent =
+ ({
+ statusId,
+ name,
+ status,
+ banner,
+ startTime,
+ endTime,
+ joinMode,
+ location,
+ }: {
+ statusId: string | null;
+ name: string;
+ status: string;
+ banner: MediaAttachment | null;
+ startTime: Date;
+ endTime: Date | null;
+ joinMode: 'restricted' | 'free';
+ location: Location | null;
+ }) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
@@ -66,23 +80,18 @@ const submitEvent = ({
if (banner) params.banner_id = banner.id;
if (location) params.location_id = location.origin_id;
- const data = await (
- statusId === null
- ? getClient(state).events.createEvent(params)
- : getClient(state).events.editEvent(statusId, params)
- );
+ const data = await (statusId === null
+ ? getClient(state).events.createEvent(params)
+ : getClient(state).events.editEvent(statusId, params));
dispatch(importEntities({ statuses: [data] }));
- toast.success(
- statusId ? messages.editSuccess : messages.success,
- {
- actionLabel: messages.view,
- actionLinkOptions: {
- to: '/@{$username}/events/$statusId',
- params: { username: data.account.acct, statusId: data.id },
- },
+ toast.success(statusId ? messages.editSuccess : messages.success, {
+ actionLabel: messages.view,
+ actionLinkOptions: {
+ to: '/@{$username}/events/$statusId',
+ params: { username: data.account.acct, statusId: data.id },
},
- );
+ });
return data;
};
@@ -111,9 +120,8 @@ interface LeaveEventFail {
previousState: Exclude['join_state'] | null;
}
-const fetchEventIcs = (statusId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).events.getEventIcs(statusId);
+const fetchEventIcs = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState).events.getEventIcs(statusId);
const cancelEventCompose = () => ({
type: EVENT_COMPOSE_CANCEL,
@@ -128,21 +136,24 @@ interface EventFormSetAction {
const initEventEdit = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: STATUS_FETCH_SOURCE_REQUEST, statusId });
- return getClient(getState()).statuses.getStatusSource(statusId).then(response => {
- dispatch({ type: STATUS_FETCH_SOURCE_SUCCESS, statusId });
- dispatch({
- type: EVENT_FORM_SET,
- composeId: `compose-event-modal-${statusId}`,
- text: response.text,
+ return getClient(getState())
+ .statuses.getStatusSource(statusId)
+ .then((response) => {
+ dispatch({ type: STATUS_FETCH_SOURCE_SUCCESS, statusId });
+ dispatch({
+ type: EVENT_FORM_SET,
+ composeId: `compose-event-modal-${statusId}`,
+ text: response.text,
+ });
+ return response;
+ })
+ .catch((error) => {
+ dispatch({ type: STATUS_FETCH_SOURCE_FAIL, statusId, error });
});
- return response;
- }).catch(error => {
- dispatch({ type: STATUS_FETCH_SOURCE_FAIL, statusId, error });
- });
};
type EventsAction =
- JoinEventRequest
+ | JoinEventRequest
| JoinEventFail
| LeaveEventRequest
| LeaveEventFail
diff --git a/packages/pl-fe/src/actions/export-data.ts b/packages/pl-fe/src/actions/export-data.ts
index 486ef47ec..6949d9867 100644
--- a/packages/pl-fe/src/actions/export-data.ts
+++ b/packages/pl-fe/src/actions/export-data.ts
@@ -7,8 +7,14 @@ import type { AppDispatch, RootState } from '@/store';
import type { Account, PaginatedResponse } from 'pl-api';
const messages = defineMessages({
- blocksSuccess: { id: 'export_data.success.blocks', defaultMessage: 'Blocks exported successfully' },
- followersSuccess: { id: 'export_data.success.followers', defaultMessage: 'Followers exported successfully' },
+ blocksSuccess: {
+ id: 'export_data.success.blocks',
+ defaultMessage: 'Blocks exported successfully',
+ },
+ followersSuccess: {
+ id: 'export_data.success.followers',
+ defaultMessage: 'Followers exported successfully',
+ },
mutesSuccess: { id: 'export_data.success.mutes', defaultMessage: 'Mutes exported successfully' },
});
@@ -41,7 +47,7 @@ const exportFollows = () => async (_dispatch: AppDispatch, getState: () => RootS
const response = await getClient(getState()).accounts.getAccountFollowing(me, { limit: 40 });
const followings = await listAccounts(response);
- const followingsCsv = followings.map(fqn => fqn + ',true');
+ const followingsCsv = followings.map((fqn) => fqn + ',true');
followingsCsv.unshift('Account address,Show boosts');
fileExport(followingsCsv.join('\n'), 'export_followings.csv');
@@ -64,8 +70,4 @@ const exportMutes = () => async (_dispatch: AppDispatch, getState: () => RootSta
toast.success(messages.mutesSuccess);
};
-export {
- exportFollows,
- exportBlocks,
- exportMutes,
-};
+export { exportFollows, exportBlocks, exportMutes };
diff --git a/packages/pl-fe/src/actions/external-auth.ts b/packages/pl-fe/src/actions/external-auth.ts
index 8e9769873..35a9a03a6 100644
--- a/packages/pl-fe/src/actions/external-auth.ts
+++ b/packages/pl-fe/src/actions/external-auth.ts
@@ -19,9 +19,10 @@ import { getInstanceScopes } from '@/utils/scopes';
import type { AppDispatch } from '@/store';
const fetchExternalInstance = (baseURL: string) =>
- (new PlApiClient(baseURL)).instance.getInstance()
- .then(instance => instance)
- .catch(error => {
+ new PlApiClient(baseURL).instance
+ .getInstance()
+ .then((instance) => instance)
+ .catch((error) => {
if (error.response?.status === 401) {
// Authenticated fetch is enabled.
// Continue with a limited featureset.
@@ -71,30 +72,26 @@ const externalLogin = (host: string) => {
});
};
-const loginWithCode = (code: string) =>
- (dispatch: AppDispatch) => {
- const app = JSON.parse(localStorage.getItem('plfe:external:app')!);
- const { client_id, client_secret, redirect_uri } = app;
- const baseURL = localStorage.getItem('plfe:external:baseurl')!;
- const scope = localStorage.getItem('plfe:external:scopes')!;
+const loginWithCode = (code: string) => (dispatch: AppDispatch) => {
+ const app = JSON.parse(localStorage.getItem('plfe:external:app')!);
+ const { client_id, client_secret, redirect_uri } = app;
+ const baseURL = localStorage.getItem('plfe:external:baseurl')!;
+ const scope = localStorage.getItem('plfe:external:scopes')!;
- const params = {
- client_id,
- client_secret,
- redirect_uri,
- grant_type: 'authorization_code',
- scope,
- code,
- };
-
- return obtainOAuthToken(params, baseURL)
- .then((token) => dispatch(authLoggedIn(token, app)))
- .then(({ access_token }) => dispatch(verifyCredentials(access_token, baseURL)))
- .then((account) => dispatch(switchAccount(account.id)))
- .then(() => window.location.href = '/');
+ const params = {
+ client_id,
+ client_secret,
+ redirect_uri,
+ grant_type: 'authorization_code',
+ scope,
+ code,
};
-export {
- externalLogin,
- loginWithCode,
+ return obtainOAuthToken(params, baseURL)
+ .then((token) => dispatch(authLoggedIn(token, app)))
+ .then(({ access_token }) => dispatch(verifyCredentials(access_token, baseURL)))
+ .then((account) => dispatch(switchAccount(account.id)))
+ .then(() => (window.location.href = '/'));
};
+
+export { externalLogin, loginWithCode };
diff --git a/packages/pl-fe/src/actions/filters.ts b/packages/pl-fe/src/actions/filters.ts
index b6e666287..ebecf102a 100644
--- a/packages/pl-fe/src/actions/filters.ts
+++ b/packages/pl-fe/src/actions/filters.ts
@@ -18,55 +18,76 @@ const messages = defineMessages({
type FilterKeywords = { keyword: string; whole_word: boolean }[];
-const fetchFilters = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
+const fetchFilters = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
- return getClient(getState).filtering.getFilters()
- .then((data) => dispatch({
+ return getClient(getState)
+ .filtering.getFilters()
+ .then((data) =>
+ dispatch({
type: FILTERS_FETCH_SUCCESS,
filters: data,
- }))
- .catch(error => ({
- error,
- }));
- };
+ }),
+ )
+ .catch((error) => ({
+ error,
+ }));
+};
-const fetchFilter = (filterId: string) =>
+const fetchFilter = (filterId: string) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState).filtering.getFilter(filterId);
+
+const createFilter =
+ (
+ title: string,
+ expires_in: number | undefined,
+ context: Array,
+ filter_action: Filter['filter_action'],
+ keywords_attributes: FilterKeywords,
+ ) =>
(dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).filtering.getFilter(filterId);
+ getClient(getState)
+ .filtering.createFilter({
+ title,
+ context,
+ filter_action,
+ expires_in,
+ keywords_attributes,
+ })
+ .then((response) => {
+ toast.success(messages.added);
-const createFilter = (title: string, expires_in: number | undefined, context: Array, filter_action: Filter['filter_action'], keywords_attributes: FilterKeywords) =>
+ return response;
+ });
+
+const updateFilter =
+ (
+ filterId: string,
+ title: string,
+ expires_in: number | undefined,
+ context: Array,
+ filter_action: Filter['filter_action'],
+ keywords_attributes: FilterKeywords,
+ ) =>
(dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).filtering.createFilter({
- title,
- context,
- filter_action,
- expires_in,
- keywords_attributes,
- }).then(response => {
- toast.success(messages.added);
+ getClient(getState)
+ .filtering.updateFilter(filterId, {
+ title,
+ context,
+ filter_action,
+ expires_in,
+ keywords_attributes,
+ })
+ .then((response) => {
+ toast.success(messages.updated);
- return response;
- });
+ return response;
+ });
-const updateFilter = (filterId: string, title: string, expires_in: number | undefined, context: Array, filter_action: Filter['filter_action'], keywords_attributes: FilterKeywords) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).filtering.updateFilter(filterId, {
- title,
- context,
- filter_action,
- expires_in,
- keywords_attributes,
- }).then(response => {
- toast.success(messages.updated);
-
- return response;
- });
-
-const deleteFilter = (filterId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).filtering.deleteFilter(filterId).then(response => {
+const deleteFilter = (filterId: string) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState)
+ .filtering.deleteFilter(filterId)
+ .then((response) => {
toast.success(messages.removed);
return response;
diff --git a/packages/pl-fe/src/actions/frontend-config.ts b/packages/pl-fe/src/actions/frontend-config.ts
index 73b4833ba..b09271bc2 100644
--- a/packages/pl-fe/src/actions/frontend-config.ts
+++ b/packages/pl-fe/src/actions/frontend-config.ts
@@ -16,29 +16,32 @@ const FRONTEND_CONFIG_REQUEST_FAIL = 'FRONTEND_CONFIG_REQUEST_FAIL' as const;
const FRONTEND_CONFIG_REMEMBER_SUCCESS = 'FRONTEND_CONFIG_REMEMBER_SUCCESS' as const;
-const getFrontendConfig = createSelector([
- (state: RootState) => state.frontendConfig,
-// Do some additional normalization with the state
-], (frontendConfig) => v.parse(frontendConfigSchema, frontendConfig));
+const getFrontendConfig = createSelector(
+ [
+ (state: RootState) => state.frontendConfig,
+ // Do some additional normalization with the state
+ ],
+ (frontendConfig) => v.parse(frontendConfigSchema, frontendConfig),
+);
-const rememberFrontendConfig = (host: string | null) =>
- (dispatch: AppDispatch) =>
- KVStore.getItemOrError(`plfe_config:${host}`).then(frontendConfig => {
+const rememberFrontendConfig = (host: string | null) => (dispatch: AppDispatch) =>
+ KVStore.getItemOrError(`plfe_config:${host}`)
+ .then((frontendConfig) => {
dispatch({ type: FRONTEND_CONFIG_REMEMBER_SUCCESS, host, frontendConfig });
return true;
- }).catch(() => false);
+ })
+ .catch(() => false);
-const fetchFrontendConfigurations = () =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).instance.getFrontendConfigurations();
+const fetchFrontendConfigurations = () => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState).instance.getFrontendConfigurations();
/** Conditionally fetches pl-fe config depending on backend features */
-const fetchFrontendConfig = (host: string | null) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const fetchFrontendConfig =
+ (host: string | null) => (dispatch: AppDispatch, getState: () => RootState) => {
const features = getState().auth.client.features;
if (features.frontendConfigurations) {
- return dispatch(fetchFrontendConfigurations()).then(data => {
+ return dispatch(fetchFrontendConfigurations()).then((data) => {
const key = 'pl_fe';
if (data[key]) {
dispatch(importFrontendConfig(data[key], host));
@@ -53,27 +56,27 @@ const fetchFrontendConfig = (host: string | null) =>
};
/** Tries to remember the config from browser storage before fetching it */
-const loadFrontendConfig = () =>
- async (dispatch: AppDispatch, getState: () => RootState) => {
- const host = getHost(getState());
+const loadFrontendConfig = () => async (dispatch: AppDispatch, getState: () => RootState) => {
+ const host = getHost(getState());
- const result = await dispatch(rememberFrontendConfig(host));
+ const result = await dispatch(rememberFrontendConfig(host));
- if (result) {
- dispatch(fetchFrontendConfig(host));
- return;
- } else {
- return dispatch(fetchFrontendConfig(host));
- }
- };
+ if (result) {
+ dispatch(fetchFrontendConfig(host));
+ return;
+ } else {
+ return dispatch(fetchFrontendConfig(host));
+ }
+};
-const fetchPlFeJson = (host: string | null) =>
- (dispatch: AppDispatch) =>
- staticFetch('/instance/pl-fe.json').then(({ json: data }) => {
+const fetchPlFeJson = (host: string | null) => (dispatch: AppDispatch) =>
+ staticFetch('/instance/pl-fe.json')
+ .then(({ json: data }) => {
if (!isObject(data)) throw 'pl-fe.json failed';
dispatch(importFrontendConfig(data, host));
return data;
- }).catch(error => {
+ })
+ .catch((error) => {
dispatch(frontendConfigFail(error, host));
});
diff --git a/packages/pl-fe/src/actions/importer.ts b/packages/pl-fe/src/actions/importer.ts
index 709bff183..e9c9e0a1e 100644
--- a/packages/pl-fe/src/actions/importer.ts
+++ b/packages/pl-fe/src/actions/importer.ts
@@ -4,12 +4,18 @@ import { queryClient } from '@/queries/client';
import { selectAccount } from '@/selectors';
import type { AppDispatch, RootState } from '@/store';
-import type { Account as BaseAccount, Group as BaseGroup, Poll as BasePoll, Relationship as BaseRelationship, Status as BaseStatus } from 'pl-api';
+import type {
+ Account as BaseAccount,
+ Group as BaseGroup,
+ Poll as BasePoll,
+ Relationship as BaseRelationship,
+ Status as BaseStatus,
+} from 'pl-api';
const STATUS_IMPORT = 'STATUS_IMPORT' as const;
const STATUSES_IMPORT = 'STATUSES_IMPORT' as const;
-const isEmpty = (object: Record) => !Object.values(object).some(value => value);
+const isEmpty = (object: Record) => !Object.values(object).some((value) => value);
interface ImportStatusAction {
type: typeof STATUS_IMPORT;
@@ -22,93 +28,105 @@ interface ImportStatusesAction {
statuses: Array;
}
-const importEntities = (entities: {
- accounts?: Array;
- groups?: Array;
- polls?: Array;
- statuses?: Array;
- relationships?: Array;
-}, options: {
- // Whether to replace existing entities. Set to false when working with potentially outdated data. Currently, only implemented for accounts.
- override?: boolean;
- withParents?: boolean;
- idempotencyKey?: string;
-} = {
- withParents: true,
-}) => (dispatch: AppDispatch, getState: () => RootState) => {
- const override = options.override ?? true;
+const importEntities =
+ (
+ entities: {
+ accounts?: Array;
+ groups?: Array;
+ polls?: Array;
+ statuses?: Array<(BaseStatus & { expectsCard?: boolean }) | undefined | null>;
+ relationships?: Array;
+ },
+ options: {
+ // Whether to replace existing entities. Set to false when working with potentially outdated data. Currently, only implemented for accounts.
+ override?: boolean;
+ withParents?: boolean;
+ idempotencyKey?: string;
+ } = {
+ withParents: true,
+ },
+ ) =>
+ (dispatch: AppDispatch, getState: () => RootState) => {
+ const override = options.override ?? true;
- const state: RootState = !override ? getState() : undefined as any;
+ const state: RootState = !override ? getState() : (undefined as any);
- const accounts: Record = {};
- const groups: Record = {};
- const polls: Record = {};
- const relationships: Record = {};
- const statuses: Record = {};
+ const accounts: Record = {};
+ const groups: Record = {};
+ const polls: Record = {};
+ const relationships: Record = {};
+ const statuses: Record = {};
- const processAccount = (account: BaseAccount, withSelf = true) => {
- if (!override && selectAccount(state, account.id)) return;
+ const processAccount = (account: BaseAccount, withSelf = true) => {
+ if (!override && selectAccount(state, account.id)) return;
- if (withSelf) accounts[account.id] = account;
+ if (withSelf) accounts[account.id] = account;
- if (account.moved) processAccount(account.moved);
- if (account.relationship) relationships[account.relationship.id] = account.relationship;
+ if (account.moved) processAccount(account.moved);
+ if (account.relationship) relationships[account.relationship.id] = account.relationship;
+ };
+
+ const processStatus = (status: BaseStatus, withSelf = true) => {
+ // Skip broken statuses
+ if (status.scheduled_at !== null) return;
+
+ if (withSelf) statuses[status.id] = status;
+
+ if (status.account) {
+ processAccount(status.account);
+ }
+
+ if (status.quote && 'quoted_status' in status.quote && status.quote.quoted_status)
+ processStatus(status.quote.quoted_status);
+ if (status.reblog) processStatus(status.reblog);
+ if (status.poll) polls[status.poll.id] = status.poll;
+ if (status.group) groups[status.group.id] = status.group;
+ };
+
+ if (options.withParents) {
+ entities.groups?.forEach((group) => group && (groups[group.id] = group));
+ entities.polls?.forEach((poll) => poll && (polls[poll.id] = poll));
+ entities.relationships?.forEach(
+ (relationship) => relationship && (relationships[relationship.id] = relationship),
+ );
+ }
+
+ entities.accounts?.forEach(
+ (account) => account && processAccount(account, options.withParents),
+ );
+
+ if (entities.statuses?.length === 1 && entities.statuses[0] && options.idempotencyKey) {
+ dispatch({
+ type: STATUS_IMPORT,
+ status: entities.statuses[0],
+ idempotencyKey: options.idempotencyKey,
+ });
+ processStatus(entities.statuses[0], false);
+ } else {
+ entities.statuses?.forEach((status) => status && processStatus(status, options.withParents));
+ }
+
+ if (!isEmpty(accounts))
+ dispatch(importEntityStoreEntities(Object.values(accounts), Entities.ACCOUNTS));
+ if (!isEmpty(groups))
+ dispatch(importEntityStoreEntities(Object.values(groups), Entities.GROUPS));
+ if (!isEmpty(polls)) {
+ for (const poll of Object.values(polls)) {
+ queryClient.setQueryData(['statuses', 'polls', poll.id], poll);
+ }
+ }
+ if (!isEmpty(relationships)) {
+ for (const relationship of Object.values(relationships)) {
+ queryClient.setQueryData(
+ ['accountRelationships', relationship.id],
+ relationship,
+ );
+ }
+ }
+ if (!isEmpty(statuses))
+ dispatch({ type: STATUSES_IMPORT, statuses: Object.values(statuses) });
};
- const processStatus = (status: BaseStatus, withSelf = true) => {
- // Skip broken statuses
- if (status.scheduled_at !== null) return;
-
- if (withSelf) statuses[status.id] = status;
-
- if (status.account) {
- processAccount(status.account);
- }
-
- if (status.quote && 'quoted_status' in status.quote && status.quote.quoted_status) processStatus(status.quote.quoted_status);
- if (status.reblog) processStatus(status.reblog);
- if (status.poll) polls[status.poll.id] = status.poll;
- if (status.group) groups[status.group.id] = status.group;
- };
-
- if (options.withParents) {
- entities.groups?.forEach(group => group && (groups[group.id] = group));
- entities.polls?.forEach(poll => poll && (polls[poll.id] = poll));
- entities.relationships?.forEach(relationship => relationship && (relationships[relationship.id] = relationship));
- }
-
- entities.accounts?.forEach((account) => account && processAccount(account, options.withParents));
-
- if (entities.statuses?.length === 1 && entities.statuses[0] && options.idempotencyKey) {
- dispatch({
- type: STATUS_IMPORT,
- status: entities.statuses[0], idempotencyKey: options.idempotencyKey,
- });
- processStatus(entities.statuses[0], false);
- } else {
- entities.statuses?.forEach((status) => status && processStatus(status, options.withParents));
- }
-
- if (!isEmpty(accounts)) dispatch(importEntityStoreEntities(Object.values(accounts), Entities.ACCOUNTS));
- if (!isEmpty(groups)) dispatch(importEntityStoreEntities(Object.values(groups), Entities.GROUPS));
- if (!isEmpty(polls)) {
- for (const poll of Object.values(polls)) {
- queryClient.setQueryData(['statuses', 'polls', poll.id], poll);
- }
- }
- if (!isEmpty(relationships)) {
- for (const relationship of Object.values(relationships)) {
- queryClient.setQueryData(['accountRelationships', relationship.id], relationship);
- }
- }
- if (!isEmpty(statuses)) dispatch({ type: STATUSES_IMPORT, statuses: Object.values(statuses) });
-};
-
type ImporterAction = ImportStatusAction | ImportStatusesAction;
-export {
- STATUS_IMPORT,
- STATUSES_IMPORT,
- importEntities,
- type ImporterAction,
-};
+export { STATUS_IMPORT, STATUSES_IMPORT, importEntities, type ImporterAction };
diff --git a/packages/pl-fe/src/actions/instance.ts b/packages/pl-fe/src/actions/instance.ts
index 681403044..6e934c53c 100644
--- a/packages/pl-fe/src/actions/instance.ts
+++ b/packages/pl-fe/src/actions/instance.ts
@@ -11,7 +11,7 @@ const STANDALONE_CHECK_SUCCESS = 'STANDALONE_CHECK_SUCCESS' as const;
/** Figure out the appropriate instance to fetch depending on the state */
const getHost = (state: RootState) => {
- const accountUrl = getMeUrl(state) ?? getAuthUserUrl(state) as string;
+ const accountUrl = getMeUrl(state) ?? (getAuthUserUrl(state) as string);
try {
return new URL(accountUrl).host;
@@ -47,13 +47,23 @@ interface StandaloneCheckSuccessAction {
const checkIfStandalone = () => (dispatch: AppDispatch) =>
staticFetch('/api/v1/instance', { method: 'GET' })
- .then(({ ok, headers }) => dispatch({ type: STANDALONE_CHECK_SUCCESS, ok: ok && !!headers.get('content-type')?.includes('application/json') }))
- .catch((err) => dispatch({ type: STANDALONE_CHECK_SUCCESS, ok: err.response?.ok }));
+ .then(({ ok, headers }) =>
+ dispatch({
+ type: STANDALONE_CHECK_SUCCESS,
+ ok: ok && !!headers.get('content-type')?.includes('application/json'),
+ }),
+ )
+ .catch((err) =>
+ dispatch({
+ type: STANDALONE_CHECK_SUCCESS,
+ ok: err.response?.ok,
+ }),
+ );
type InstanceAction =
- InstanceFetchSuccessAction
+ | InstanceFetchSuccessAction
| InstanceFetchFailAction
- | StandaloneCheckSuccessAction
+ | StandaloneCheckSuccessAction;
export {
INSTANCE_FETCH_SUCCESS,
diff --git a/packages/pl-fe/src/actions/interactions.ts b/packages/pl-fe/src/actions/interactions.ts
index b13c8edc8..03a33959d 100644
--- a/packages/pl-fe/src/actions/interactions.ts
+++ b/packages/pl-fe/src/actions/interactions.ts
@@ -18,18 +18,27 @@ const PIN_SUCCESS = 'PIN_SUCCESS' as const;
const UNPIN_SUCCESS = 'UNPIN_SUCCESS' as const;
-type InteractionsAction = {
- type: typeof REBLOG_REQUEST | typeof UNREBLOG_REQUEST | typeof FAVOURITE_REQUEST | typeof UNFAVOURITE_REQUEST | typeof DISLIKE_REQUEST | typeof UNDISLIKE_REQUEST;
- statusId: string;
-} | {
- type: typeof REBLOG_FAIL | typeof UNREBLOG_FAIL | typeof FAVOURITE_FAIL | typeof DISLIKE_FAIL;
- statusId: string;
- error: unknown;
-} | {
- type: typeof PIN_SUCCESS | typeof UNPIN_SUCCESS;
- statusId: string;
- accountId: string;
-};
+type InteractionsAction =
+ | {
+ type:
+ | typeof REBLOG_REQUEST
+ | typeof UNREBLOG_REQUEST
+ | typeof FAVOURITE_REQUEST
+ | typeof UNFAVOURITE_REQUEST
+ | typeof DISLIKE_REQUEST
+ | typeof UNDISLIKE_REQUEST;
+ statusId: string;
+ }
+ | {
+ type: typeof REBLOG_FAIL | typeof UNREBLOG_FAIL | typeof FAVOURITE_FAIL | typeof DISLIKE_FAIL;
+ statusId: string;
+ error: unknown;
+ }
+ | {
+ type: typeof PIN_SUCCESS | typeof UNPIN_SUCCESS;
+ statusId: string;
+ accountId: string;
+ };
export {
REBLOG_REQUEST,
diff --git a/packages/pl-fe/src/actions/markers.ts b/packages/pl-fe/src/actions/markers.ts
index 5f7488914..20a8f2750 100644
--- a/packages/pl-fe/src/actions/markers.ts
+++ b/packages/pl-fe/src/actions/markers.ts
@@ -7,32 +7,30 @@ const MARKER_FETCH_SUCCESS = 'MARKER_FETCH_SUCCESS' as const;
const MARKER_SAVE_SUCCESS = 'MARKER_SAVE_SUCCESS' as const;
-const fetchMarker = (timeline: Array) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).timelines.getMarkers(timeline).then((marker) => {
- dispatch({ type: MARKER_FETCH_SUCCESS, marker });
- });
+const fetchMarker =
+ (timeline: Array) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState)
+ .timelines.getMarkers(timeline)
+ .then((marker) => {
+ dispatch({ type: MARKER_FETCH_SUCCESS, marker });
+ });
-const saveMarker = (marker: SaveMarkersParams) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).timelines.saveMarkers(marker).then((marker) => {
- dispatch({ type: MARKER_SAVE_SUCCESS, marker });
- });
+const saveMarker =
+ (marker: SaveMarkersParams) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState)
+ .timelines.saveMarkers(marker)
+ .then((marker) => {
+ dispatch({ type: MARKER_SAVE_SUCCESS, marker });
+ });
type MarkersAction =
| {
- type: typeof MARKER_FETCH_SUCCESS;
- marker: Markers;
- }
+ type: typeof MARKER_FETCH_SUCCESS;
+ marker: Markers;
+ }
| {
- type: typeof MARKER_SAVE_SUCCESS;
- marker: Markers;
- };
+ type: typeof MARKER_SAVE_SUCCESS;
+ marker: Markers;
+ };
-export {
- MARKER_FETCH_SUCCESS,
- MARKER_SAVE_SUCCESS,
- fetchMarker,
- saveMarker,
- type MarkersAction,
-};
+export { MARKER_FETCH_SUCCESS, MARKER_SAVE_SUCCESS, fetchMarker, saveMarker, type MarkersAction };
diff --git a/packages/pl-fe/src/actions/me.ts b/packages/pl-fe/src/actions/me.ts
index 3b435aef4..d5799596c 100644
--- a/packages/pl-fe/src/actions/me.ts
+++ b/packages/pl-fe/src/actions/me.ts
@@ -19,9 +19,10 @@ const ME_FETCH_SKIP = 'ME_FETCH_SKIP' as const;
const ME_PATCH_SUCCESS = 'ME_PATCH_SUCCESS' as const;
-const noOp = () => new Promise(f =>{
- f(undefined);
-});
+const noOp = () =>
+ new Promise((f) => {
+ f(undefined);
+ });
const getMeId = (state: RootState) => state.me ?? getAuthUserId(state);
@@ -42,30 +43,31 @@ interface MeFetchSkipAction {
type: typeof ME_FETCH_SKIP;
}
-const fetchMe = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const state = getState();
- const token = getMeToken(state);
- const accountUrl = getMeUrl(state);
+const fetchMe = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const state = getState();
+ const token = getMeToken(state);
+ const accountUrl = getMeUrl(state);
- if (!token) {
- dispatch({ type: ME_FETCH_SKIP });
- return noOp();
- }
+ if (!token) {
+ dispatch({ type: ME_FETCH_SKIP });
+ return noOp();
+ }
- return dispatch(loadCredentials(token, accountUrl!))
- .catch(error => dispatch(fetchMeFail(error)));
- };
+ return dispatch(loadCredentials(token, accountUrl!)).catch((error) =>
+ dispatch(fetchMeFail(error)),
+ );
+};
/** Update the auth account in IndexedDB for Mastodon, etc. */
const persistAuthAccount = (account: CredentialAccount, params: Record) => {
if (account && account.url) {
const key = `authAccount:${account.url}`;
- KVStore.getItem(key).then((oldAccount: any) => {
- const settings = oldAccount?.settings_store ?? {};
- account.settings_store ??= settings;
- KVStore.setItem(key, account);
- })
+ KVStore.getItem(key)
+ .then((oldAccount: any) => {
+ const settings = oldAccount?.settings_store ?? {};
+ account.settings_store ??= settings;
+ KVStore.setItem(key, account);
+ })
.catch(console.error);
}
if (account && account.url) {
@@ -74,10 +76,11 @@ const persistAuthAccount = (account: CredentialAccount, params: Record
- (dispatch: AppDispatch, getState: () => RootState) =>
- getClient(getState).settings.updateCredentials(params)
- .then(response => {
+const patchMe =
+ (params: UpdateCredentialsParams) => (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState)
+ .settings.updateCredentials(params)
+ .then((response) => {
persistAuthAccount(response, params);
dispatch(patchMeSuccess(response));
});
@@ -104,20 +107,19 @@ interface MePatchSuccessAction {
me: CredentialAccount;
}
-const patchMeSuccess = (me: CredentialAccount) =>
- (dispatch: AppDispatch) => {
- dispatch(importEntities({ accounts: [me] }));
- dispatch({
- type: ME_PATCH_SUCCESS,
- me,
- });
- };
+const patchMeSuccess = (me: CredentialAccount) => (dispatch: AppDispatch) => {
+ dispatch(importEntities({ accounts: [me] }));
+ dispatch({
+ type: ME_PATCH_SUCCESS,
+ me,
+ });
+};
type MeAction =
| ReturnType
| ReturnType
| MeFetchSkipAction
- | MePatchSuccessAction
+ | MePatchSuccessAction;
export {
ME_FETCH_SUCCESS,
diff --git a/packages/pl-fe/src/actions/media.ts b/packages/pl-fe/src/actions/media.ts
index e9b2d4f8d..1ce3c537c 100644
--- a/packages/pl-fe/src/actions/media.ts
+++ b/packages/pl-fe/src/actions/media.ts
@@ -12,99 +12,116 @@ import type { AppDispatch, RootState } from '@/store';
import type { MediaAttachment, UpdateMediaParams, UploadMediaParams } from 'pl-api';
const messages = defineMessages({
- exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
- exceededVideoSizeLimit: { id: 'upload_error.video_size_limit', defaultMessage: 'Video exceeds the current file size limit ({limit})' },
- exceededVideoDurationLimit: { id: 'upload_error.video_duration_limit', defaultMessage: 'Video exceeds the current duration limit ({limit, plural, one {# second} other {# seconds}})' },
+ exceededImageSizeLimit: {
+ id: 'upload_error.image_size_limit',
+ defaultMessage: 'Image exceeds the current file size limit ({limit})',
+ },
+ exceededVideoSizeLimit: {
+ id: 'upload_error.video_size_limit',
+ defaultMessage: 'Video exceeds the current file size limit ({limit})',
+ },
+ exceededVideoDurationLimit: {
+ id: 'upload_error.video_duration_limit',
+ defaultMessage:
+ 'Video exceeds the current duration limit ({limit, plural, one {# second} other {# seconds}})',
+ },
});
const noOp = () => {};
-const updateMedia = (mediaId: string, params: UpdateMediaParams) =>
+const updateMedia =
+ (mediaId: string, params: UpdateMediaParams) =>
(_dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState()).media.updateMedia(mediaId, params);
-const uploadMedia = (body: UploadMediaParams, onUploadProgress: (e: ProgressEvent) => void = noOp) =>
+const uploadMedia =
+ (body: UploadMediaParams, onUploadProgress: (e: ProgressEvent) => void = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState()).media.uploadMedia(body, { onUploadProgress });
-const uploadFile = (
- file: File,
- intl: IntlShape,
- onSuccess: (data: MediaAttachment) => void = () => {},
- onFail: (error: unknown) => void = () => {},
- onProgress: (e: ProgressEvent) => void = () => {},
- changeTotal: (value: number) => void = () => {},
-) => async (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
- const { stripMetadata } = useSettingsStore.getState().settings;
+const uploadFile =
+ (
+ file: File,
+ intl: IntlShape,
+ onSuccess: (data: MediaAttachment) => void = () => {},
+ onFail: (error: unknown) => void = () => {},
+ onProgress: (e: ProgressEvent) => void = () => {},
+ changeTotal: (value: number) => void = () => {},
+ ) =>
+ async (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
+ const { stripMetadata } = useSettingsStore.getState().settings;
- const maxImageSize = getState().instance.configuration.media_attachments.image_size_limit;
- const maxVideoSize = getState().instance.configuration.media_attachments.video_size_limit;
- const maxVideoDuration = getState().instance.configuration.media_attachments.video_duration_limit;
+ const maxImageSize = getState().instance.configuration.media_attachments.image_size_limit;
+ const maxVideoSize = getState().instance.configuration.media_attachments.video_size_limit;
+ const maxVideoDuration =
+ getState().instance.configuration.media_attachments.video_duration_limit;
- const imageMatrixLimit = getState().instance.configuration.media_attachments.image_matrix_limit;
+ const imageMatrixLimit = getState().instance.configuration.media_attachments.image_matrix_limit;
- const isImage = file.type.match(/image.*/);
- const isVideo = file.type.match(/video.*/);
- const videoDurationInSeconds = (isVideo && maxVideoDuration) ? await getVideoDuration(file) : 0;
+ const isImage = file.type.match(/image.*/);
+ const isVideo = file.type.match(/video.*/);
+ const videoDurationInSeconds = isVideo && maxVideoDuration ? await getVideoDuration(file) : 0;
- if (isImage && maxImageSize && (file.size > maxImageSize)) {
- const limit = formatBytes(maxImageSize);
- const message = intl.formatMessage(messages.exceededImageSizeLimit, { limit });
- toast.error(message);
- onFail(true);
- return;
- } else if (isVideo && maxVideoSize && (file.size > maxVideoSize)) {
- const limit = formatBytes(maxVideoSize);
- const message = intl.formatMessage(messages.exceededVideoSizeLimit, { limit });
- toast.error(message);
- onFail(true);
- return;
- } else if (isVideo && maxVideoDuration && (videoDurationInSeconds > maxVideoDuration)) {
- const message = intl.formatMessage(messages.exceededVideoDurationLimit, { limit: maxVideoDuration });
- toast.error(message);
- onFail(true);
- return;
- }
-
- // FIXME: Don't define const in loop
- resizeImage(file, imageMatrixLimit, stripMetadata).then(resized => {
- const data = new FormData();
- data.append('file', resized);
- // Account for disparity in size of original image and resized data
- changeTotal(resized.size - file.size);
-
- return dispatch(uploadMedia({ file: resized }, onProgress))
- .then((data) => {
- // If server-side processing of the media attachment has not completed yet,
- // poll the server until it is, before showing the media attachment as uploaded
- if (data.url) {
- onSuccess(data);
- } else if (data.url === null) {
- const poll = () => {
- getClient(getState()).media.getMedia(data.id).then((data) => {
- if (data.url) {
- onSuccess(data);
- } else if (data.url === null) {
- setTimeout(() =>{
- poll();
- }, 1000);
- }
- }).catch(error =>{
- onFail(error);
- });
- };
-
- poll();
- }
+ if (isImage && maxImageSize && file.size > maxImageSize) {
+ const limit = formatBytes(maxImageSize);
+ const message = intl.formatMessage(messages.exceededImageSizeLimit, { limit });
+ toast.error(message);
+ onFail(true);
+ return;
+ } else if (isVideo && maxVideoSize && file.size > maxVideoSize) {
+ const limit = formatBytes(maxVideoSize);
+ const message = intl.formatMessage(messages.exceededVideoSizeLimit, { limit });
+ toast.error(message);
+ onFail(true);
+ return;
+ } else if (isVideo && maxVideoDuration && videoDurationInSeconds > maxVideoDuration) {
+ const message = intl.formatMessage(messages.exceededVideoDurationLimit, {
+ limit: maxVideoDuration,
});
- }).catch(error =>{
- onFail(error);
- });
-};
+ toast.error(message);
+ onFail(true);
+ return;
+ }
-export {
- updateMedia,
- uploadMedia,
- uploadFile,
-};
+ // FIXME: Don't define const in loop
+ resizeImage(file, imageMatrixLimit, stripMetadata)
+ .then((resized) => {
+ const data = new FormData();
+ data.append('file', resized);
+ // Account for disparity in size of original image and resized data
+ changeTotal(resized.size - file.size);
+
+ return dispatch(uploadMedia({ file: resized }, onProgress)).then((data) => {
+ // If server-side processing of the media attachment has not completed yet,
+ // poll the server until it is, before showing the media attachment as uploaded
+ if (data.url) {
+ onSuccess(data);
+ } else if (data.url === null) {
+ const poll = () => {
+ getClient(getState())
+ .media.getMedia(data.id)
+ .then((data) => {
+ if (data.url) {
+ onSuccess(data);
+ } else if (data.url === null) {
+ setTimeout(() => {
+ poll();
+ }, 1000);
+ }
+ })
+ .catch((error) => {
+ onFail(error);
+ });
+ };
+
+ poll();
+ }
+ });
+ })
+ .catch((error) => {
+ onFail(error);
+ });
+ };
+
+export { updateMedia, uploadMedia, uploadFile };
diff --git a/packages/pl-fe/src/actions/moderation.tsx b/packages/pl-fe/src/actions/moderation.tsx
index 6419f1043..0bc94d565 100644
--- a/packages/pl-fe/src/actions/moderation.tsx
+++ b/packages/pl-fe/src/actions/moderation.tsx
@@ -14,29 +14,88 @@ import toast from '@/toast';
import type { AppDispatch, RootState } from '@/store';
const messages = defineMessages({
- deactivateUserHeading: { id: 'confirmations.admin.deactivate_user.heading', defaultMessage: 'Deactivate @{acct}' },
- deactivateUserConfirm: { id: 'confirmations.admin.deactivate_user.confirm', defaultMessage: 'Deactivate @{name}' },
- userDeactivated: { id: 'admin.users.user_deactivated_message', defaultMessage: '@{acct} was deactivated' },
- deleteUserHeading: { id: 'confirmations.admin.delete_user.heading', defaultMessage: 'Delete @{acct}' },
- deleteUserPrompt: { id: 'confirmations.admin.delete_user.message', defaultMessage: 'You are about to delete @{acct}. THIS IS A DESTRUCTIVE ACTION THAT CANNOT BE UNDONE.' },
- deleteUserConfirm: { id: 'confirmations.admin.delete_user.confirm', defaultMessage: 'Delete @{name}' },
- deleteLocalUserCheckbox: { id: 'confirmations.admin.delete_local_user.checkbox', defaultMessage: 'I understand that I am about to delete a local user.' },
+ deactivateUserHeading: {
+ id: 'confirmations.admin.deactivate_user.heading',
+ defaultMessage: 'Deactivate @{acct}',
+ },
+ deactivateUserConfirm: {
+ id: 'confirmations.admin.deactivate_user.confirm',
+ defaultMessage: 'Deactivate @{name}',
+ },
+ userDeactivated: {
+ id: 'admin.users.user_deactivated_message',
+ defaultMessage: '@{acct} was deactivated',
+ },
+ deleteUserHeading: {
+ id: 'confirmations.admin.delete_user.heading',
+ defaultMessage: 'Delete @{acct}',
+ },
+ deleteUserPrompt: {
+ id: 'confirmations.admin.delete_user.message',
+ defaultMessage:
+ 'You are about to delete @{acct}. THIS IS A DESTRUCTIVE ACTION THAT CANNOT BE UNDONE.',
+ },
+ deleteUserConfirm: {
+ id: 'confirmations.admin.delete_user.confirm',
+ defaultMessage: 'Delete @{name}',
+ },
+ deleteLocalUserCheckbox: {
+ id: 'confirmations.admin.delete_local_user.checkbox',
+ defaultMessage: 'I understand that I am about to delete a local user.',
+ },
userDeleted: { id: 'admin.users.user_deleted_message', defaultMessage: '@{acct} was deleted' },
- deleteStatusHeading: { id: 'confirmations.admin.delete_status.heading', defaultMessage: 'Delete post' },
- deleteStatusPrompt: { id: 'confirmations.admin.delete_status.message', defaultMessage: 'You are about to delete a post by @{acct}. This action cannot be undone.' },
- deleteStatusConfirm: { id: 'confirmations.admin.delete_status.confirm', defaultMessage: 'Delete post' },
- statusDeleted: { id: 'admin.statuses.status_deleted_message', defaultMessage: 'Post by @{acct} was deleted' },
- markStatusSensitiveHeading: { id: 'confirmations.admin.mark_status_sensitive.heading', defaultMessage: 'Mark post sensitive' },
- markStatusNotSensitiveHeading: { id: 'confirmations.admin.mark_status_not_sensitive.heading', defaultMessage: 'Mark post not sensitive.' },
- markStatusSensitivePrompt: { id: 'confirmations.admin.mark_status_sensitive.message', defaultMessage: 'You are about to mark a post by @{acct} sensitive.' },
- markStatusNotSensitivePrompt: { id: 'confirmations.admin.mark_status_not_sensitive.message', defaultMessage: 'You are about to mark a post by @{acct} not sensitive.' },
- markStatusSensitiveConfirm: { id: 'confirmations.admin.mark_status_sensitive.confirm', defaultMessage: 'Mark post sensitive' },
- markStatusNotSensitiveConfirm: { id: 'confirmations.admin.mark_status_not_sensitive.confirm', defaultMessage: 'Mark post not sensitive' },
- statusMarkedSensitive: { id: 'admin.statuses.status_marked_message_sensitive', defaultMessage: 'Post by @{acct} was marked sensitive' },
- statusMarkedNotSensitive: { id: 'admin.statuses.status_marked_message_not_sensitive', defaultMessage: 'Post by @{acct} was marked not sensitive' },
+ deleteStatusHeading: {
+ id: 'confirmations.admin.delete_status.heading',
+ defaultMessage: 'Delete post',
+ },
+ deleteStatusPrompt: {
+ id: 'confirmations.admin.delete_status.message',
+ defaultMessage: 'You are about to delete a post by @{acct}. This action cannot be undone.',
+ },
+ deleteStatusConfirm: {
+ id: 'confirmations.admin.delete_status.confirm',
+ defaultMessage: 'Delete post',
+ },
+ statusDeleted: {
+ id: 'admin.statuses.status_deleted_message',
+ defaultMessage: 'Post by @{acct} was deleted',
+ },
+ markStatusSensitiveHeading: {
+ id: 'confirmations.admin.mark_status_sensitive.heading',
+ defaultMessage: 'Mark post sensitive',
+ },
+ markStatusNotSensitiveHeading: {
+ id: 'confirmations.admin.mark_status_not_sensitive.heading',
+ defaultMessage: 'Mark post not sensitive.',
+ },
+ markStatusSensitivePrompt: {
+ id: 'confirmations.admin.mark_status_sensitive.message',
+ defaultMessage: 'You are about to mark a post by @{acct} sensitive.',
+ },
+ markStatusNotSensitivePrompt: {
+ id: 'confirmations.admin.mark_status_not_sensitive.message',
+ defaultMessage: 'You are about to mark a post by @{acct} not sensitive.',
+ },
+ markStatusSensitiveConfirm: {
+ id: 'confirmations.admin.mark_status_sensitive.confirm',
+ defaultMessage: 'Mark post sensitive',
+ },
+ markStatusNotSensitiveConfirm: {
+ id: 'confirmations.admin.mark_status_not_sensitive.confirm',
+ defaultMessage: 'Mark post not sensitive',
+ },
+ statusMarkedSensitive: {
+ id: 'admin.statuses.status_marked_message_sensitive',
+ defaultMessage: 'Post by @{acct} was marked sensitive',
+ },
+ statusMarkedNotSensitive: {
+ id: 'admin.statuses.status_marked_message_not_sensitive',
+ defaultMessage: 'Post by @{acct} was marked not sensitive',
+ },
});
-const deactivateUserModal = (intl: IntlShape, accountId: string, afterConfirm = () => {}) =>
+const deactivateUserModal =
+ (intl: IntlShape, accountId: string, afterConfirm = () => {}) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const acct = selectAccount(state, accountId)!.acct;
@@ -49,7 +108,11 @@ const deactivateUserModal = (intl: IntlShape, accountId: string, afterConfirm =
-
+
);
@@ -59,16 +122,19 @@ const deactivateUserModal = (intl: IntlShape, accountId: string, afterConfirm =
message,
confirm: intl.formatMessage(messages.deactivateUserConfirm, { name }),
onConfirm: () => {
- dispatch(deactivateUser(accountId)).then(() => {
- const message = intl.formatMessage(messages.userDeactivated, { acct });
- toast.success(message);
- afterConfirm();
- }).catch(() => {});
+ dispatch(deactivateUser(accountId))
+ .then(() => {
+ const message = intl.formatMessage(messages.userDeactivated, { acct });
+ toast.success(message);
+ afterConfirm();
+ })
+ .catch(() => {});
},
});
};
-const deleteUserModal = (intl: IntlShape, accountId: string, afterConfirm = () => {}) =>
+const deleteUserModal =
+ (intl: IntlShape, accountId: string, afterConfirm = () => {}) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const account = selectAccount(state, accountId)!;
@@ -83,7 +149,11 @@ const deleteUserModal = (intl: IntlShape, accountId: string, afterConfirm = () =
-
+
);
@@ -97,57 +167,72 @@ const deleteUserModal = (intl: IntlShape, accountId: string, afterConfirm = () =
confirm,
checkbox,
onConfirm: () => {
- dispatch(deleteUser(accountId)).then(() => {
- const message = intl.formatMessage(messages.userDeleted, { acct });
- dispatch(fetchAccountByUsername(acct));
- toast.success(message);
- afterConfirm();
- }).catch(() => {});
+ dispatch(deleteUser(accountId))
+ .then(() => {
+ const message = intl.formatMessage(messages.userDeleted, { acct });
+ dispatch(fetchAccountByUsername(acct));
+ toast.success(message);
+ afterConfirm();
+ })
+ .catch(() => {});
},
});
};
-const toggleStatusSensitivityModal = (intl: IntlShape, statusId: string, sensitive: boolean, afterConfirm = () => {}) =>
+const toggleStatusSensitivityModal =
+ (intl: IntlShape, statusId: string, sensitive: boolean, afterConfirm = () => {}) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const acct = state.statuses[statusId].account.acct;
useModalsStore.getState().actions.openModal('CONFIRM', {
- heading: intl.formatMessage(!sensitive ? messages.markStatusSensitiveHeading : messages.markStatusNotSensitiveHeading),
- message: intl.formatMessage(!sensitive ? messages.markStatusSensitivePrompt : messages.markStatusNotSensitivePrompt, { acct }),
- confirm: intl.formatMessage(!sensitive ? messages.markStatusSensitiveConfirm : messages.markStatusNotSensitiveConfirm),
+ heading: intl.formatMessage(
+ !sensitive ? messages.markStatusSensitiveHeading : messages.markStatusNotSensitiveHeading,
+ ),
+ message: intl.formatMessage(
+ !sensitive ? messages.markStatusSensitivePrompt : messages.markStatusNotSensitivePrompt,
+ { acct },
+ ),
+ confirm: intl.formatMessage(
+ !sensitive ? messages.markStatusSensitiveConfirm : messages.markStatusNotSensitiveConfirm,
+ ),
onConfirm: () => {
- dispatch(toggleStatusSensitivity(statusId, sensitive)).then(() => {
- const message = intl.formatMessage(!sensitive ? messages.statusMarkedSensitive : messages.statusMarkedNotSensitive, { acct });
- toast.success(message);
- }).catch(() => {});
+ dispatch(toggleStatusSensitivity(statusId, sensitive))
+ .then(() => {
+ const message = intl.formatMessage(
+ !sensitive ? messages.statusMarkedSensitive : messages.statusMarkedNotSensitive,
+ { acct },
+ );
+ toast.success(message);
+ })
+ .catch(() => {});
afterConfirm();
},
});
};
-const deleteStatusModal = (intl: IntlShape, statusId: string, afterConfirm = () => {}) =>
+const deleteStatusModal =
+ (intl: IntlShape, statusId: string, afterConfirm = () => {}) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const acct = state.statuses[statusId].account.acct;
useModalsStore.getState().actions.openModal('CONFIRM', {
heading: intl.formatMessage(messages.deleteStatusHeading),
- message: intl.formatMessage(messages.deleteStatusPrompt, { acct: {acct} }),
+ message: intl.formatMessage(messages.deleteStatusPrompt, {
+ acct: {acct} ,
+ }),
confirm: intl.formatMessage(messages.deleteStatusConfirm),
onConfirm: () => {
- dispatch(deleteStatus(statusId)).then(() => {
- const message = intl.formatMessage(messages.statusDeleted, { acct });
- toast.success(message);
- }).catch(() => {});
+ dispatch(deleteStatus(statusId))
+ .then(() => {
+ const message = intl.formatMessage(messages.statusDeleted, { acct });
+ toast.success(message);
+ })
+ .catch(() => {});
afterConfirm();
},
});
};
-export {
- deactivateUserModal,
- deleteUserModal,
- toggleStatusSensitivityModal,
- deleteStatusModal,
-};
+export { deactivateUserModal, deleteUserModal, toggleStatusSensitivityModal, deleteStatusModal };
diff --git a/packages/pl-fe/src/actions/mrf.ts b/packages/pl-fe/src/actions/mrf.ts
index 89be525a3..4a20d32c7 100644
--- a/packages/pl-fe/src/actions/mrf.ts
+++ b/packages/pl-fe/src/actions/mrf.ts
@@ -5,7 +5,11 @@ import { fetchConfig, updateConfig } from './admin';
import type { AppDispatch, RootState } from '@/store';
-const simplePolicyMerge = (simplePolicy: Partial, host: string, restrictions: Record): MRFSimple => {
+const simplePolicyMerge = (
+ simplePolicy: Partial,
+ host: string,
+ restrictions: Record,
+): MRFSimple => {
const entries = Object.entries(simplePolicy).map(([key, hosts]) => {
const isRestricted = restrictions[key];
@@ -30,7 +34,8 @@ const simplePolicyMerge = (simplePolicy: Partial, host: string, restr
]);
};
-const updateMrf = (host: string, restrictions: Record) =>
+const updateMrf =
+ (host: string, restrictions: Record) =>
(dispatch: AppDispatch, getState: () => RootState) =>
dispatch(fetchConfig()).then(() => {
const configs = getState().admin.configs;
diff --git a/packages/pl-fe/src/actions/notifications.ts b/packages/pl-fe/src/actions/notifications.ts
index ce397174a..2346b0f76 100644
--- a/packages/pl-fe/src/actions/notifications.ts
+++ b/packages/pl-fe/src/actions/notifications.ts
@@ -20,7 +20,13 @@ import { saveMarker } from './markers';
import { saveSettings } from './settings';
import type { AppDispatch, RootState } from '@/store';
-import type { Notification as BaseNotification, GetGroupedNotificationsParams, GroupedNotificationsResults, NotificationGroup, PaginatedResponse } from 'pl-api';
+import type {
+ Notification as BaseNotification,
+ GetGroupedNotificationsParams,
+ GroupedNotificationsResults,
+ NotificationGroup,
+ PaginatedResponse,
+} from 'pl-api';
const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE' as const;
const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP' as const;
@@ -50,8 +56,14 @@ defineMessages({
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
});
-const fetchRelatedRelationships = (dispatch: AppDispatch, notifications: Array) => {
- const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.sample_account_ids).flat();
+const fetchRelatedRelationships = (
+ dispatch: AppDispatch,
+ notifications: Array,
+) => {
+ const accountIds = notifications
+ .filter((item) => item.type === 'follow')
+ .map((item) => item.sample_account_ids)
+ .flat();
if (accountIds.length > 0) {
dispatch(fetchRelationships(accountIds));
@@ -63,38 +75,48 @@ interface NotificationsUpdateAction {
notification: NotificationGroup;
}
-const updateNotifications = (notification: BaseNotification) =>
- (dispatch: AppDispatch) => {
- const selectedFilter = useSettingsStore.getState().settings.notifications.quickFilter.active;
- const showInColumn = selectedFilter === 'all' ? true : (FILTER_TYPES[selectedFilter as FilterType] ?? [notification.type]).includes(notification.type);
+const updateNotifications = (notification: BaseNotification) => (dispatch: AppDispatch) => {
+ const selectedFilter = useSettingsStore.getState().settings.notifications.quickFilter.active;
+ const showInColumn =
+ selectedFilter === 'all'
+ ? true
+ : (FILTER_TYPES[selectedFilter as FilterType] ?? [notification.type]).includes(
+ notification.type,
+ );
- dispatch(importEntities({
- accounts: [notification.account, notification.type === 'move' ? notification.target : undefined],
+ dispatch(
+ importEntities({
+ accounts: [
+ notification.account,
+ notification.type === 'move' ? notification.target : undefined,
+ ],
statuses: [getNotificationStatus(notification) as any],
- }));
+ }),
+ );
- if (showInColumn) {
- const normalizedNotification = normalizeNotification(notification);
+ if (showInColumn) {
+ const normalizedNotification = normalizeNotification(notification);
- if (normalizedNotification.type === 'follow_request') {
- normalizedNotification.sample_account_ids.forEach(appendFollowRequest);
- }
-
- dispatch({
- type: NOTIFICATIONS_UPDATE,
- notification: normalizedNotification,
- });
-
- fetchRelatedRelationships(dispatch, [normalizedNotification]);
+ if (normalizedNotification.type === 'follow_request') {
+ normalizedNotification.sample_account_ids.forEach(appendFollowRequest);
}
- };
+
+ dispatch({
+ type: NOTIFICATIONS_UPDATE,
+ notification: normalizedNotification,
+ });
+
+ fetchRelatedRelationships(dispatch, [normalizedNotification]);
+ }
+};
interface NotificationsUpdateNoopAction {
type: typeof NOTIFICATIONS_UPDATE_NOOP;
meta: { sound: 'boop' };
}
-const updateNotificationsQueue = (notification: BaseNotification, intlMessages: Record, intlLocale: string) =>
+const updateNotificationsQueue =
+ (notification: BaseNotification, intlMessages: Record, intlLocale: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!notification.type) return; // drop invalid notifications
if (notification.type === 'chat_mention') return; // Drop chat notifications, handle them per-chat
@@ -108,29 +130,44 @@ const updateNotificationsQueue = (notification: BaseNotification, intlMessages:
if (notification.type === 'mention' || notification.type === 'status') {
const regex = regexFromFilters(filters);
- const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
+ const searchIndex =
+ notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
filtered = regex && regex.test(searchIndex);
}
// Desktop notifications
try {
- // eslint-disable-next-line compat/compat
const isNotificationsEnabled = window.Notification?.permission === 'granted';
if (!filtered && isNotificationsEnabled) {
- const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username }) as string;
- const body = (status && status.spoiler_text.length > 0) ? status.spoiler_text : unescapeHTML(status ? status.content : '');
+ const title = new IntlMessageFormat(
+ intlMessages[`notification.${notification.type}`],
+ intlLocale,
+ ).format({
+ name:
+ notification.account.display_name.length > 0
+ ? notification.account.display_name
+ : notification.account.username,
+ }) as string;
+ const body =
+ status && status.spoiler_text.length > 0
+ ? status.spoiler_text
+ : unescapeHTML(status ? status.content : '');
- navigator.serviceWorker.ready.then(serviceWorkerRegistration => {
- serviceWorkerRegistration.showNotification(title, {
- body,
- icon: notification.account.avatar,
- tag: notification.id,
- data: {
- url: joinPublicPath('/notifications'),
- },
- }).catch(console.error);
- }).catch(console.error);
+ navigator.serviceWorker.ready
+ .then((serviceWorkerRegistration) => {
+ serviceWorkerRegistration
+ .showNotification(title, {
+ body,
+ icon: notification.account.avatar,
+ tag: notification.id,
+ data: {
+ url: joinPublicPath('/notifications'),
+ },
+ })
+ .catch(console.error);
+ })
+ .catch(console.error);
}
} catch (e) {
console.warn(e);
@@ -146,21 +183,25 @@ const updateNotificationsQueue = (notification: BaseNotification, intlMessages:
dispatch(updateNotifications(notification));
};
-const excludeTypesFromFilter = (filters: string[]) => NOTIFICATION_TYPES.filter(item => !filters.includes(item));
+const excludeTypesFromFilter = (filters: string[]) =>
+ NOTIFICATION_TYPES.filter((item) => !filters.includes(item));
-const noOp = () => new Promise(f =>{
- f(undefined);
-});
+const noOp = () =>
+ new Promise((f) => {
+ f(undefined);
+ });
let abortExpandNotifications = new AbortController();
-const expandNotifications = ({ maxId }: Record = {}, done: () => any = noOp, abort?: boolean) =>
+const expandNotifications =
+ ({ maxId }: Record = {}, done: () => any = noOp, abort?: boolean) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return dispatch(noOp);
const state = getState();
const features = state.auth.client.features;
- const activeFilter = useSettingsStore.getState().settings.notifications.quickFilter.active as FilterType;
+ const activeFilter = useSettingsStore.getState().settings.notifications.quickFilter
+ .active as FilterType;
const notifications = state.notifications;
if (notifications.isLoading) {
@@ -179,7 +220,7 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an
if (activeFilter === 'all') {
if (features.notificationsIncludeTypes) {
- params.types = NOTIFICATION_TYPES.filter(type => !EXCLUDE_TYPES.includes(type as any));
+ params.types = NOTIFICATION_TYPES.filter((type) => !EXCLUDE_TYPES.includes(type as any));
} else {
params.exclude_types = [...EXCLUDE_TYPES];
}
@@ -195,12 +236,19 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an
dispatch(expandNotificationsRequest());
try {
- const { items: { accounts, statuses, notification_groups }, next } = await getClient(state).groupedNotifications.getGroupedNotifications(params, { signal: abortExpandNotifications.signal });
+ const {
+ items: { accounts, statuses, notification_groups },
+ next,
+ } = await getClient(state).groupedNotifications.getGroupedNotifications(params, {
+ signal: abortExpandNotifications.signal,
+ });
- dispatch(importEntities({
- accounts,
- statuses,
- }));
+ dispatch(
+ importEntities({
+ accounts,
+ statuses,
+ }),
+ );
dispatch(expandNotificationsSuccess(notification_groups, next));
fetchRelatedRelationships(dispatch, notification_groups);
@@ -213,7 +261,10 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an
const expandNotificationsRequest = () => ({ type: NOTIFICATIONS_EXPAND_REQUEST });
-const expandNotificationsSuccess = (notifications: Array, next: (() => Promise>) | null) => ({
+const expandNotificationsSuccess = (
+ notifications: Array,
+ next: (() => Promise>) | null,
+) => ({
type: NOTIFICATIONS_EXPAND_SUCCESS,
notifications,
next,
@@ -229,50 +280,47 @@ interface NotificationsScrollTopAction {
top: boolean;
}
-const scrollTopNotifications = (top: boolean) =>
- (dispatch: AppDispatch) => {
- dispatch(markReadNotifications());
- return dispatch({
- type: NOTIFICATIONS_SCROLL_TOP,
- top,
- });
- };
+const scrollTopNotifications = (top: boolean) => (dispatch: AppDispatch) => {
+ dispatch(markReadNotifications());
+ return dispatch({
+ type: NOTIFICATIONS_SCROLL_TOP,
+ top,
+ });
+};
interface SetFilterAction {
type: typeof NOTIFICATIONS_FILTER_SET;
}
-const setFilter = (filterType: FilterType, abort?: boolean) =>
- (dispatch: AppDispatch) => {
- const settingsStore = useSettingsStore.getState();
- const activeFilter = settingsStore.settings.notifications.quickFilter.active as FilterType;
+const setFilter = (filterType: FilterType, abort?: boolean) => (dispatch: AppDispatch) => {
+ const settingsStore = useSettingsStore.getState();
+ const activeFilter = settingsStore.settings.notifications.quickFilter.active as FilterType;
- settingsStore.actions.changeSetting(['notifications', 'quickFilter', 'active'], filterType);
+ settingsStore.actions.changeSetting(['notifications', 'quickFilter', 'active'], filterType);
- dispatch(expandNotifications(undefined, undefined, abort));
- if (activeFilter !== filterType) dispatch(saveSettings());
+ dispatch(expandNotifications(undefined, undefined, abort));
+ if (activeFilter !== filterType) dispatch(saveSettings());
- return dispatch({ type: NOTIFICATIONS_FILTER_SET });
- };
+ return dispatch({ type: NOTIFICATIONS_FILTER_SET });
+};
-const markReadNotifications = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
+const markReadNotifications = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
- const state = getState();
- const topNotificationId = state.notifications.items[0]?.page_max_id;
- const lastReadId = state.notifications.lastRead;
+ const state = getState();
+ const topNotificationId = state.notifications.items[0]?.page_max_id;
+ const lastReadId = state.notifications.lastRead;
- if (topNotificationId && (lastReadId === -1 || compareId(topNotificationId, lastReadId) > 0)) {
- const marker = {
- notifications: {
- last_read_id: topNotificationId,
- },
- };
+ if (topNotificationId && (lastReadId === -1 || compareId(topNotificationId, lastReadId) > 0)) {
+ const marker = {
+ notifications: {
+ last_read_id: topNotificationId,
+ },
+ };
- dispatch(saveMarker(marker));
- }
- };
+ dispatch(saveMarker(marker));
+ }
+};
type NotificationsAction =
| NotificationsUpdateAction
diff --git a/packages/pl-fe/src/actions/oauth.ts b/packages/pl-fe/src/actions/oauth.ts
index 39a16db07..a674a73bd 100644
--- a/packages/pl-fe/src/actions/oauth.ts
+++ b/packages/pl-fe/src/actions/oauth.ts
@@ -13,21 +13,18 @@ import { getBaseURL } from '@/utils/state';
import type { AppDispatch, RootState } from '@/store';
-const obtainOAuthToken = async (params: GetTokenParams, baseURL?: string) =>{
+const obtainOAuthToken = async (params: GetTokenParams, baseURL?: string) => {
const client = new PlApiClient((baseURL ?? BuildConfig.BACKEND_URL) || '');
await client.instance.getInstance();
return client.oauth.getToken(params);
};
-const revokeOAuthToken = (params: RevokeTokenParams) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const revokeOAuthToken =
+ (params: RevokeTokenParams) => (dispatch: AppDispatch, getState: () => RootState) => {
const baseURL = getBaseURL(getState());
const client = new PlApiClient(baseURL || '');
return client.oauth.revokeToken(params);
};
-export {
- obtainOAuthToken,
- revokeOAuthToken,
-};
+export { obtainOAuthToken, revokeOAuthToken };
diff --git a/packages/pl-fe/src/actions/preload.ts b/packages/pl-fe/src/actions/preload.ts
index 9ba21fdfd..785c5b5e2 100644
--- a/packages/pl-fe/src/actions/preload.ts
+++ b/packages/pl-fe/src/actions/preload.ts
@@ -17,7 +17,7 @@ const decodeUTF8Base64 = (data: string) => {
};
const decodePleromaData = (data: Record) =>
- mapValues(data, base64string => JSON.parse(decodeUTF8Base64(base64string)));
+ mapValues(data, (base64string) => JSON.parse(decodeUTF8Base64(base64string)));
const pleromaDecoder = (json: string) => decodePleromaData(JSON.parse(json));
@@ -28,7 +28,12 @@ const decodeFromMarkup = (elementId: string, decoder: (json: string) => Record Record, action: (data: Record) => any) =>
+const preloadFromMarkup =
+ (
+ elementId: string,
+ decoder: (json: string) => Record,
+ action: (data: Record) => any,
+ ) =>
(dispatch: AppDispatch) => {
try {
const data = decodeFromMarkup(elementId, decoder);
@@ -38,26 +43,24 @@ const preloadFromMarkup = (elementId: string, decoder: (json: string) => Record<
}
};
-const preload = () =>
- (dispatch: AppDispatch) => {
- dispatch(preloadFromMarkup('initial-results', pleromaDecoder, preloadPleroma));
- dispatch(preloadFromMarkup('initial-state', JSON.parse, preloadMastodon));
- };
+const preload = () => (dispatch: AppDispatch) => {
+ dispatch(preloadFromMarkup('initial-results', pleromaDecoder, preloadPleroma));
+ dispatch(preloadFromMarkup('initial-state', JSON.parse, preloadMastodon));
+};
const preloadPleroma = (data: Record): PreloadAction => ({
type: PLEROMA_PRELOAD_IMPORT,
data,
});
-const preloadMastodon = (data: Record) =>
- (dispatch: AppDispatch) => {
- const { me, access_token } = data.meta;
- const { url } = data.accounts[me];
+const preloadMastodon = (data: Record) => (dispatch: AppDispatch) => {
+ const { me, access_token } = data.meta;
+ const { url } = data.accounts[me];
- dispatch(importEntities({ accounts: Object.values(data.accounts) }));
- dispatch(verifyCredentials(access_token, url));
- dispatch({ type: MASTODON_PRELOAD_IMPORT, data });
- };
+ dispatch(importEntities({ accounts: Object.values(data.accounts) }));
+ dispatch(verifyCredentials(access_token, url));
+ dispatch({ type: MASTODON_PRELOAD_IMPORT, data });
+};
interface PreloadAction {
type: typeof PLEROMA_PRELOAD_IMPORT | typeof MASTODON_PRELOAD_IMPORT;
diff --git a/packages/pl-fe/src/actions/push-notifications/registerer.ts b/packages/pl-fe/src/actions/push-notifications/registerer.ts
index fc741bbc0..1f6c6a83b 100644
--- a/packages/pl-fe/src/actions/push-notifications/registerer.ts
+++ b/packages/pl-fe/src/actions/push-notifications/registerer.ts
@@ -10,10 +10,8 @@ import type { Me } from '@/types/pl-fe';
// Taken from https://www.npmjs.com/package/web-push
const urlBase64ToUint8Array = (base64String: string) => {
- const padding = '='.repeat((4 - base64String.length % 4) % 4);
- const base64 = (base64String + padding)
- .replace(/-/g, '+')
- .replace(/_/g, '/');
+ const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
+ const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
return decodeBase64(base64);
};
@@ -27,8 +25,9 @@ const getRegistration = () => {
};
const getPushSubscription = (registration: ServiceWorkerRegistration) =>
- registration.pushManager.getSubscription()
- .then(subscription => ({ registration, subscription }));
+ registration.pushManager
+ .getSubscription()
+ .then((subscription) => ({ registration, subscription }));
const subscribe = (registration: ServiceWorkerRegistration, getState: () => RootState) =>
registration.pushManager.subscribe({
@@ -36,15 +35,21 @@ const subscribe = (registration: ServiceWorkerRegistration, getState: () => Root
applicationServerKey: urlBase64ToUint8Array(getVapidKey(getState())),
});
-const unsubscribe = ({ registration, subscription }: {
+const unsubscribe = ({
+ registration,
+ subscription,
+}: {
registration: ServiceWorkerRegistration;
subscription: PushSubscription | null;
}) =>
- subscription ? subscription.unsubscribe().then(() => registration) : new Promise(r =>{
- r(registration);
- });
+ subscription
+ ? subscription.unsubscribe().then(() => registration)
+ : new Promise((r) => {
+ r(registration);
+ });
-const sendSubscriptionToBackend = (subscription: PushSubscription, me: Me) =>
+const sendSubscriptionToBackend =
+ (subscription: PushSubscription, me: Me) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const alerts = getState().push_notifications.alerts;
const params = { subscription, data: { alerts } };
@@ -61,81 +66,87 @@ const sendSubscriptionToBackend = (subscription: PushSubscription, me: Me) =>
// Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
// eslint-disable-next-line compat/compat
-const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype);
+const supportsPushNotifications =
+ 'serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype;
-const register = () =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const me = getState().me;
- const vapidKey = getVapidKey(getState());
+const register = () => (dispatch: AppDispatch, getState: () => RootState) => {
+ const me = getState().me;
+ const vapidKey = getVapidKey(getState());
- dispatch(setBrowserSupport(supportsPushNotifications));
+ dispatch(setBrowserSupport(supportsPushNotifications));
- if (!supportsPushNotifications) {
- console.warn('Your browser does not support Web Push Notifications.');
- return;
- }
+ if (!supportsPushNotifications) {
+ console.warn('Your browser does not support Web Push Notifications.');
+ return;
+ }
- if (!vapidKey) {
- console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.');
- return;
- }
+ if (!vapidKey) {
+ console.error(
+ 'The VAPID public key is not set. You will not be able to receive Web Push Notifications.',
+ );
+ return;
+ }
- getRegistration()
- .then(getPushSubscription)
- .then(async ({ registration, subscription }) => {
- if (subscription !== null) {
- // We have a subscription, check if it is still valid
- const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey!)).toString();
- const subscriptionServerKey = urlBase64ToUint8Array(vapidKey).toString();
- const serverEndpoint = getState().push_notifications.subscription?.endpoint;
+ getRegistration()
+ .then(getPushSubscription)
+ .then(async ({ registration, subscription }) => {
+ if (subscription !== null) {
+ // We have a subscription, check if it is still valid
+ const currentServerKey = new Uint8Array(
+ subscription.options.applicationServerKey!,
+ ).toString();
+ const subscriptionServerKey = urlBase64ToUint8Array(vapidKey).toString();
+ const serverEndpoint = getState().push_notifications.subscription?.endpoint;
- // If the VAPID public key did not change and the endpoint corresponds
- // to the endpoint saved in the backend, the subscription is valid
- if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint) {
- return subscription;
- } else {
- // Something went wrong, try to subscribe again
- const swRegistration = await unsubscribe({ registration, subscription });
- const pushSubscription = await subscribe(swRegistration, getState);
- await dispatch(sendSubscriptionToBackend(pushSubscription, me));
- }
+ // If the VAPID public key did not change and the endpoint corresponds
+ // to the endpoint saved in the backend, the subscription is valid
+ if (
+ subscriptionServerKey === currentServerKey &&
+ subscription.endpoint === serverEndpoint
+ ) {
+ return subscription;
+ } else {
+ // Something went wrong, try to subscribe again
+ const swRegistration = await unsubscribe({ registration, subscription });
+ const pushSubscription = await subscribe(swRegistration, getState);
+ await dispatch(sendSubscriptionToBackend(pushSubscription, me));
}
+ }
- // No subscription, try to subscribe
- return subscribe(registration, getState)
- .then((pushSubscription) => dispatch(sendSubscriptionToBackend(pushSubscription, me)));
- })
- .then((subscription) => {
- // If we got a PushSubscription (and not a subscription object from the backend)
- // it means that the backend subscription is valid (and was set during hydration)
- if (!(subscription instanceof PushSubscription)) {
- dispatch(setSubscription(subscription));
- if (me) {
- pushNotificationsSetting.set(me, { alerts: subscription.alerts });
- }
- }
- })
- .catch(error => {
- if (error.code === 20 && error.name === 'AbortError') {
- console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.');
- } else if (error.code === 5 && error.name === 'InvalidCharacterError') {
- console.error('The VAPID public key seems to be invalid:', vapidKey);
- }
-
- // Clear alerts and hide UI settings
- dispatch(clearSubscription());
-
+ // No subscription, try to subscribe
+ return subscribe(registration, getState).then((pushSubscription) =>
+ dispatch(sendSubscriptionToBackend(pushSubscription, me)),
+ );
+ })
+ .then((subscription) => {
+ // If we got a PushSubscription (and not a subscription object from the backend)
+ // it means that the backend subscription is valid (and was set during hydration)
+ if (!(subscription instanceof PushSubscription)) {
+ dispatch(setSubscription(subscription));
if (me) {
- pushNotificationsSetting.remove(me);
+ pushNotificationsSetting.set(me, { alerts: subscription.alerts });
}
+ }
+ })
+ .catch((error) => {
+ if (error.code === 20 && error.name === 'AbortError') {
+ console.warn(
+ 'Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.',
+ );
+ } else if (error.code === 5 && error.name === 'InvalidCharacterError') {
+ console.error('The VAPID public key seems to be invalid:', vapidKey);
+ }
- return getRegistration()
- .then(getPushSubscription)
- .then(unsubscribe);
- })
- .catch(console.warn);
- };
+ // Clear alerts and hide UI settings
+ dispatch(clearSubscription());
-export {
- register,
+ if (me) {
+ pushNotificationsSetting.remove(me);
+ }
+
+ return getRegistration().then(getPushSubscription).then(unsubscribe);
+ })
+ .catch(console.warn);
};
+
+export { register };
diff --git a/packages/pl-fe/src/actions/push-subscriptions.ts b/packages/pl-fe/src/actions/push-subscriptions.ts
index 346d68cdd..0d4cba206 100644
--- a/packages/pl-fe/src/actions/push-subscriptions.ts
+++ b/packages/pl-fe/src/actions/push-subscriptions.ts
@@ -3,10 +3,9 @@ import { getClient } from '../api';
import type { AppDispatch, RootState } from '@/store';
import type { CreatePushNotificationsSubscriptionParams } from 'pl-api';
-const createPushSubscription = (params: CreatePushNotificationsSubscriptionParams) =>
+const createPushSubscription =
+ (params: CreatePushNotificationsSubscriptionParams) =>
(dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).pushNotifications.createSubscription(params);
-export {
- createPushSubscription,
-};
+export { createPushSubscription };
diff --git a/packages/pl-fe/src/actions/remote-timeline.ts b/packages/pl-fe/src/actions/remote-timeline.ts
index 8d32eba48..233ac2a18 100644
--- a/packages/pl-fe/src/actions/remote-timeline.ts
+++ b/packages/pl-fe/src/actions/remote-timeline.ts
@@ -8,23 +8,23 @@ const getPinnedHosts = (state: RootState) => {
return settings.remote_timeline.pinnedHosts;
};
-const pinHost = (host: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const state = getState();
- const pinnedHosts = getPinnedHosts(state);
+const pinHost = (host: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ const state = getState();
+ const pinnedHosts = getPinnedHosts(state);
- dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], [...pinnedHosts, host]));
- };
-
-const unpinHost = (host: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const state = getState();
- const pinnedHosts = getPinnedHosts(state);
-
- dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.filter(value => value !== host)));
- };
-
-export {
- pinHost,
- unpinHost,
+ dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], [...pinnedHosts, host]));
};
+
+const unpinHost = (host: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ const state = getState();
+ const pinnedHosts = getPinnedHosts(state);
+
+ dispatch(
+ changeSetting(
+ ['remote_timeline', 'pinnedHosts'],
+ pinnedHosts.filter((value) => value !== host),
+ ),
+ );
+};
+
+export { pinHost, unpinHost };
diff --git a/packages/pl-fe/src/actions/reports.ts b/packages/pl-fe/src/actions/reports.ts
index 07029b93a..a7db17d5d 100644
--- a/packages/pl-fe/src/actions/reports.ts
+++ b/packages/pl-fe/src/actions/reports.ts
@@ -8,34 +8,40 @@ import type { Account } from 'pl-api';
enum ReportableEntities {
ACCOUNT = 'ACCOUNT',
- STATUS = 'STATUS'
+ STATUS = 'STATUS',
}
type ReportedEntity = {
status?: Pick;
statusId?: string;
-}
-
-const initReport = (entityType: ReportableEntities, account: Pick, entities?: ReportedEntity) => (dispatch: AppDispatch) => {
- const { status, statusId } = entities ?? {};
-
- useModalsStore.getState().actions.openModal('REPORT', {
- accountId: account.id,
- entityType,
- statusIds: [status?.id, statusId].filter((id): id is string => !!id),
- });
};
-const submitReport = (accountId: string, statusIds: string[], ruleIds?: string[], comment?: string, forward?: boolean) =>
- (dispatch: AppDispatch, getState: () => RootState) => getClient(getState()).accounts.reportAccount(accountId, {
- status_ids: statusIds,
- rule_ids: ruleIds,
- comment: comment,
- forward: forward,
- });
+const initReport =
+ (entityType: ReportableEntities, account: Pick, entities?: ReportedEntity) =>
+ (dispatch: AppDispatch) => {
+ const { status, statusId } = entities ?? {};
-export {
- ReportableEntities,
- initReport,
- submitReport,
-};
+ useModalsStore.getState().actions.openModal('REPORT', {
+ accountId: account.id,
+ entityType,
+ statusIds: [status?.id, statusId].filter((id): id is string => !!id),
+ });
+ };
+
+const submitReport =
+ (
+ accountId: string,
+ statusIds: string[],
+ ruleIds?: string[],
+ comment?: string,
+ forward?: boolean,
+ ) =>
+ (dispatch: AppDispatch, getState: () => RootState) =>
+ getClient(getState()).accounts.reportAccount(accountId, {
+ status_ids: statusIds,
+ rule_ids: ruleIds,
+ comment: comment,
+ forward: forward,
+ });
+
+export { ReportableEntities, initReport, submitReport };
diff --git a/packages/pl-fe/src/actions/security.ts b/packages/pl-fe/src/actions/security.ts
index bce339781..99a4369d6 100644
--- a/packages/pl-fe/src/actions/security.ts
+++ b/packages/pl-fe/src/actions/security.ts
@@ -14,12 +14,13 @@ import { AUTH_LOGGED_OUT, messages } from './auth';
import type { AppDispatch, RootState } from '@/store';
import type { Account } from 'pl-api';
-const changePassword = (oldPassword: string, newPassword: string) =>
+const changePassword =
+ (oldPassword: string, newPassword: string) =>
(dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).settings.changePassword(oldPassword, newPassword);
-const resetPassword = (usernameOrEmail: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const resetPassword =
+ (usernameOrEmail: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const input = normalizeUsername(usernameOrEmail);
return getClient(getState).settings.resetPassword(
@@ -28,27 +29,30 @@ const resetPassword = (usernameOrEmail: string) =>
);
};
-const changeEmail = (email: string, password: string) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
+const changeEmail =
+ (email: string, password: string) => (dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).settings.changeEmail(email, password);
-const deleteAccount = (password: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const account = getLoggedInAccount(getState())!;
+const deleteAccount = (password: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ const account = getLoggedInAccount(getState())!;
- const client = getClient(getState);
+ const client = getClient(getState);
- return (client.features.deleteAccount ? client.settings.deleteAccount(password) : client.settings.deleteAccountWithoutPassword()).then(() => {
- dispatch({ type: AUTH_LOGGED_OUT, account });
- toast.success(messages.loggedOut);
- });
- };
+ return (
+ client.features.deleteAccount
+ ? client.settings.deleteAccount(password)
+ : client.settings.deleteAccountWithoutPassword()
+ ).then(() => {
+ dispatch({ type: AUTH_LOGGED_OUT, account });
+ toast.success(messages.loggedOut);
+ });
+};
-const moveAccount = (targetAccount: string, password: string) =>
- (dispatch: AppDispatch, getState: () => RootState) =>
+const moveAccount =
+ (targetAccount: string, password: string) => (dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).settings.moveAccount(targetAccount, password);
-type SecurityAction = { type: typeof AUTH_LOGGED_OUT; account: Account }
+type SecurityAction = { type: typeof AUTH_LOGGED_OUT; account: Account };
export {
changePassword,
diff --git a/packages/pl-fe/src/actions/settings.ts b/packages/pl-fe/src/actions/settings.ts
index aa1339dfd..d52229a4f 100644
--- a/packages/pl-fe/src/actions/settings.ts
+++ b/packages/pl-fe/src/actions/settings.ts
@@ -19,9 +19,12 @@ type SettingOpts = {
/** Whether to display an alert when settings are saved. */
showAlert?: boolean;
save?: boolean;
-}
+};
-const saveSuccessMessage = defineMessage({ id: 'settings.save.success', defaultMessage: 'Your preferences have been saved!' });
+const saveSuccessMessage = defineMessage({
+ id: 'settings.save.success',
+ defaultMessage: 'Your preferences have been saved!',
+});
const changeSetting = (path: string[], value: any, opts?: SettingOpts) => {
useSettingsStore.getState().actions.changeSetting(path, value);
@@ -30,25 +33,29 @@ const changeSetting = (path: string[], value: any, opts?: SettingOpts) => {
return () => {};
};
-const saveSettings = (opts?: SettingOpts) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
+const saveSettings = (opts?: SettingOpts) => (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
- const { userSettings, actions: { userSettingsSaving } } = useSettingsStore.getState();
- if (userSettings.saved) return;
+ const {
+ userSettings,
+ actions: { userSettingsSaving },
+ } = useSettingsStore.getState();
+ if (userSettings.saved) return;
- const { saved, ...data } = userSettings;
+ const { saved, ...data } = userSettings;
- dispatch(updateSettingsStore(data)).then(() => {
+ dispatch(updateSettingsStore(data))
+ .then(() => {
userSettingsSaving();
if (opts?.showAlert) {
toast.success(saveSuccessMessage);
}
- }).catch(error => {
+ })
+ .catch((error) => {
toast.showAlertForError(error);
});
- };
+};
/** Update settings store for Mastodon, etc. */
const updateAuthAccount = async (url: string, settings: any) => {
@@ -64,17 +71,19 @@ const updateAuthAccount = async (url: string, settings: any) => {
}
};
-const updateSettingsStore = (settings: any) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const updateSettingsStore =
+ (settings: any) => (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const client = getClient(state);
if (client.features.frontendConfigurations) {
- return dispatch(patchMe({
- settings_store: {
- [FE_NAME]: settings,
- },
- }));
+ return dispatch(
+ patchMe({
+ settings_store: {
+ [FE_NAME]: settings,
+ },
+ }),
+ );
} else {
const accountUrl = selectOwnAccount(state)!.url;
@@ -85,13 +94,11 @@ const updateSettingsStore = (settings: any) =>
const getLocale = (fallback = 'en') => {
const localeWithVariant = useSettingsStore.getState().settings.locale.replace('_', '-');
const locale = localeWithVariant.split('-')[0];
- return Object.keys(messages).includes(localeWithVariant) ? localeWithVariant : Object.keys(messages).includes(locale) ? locale : fallback;
+ return Object.keys(messages).includes(localeWithVariant)
+ ? localeWithVariant
+ : Object.keys(messages).includes(locale)
+ ? locale
+ : fallback;
};
-export {
- FE_NAME,
- changeSetting,
- saveSettings,
- updateSettingsStore,
- getLocale,
-};
+export { FE_NAME, changeSetting, saveSettings, updateSettingsStore, getLocale };
diff --git a/packages/pl-fe/src/actions/statuses.ts b/packages/pl-fe/src/actions/statuses.ts
index 8a7d79269..bb8e5c767 100644
--- a/packages/pl-fe/src/actions/statuses.ts
+++ b/packages/pl-fe/src/actions/statuses.ts
@@ -13,7 +13,13 @@ import { deleteFromTimelines } from './timelines';
import type { Status } from '@/normalizers/status';
import type { AppDispatch, RootState } from '@/store';
-import type { CreateStatusParams, Status as BaseStatus, ScheduledStatus, StatusSource, Poll } from 'pl-api';
+import type {
+ CreateStatusParams,
+ Status as BaseStatus,
+ ScheduledStatus,
+ StatusSource,
+ Poll,
+} from 'pl-api';
import type { IntlShape } from 'react-intl';
const STATUS_CREATE_REQUEST = 'STATUS_CREATE_REQUEST' as const;
@@ -36,9 +42,22 @@ const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS' as const;
const STATUS_UNFILTER = 'STATUS_UNFILTER' as const;
-const createStatus = (params: CreateStatusParams, idempotencyKey: string, editedId: string | null, redacting = false) =>
+const createStatus =
+ (
+ params: CreateStatusParams,
+ idempotencyKey: string,
+ editedId: string | null,
+ redacting = false,
+ ) =>
(dispatch: AppDispatch, getState: () => RootState) => {
- if (!params.preview) dispatch({ type: STATUS_CREATE_REQUEST, params, idempotencyKey, editing: !!editedId, redacting });
+ if (!params.preview)
+ dispatch({
+ type: STATUS_CREATE_REQUEST,
+ params,
+ idempotencyKey,
+ editing: !!editedId,
+ redacting,
+ });
const client = getClient(getState());
@@ -56,33 +75,54 @@ const createStatus = (params: CreateStatusParams, idempotencyKey: string, edited
const expectsCard = status.scheduled_at === null && !status.card && shouldHaveCard(status);
if (status.scheduled_at === null) {
- dispatch(importEntities({ statuses: [{ ...status, expectsCard }] }, { idempotencyKey, withParents: true }));
+ dispatch(
+ importEntities(
+ { statuses: [{ ...status, expectsCard }] },
+ { idempotencyKey, withParents: true },
+ ),
+ );
} else {
queryClient.invalidateQueries(scheduledStatusesQueryOptions);
}
- dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!editedId });
+ dispatch({
+ type: STATUS_CREATE_SUCCESS,
+ status,
+ params,
+ idempotencyKey,
+ editing: !!editedId,
+ });
// Poll the backend for the updated card
if (expectsCard) {
const delay = 1000;
const poll = (retries = 5) => {
- return getClient(getState()).statuses.getStatus(status.id).then(response => {
- if (response.card) {
- dispatch(importEntities({ statuses: [response] }));
- } else if (retries > 0 && response) {
- setTimeout(() => poll(retries - 1), delay);
- }
- }).catch(console.error);
+ return getClient(getState())
+ .statuses.getStatus(status.id)
+ .then((response) => {
+ if (response.card) {
+ dispatch(importEntities({ statuses: [response] }));
+ } else if (retries > 0 && response) {
+ setTimeout(() => poll(retries - 1), delay);
+ }
+ })
+ .catch(console.error);
};
setTimeout(() => poll(), delay);
}
return status;
- }).catch(error => {
- dispatch({ type: STATUS_CREATE_FAIL, error, params, idempotencyKey, editing: !!editedId });
+ })
+ .catch((error) => {
+ dispatch({
+ type: STATUS_CREATE_FAIL,
+ error,
+ params,
+ idempotencyKey,
+ editing: !!editedId,
+ });
throw error;
});
};
@@ -91,39 +131,61 @@ const editStatus = (statusId: string) => (dispatch: AppDispatch, getState: () =>
const state = getState();
const status = state.statuses[statusId];
- const poll = status.poll_id ? queryClient.getQueryData(['statuses', 'polls', status.poll_id]) : undefined;
+ const poll = status.poll_id
+ ? queryClient.getQueryData(['statuses', 'polls', status.poll_id])
+ : undefined;
dispatch({ type: STATUS_FETCH_SOURCE_REQUEST });
- return getClient(state).statuses.getStatusSource(statusId).then(response => {
- dispatch({ type: STATUS_FETCH_SOURCE_SUCCESS });
- dispatch(setComposeToStatus(status, poll, response.text, response.spoiler_text, response.content_type, false));
- useModalsStore.getState().actions.openModal('COMPOSE');
- }).catch(error => {
- dispatch({ type: STATUS_FETCH_SOURCE_FAIL, error });
- });
+ return getClient(state)
+ .statuses.getStatusSource(statusId)
+ .then((response) => {
+ dispatch({ type: STATUS_FETCH_SOURCE_SUCCESS });
+ dispatch(
+ setComposeToStatus(
+ status,
+ poll,
+ response.text,
+ response.spoiler_text,
+ response.content_type,
+ false,
+ ),
+ );
+ useModalsStore.getState().actions.openModal('COMPOSE');
+ })
+ .catch((error) => {
+ dispatch({ type: STATUS_FETCH_SOURCE_FAIL, error });
+ });
};
-const fetchStatus = (statusId: string, intl?: IntlShape) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const params = intl && useSettingsStore.getState().settings.autoTranslate ? {
- language: intl.locale,
- } : undefined;
+const fetchStatus =
+ (statusId: string, intl?: IntlShape) => (dispatch: AppDispatch, getState: () => RootState) => {
+ const params =
+ intl && useSettingsStore.getState().settings.autoTranslate
+ ? {
+ language: intl.locale,
+ }
+ : undefined;
- return getClient(getState()).statuses.getStatus(statusId, params).then(status => {
- dispatch(importEntities({ statuses: [status] }));
- return status;
- });
+ return getClient(getState())
+ .statuses.getStatus(statusId, params)
+ .then((status) => {
+ dispatch(importEntities({ statuses: [status] }));
+ return status;
+ });
};
-const deleteStatus = (statusId: string, groupId?: string, withRedraft = false) =>
+const deleteStatus =
+ (statusId: string, groupId?: string, withRedraft = false) =>
(dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return null;
const state = getState();
const status = state.statuses[statusId];
- const poll = status.poll_id ? queryClient.getQueryData(['statuses', 'polls', status.poll_id]) : undefined;
+ const poll = status.poll_id
+ ? queryClient.getQueryData(['statuses', 'polls', status.poll_id])
+ : undefined;
dispatch({ type: STATUS_DELETE_REQUEST, params: status });
@@ -131,66 +193,81 @@ const deleteStatus = (statusId: string, groupId?: string, withRedraft = false) =
groupId
? getClient(state).experimental.groups.deleteGroupStatus(statusId, groupId)
: getClient(state).statuses.deleteStatus(statusId)
- ).then(response => {
- dispatch({ type: STATUS_DELETE_SUCCESS, statusId });
- dispatch(deleteFromTimelines(statusId));
+ )
+ .then((response) => {
+ dispatch({ type: STATUS_DELETE_SUCCESS, statusId });
+ dispatch(deleteFromTimelines(statusId));
- if (withRedraft) {
- dispatch(setComposeToStatus(status, poll, response.text ?? '', response.spoiler_text, (response as StatusSource).content_type, withRedraft));
- useModalsStore.getState().actions.openModal('COMPOSE');
- }
- })
- .catch(error => {
+ if (withRedraft) {
+ dispatch(
+ setComposeToStatus(
+ status,
+ poll,
+ response.text ?? '',
+ response.spoiler_text,
+ (response as StatusSource).content_type,
+ withRedraft,
+ ),
+ );
+ useModalsStore.getState().actions.openModal('COMPOSE');
+ }
+ })
+ .catch((error) => {
dispatch({ type: STATUS_DELETE_FAIL, params: status, error });
});
};
-const updateStatus = (status: BaseStatus) => (dispatch: AppDispatch) =>{
+const updateStatus = (status: BaseStatus) => (dispatch: AppDispatch) => {
dispatch(importEntities({ statuses: [status] }));
};
-const fetchContext = (statusId: string, intl?: IntlShape) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- const params = intl && useSettingsStore.getState().settings.autoTranslate ? {
- language: intl.locale,
- } : undefined;
+const fetchContext =
+ (statusId: string, intl?: IntlShape) => (dispatch: AppDispatch, getState: () => RootState) => {
+ const params =
+ intl && useSettingsStore.getState().settings.autoTranslate
+ ? {
+ language: intl.locale,
+ }
+ : undefined;
- return getClient(getState()).statuses.getContext(statusId, params).then(context => {
- const { ancestors, descendants } = context;
- const statuses = ancestors.concat(descendants);
- dispatch(importEntities({ statuses }));
- dispatch({ type: CONTEXT_FETCH_SUCCESS, statusId, ancestors, descendants });
- return context;
- }).catch(error => {
- if (error.response?.status === 404) {
- dispatch(deleteFromTimelines(statusId));
- }
- });
+ return getClient(getState())
+ .statuses.getContext(statusId, params)
+ .then((context) => {
+ const { ancestors, descendants } = context;
+ const statuses = ancestors.concat(descendants);
+ dispatch(importEntities({ statuses }));
+ dispatch({ type: CONTEXT_FETCH_SUCCESS, statusId, ancestors, descendants });
+ return context;
+ })
+ .catch((error) => {
+ if (error.response?.status === 404) {
+ dispatch(deleteFromTimelines(statusId));
+ }
+ });
};
-const fetchStatusWithContext = (statusId: string, intl?: IntlShape) =>
- (dispatch: AppDispatch) => Promise.all([
- dispatch(fetchContext(statusId, intl)),
- dispatch(fetchStatus(statusId, intl)),
- ]);
+const fetchStatusWithContext = (statusId: string, intl?: IntlShape) => (dispatch: AppDispatch) =>
+ Promise.all([dispatch(fetchContext(statusId, intl)), dispatch(fetchStatus(statusId, intl))]);
-const muteStatus = (statusId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
+const muteStatus = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
- return getClient(getState()).statuses.muteStatus(statusId).then((status) => {
+ return getClient(getState())
+ .statuses.muteStatus(statusId)
+ .then((status) => {
dispatch({ type: STATUS_MUTE_SUCCESS, statusId });
});
- };
+};
-const unmuteStatus = (statusId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
- if (!isLoggedIn(getState)) return;
+const unmuteStatus = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
+ if (!isLoggedIn(getState)) return;
- return getClient(getState()).statuses.unmuteStatus(statusId).then(() => {
+ return getClient(getState())
+ .statuses.unmuteStatus(statusId)
+ .then(() => {
dispatch({ type: STATUS_UNMUTE_SUCCESS, statusId });
});
- };
+};
const toggleMuteStatus = (status: Pick) =>
status.muted ? unmuteStatus(status.id) : muteStatus(status.id);
@@ -250,19 +327,46 @@ const unfilterStatus = (statusId: string) => ({
});
type StatusesAction =
- | { type: typeof STATUS_CREATE_REQUEST; params: CreateStatusParams; idempotencyKey: string; editing: boolean; redacting: boolean }
- | { type: typeof STATUS_CREATE_SUCCESS; status: BaseStatus | ScheduledStatus; params: CreateStatusParams; idempotencyKey: string; editing: boolean }
- | { type: typeof STATUS_CREATE_FAIL; error: unknown; params: CreateStatusParams; idempotencyKey: string; editing: boolean }
+ | {
+ type: typeof STATUS_CREATE_REQUEST;
+ params: CreateStatusParams;
+ idempotencyKey: string;
+ editing: boolean;
+ redacting: boolean;
+ }
+ | {
+ type: typeof STATUS_CREATE_SUCCESS;
+ status: BaseStatus | ScheduledStatus;
+ params: CreateStatusParams;
+ idempotencyKey: string;
+ editing: boolean;
+ }
+ | {
+ type: typeof STATUS_CREATE_FAIL;
+ error: unknown;
+ params: CreateStatusParams;
+ idempotencyKey: string;
+ editing: boolean;
+ }
| { type: typeof STATUS_FETCH_SOURCE_REQUEST }
| { type: typeof STATUS_FETCH_SOURCE_SUCCESS }
| { type: typeof STATUS_FETCH_SOURCE_FAIL; error: unknown }
| { type: typeof STATUS_DELETE_REQUEST; params: Pick }
| { type: typeof STATUS_DELETE_SUCCESS; statusId: string }
- | { type: typeof STATUS_DELETE_FAIL; params: Pick; error: unknown }
- | { type: typeof CONTEXT_FETCH_SUCCESS; statusId: string; ancestors: Array; descendants: Array }
+ | {
+ type: typeof STATUS_DELETE_FAIL;
+ params: Pick;
+ error: unknown;
+ }
+ | {
+ type: typeof CONTEXT_FETCH_SUCCESS;
+ statusId: string;
+ ancestors: Array;
+ descendants: Array;
+ }
| { type: typeof STATUS_MUTE_SUCCESS; statusId: string }
| { type: typeof STATUS_UNMUTE_SUCCESS; statusId: string }
- | ReturnType
+ | ReturnType;
export {
STATUS_CREATE_REQUEST,
diff --git a/packages/pl-fe/src/actions/timelines.ts b/packages/pl-fe/src/actions/timelines.ts
index 271a7aba7..39b96857b 100644
--- a/packages/pl-fe/src/actions/timelines.ts
+++ b/packages/pl-fe/src/actions/timelines.ts
@@ -35,18 +35,21 @@ const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL' as const;
const MAX_QUEUED_ITEMS = 40;
-const processTimelineUpdate = (timeline: string, status: BaseStatus) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const processTimelineUpdate =
+ (timeline: string, status: BaseStatus) => (dispatch: AppDispatch, getState: () => RootState) => {
const me = getState().me;
const ownStatus = status.account?.id === me;
const hasPendingStatuses = !!getState().pending_statuses.length;
const columnSettings = useSettingsStore.getState().settings.timelines[timeline];
- const shouldSkipQueue = shouldFilter({
- in_reply_to_id: status.in_reply_to_id,
- visibility: status.visibility,
- reblog_id: status.reblog?.id ?? null,
- }, columnSettings);
+ const shouldSkipQueue = shouldFilter(
+ {
+ in_reply_to_id: status.in_reply_to_id,
+ visibility: status.visibility,
+ reblog_id: status.reblog?.id ?? null,
+ },
+ columnSettings,
+ );
if (ownStatus && hasPendingStatuses) {
// WebSockets push statuses without the Idempotency-Key,
@@ -71,9 +74,9 @@ const updateTimeline = (timeline: string, statusId: string) => ({
});
const updateTimelineQueue = (timeline: string, statusId: string) => ({
-// if (typeof accept === 'function' && !accept(status)) {
-// return;
-// }
+ // if (typeof accept === 'function' && !accept(status)) {
+ // return;
+ // }
type: TIMELINE_UPDATE_QUEUE,
timeline,
statusId,
@@ -84,7 +87,8 @@ interface TimelineDequeueAction {
timeline: string;
}
-const dequeueTimeline = (timelineId: string, expandFunc?: (lastStatusId: string) => void) =>
+const dequeueTimeline =
+ (timelineId: string, expandFunc?: (lastStatusId: string) => void) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const queuedCount = state.timelines[timelineId]?.totalQueuedItemsCount || 0;
@@ -117,8 +121,8 @@ interface TimelineDeleteAction {
reblogOf: string | null;
}
-const deleteFromTimelines = (statusId: string) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const deleteFromTimelines =
+ (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const accountId = getState().statuses[statusId]?.account?.id;
const references: Array<[string, string]> = Object.entries(getState().statuses)
.filter(([key, status]) => [key, status.reblog_id === statusId])
@@ -136,7 +140,7 @@ const deleteFromTimelines = (statusId: string) =>
const clearTimeline = (timeline: string) => ({ type: TIMELINE_CLEAR, timeline });
-const noOp = () => { };
+const noOp = () => {};
const parseTags = (tags: Record = {}, mode: 'any' | 'all' | 'none') =>
(tags[mode] || []).map((tag) => tag.value);
@@ -145,12 +149,20 @@ const deduplicateStatuses = (statuses: Array) => {
const deduplicatedStatuses: Array }> = [];
for (const status of statuses) {
- const reblogged = status.reblog && deduplicatedStatuses.find((deduplicatedStatus) => deduplicatedStatus.reblog?.id === status.reblog?.id);
+ const reblogged =
+ status.reblog &&
+ deduplicatedStatuses.find(
+ (deduplicatedStatus) => deduplicatedStatus.reblog?.id === status.reblog?.id,
+ );
if (reblogged) {
reblogged.accounts.push(status.account);
reblogged.id += ':' + status.id;
- } else if (!deduplicatedStatuses.find((deduplicatedStatus) => deduplicatedStatus.reblog?.id === status.id)) {
+ } else if (
+ !deduplicatedStatuses.find(
+ (deduplicatedStatus) => deduplicatedStatus.reblog?.id === status.id,
+ )
+ ) {
deduplicatedStatuses.push({ accounts: [status.account], ...status });
}
}
@@ -158,7 +170,13 @@ const deduplicateStatuses = (statuses: Array) => {
return deduplicatedStatuses;
};
-const handleTimelineExpand = (timelineId: string, fn: Promise>, done = noOp, onError?: (error: any) => void) =>
+const handleTimelineExpand =
+ (
+ timelineId: string,
+ fn: Promise>,
+ done = noOp,
+ onError?: (error: any) => void,
+ ) =>
async (dispatch: AppDispatch) => {
dispatch(expandTimelineRequest(timelineId));
@@ -168,15 +186,17 @@ const handleTimelineExpand = (timelineId: string, fn: Promise status.accounts) }));
+ dispatch(importEntities({ statuses: statuses.filter((status) => status.accounts) }));
- dispatch(expandTimelineSuccess(
- timelineId,
- statuses,
- response.next,
- response.previous,
- response.partial,
- ));
+ dispatch(
+ expandTimelineSuccess(
+ timelineId,
+ statuses,
+ response.next,
+ response.previous,
+ response.partial,
+ ),
+ );
done();
} catch (error) {
dispatch(expandTimelineFail(timelineId, error));
@@ -185,7 +205,8 @@ const handleTimelineExpand = (timelineId: string, fn: Promise
+const fetchHomeTimeline =
+ (expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
@@ -194,27 +215,41 @@ const fetchHomeTimeline = (expand = false, done = noOp) =>
if (expand && state.timelines.home?.isLoading) return;
- const fn = (expand && state.timelines.home?.next?.()) ?? getClient(state).timelines.homeTimeline(params);
+ const fn =
+ (expand && state.timelines.home?.next?.()) ?? getClient(state).timelines.homeTimeline(params);
return dispatch(handleTimelineExpand('home', fn, done));
};
-const fetchPublicTimeline = ({ onlyMedia, local, instance }: Record = {}, expand = false, done = noOp, onError = noOp) =>
+const fetchPublicTimeline =
+ (
+ { onlyMedia, local, instance }: Record = {},
+ expand = false,
+ done = noOp,
+ onError = noOp,
+ ) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `${instance ? 'remote' : 'public'}${local ? ':local' : ''}${onlyMedia ? ':media' : ''}${instance ? `:${instance}` : ''}`;
- const params: PublicTimelineParams = { only_media: onlyMedia, local: instance ? false : local, instance };
+ const params: PublicTimelineParams = {
+ only_media: onlyMedia,
+ local: instance ? false : local,
+ instance,
+ };
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
if (expand && state.timelines[timelineId]?.isLoading) return;
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).timelines.publicTimeline(params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).timelines.publicTimeline(params);
return dispatch(handleTimelineExpand(timelineId, fn, done, onError));
};
-const fetchBubbleTimeline = ({ onlyMedia }: Record = {}, expand = false, done = noOp) =>
+const fetchBubbleTimeline =
+ ({ onlyMedia }: Record = {}, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `bubble${onlyMedia ? ':media' : ''}`;
@@ -224,12 +259,15 @@ const fetchBubbleTimeline = ({ onlyMedia }: Record = {}, expand = f
if (expand && state.timelines[timelineId]?.isLoading) return;
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).timelines.bubbleTimeline(params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).timelines.bubbleTimeline(params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
-const fetchWrenchedTimeline = ({ onlyMedia }: Record = {}, expand = false, done = noOp) =>
+const fetchWrenchedTimeline =
+ ({ onlyMedia }: Record = {}, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `wrenched${onlyMedia ? ':media' : ''}`;
@@ -239,12 +277,20 @@ const fetchWrenchedTimeline = ({ onlyMedia }: Record = {}, expand =
if (expand && state.timelines[timelineId]?.isLoading) return;
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).timelines.wrenchedTimeline(params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).timelines.wrenchedTimeline(params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
-const fetchAccountTimeline = (accountId: string, { exclude_replies, pinned, only_media, limit }: Record = {}, expand = false, done = noOp) =>
+const fetchAccountTimeline =
+ (
+ accountId: string,
+ { exclude_replies, pinned, only_media, limit }: Record = {},
+ expand = false,
+ done = noOp,
+ ) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `account:${accountId}${!exclude_replies ? ':with_replies' : ''}${pinned ? ':pinned' : only_media ? ':media' : ''}`;
@@ -256,12 +302,15 @@ const fetchAccountTimeline = (accountId: string, { exclude_replies, pinned, only
if (!expand && state.timelines[timelineId]?.loaded) return;
if (expand && state.timelines[timelineId]?.isLoading) return;
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).accounts.getAccountStatuses(accountId, params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).accounts.getAccountStatuses(accountId, params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
-const fetchListTimeline = (listId: string, expand = false, done = noOp) =>
+const fetchListTimeline =
+ (listId: string, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `list:${listId}`;
@@ -271,12 +320,15 @@ const fetchListTimeline = (listId: string, expand = false, done = noOp) =>
if (expand && state.timelines[timelineId]?.isLoading) return;
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).timelines.listTimeline(listId, params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).timelines.listTimeline(listId, params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
-const fetchCircleTimeline = (circleId: string, expand = false, done = noOp) =>
+const fetchCircleTimeline =
+ (circleId: string, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `circle:${circleId}`;
@@ -286,12 +338,15 @@ const fetchCircleTimeline = (circleId: string, expand = false, done = noOp) =>
if (expand && state.timelines[timelineId]?.isLoading) return;
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).circles.getCircleStatuses(circleId, params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).circles.getCircleStatuses(circleId, params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
-const fetchAntennaTimeline = (antennaId: string, expand = false, done = noOp) =>
+const fetchAntennaTimeline =
+ (antennaId: string, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `antenna:${antennaId}`;
@@ -301,12 +356,15 @@ const fetchAntennaTimeline = (antennaId: string, expand = false, done = noOp) =>
if (expand && state.timelines[timelineId]?.isLoading) return;
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).timelines.antennaTimeline(antennaId, params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).timelines.antennaTimeline(antennaId, params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
-const fetchGroupTimeline = (groupId: string, { only_media, limit }: Record = {}, expand = false, done = noOp) =>
+const fetchGroupTimeline =
+ (groupId: string, { only_media, limit }: Record = {}, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `group:${groupId}${only_media ? ':media' : ''}`;
@@ -317,12 +375,15 @@ const fetchGroupTimeline = (groupId: string, { only_media, limit }: Record = {}, expand = false, done = noOp) =>
+const fetchHashtagTimeline =
+ (hashtag: string, { tags }: Record = {}, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `hashtag:${hashtag}`;
@@ -337,12 +398,15 @@ const fetchHashtagTimeline = (hashtag: string, { tags }: Record = {
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).timelines.hashtagTimeline(hashtag, params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).timelines.hashtagTimeline(hashtag, params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
-const fetchLinkTimeline = (url: string, expand = false, done = noOp) =>
+const fetchLinkTimeline =
+ (url: string, expand = false, done = noOp) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const timelineId = `link:${url}`;
@@ -353,7 +417,9 @@ const fetchLinkTimeline = (url: string, expand = false, done = noOp) =>
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
- const fn = (expand && state.timelines[timelineId]?.next?.()) ?? getClient(state).timelines.linkTimeline(url, params);
+ const fn =
+ (expand && state.timelines[timelineId]?.next?.()) ??
+ getClient(state).timelines.linkTimeline(url, params);
return dispatch(handleTimelineExpand(timelineId, fn, done));
};
diff --git a/packages/pl-fe/src/api/batcher.ts b/packages/pl-fe/src/api/batcher.ts
index 58aac2493..5a9482d87 100644
--- a/packages/pl-fe/src/api/batcher.ts
+++ b/packages/pl-fe/src/api/batcher.ts
@@ -3,18 +3,22 @@ import memoize from 'lodash/memoize';
import type { PlApiClient } from 'pl-api';
-const relationships = memoize((client: PlApiClient) => create({
- fetcher: (ids: string[]) => client.accounts.getRelationships(ids),
- resolver: keyResolver('id'),
- scheduler: bufferScheduler(200),
-}));
+const relationships = memoize((client: PlApiClient) =>
+ create({
+ fetcher: (ids: string[]) => client.accounts.getRelationships(ids),
+ resolver: keyResolver('id'),
+ scheduler: bufferScheduler(200),
+ }),
+);
// TODO: proper multi-client support
-const translations = memoize((lang: string, client: PlApiClient) => create({
- fetcher: (ids: string[]) => client.statuses.translateStatuses(ids, lang),
- resolver: keyResolver('id'),
- scheduler: bufferScheduler(100),
-}));
+const translations = memoize((lang: string, client: PlApiClient) =>
+ create({
+ fetcher: (ids: string[]) => client.statuses.translateStatuses(ids, lang),
+ resolver: keyResolver('id'),
+ scheduler: bufferScheduler(100),
+ }),
+);
const batcher = {
relationships,
diff --git a/packages/pl-fe/src/api/hooks/accounts/use-account-lookup.ts b/packages/pl-fe/src/api/hooks/accounts/use-account-lookup.ts
index 1c730cea5..da5bbe6f1 100644
--- a/packages/pl-fe/src/api/hooks/accounts/use-account-lookup.ts
+++ b/packages/pl-fe/src/api/hooks/accounts/use-account-lookup.ts
@@ -26,15 +26,17 @@ const useAccountLookup = (acct: string | undefined, opts: UseAccountLookupOpts =
{ enabled: !!acct },
);
- const {
- data: relationship,
- isLoading: isRelationshipLoading,
- } = useRelationshipQuery(withRelationship ? entity?.id : undefined);
+ const { data: relationship, isLoading: isRelationshipLoading } = useRelationshipQuery(
+ withRelationship ? entity?.id : undefined,
+ );
const isBlocked = entity?.relationship?.blocked_by === true;
- const isUnavailable = (me === entity?.id) ? false : (isBlocked && !features.blockersVisible);
+ const isUnavailable = me === entity?.id ? false : isBlocked && !features.blockersVisible;
- const account = useMemo(() => entity ? { ...entity, relationship } : undefined, [entity, relationship]);
+ const account = useMemo(
+ () => (entity ? { ...entity, relationship } : undefined),
+ [entity, relationship],
+ );
return {
...result,
diff --git a/packages/pl-fe/src/api/hooks/accounts/use-account.ts b/packages/pl-fe/src/api/hooks/accounts/use-account.ts
index 0368b64b8..7110f7ed3 100644
--- a/packages/pl-fe/src/api/hooks/accounts/use-account.ts
+++ b/packages/pl-fe/src/api/hooks/accounts/use-account.ts
@@ -26,24 +26,26 @@ const useAccount = (accountId?: string, opts: UseAccountOpts = {}) => {
{ enabled: !!accountId },
);
- const meta = useAppSelector((state) => accountId ? state.accounts_meta[accountId] : undefined);
+ const meta = useAppSelector((state) => (accountId ? state.accounts_meta[accountId] : undefined));
- const {
- data: relationship,
- isLoading: isRelationshipLoading,
- } = useRelationshipQuery(withRelationship ? entity?.id : undefined);
+ const { data: relationship, isLoading: isRelationshipLoading } = useRelationshipQuery(
+ withRelationship ? entity?.id : undefined,
+ );
const isBlocked = entity?.relationship?.blocked_by === true;
- const isUnavailable = (me === entity?.id) ? false : (isBlocked && !features.blockersVisible);
+ const isUnavailable = me === entity?.id ? false : isBlocked && !features.blockersVisible;
const account = useMemo(
- () => entity ? {
- ...entity,
- relationship,
- __meta: { meta, ...entity.__meta },
- // @ts-ignore
- is_admin: meta?.role ? (meta.role.permissions & 0x1) === 0x1 : entity.is_admin,
- } : undefined,
+ () =>
+ entity
+ ? {
+ ...entity,
+ relationship,
+ __meta: { meta, ...entity.__meta },
+ // @ts-ignore
+ is_admin: meta?.role ? (meta.role.permissions & 0x1) === 0x1 : entity.is_admin,
+ }
+ : undefined,
[entity, relationship],
);
diff --git a/packages/pl-fe/src/api/hooks/admin/use-verify.ts b/packages/pl-fe/src/api/hooks/admin/use-verify.ts
index 7148ac12f..a03b8e24c 100644
--- a/packages/pl-fe/src/api/hooks/admin/use-verify.ts
+++ b/packages/pl-fe/src/api/hooks/admin/use-verify.ts
@@ -22,7 +22,7 @@ const useVerify = () => {
};
transaction({
- Accounts: ({ [accountId]: updater }),
+ Accounts: { [accountId]: updater },
});
};
diff --git a/packages/pl-fe/src/api/hooks/groups/use-delete-group.ts b/packages/pl-fe/src/api/hooks/groups/use-delete-group.ts
index f68a67c9d..3961eaea0 100644
--- a/packages/pl-fe/src/api/hooks/groups/use-delete-group.ts
+++ b/packages/pl-fe/src/api/hooks/groups/use-delete-group.ts
@@ -5,9 +5,8 @@ import { useClient } from '@/hooks/use-client';
const useDeleteGroup = () => {
const client = useClient();
- const { deleteEntity, isSubmitting } = useDeleteEntity(
- Entities.GROUPS,
- (groupId: string) => client.experimental.groups.deleteGroup(groupId),
+ const { deleteEntity, isSubmitting } = useDeleteEntity(Entities.GROUPS, (groupId: string) =>
+ client.experimental.groups.deleteGroup(groupId),
);
return {
diff --git a/packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts b/packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts
index bdfbbdad9..9e76e5686 100644
--- a/packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts
+++ b/packages/pl-fe/src/api/hooks/groups/use-demote-group-member.ts
@@ -11,8 +11,14 @@ const useDemoteGroupMember = (group: Pick, groupMember: Pick client.experimental.groups.demoteGroupUsers(group.id, account_ids, role),
- { schema: v.pipe(v.any(), v.transform(arr => arr[0])) },
+ ({ account_ids, role }: { account_ids: string[]; role: GroupRole }) =>
+ client.experimental.groups.demoteGroupUsers(group.id, account_ids, role),
+ {
+ schema: v.pipe(
+ v.any(),
+ v.transform((arr) => arr[0]),
+ ),
+ },
);
return createEntity;
diff --git a/packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts b/packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts
index e8b084720..cc19ce139 100644
--- a/packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts
+++ b/packages/pl-fe/src/api/hooks/groups/use-group-membership-requests.ts
@@ -1,7 +1,7 @@
import { GroupRoles } from 'pl-api';
import { Entities } from '@/entity-store/entities';
-import { useDismissEntity } from '@/entity-store/hooks/use-dismiss-entity';
+import { useDismissEntity } from '@/entity-store/hooks/use-dismiss-entity';
import { useEntities } from '@/entity-store/hooks/use-entities';
import { useClient } from '@/hooks/use-client';
@@ -25,13 +25,19 @@ const useGroupMembershipRequests = (groupId: string) => {
);
const { dismissEntity: authorize } = useDismissEntity(path, async (accountId: string) => {
- const response = await client.experimental.groups.acceptGroupMembershipRequest(groupId, accountId);
+ const response = await client.experimental.groups.acceptGroupMembershipRequest(
+ groupId,
+ accountId,
+ );
invalidate();
return response;
});
const { dismissEntity: reject } = useDismissEntity(path, async (accountId: string) => {
- const response = await client.experimental.groups.rejectGroupMembershipRequest(groupId, accountId);
+ const response = await client.experimental.groups.rejectGroupMembershipRequest(
+ groupId,
+ accountId,
+ );
invalidate();
return response;
});
diff --git a/packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts b/packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts
index b747f8312..dcff57403 100644
--- a/packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts
+++ b/packages/pl-fe/src/api/hooks/groups/use-group-relationship.ts
@@ -14,7 +14,10 @@ const useGroupRelationship = (groupId: string | undefined) => {
() => client.experimental.groups.getGroupRelationships([groupId!]),
{
enabled: !!groupId,
- schema: v.pipe(v.any(), v.transform(arr => arr[0])),
+ schema: v.pipe(
+ v.any(),
+ v.transform((arr) => arr[0]),
+ ),
},
);
diff --git a/packages/pl-fe/src/api/hooks/groups/use-group.ts b/packages/pl-fe/src/api/hooks/groups/use-group.ts
index 0163ca69d..76646284d 100644
--- a/packages/pl-fe/src/api/hooks/groups/use-group.ts
+++ b/packages/pl-fe/src/api/hooks/groups/use-group.ts
@@ -14,7 +14,11 @@ const useGroup = (groupId: string, refetch = true) => {
const location = useLocation();
const navigate = useNavigate();
- const { entity: group, isUnauthorized, ...result } = useEntity(
+ const {
+ entity: group,
+ isUnauthorized,
+ ...result
+ } = useEntity(
[Entities.GROUPS, groupId],
() => client.experimental.groups.getGroup(groupId),
{
diff --git a/packages/pl-fe/src/api/hooks/groups/use-groups.ts b/packages/pl-fe/src/api/hooks/groups/use-groups.ts
index 65a5c363f..291392912 100644
--- a/packages/pl-fe/src/api/hooks/groups/use-groups.ts
+++ b/packages/pl-fe/src/api/hooks/groups/use-groups.ts
@@ -18,7 +18,7 @@ const useGroups = () => {
);
const { relationships } = useGroupRelationships(
['search', ''],
- entities.map(entity => entity.id),
+ entities.map((entity) => entity.id),
);
const groups = entities.map((group) => ({
diff --git a/packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts b/packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts
index 7b5ea3ce9..2ad3c85ba 100644
--- a/packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts
+++ b/packages/pl-fe/src/api/hooks/groups/use-promote-group-member.ts
@@ -11,8 +11,14 @@ const usePromoteGroupMember = (group: Pick, groupMember: Pick client.experimental.groups.promoteGroupUsers(group.id, account_ids, role),
- { schema: v.pipe(v.any(), v.transform(arr => arr[0])) },
+ ({ account_ids, role }: { account_ids: string[]; role: GroupRole }) =>
+ client.experimental.groups.promoteGroupUsers(group.id, account_ids, role),
+ {
+ schema: v.pipe(
+ v.any(),
+ v.transform((arr) => arr[0]),
+ ),
+ },
);
return createEntity;
diff --git a/packages/pl-fe/src/api/hooks/streaming/use-timeline-stream.ts b/packages/pl-fe/src/api/hooks/streaming/use-timeline-stream.ts
index 8c733d137..480d7c3d9 100644
--- a/packages/pl-fe/src/api/hooks/streaming/use-timeline-stream.ts
+++ b/packages/pl-fe/src/api/hooks/streaming/use-timeline-stream.ts
@@ -7,25 +7,36 @@ import { getAccessToken } from '@/utils/auth';
import type { StreamingEvent } from 'pl-api';
-const useTimelineStream = (stream: string, params: { list?: string; tag?: string } = {}, enabled = true, listener?: (event: StreamingEvent) => any) => {
+const useTimelineStream = (
+ stream: string,
+ params: { list?: string; tag?: string } = {},
+ enabled = true,
+ listener?: (event: StreamingEvent) => any,
+) => {
const firstUpdate = useRef(true);
const client = useClient();
const instance = useInstance();
- const socket = useRef<({
+ const socket = useRef<{
listen: (listener: any, stream?: string) => number;
unlisten: (listener: any) => void;
- subscribe: (stream: string, params?: {
+ subscribe: (
+ stream: string,
+ params?: {
list?: string;
tag?: string;
- }) => void;
- unsubscribe: (stream: string, params?: {
+ },
+ ) => void;
+ unsubscribe: (
+ stream: string,
+ params?: {
list?: string;
tag?: string;
- }) => void;
+ },
+ ) => void;
close: () => void;
- }) | null>(null);
+ } | null>(null);
const accessToken = useAppSelector(getAccessToken);
const streamingUrl = instance.configuration.urls.streaming;
diff --git a/packages/pl-fe/src/api/hooks/streaming/use-user-stream.ts b/packages/pl-fe/src/api/hooks/streaming/use-user-stream.ts
index 82b637ed6..fb0d0d124 100644
--- a/packages/pl-fe/src/api/hooks/streaming/use-user-stream.ts
+++ b/packages/pl-fe/src/api/hooks/streaming/use-user-stream.ts
@@ -21,11 +21,17 @@ import { updateReactions } from '../../../queries/announcements/use-announcement
import { useTimelineStream } from './use-timeline-stream';
import type { AppDispatch, RootState } from '@/store';
-import type { Announcement, AnnouncementReaction, FollowRelationshipUpdate, Relationship, StreamingEvent } from 'pl-api';
+import type {
+ Announcement,
+ AnnouncementReaction,
+ FollowRelationshipUpdate,
+ Relationship,
+ StreamingEvent,
+} from 'pl-api';
const updateAnnouncementReactions = (reaction: AnnouncementReaction) => {
queryClient.setQueryData(['announcements'], (prevResult: Announcement[]) =>
- prevResult.map(value => {
+ prevResult.map((value) => {
if (value.id !== reaction.announcement_id) return value;
return {
@@ -40,16 +46,16 @@ const updateAnnouncement = (announcement: Announcement) =>
queryClient.setQueryData(['announcements'], (prevResult: Announcement[]) => {
let updated = false;
- const result = prevResult.map(value => value.id === announcement.id
- ? (updated = true, announcement)
- : value);
+ const result = prevResult.map((value) =>
+ value.id === announcement.id ? ((updated = true), announcement) : value,
+ );
if (!updated) return [announcement, ...result];
});
const deleteAnnouncement = (announcementId: string) =>
queryClient.setQueryData(['announcements'], (prevResult: Announcement[]) =>
- prevResult.filter(value => value.id !== announcementId),
+ prevResult.filter((value) => value.id !== announcementId),
);
const followStateToRelationship = (followState: FollowRelationshipUpdate['state']) => {
@@ -65,17 +71,23 @@ const followStateToRelationship = (followState: FollowRelationshipUpdate['state'
}
};
-const updateFollowRelationships = (update: FollowRelationshipUpdate) =>
- (dispatch: AppDispatch, getState: () => RootState) => {
+const updateFollowRelationships =
+ (update: FollowRelationshipUpdate) => (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const me = state.me;
if (update.follower.id === me) {
- queryClient.setQueryData(['accountRelationships', update.following.id], (relationship) => relationship ? ({
- ...relationship,
- ...followStateToRelationship(update.state),
- }) : undefined);
+ queryClient.setQueryData(
+ ['accountRelationships', update.following.id],
+ (relationship) =>
+ relationship
+ ? {
+ ...relationship,
+ ...followStateToRelationship(update.state),
+ }
+ : undefined,
+ );
}
};
@@ -110,11 +122,13 @@ const useUserStream = () => {
dispatch(deleteFromTimelines(event.payload));
break;
case 'notification':
- messages[getLocale()]().then(messages => {
- dispatch(updateNotificationsQueue(event.payload, messages, getLocale()));
- }).catch(error => {
- console.error(error);
- });
+ messages[getLocale()]()
+ .then((messages) => {
+ dispatch(updateNotificationsQueue(event.payload, messages, getLocale()));
+ })
+ .catch((error) => {
+ console.error(error);
+ });
break;
case 'conversation':
dispatch(updateConversations(event.payload));
diff --git a/packages/pl-fe/src/api/index.ts b/packages/pl-fe/src/api/index.ts
index c93f9c983..707aca40f 100644
--- a/packages/pl-fe/src/api/index.ts
+++ b/packages/pl-fe/src/api/index.ts
@@ -8,15 +8,15 @@ import { buildFullPath } from '@/utils/url';
import type { RootState, Store } from '@/store';
let store: Store;
-import('@/store').then((value) => store = value.store).catch(() => {});
+import('@/store').then((value) => (store = value.store)).catch(() => {});
type PlfeResponse = Response & { data: string; json: T };
/**
- * Dumb client for grabbing static files.
- * It uses FE_SUBDIRECTORY and parses JSON if possible.
- * No authorization is needed.
- */
+ * Dumb client for grabbing static files.
+ * It uses FE_SUBDIRECTORY and parses JSON if possible.
+ * No authorization is needed.
+ */
const staticFetch = async (input: URL | RequestInfo, init?: RequestInit) => {
const fullPath = buildFullPath(input.toString(), BuildConfig.BACKEND_URL);
@@ -34,7 +34,17 @@ const staticFetch = async (input: URL | RequestInfo, init?: RequestInit) => {
const { headers, ok, redirected, status, statusText, type, url } = response;
- return { headers, ok, redirected, status, statusText, type, url, data, json } as any as PlfeResponse;
+ return {
+ headers,
+ ok,
+ redirected,
+ status,
+ statusText,
+ type,
+ url,
+ data,
+ json,
+ } as any as PlfeResponse;
};
const getClient = (state: RootState | (() => RootState) = store?.getState()) => {
@@ -43,8 +53,4 @@ const getClient = (state: RootState | (() => RootState) = store?.getState()) =>
return state.auth.client;
};
-export {
- type PlfeResponse,
- staticFetch,
- getClient,
-};
+export { type PlfeResponse, staticFetch, getClient };
diff --git a/packages/pl-fe/src/build-config.ts b/packages/pl-fe/src/build-config.ts
index f7ff9d701..04deae652 100644
--- a/packages/pl-fe/src/build-config.ts
+++ b/packages/pl-fe/src/build-config.ts
@@ -4,12 +4,7 @@
*/
const env = compileTime(() => {
- const {
- NODE_ENV,
- BACKEND_URL,
- FE_SUBDIRECTORY,
- WITH_LANDING_PAGE,
- } = process.env;
+ const { NODE_ENV, BACKEND_URL, FE_SUBDIRECTORY, WITH_LANDING_PAGE } = process.env;
const sanitizeURL = (url: string | undefined = ''): string => {
try {
@@ -19,7 +14,8 @@ const env = compileTime(() => {
}
};
- const sanitizeBasename = (path: string | undefined = ''): string => `/${path.replace(/^\/+|\/+$/g, '')}`;
+ const sanitizeBasename = (path: string | undefined = ''): string =>
+ `/${path.replace(/^\/+|\/+$/g, '')}`;
return {
NODE_ENV: NODE_ENV ?? 'development',
@@ -29,18 +25,8 @@ const env = compileTime(() => {
};
});
-const {
- NODE_ENV,
- BACKEND_URL,
- FE_SUBDIRECTORY,
- WITH_LANDING_PAGE,
-} = env;
+const { NODE_ENV, BACKEND_URL, FE_SUBDIRECTORY, WITH_LANDING_PAGE } = env;
export type PlFeEnv = typeof env;
-export {
- NODE_ENV,
- BACKEND_URL,
- FE_SUBDIRECTORY,
- WITH_LANDING_PAGE,
-};
+export { NODE_ENV, BACKEND_URL, FE_SUBDIRECTORY, WITH_LANDING_PAGE };
diff --git a/packages/pl-fe/src/columns/notifications.tsx b/packages/pl-fe/src/columns/notifications.tsx
index 8550abdbc..6b7c8ceea 100644
--- a/packages/pl-fe/src/columns/notifications.tsx
+++ b/packages/pl-fe/src/columns/notifications.tsx
@@ -32,10 +32,17 @@ import type { VirtuosoHandle } from 'react-virtuoso';
const messages = defineMessages({
title: { id: 'column.notifications', defaultMessage: 'Notifications' },
- queue: { id: 'notifications.queue_label', defaultMessage: 'Click to see {count} new {count, plural, one {notification} other {notifications}}' },
+ queue: {
+ id: 'notifications.queue_label',
+ defaultMessage:
+ 'Click to see {count} new {count, plural, one {notification} other {notifications}}',
+ },
all: { id: 'notifications.filter.all', defaultMessage: 'All' },
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
- statuses: { id: 'notifications.filter.statuses', defaultMessage: 'Updates from people you follow' },
+ statuses: {
+ id: 'notifications.filter.statuses',
+ defaultMessage: 'Updates from people you follow',
+ },
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Likes' },
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Reposts' },
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
@@ -81,12 +88,18 @@ const FilterBar = () => {
action: onClick('mention'),
name: 'mention',
});
- if (features.accountNotifies) items.push({
- text: ,
- title: intl.formatMessage(messages.statuses),
- action: onClick('status'),
- name: 'status',
- });
+ if (features.accountNotifies)
+ items.push({
+ text: (
+
+ ),
+ title: intl.formatMessage(messages.statuses),
+ action: onClick('status'),
+ name: 'status',
+ });
items.push({
text: ,
title: intl.formatMessage(messages.favourites),
@@ -99,18 +112,27 @@ const FilterBar = () => {
action: onClick('reblog'),
name: 'reblog',
});
- if (features.polls) items.push({
- text: ,
- title: intl.formatMessage(messages.polls),
- action: onClick('poll'),
- name: 'poll',
- });
- if (features.events) items.push({
- text: ,
- title: intl.formatMessage(messages.events),
- action: onClick('events'),
- name: 'events',
- });
+ if (features.polls)
+ items.push({
+ text: (
+
+ ),
+ title: intl.formatMessage(messages.polls),
+ action: onClick('poll'),
+ name: 'poll',
+ });
+ if (features.events)
+ items.push({
+ text: (
+
+ ),
+ title: intl.formatMessage(messages.events),
+ action: onClick('events'),
+ name: 'events',
+ });
items.push({
text: ,
title: intl.formatMessage(messages.follows),
@@ -122,27 +144,30 @@ const FilterBar = () => {
return ;
};
-const getNotifications = createSelector([
- (state: RootState) => state.notifications.items,
- (_, topNotification?: string) => topNotification,
-], (notifications, topNotificationId) => {
- if (topNotificationId) {
- const queuedNotificationCount = notifications.findIndex((notification) =>
- notification.most_recent_notification_id <= topNotificationId,
- );
- const displayedNotifications = notifications.slice(queuedNotificationCount);
+const getNotifications = createSelector(
+ [
+ (state: RootState) => state.notifications.items,
+ (_, topNotification?: string) => topNotification,
+ ],
+ (notifications, topNotificationId) => {
+ if (topNotificationId) {
+ const queuedNotificationCount = notifications.findIndex(
+ (notification) => notification.most_recent_notification_id <= topNotificationId,
+ );
+ const displayedNotifications = notifications.slice(queuedNotificationCount);
+
+ return {
+ queuedNotificationCount,
+ displayedNotifications,
+ };
+ }
return {
- queuedNotificationCount,
- displayedNotifications,
+ queuedNotificationCount: 0,
+ displayedNotifications: notifications,
};
- }
-
- return {
- queuedNotificationCount: 0,
- displayedNotifications: notifications,
- };
-});
+ },
+);
interface INotificationsColumn {
multiColumn?: boolean;
@@ -153,13 +178,17 @@ const NotificationsColumn: React.FC = ({ multiColumn }) =>
const features = useFeatures();
const settings = useSettings();
- const showFilterBar = (features.notificationsExcludeTypes || features.notificationsIncludeTypes) && settings.notifications.quickFilter.show;
+ const showFilterBar =
+ (features.notificationsExcludeTypes || features.notificationsIncludeTypes) &&
+ settings.notifications.quickFilter.show;
const activeFilter = settings.notifications.quickFilter.active;
const [topNotification, setTopNotification] = useState();
- const { queuedNotificationCount, displayedNotifications } = useAppSelector(state => getNotifications(state, topNotification));
- const isLoading = useAppSelector(state => state.notifications.isLoading);
+ const { queuedNotificationCount, displayedNotifications } = useAppSelector((state) =>
+ getNotifications(state, topNotification),
+ );
+ const isLoading = useAppSelector((state) => state.notifications.isLoading);
// const isUnread = useAppSelector(state => state.notifications.unread > 0);
- const hasMore = useAppSelector(state => state.notifications.hasMore);
+ const hasMore = useAppSelector((state) => state.notifications.hasMore);
const node = useRef(null);
const scrollableContentRef = useRef | null>(null);
@@ -168,31 +197,47 @@ const NotificationsColumn: React.FC = ({ multiColumn }) =>
// dispatch(expandNotifications({ maxId }));
// };
- const handleLoadOlder = useCallback(debounce(() => {
- const minId = displayedNotifications.reduce(
- (minId, notification) => minId && notification.page_min_id && notification.page_min_id > minId
- ? minId
- : notification.page_min_id,
- undefined,
- );
- dispatch(expandNotifications({ maxId: minId }));
- }, 300, { leading: true }), [displayedNotifications]);
+ const handleLoadOlder = useCallback(
+ debounce(
+ () => {
+ const minId = displayedNotifications.reduce(
+ (minId, notification) =>
+ minId && notification.page_min_id && notification.page_min_id > minId
+ ? minId
+ : notification.page_min_id,
+ undefined,
+ );
+ dispatch(expandNotifications({ maxId: minId }));
+ },
+ 300,
+ { leading: true },
+ ),
+ [displayedNotifications],
+ );
- const handleScrollToTop = useCallback(debounce(() => {
- dispatch(scrollTopNotifications(true));
- }, 100), []);
+ const handleScrollToTop = useCallback(
+ debounce(() => {
+ dispatch(scrollTopNotifications(true));
+ }, 100),
+ [],
+ );
- const handleScroll = useCallback(debounce(() => {
- dispatch(scrollTopNotifications(false));
- }, 100), []);
+ const handleScroll = useCallback(
+ debounce(() => {
+ dispatch(scrollTopNotifications(false));
+ }, 100),
+ [],
+ );
const handleMoveUp = (id: string) => {
- const elementIndex = displayedNotifications.findIndex(item => item !== null && item.group_key === id) - 1;
+ const elementIndex =
+ displayedNotifications.findIndex((item) => item !== null && item.group_key === id) - 1;
selectChild(elementIndex, node);
};
const handleMoveDown = (id: string) => {
- const elementIndex = displayedNotifications.findIndex(item => item !== null && item.group_key === id) + 1;
+ const elementIndex =
+ displayedNotifications.findIndex((item) => item !== null && item.group_key === id) + 1;
selectChild(elementIndex, node, undefined, displayedNotifications.length);
};
@@ -220,15 +265,22 @@ const NotificationsColumn: React.FC = ({ multiColumn }) =>
setTopNotification(displayedNotifications[0].most_recent_notification_id);
}, [displayedNotifications.length]);
- const emptyMessage = activeFilter === 'all'
- ?
- : ;
+ const emptyMessage =
+ activeFilter === 'all' ? (
+
+ ) : (
+
+ );
let scrollableContent: Array | null = null;
- const filterBarContainer = showFilterBar
- ? ( )
- : null;
+ const filterBarContainer = showFilterBar ? : null;
if (isLoading && scrollableContentRef.current) {
scrollableContent = scrollableContentRef.current;
@@ -281,9 +333,7 @@ const NotificationsColumn: React.FC = ({ multiColumn }) =>
/>
-
- {scrollContainer}
-
+ {scrollContainer}
>
);
};
diff --git a/packages/pl-fe/src/columns/search.tsx b/packages/pl-fe/src/columns/search.tsx
index 8be0b288a..ca6065b5f 100644
--- a/packages/pl-fe/src/columns/search.tsx
+++ b/packages/pl-fe/src/columns/search.tsx
@@ -9,7 +9,11 @@ import StatusContainer from '@/containers/status-container';
import PlaceholderAccount from '@/features/placeholder/components/placeholder-account';
import PlaceholderHashtag from '@/features/placeholder/components/placeholder-hashtag';
import PlaceholderStatus from '@/features/placeholder/components/placeholder-status';
-import { useSearchAccounts, useSearchHashtags, useSearchStatuses } from '@/queries/search/use-search';
+import {
+ useSearchAccounts,
+ useSearchHashtags,
+ useSearchStatuses,
+} from '@/queries/search/use-search';
import { selectChild } from '@/utils/scroll-utils';
import TrendsColumn from './trends';
@@ -28,20 +32,20 @@ const SearchColumn: React.FC = ({ type, query, accountId, multiCo
const node = useRef(null);
- const searchAccountsQuery = useSearchAccounts(type === 'accounts' && query || '');
- const searchStatusesQuery = useSearchStatuses(type === 'statuses' && query || '', {
+ const searchAccountsQuery = useSearchAccounts((type === 'accounts' && query) || '');
+ const searchStatusesQuery = useSearchStatuses((type === 'statuses' && query) || '', {
account_id: accountId,
});
- const searchHashtagsQuery = useSearchHashtags(type === 'hashtags' && query || '');
+ const searchHashtagsQuery = useSearchHashtags((type === 'hashtags' && query) || '');
- const activeQuery = ({
+ const activeQuery = {
accounts: searchAccountsQuery,
statuses: searchStatusesQuery,
hashtags: searchHashtagsQuery,
links: searchStatusesQuery,
- })[type];
+ }[type];
- const getCurrentIndex = (id: string): number => resultsIds?.findIndex(key => key === id);
+ const getCurrentIndex = (id: string): number => resultsIds?.findIndex((key) => key === id);
const handleMoveUp = (id: string) => {
if (!resultsIds) return;
@@ -54,7 +58,12 @@ const SearchColumn: React.FC = ({ type, query, accountId, multiCo
if (!resultsIds) return;
const elementIndex = getCurrentIndex(id) + 1;
- selectChild(elementIndex, node, document.getElementById('search-results') ?? undefined, resultsIds.length);
+ selectChild(
+ elementIndex,
+ node,
+ document.getElementById('search-results') ?? undefined,
+ resultsIds.length,
+ );
};
const handleLoadMore = () => activeQuery.fetchNextPage({ cancelRefetch: false });
@@ -70,7 +79,9 @@ const SearchColumn: React.FC = ({ type, query, accountId, multiCo
if (!query) return ;
if (searchAccountsQuery.data && searchAccountsQuery.data.length > 0) {
resultsIds = searchAccountsQuery.data;
- searchResults = searchAccountsQuery.data.map(accountId => );
+ searchResults = searchAccountsQuery.data.map((accountId) => (
+
+ ));
} else if (!isFetching) {
return (
@@ -89,7 +100,14 @@ const SearchColumn: React.FC
= ({ type, query, accountId, multiCo
if (!query) return ;
if (searchStatusesQuery.data && searchStatusesQuery.data.length > 0) {
resultsIds = searchStatusesQuery.data;
- searchResults = searchStatusesQuery.data.map(statusId => );
+ searchResults = searchStatusesQuery.data.map((statusId) => (
+
+ ));
} else if (!isFetching) {
return (
@@ -107,8 +125,10 @@ const SearchColumn: React.FC
= ({ type, query, accountId, multiCo
placeholderComponent = PlaceholderHashtag;
if (!query) return ;
if (searchHashtagsQuery.data && searchHashtagsQuery.data.length > 0) {
- resultsIds = searchHashtagsQuery.data.map(hashtag => hashtag.name);
- searchResults = searchHashtagsQuery.data.map(hashtag => );
+ resultsIds = searchHashtagsQuery.data.map((hashtag) => hashtag.name);
+ searchResults = searchHashtagsQuery.data.map((hashtag) => (
+
+ ));
} else if (!isFetching) {
return (
diff --git a/packages/pl-fe/src/columns/trends.tsx b/packages/pl-fe/src/columns/trends.tsx
index 66053395e..4f45fbd24 100644
--- a/packages/pl-fe/src/columns/trends.tsx
+++ b/packages/pl-fe/src/columns/trends.tsx
@@ -21,10 +21,22 @@ interface ITrendsColumn {
}
const TrendsColumn: React.FC
= ({ type, multiColumn }) => {
- const { data: accounts, isFetching: isFetchingAccounts, isLoading: isLoadingAccounts } = useSuggestedAccounts();
+ const {
+ data: accounts,
+ isFetching: isFetchingAccounts,
+ isLoading: isLoadingAccounts,
+ } = useSuggestedAccounts();
const { data: trendingTags, isFetching: isFetchingTags, isLoading: isLoadingTags } = useTrends();
- const { data: trendingStatuses, isFetching: isFetchingStatuses, isLoading: isLoadingStatuses } = useTrendingStatuses();
- const { data: trendingLinks, isFetching: isFetchingLinks, isLoading: isLoadingLinks } = useTrendingLinks();
+ const {
+ data: trendingStatuses,
+ isFetching: isFetchingStatuses,
+ isLoading: isLoadingStatuses,
+ } = useTrendingStatuses();
+ const {
+ data: trendingLinks,
+ isFetching: isFetchingLinks,
+ isLoading: isLoadingLinks,
+ } = useTrendingLinks();
let placeholderComponent = PlaceholderStatus;
@@ -34,27 +46,31 @@ const TrendsColumn: React.FC = ({ type, multiColumn }) => {
switch (type) {
case 'accounts': {
- children = accounts?.map(account => );
+ children = accounts?.map((account) => (
+
+ ));
isFetching = isFetchingAccounts;
isLoading = isLoadingAccounts;
placeholderComponent = PlaceholderAccount;
break;
}
case 'hashtags': {
- children = trendingTags?.map(tag => );
+ children = trendingTags?.map((tag) => );
isFetching = isFetchingTags;
isLoading = isLoadingTags;
placeholderComponent = PlaceholderHashtag;
break;
}
case 'statuses': {
- children = trendingStatuses?.map(statusId => );
+ children = trendingStatuses?.map((statusId) => (
+
+ ));
isFetching = isFetchingStatuses;
isLoading = isLoadingStatuses;
break;
}
case 'links': {
- children = trendingLinks?.map(link => );
+ children = trendingLinks?.map((link) => );
isFetching = isFetchingLinks;
isLoading = isLoadingLinks;
break;
diff --git a/packages/pl-fe/src/components/account-hover-card.tsx b/packages/pl-fe/src/components/account-hover-card.tsx
index 35554964d..ec730b81c 100644
--- a/packages/pl-fe/src/components/account-hover-card.tsx
+++ b/packages/pl-fe/src/components/account-hover-card.tsx
@@ -33,15 +33,32 @@ const messages = {
pronouns: { id: 'account.pronouns.with_label', defaultMessage: 'Pronouns: {pronouns}' },
};
-const getBadges = (
- account?: Pick,
-): JSX.Element[] => {
+const getBadges = (account?: Pick): JSX.Element[] => {
const badges = [];
if (account?.is_admin) {
- badges.push( } />);
+ badges.push(
+
+ }
+ />,
+ );
} else if (account?.is_moderator) {
- badges.push( } />);
+ badges.push(
+
+ }
+ />,
+ );
}
return badges;
@@ -60,7 +77,7 @@ const AccountHoverCard: React.FC = ({ visible = true }) => {
const { accountId, ref } = useAccountHoverCardStore();
const { updateAccountHoverCard, closeAccountHoverCard } = useAccountHoverCardActions();
- const me = useAppSelector(state => state.me);
+ const me = useAppSelector((state) => state.me);
const { account } = useAccount(accountId ?? undefined, { withRelationship: true });
const { data: scrobble } = useQuery(accountScrobbleQueryOptions(account?.id));
const badges = getBadges(account);
@@ -120,14 +137,14 @@ const AccountHoverCard: React.FC = ({ visible = true }) => {
const memberSinceDate = intl.formatDate(account.created_at, { month: 'long', year: 'numeric' });
const followedBy = me !== account.id && account.relationship?.followed_by === true;
- const timezoneField = account.fields.find(field => isTimezoneLabel(field.name));
+ const timezoneField = account.fields.find((field) => isTimezoneLabel(field.name));
return (
= ({ visible = true }) => {
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
-
+
= ({ visible = true }) => {
@@ -165,9 +187,7 @@ const AccountHoverCard: React.FC = ({ visible = true }) => {
) : null}
- {timezoneField && (
-
- )}
+ {timezoneField && }
{account.pronouns.length > 0 && (
@@ -178,7 +198,9 @@ const AccountHoverCard: React.FC = ({ visible = true }) => {
{account.pronouns.join('/')}
diff --git a/packages/pl-fe/src/components/account-local-time.tsx b/packages/pl-fe/src/components/account-local-time.tsx
index 84b2b9bac..d0e35ce38 100644
--- a/packages/pl-fe/src/components/account-local-time.tsx
+++ b/packages/pl-fe/src/components/account-local-time.tsx
@@ -12,13 +12,17 @@ const supportedTimeZones = Intl.supportedValuesOf('timeZone');
const UTC_REGEX = /(GMT|UTC)([+-])([0-9]{1,2})/i;
const getSupportedTimezone = (value: string): string | false => {
- let foundTimezone = supportedTimeZones.find((tz) => value.toLowerCase().startsWith(tz.toLowerCase()));
+ let foundTimezone = supportedTimeZones.find((tz) =>
+ value.toLowerCase().startsWith(tz.toLowerCase()),
+ );
if (!foundTimezone) {
const match = value.match(UTC_REGEX);
if (match) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, sign, hours] = match;
- foundTimezone = supportedTimeZones.find((tz) => tz.toLowerCase() === `etc/gmt${sign === '+' ? '-' : '+'}${+hours}`);
+ foundTimezone = supportedTimeZones.find(
+ (tz) => tz.toLowerCase() === `etc/gmt${sign === '+' ? '-' : '+'}${+hours}`,
+ );
}
}
return foundTimezone ?? false;
@@ -76,7 +80,12 @@ const AccountLocalTime: React.FC = ({ accountId, field }) =>
if (!localTime) return null;
return (
-