Merge branch 'develop' into frontend-rw
Some checks are pending
pl-api CI / Test for a successful build (push) Waiting to run
pl-fe CI / Test and upload artifacts (push) Waiting to run
pl-fe CI / deploy (push) Blocked by required conditions
pl-hooks CI / Test for a successful build (push) Waiting to run

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-10-26 22:52:32 +02:00
commit 734e1db380
42 changed files with 279 additions and 369 deletions

View file

@ -1,13 +1,12 @@
import { staticFetch } from '../api'; import { staticFetch } from '../api';
import type { RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
import type { AnyAction } from 'redux';
const FETCH_ABOUT_PAGE_REQUEST = 'FETCH_ABOUT_PAGE_REQUEST' as const; const FETCH_ABOUT_PAGE_REQUEST = 'FETCH_ABOUT_PAGE_REQUEST' as const;
const FETCH_ABOUT_PAGE_SUCCESS = 'FETCH_ABOUT_PAGE_SUCCESS' as const; const FETCH_ABOUT_PAGE_SUCCESS = 'FETCH_ABOUT_PAGE_SUCCESS' as const;
const FETCH_ABOUT_PAGE_FAIL = 'FETCH_ABOUT_PAGE_FAIL' as const; const FETCH_ABOUT_PAGE_FAIL = 'FETCH_ABOUT_PAGE_FAIL' as const;
const fetchAboutPage = (slug = 'index', locale?: string) => (dispatch: React.Dispatch<AnyAction>, getState: () => RootState) => { const fetchAboutPage = (slug = 'index', locale?: string) => (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: FETCH_ABOUT_PAGE_REQUEST, slug, locale }); dispatch({ type: FETCH_ABOUT_PAGE_REQUEST, slug, locale });
const filename = `${slug}${locale ? `.${locale}` : ''}.html`; const filename = `${slug}${locale ? `.${locale}` : ''}.html`;

View file

@ -1,14 +1,12 @@
import { importEntities } from 'pl-fe/entity-store/actions'; import { importEntities } from 'pl-fe/actions/importer';
import { Entities } from 'pl-fe/entity-store/entities';
import { getClient } from '../api'; import { getClient } from '../api';
import type { RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
import type { AnyAction } from 'redux';
const submitAccountNote = (accountId: string, value: string) => const submitAccountNote = (accountId: string, value: string) =>
(dispatch: React.Dispatch<AnyAction>, getState: () => RootState) => (dispatch: AppDispatch, getState: () => RootState) =>
getClient(getState).accounts.updateAccountNote(accountId, value) getClient(getState).accounts.updateAccountNote(accountId, value)
.then(response => dispatch(importEntities([response], Entities.RELATIONSHIPS))); .then(response => dispatch(importEntities({ relationships: [response] })));
export { submitAccountNote }; export { submitAccountNote };

View file

@ -1,13 +1,12 @@
import { PLEROMA, type UpdateNotificationSettingsParams, type Account, type CreateAccountParams, type PaginatedResponse, type Relationship } from 'pl-api'; import { PLEROMA, type UpdateNotificationSettingsParams, type Account, type CreateAccountParams, type PaginatedResponse, type Relationship } from 'pl-api';
import { importEntities } from 'pl-fe/entity-store/actions';
import { Entities } from 'pl-fe/entity-store/entities'; import { Entities } from 'pl-fe/entity-store/entities';
import { selectAccount } from 'pl-fe/selectors'; import { selectAccount } from 'pl-fe/selectors';
import { isLoggedIn } from 'pl-fe/utils/auth'; import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient, type PlfeResponse } from '../api'; import { getClient, type PlfeResponse } from '../api';
import { importFetchedAccount, importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import type { Map as ImmutableMap } from 'immutable'; import type { Map as ImmutableMap } from 'immutable';
import type { MinifiedStatus } from 'pl-fe/reducers/statuses'; import type { MinifiedStatus } from 'pl-fe/reducers/statuses';
@ -100,7 +99,7 @@ const fetchAccount = (accountId: string) =>
return getClient(getState()).accounts.getAccount(accountId) return getClient(getState()).accounts.getAccount(accountId)
.then(response => { .then(response => {
dispatch(importFetchedAccount(response)); dispatch(importEntities({ accounts: [response] }));
dispatch(fetchAccountSuccess(response)); dispatch(fetchAccountSuccess(response));
}) })
.catch(error => { .catch(error => {
@ -116,7 +115,7 @@ const fetchAccountByUsername = (username: string, history?: History) =>
if (features.accountByUsername && (me || !features.accountLookup)) { if (features.accountByUsername && (me || !features.accountLookup)) {
return getClient(getState()).accounts.getAccount(username).then(response => { return getClient(getState()).accounts.getAccount(username).then(response => {
dispatch(fetchRelationships([response.id])); dispatch(fetchRelationships([response.id]));
dispatch(importFetchedAccount(response)); dispatch(importEntities({ accounts: [response] }));
dispatch(fetchAccountSuccess(response)); dispatch(fetchAccountSuccess(response));
}).catch(error => { }).catch(error => {
dispatch(fetchAccountFail(null, error)); dispatch(fetchAccountFail(null, error));
@ -170,7 +169,7 @@ const blockAccount = (accountId: string) =>
return getClient(getState).filtering.blockAccount(accountId) return getClient(getState).filtering.blockAccount(accountId)
.then(response => { .then(response => {
dispatch(importEntities([response], Entities.RELATIONSHIPS)); dispatch(importEntities({ relationships: [response] }));
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
return dispatch(blockAccountSuccess(response, getState().statuses)); return dispatch(blockAccountSuccess(response, getState().statuses));
}).catch(error => dispatch(blockAccountFail(error))); }).catch(error => dispatch(blockAccountFail(error)));
@ -182,7 +181,7 @@ const unblockAccount = (accountId: string) =>
return getClient(getState).filtering.unblockAccount(accountId) return getClient(getState).filtering.unblockAccount(accountId)
.then(response => { .then(response => {
dispatch(importEntities([response], Entities.RELATIONSHIPS)); dispatch(importEntities({ relationships: [response] }));
}); });
}; };
@ -226,7 +225,7 @@ const muteAccount = (accountId: string, notifications?: boolean, duration = 0) =
return client.filtering.muteAccount(accountId, params) return client.filtering.muteAccount(accountId, params)
.then(response => { .then(response => {
dispatch(importEntities([response], Entities.RELATIONSHIPS)); dispatch(importEntities({ relationships: [response] }));
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
return dispatch(muteAccountSuccess(response, getState().statuses)); return dispatch(muteAccountSuccess(response, getState().statuses));
}) })
@ -238,7 +237,7 @@ const unmuteAccount = (accountId: string) =>
if (!isLoggedIn(getState)) return null; if (!isLoggedIn(getState)) return null;
return getClient(getState()).filtering.unmuteAccount(accountId) return getClient(getState()).filtering.unmuteAccount(accountId)
.then(response => dispatch(importEntities([response], Entities.RELATIONSHIPS))); .then(response => dispatch(importEntities({ relationships: [response] })));
}; };
const muteAccountRequest = (accountId: string) => ({ const muteAccountRequest = (accountId: string) => ({
@ -263,7 +262,7 @@ const removeFromFollowers = (accountId: string) =>
if (!isLoggedIn(getState)) return null; if (!isLoggedIn(getState)) return null;
return getClient(getState()).accounts.removeAccountFromFollowers(accountId) return getClient(getState()).accounts.removeAccountFromFollowers(accountId)
.then(response => dispatch(importEntities([response], Entities.RELATIONSHIPS))); .then(response => dispatch(importEntities({ relationships: [response] })));
}; };
const fetchRelationships = (accountIds: string[]) => const fetchRelationships = (accountIds: string[]) =>
@ -278,7 +277,7 @@ const fetchRelationships = (accountIds: string[]) =>
} }
return getClient(getState()).accounts.getRelationships(newAccountIds) return getClient(getState()).accounts.getRelationships(newAccountIds)
.then(response => dispatch(importEntities(response, Entities.RELATIONSHIPS))); .then(response => dispatch(importEntities({ relationships: response })));
}; };
const fetchFollowRequests = () => const fetchFollowRequests = () =>
@ -289,7 +288,7 @@ const fetchFollowRequests = () =>
return getClient(getState()).myAccount.getFollowRequests() return getClient(getState()).myAccount.getFollowRequests()
.then(response => { .then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
dispatch(fetchFollowRequestsSuccess(response.items, response.next)); dispatch(fetchFollowRequestsSuccess(response.items, response.next));
}) })
.catch(error => dispatch(fetchFollowRequestsFail(error))); .catch(error => dispatch(fetchFollowRequestsFail(error)));
@ -321,7 +320,7 @@ const expandFollowRequests = () =>
dispatch(expandFollowRequestsRequest()); dispatch(expandFollowRequestsRequest());
return next().then(response => { return next().then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
dispatch(expandFollowRequestsSuccess(response.items, response.next)); dispatch(expandFollowRequestsSuccess(response.items, response.next));
}).catch(error => dispatch(expandFollowRequestsFail(error))); }).catch(error => dispatch(expandFollowRequestsFail(error)));
}; };
@ -400,7 +399,7 @@ const pinAccount = (accountId: string) =>
if (!isLoggedIn(getState)) return dispatch(noOp); if (!isLoggedIn(getState)) return dispatch(noOp);
return getClient(getState).accounts.pinAccount(accountId).then(response => return getClient(getState).accounts.pinAccount(accountId).then(response =>
dispatch(importEntities([response], Entities.RELATIONSHIPS)), dispatch(importEntities({ relationships: [response] })),
); );
}; };
@ -409,7 +408,7 @@ const unpinAccount = (accountId: string) =>
if (!isLoggedIn(getState)) return dispatch(noOp); if (!isLoggedIn(getState)) return dispatch(noOp);
return getClient(getState).accounts.unpinAccount(accountId).then(response => return getClient(getState).accounts.unpinAccount(accountId).then(response =>
dispatch(importEntities([response], Entities.RELATIONSHIPS)), dispatch(importEntities({ relationships: [response] })),
); );
}; };
@ -429,7 +428,7 @@ const fetchPinnedAccounts = (accountId: string) =>
dispatch(fetchPinnedAccountsRequest(accountId)); dispatch(fetchPinnedAccountsRequest(accountId));
return getClient(getState).accounts.getAccountEndorsements(accountId).then(response => { return getClient(getState).accounts.getAccountEndorsements(accountId).then(response => {
dispatch(importFetchedAccounts(response)); dispatch(importEntities({ accounts: response }));
dispatch(fetchPinnedAccountsSuccess(accountId, response, null)); dispatch(fetchPinnedAccountsSuccess(accountId, response, null));
}).catch(error => { }).catch(error => {
dispatch(fetchPinnedAccountsFail(accountId, error)); dispatch(fetchPinnedAccountsFail(accountId, error));
@ -458,7 +457,7 @@ const accountSearch = (q: string, signal?: AbortSignal) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: ACCOUNT_SEARCH_REQUEST, params: { q } }); dispatch({ type: ACCOUNT_SEARCH_REQUEST, params: { q } });
return getClient(getState()).accounts.searchAccounts(q, { resolve: false, limit: 4, following: true }, { signal }).then((accounts) => { return getClient(getState()).accounts.searchAccounts(q, { resolve: false, limit: 4, following: true }, { signal }).then((accounts) => {
dispatch(importFetchedAccounts(accounts)); dispatch(importEntities({ accounts }));
dispatch({ type: ACCOUNT_SEARCH_SUCCESS, accounts }); dispatch({ type: ACCOUNT_SEARCH_SUCCESS, accounts });
return accounts; return accounts;
}).catch(error => { }).catch(error => {
@ -471,7 +470,7 @@ const accountLookup = (acct: string, signal?: AbortSignal) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: ACCOUNT_LOOKUP_REQUEST, acct }); dispatch({ type: ACCOUNT_LOOKUP_REQUEST, acct });
return getClient(getState()).accounts.lookupAccount(acct, { signal }).then((account) => { return getClient(getState()).accounts.lookupAccount(acct, { signal }).then((account) => {
if (account && account.id) dispatch(importFetchedAccount(account)); if (account && account.id) dispatch(importEntities({ accounts: [account] }));
dispatch({ type: ACCOUNT_LOOKUP_SUCCESS, account }); dispatch({ type: ACCOUNT_LOOKUP_SUCCESS, account });
return account; return account;
}).catch(error => { }).catch(error => {
@ -489,7 +488,7 @@ const fetchBirthdayReminders = (month: number, day: number) =>
dispatch({ type: BIRTHDAY_REMINDERS_FETCH_REQUEST, day, month, accountId: me }); dispatch({ type: BIRTHDAY_REMINDERS_FETCH_REQUEST, day, month, accountId: me });
return getClient(getState).accounts.getBirthdays(day, month).then(response => { return getClient(getState).accounts.getBirthdays(day, month).then(response => {
dispatch(importFetchedAccounts(response)); dispatch(importEntities({ accounts: response }));
dispatch({ dispatch({
type: BIRTHDAY_REMINDERS_FETCH_SUCCESS, type: BIRTHDAY_REMINDERS_FETCH_SUCCESS,
accounts: response, accounts: response,

View file

@ -1,12 +1,12 @@
import { fetchRelationships } from 'pl-fe/actions/accounts'; import { fetchRelationships } from 'pl-fe/actions/accounts';
import { importFetchedAccount, importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from 'pl-fe/actions/importer'; import { importEntities } from 'pl-fe/actions/importer';
import { filterBadges, getTagDiff } from 'pl-fe/utils/badges'; import { filterBadges, getTagDiff } from 'pl-fe/utils/badges';
import { getClient } from '../api'; import { getClient } from '../api';
import { deleteFromTimelines } from './timelines'; import { deleteFromTimelines } from './timelines';
import type { Account, AdminGetAccountsParams, AdminGetReportsParams, PleromaConfig } from 'pl-api'; import type { Account, AdminGetAccountsParams, AdminGetReportsParams, PleromaConfig, Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST' as const; const ADMIN_CONFIG_FETCH_REQUEST = 'ADMIN_CONFIG_FETCH_REQUEST' as const;
@ -111,9 +111,7 @@ const fetchReports = (params?: AdminGetReportsParams) =>
return getClient(state).admin.reports.getReports(params) return getClient(state).admin.reports.getReports(params)
.then(({ items }) => { .then(({ items }) => {
items.forEach((report) => { items.forEach((report) => {
if (report.account?.account) dispatch(importFetchedAccount(report.account.account)); dispatch(importEntities({ statuses: report.statuses as Array<Status>, accounts: [report.account?.account, report.target_account?.account] }));
if (report.target_account?.account) dispatch(importFetchedAccount(report.target_account.account));
dispatch(importFetchedStatuses(report.statuses));
dispatch({ type: ADMIN_REPORTS_FETCH_SUCCESS, reports: items, params }); dispatch({ type: ADMIN_REPORTS_FETCH_SUCCESS, reports: items, params });
}); });
}).catch(error => { }).catch(error => {
@ -141,7 +139,7 @@ const fetchUsers = (params?: AdminGetAccountsParams) =>
dispatch({ type: ADMIN_USERS_FETCH_REQUEST, params }); dispatch({ type: ADMIN_USERS_FETCH_REQUEST, params });
return getClient(state).admin.accounts.getAccounts(params).then((res) => { return getClient(state).admin.accounts.getAccounts(params).then((res) => {
dispatch(importFetchedAccounts(res.items.map(({ account }) => account).filter((account): account is Account => account !== null))); dispatch(importEntities({ accounts: res.items.map(({ account }) => account).filter((account): account is Account => account !== null) }));
dispatch(fetchRelationships(res.items.map((account) => account.id))); dispatch(fetchRelationships(res.items.map((account) => account.id)));
dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users: res.items, params, next: res.next }); dispatch({ type: ADMIN_USERS_FETCH_SUCCESS, users: res.items, params, next: res.next });
return res; return res;
@ -203,7 +201,7 @@ const toggleStatusSensitivity = (statusId: string, sensitive: boolean) =>
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST, statusId }); dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_REQUEST, statusId });
return getClient(getState).admin.statuses.updateStatus(statusId, { sensitive: !sensitive }) return getClient(getState).admin.statuses.updateStatus(statusId, { sensitive: !sensitive })
.then((status) => { .then((status) => {
dispatch(importFetchedStatus(status)); dispatch(importEntities({ statuses: [status] }));
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS, statusId, status }); dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_SUCCESS, statusId, status });
}).catch(error => { }).catch(error => {
dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL, error, statusId }); dispatch({ type: ADMIN_STATUS_TOGGLE_SENSITIVITY_FAIL, error, statusId });

View file

@ -5,7 +5,7 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import type { Account as BaseAccount } from 'pl-api'; import type { Account as BaseAccount } from 'pl-api';
import type { Account } from 'pl-fe/normalizers/account'; import type { Account } from 'pl-fe/normalizers/account';
@ -63,7 +63,7 @@ const fetchAliasesSuggestions = (q: string) =>
return getClient(getState()).accounts.searchAccounts(q, { resolve: true, limit: 4 }) return getClient(getState()).accounts.searchAccounts(q, { resolve: true, limit: 4 })
.then((data) => { .then((data) => {
dispatch(importFetchedAccounts(data)); dispatch(importEntities({ accounts: data }));
dispatch(fetchAliasesSuggestionsReady(q, data)); dispatch(fetchAliasesSuggestionsReady(q, data));
}).catch(error => toast.showAlertForError(error)); }).catch(error => toast.showAlertForError(error));
}; };

View file

@ -30,7 +30,7 @@ import { isStandalone } from 'pl-fe/utils/state';
import { type PlfeResponse, getClient } from '../api'; import { type PlfeResponse, getClient } from '../api';
import { importFetchedAccount } from './importer'; import { importEntities } from './importer';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -150,7 +150,7 @@ const verifyCredentials = (token: string, accountUrl?: string) =>
const client = new PlApiClient(baseURL, token); const client = new PlApiClient(baseURL, token);
return client.settings.verifyCredentials().then((account) => { return client.settings.verifyCredentials().then((account) => {
dispatch(importFetchedAccount(account)); dispatch(importEntities({ accounts: [account] }));
dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account }); dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account });
if (account.id === getState().me) dispatch(fetchMeSuccess(account)); if (account.id === getState().me) dispatch(fetchMeSuccess(account));
return account; return account;
@ -159,7 +159,7 @@ const verifyCredentials = (token: string, accountUrl?: string) =>
// The user is waitlisted // The user is waitlisted
const account = error.response.json; const account = error.response.json;
const parsedAccount = v.parse(credentialAccountSchema, error.response.json); const parsedAccount = v.parse(credentialAccountSchema, error.response.json);
dispatch(importFetchedAccount(parsedAccount)); dispatch(importEntities({ accounts: [parsedAccount] }));
dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account: parsedAccount }); dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account: parsedAccount });
if (account.id === getState().me) dispatch(fetchMeSuccess(parsedAccount)); if (account.id === getState().me) dispatch(fetchMeSuccess(parsedAccount));
return parsedAccount; return parsedAccount;
@ -175,7 +175,7 @@ const rememberAuthAccount = (accountUrl: string) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: AUTH_ACCOUNT_REMEMBER_REQUEST, accountUrl }); dispatch({ type: AUTH_ACCOUNT_REMEMBER_REQUEST, accountUrl });
return KVStore.getItemOrError(`authAccount:${accountUrl}`).then(account => { return KVStore.getItemOrError(`authAccount:${accountUrl}`).then(account => {
dispatch(importFetchedAccount(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)); if (account.id === getState().me) dispatch(fetchMeSuccess(account));
return account; return account;

View file

@ -1,6 +1,6 @@
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import type { PaginatedResponse, Status } from 'pl-api'; import type { PaginatedResponse, Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -24,7 +24,7 @@ const fetchBookmarkedStatuses = (folderId?: string) =>
dispatch(fetchBookmarkedStatusesRequest(folderId)); dispatch(fetchBookmarkedStatusesRequest(folderId));
return getClient(getState()).myAccount.getBookmarks({ folder_id: folderId }).then(response => { return getClient(getState()).myAccount.getBookmarks({ folder_id: folderId }).then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
return dispatch(fetchBookmarkedStatusesSuccess(response.items, response.next, folderId)); return dispatch(fetchBookmarkedStatusesSuccess(response.items, response.next, folderId));
}).catch(error => { }).catch(error => {
dispatch(fetchBookmarkedStatusesFail(error, folderId)); dispatch(fetchBookmarkedStatusesFail(error, folderId));
@ -61,7 +61,7 @@ const expandBookmarkedStatuses = (folderId?: string) =>
dispatch(expandBookmarkedStatusesRequest(folderId)); dispatch(expandBookmarkedStatusesRequest(folderId));
return next().then(response => { return next().then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
return dispatch(expandBookmarkedStatusesSuccess(response.items, response.next, folderId)); return dispatch(expandBookmarkedStatusesSuccess(response.items, response.next, folderId));
}).catch(error => { }).catch(error => {
dispatch(expandBookmarkedStatusesFail(error, folderId)); dispatch(expandBookmarkedStatusesFail(error, folderId));

View file

@ -14,7 +14,7 @@ import toast from 'pl-fe/toast';
import { isLoggedIn } from 'pl-fe/utils/auth'; import { isLoggedIn } from 'pl-fe/utils/auth';
import { chooseEmoji } from './emojis'; import { chooseEmoji } from './emojis';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import { rememberLanguageUse } from './languages'; import { rememberLanguageUse } from './languages';
import { uploadFile, updateMedia } from './media'; import { uploadFile, updateMedia } from './media';
import { createStatus } from './statuses'; import { createStatus } from './statuses';
@ -357,12 +357,13 @@ interface SubmitComposeOpts {
history?: History; history?: History;
force?: boolean; force?: boolean;
onSubmit?: () => void; onSubmit?: () => void;
onSuccess?: () => void;
propagate?: boolean; propagate?: boolean;
} }
const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) => const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
async (dispatch: AppDispatch, getState: () => RootState) => { async (dispatch: AppDispatch, getState: () => RootState) => {
const { history, force = false, onSubmit, propagate } = opts; const { history, force = false, onSubmit, onSuccess, propagate } = opts;
if (!isLoggedIn(getState)) return; if (!isLoggedIn(getState)) return;
const state = getState(); const state = getState();
@ -388,7 +389,7 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
useModalsStore.getState().openModal('MISSING_DESCRIPTION', { useModalsStore.getState().openModal('MISSING_DESCRIPTION', {
onContinue: () => { onContinue: () => {
useModalsStore.getState().closeModal('MISSING_DESCRIPTION'); useModalsStore.getState().closeModal('MISSING_DESCRIPTION');
dispatch(submitCompose(composeId, { history, force: true })); dispatch(submitCompose(composeId, { history, force: true, onSuccess }));
}, },
}); });
return; return;
@ -460,7 +461,8 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
history.push('/conversations'); history.push('/conversations');
} }
handleComposeSubmit(dispatch, getState, composeId, data, status, !!statusId, propagate); handleComposeSubmit(dispatch, getState, composeId, data, status, !!statusId, propagate);
if (onSubmit) onSubmit(); onSubmit?.();
onSuccess?.();
}).catch((error) => { }).catch((error) => {
dispatch(submitComposeFail(composeId, error)); dispatch(submitComposeFail(composeId, error));
}); });
@ -610,7 +612,7 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, composeId,
return getClient(getState).accounts.searchAccounts(token.slice(1), { resolve: false, limit: 10 }, { signal }) return getClient(getState).accounts.searchAccounts(token.slice(1), { resolve: false, limit: 10 }, { signal })
.then(response => { .then(response => {
dispatch(importFetchedAccounts(response)); dispatch(importEntities({ accounts: response }));
dispatch(readyComposeSuggestionsAccounts(composeId, token, response)); dispatch(readyComposeSuggestionsAccounts(composeId, token, response));
}).catch(error => { }).catch(error => {
if (!signal.aborted) { if (!signal.aborted) {

View file

@ -2,13 +2,9 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { import { importEntities } from './importer';
importFetchedAccounts,
importFetchedStatuses,
importFetchedStatus,
} from './importer';
import type { Account, Conversation, PaginatedResponse, Status } from 'pl-api'; import type { Account, Conversation, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT' as const; const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT' as const;
@ -48,8 +44,10 @@ const expandConversations = (expand = true) => (dispatch: AppDispatch, getState:
return (state.conversations.next?.() || getClient(state).timelines.getConversations()) return (state.conversations.next?.() || getClient(state).timelines.getConversations())
.then(response => { .then(response => {
dispatch(importFetchedAccounts(response.items.reduce((aggr: Array<Account>, item) => aggr.concat(item.accounts), []))); dispatch(importEntities({
dispatch(importFetchedStatuses(response.items.map((item) => item.last_status).filter((x): x is Status => x !== null))); accounts: response.items.reduce((aggr: Array<Account>, item) => aggr.concat(item.accounts), []),
statuses: response.items.map((item) => item.last_status),
}));
dispatch(expandConversationsSuccess(response.items, response.next, isLoadingRecent)); dispatch(expandConversationsSuccess(response.items, response.next, isLoadingRecent));
}) })
.catch(err => dispatch(expandConversationsFail(err))); .catch(err => dispatch(expandConversationsFail(err)));
@ -74,11 +72,10 @@ const expandConversationsFail = (error: unknown) => ({
}); });
const updateConversations = (conversation: Conversation) => (dispatch: AppDispatch) => { const updateConversations = (conversation: Conversation) => (dispatch: AppDispatch) => {
dispatch(importFetchedAccounts(conversation.accounts)); dispatch(importEntities({
accounts: conversation.accounts,
if (conversation.last_status) { statuses: [conversation.last_status],
dispatch(importFetchedStatus(conversation.last_status)); }));
}
return dispatch({ return dispatch({
type: CONVERSATIONS_UPDATE, type: CONVERSATIONS_UPDATE,

View file

@ -1,7 +1,7 @@
import { getClient } from '../api'; import { getClient } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import type { Account, ProfileDirectoryParams } from 'pl-api'; import type { Account, ProfileDirectoryParams } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -19,7 +19,7 @@ const fetchDirectory = (params: ProfileDirectoryParams) =>
dispatch(fetchDirectoryRequest()); dispatch(fetchDirectoryRequest());
return getClient(getState()).instance.profileDirectory({ ...params, limit: 20 }).then((data) => { return getClient(getState()).instance.profileDirectory({ ...params, limit: 20 }).then((data) => {
dispatch(importFetchedAccounts(data)); dispatch(importEntities({ accounts: data }));
dispatch(fetchDirectorySuccess(data)); dispatch(fetchDirectorySuccess(data));
dispatch(fetchRelationships(data.map((x) => x.id))); dispatch(fetchRelationships(data.map((x) => x.id)));
}).catch(error => dispatch(fetchDirectoryFail(error))); }).catch(error => dispatch(fetchDirectoryFail(error)));
@ -46,7 +46,7 @@ const expandDirectory = (params: Record<string, any>) =>
const loadedItems = getState().user_lists.directory.items.size; const loadedItems = getState().user_lists.directory.items.size;
return getClient(getState()).instance.profileDirectory({ ...params, offset: loadedItems, limit: 20 }).then((data) => { return getClient(getState()).instance.profileDirectory({ ...params, offset: loadedItems, limit: 20 }).then((data) => {
dispatch(importFetchedAccounts(data)); dispatch(importEntities({ accounts: data }));
dispatch(expandDirectorySuccess(data)); dispatch(expandDirectorySuccess(data));
dispatch(fetchRelationships(data.map((x) => x.id))); dispatch(fetchRelationships(data.map((x) => x.id)));
}).catch(error => dispatch(expandDirectoryFail(error))); }).catch(error => dispatch(expandDirectoryFail(error)));

View file

@ -2,7 +2,7 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedStatus } from './importer'; import { importEntities } from './importer';
import type { Status } from 'pl-api'; import type { Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -24,7 +24,7 @@ const emojiReact = (status: Pick<Status, 'id'>, emoji: string, custom?: string)
dispatch(emojiReactRequest(status.id, emoji, custom)); dispatch(emojiReactRequest(status.id, emoji, custom));
return getClient(getState).statuses.createStatusReaction(status.id, emoji).then((response) => { return getClient(getState).statuses.createStatusReaction(status.id, emoji).then((response) => {
dispatch(importFetchedStatus(response)); dispatch(importEntities({ statuses: [response] }));
dispatch(emojiReactSuccess(response, emoji)); dispatch(emojiReactSuccess(response, emoji));
}).catch((error) => { }).catch((error) => {
dispatch(emojiReactFail(status.id, emoji, error)); dispatch(emojiReactFail(status.id, emoji, error));
@ -38,7 +38,7 @@ const unEmojiReact = (status: Pick<Status, 'id'>, emoji: string) =>
dispatch(unEmojiReactRequest(status.id, emoji)); dispatch(unEmojiReactRequest(status.id, emoji));
return getClient(getState).statuses.deleteStatusReaction(status.id, emoji).then(response => { return getClient(getState).statuses.deleteStatusReaction(status.id, emoji).then(response => {
dispatch(importFetchedStatus(response)); dispatch(importEntities({ statuses: [response] }));
dispatch(unEmojiReactSuccess(response, emoji)); dispatch(unEmojiReactSuccess(response, emoji));
}).catch(error => { }).catch(error => {
dispatch(unEmojiReactFail(status.id, emoji, error)); dispatch(unEmojiReactFail(status.id, emoji, error));

View file

@ -4,7 +4,7 @@ import { getClient } from 'pl-fe/api';
import { useModalsStore } from 'pl-fe/stores/modals'; import { useModalsStore } from 'pl-fe/stores/modals';
import toast from 'pl-fe/toast'; import toast from 'pl-fe/toast';
import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer'; 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 { Account, CreateEventParams, Location, MediaAttachment, PaginatedResponse, Status } from 'pl-api'; import type { Account, CreateEventParams, Location, MediaAttachment, PaginatedResponse, Status } from 'pl-api';
@ -132,7 +132,7 @@ const submitEvent = ({
: getClient(state).events.editEvent(statusId, params) : getClient(state).events.editEvent(statusId, params)
).then((data) => { ).then((data) => {
useModalsStore.getState().closeModal('COMPOSE_EVENT'); useModalsStore.getState().closeModal('COMPOSE_EVENT');
dispatch(importFetchedStatus(data)); dispatch(importEntities({ statuses: [data] }));
dispatch(submitEventSuccess(data)); dispatch(submitEventSuccess(data));
toast.success( toast.success(
statusId ? messages.editSuccess : messages.success, statusId ? messages.editSuccess : messages.success,
@ -171,7 +171,7 @@ const joinEvent = (statusId: string, participationMessage?: string) =>
dispatch(joinEventRequest(status.id)); dispatch(joinEventRequest(status.id));
return getClient(getState).events.joinEvent(statusId, participationMessage).then((data) => { return getClient(getState).events.joinEvent(statusId, participationMessage).then((data) => {
dispatch(importFetchedStatus(data)); dispatch(importEntities({ statuses: [data] }));
dispatch(joinEventSuccess(status.id)); dispatch(joinEventSuccess(status.id));
toast.success( toast.success(
data.event?.join_state === 'pending' ? messages.joinRequestSuccess : messages.joinSuccess, data.event?.join_state === 'pending' ? messages.joinRequestSuccess : messages.joinSuccess,
@ -213,7 +213,7 @@ const leaveEvent = (statusId: string) =>
dispatch(leaveEventRequest(status.id)); dispatch(leaveEventRequest(status.id));
return getClient(getState).events.leaveEvent(statusId).then((data) => { return getClient(getState).events.leaveEvent(statusId).then((data) => {
dispatch(importFetchedStatus(data)); dispatch(importEntities({ statuses: [data] }));
dispatch(leaveEventSuccess(status.id)); dispatch(leaveEventSuccess(status.id));
}).catch((error) => { }).catch((error) => {
dispatch(leaveEventFail(error, status.id)); dispatch(leaveEventFail(error, status.id));
@ -241,7 +241,7 @@ const fetchEventParticipations = (statusId: string) =>
dispatch(fetchEventParticipationsRequest(statusId)); dispatch(fetchEventParticipationsRequest(statusId));
return getClient(getState).events.getEventParticipations(statusId).then(response => { return getClient(getState).events.getEventParticipations(statusId).then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
return dispatch(fetchEventParticipationsSuccess(statusId, response.items, response.next)); return dispatch(fetchEventParticipationsSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(fetchEventParticipationsFail(statusId, error)); dispatch(fetchEventParticipationsFail(statusId, error));
@ -277,7 +277,7 @@ const expandEventParticipations = (statusId: string) =>
dispatch(expandEventParticipationsRequest(statusId)); dispatch(expandEventParticipationsRequest(statusId));
return next().then(response => { return next().then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
return dispatch(expandEventParticipationsSuccess(statusId, response.items, response.next)); return dispatch(expandEventParticipationsSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(expandEventParticipationsFail(statusId, error)); dispatch(expandEventParticipationsFail(statusId, error));
@ -307,7 +307,7 @@ const fetchEventParticipationRequests = (statusId: string) =>
dispatch(fetchEventParticipationRequestsRequest(statusId)); dispatch(fetchEventParticipationRequestsRequest(statusId));
return getClient(getState).events.getEventParticipationRequests(statusId).then(response => { return getClient(getState).events.getEventParticipationRequests(statusId).then(response => {
dispatch(importFetchedAccounts(response.items.map(({ account }) => account))); dispatch(importEntities({ accounts: response.items.map(({ account }) => account) }));
return dispatch(fetchEventParticipationRequestsSuccess(statusId, response.items, response.next)); return dispatch(fetchEventParticipationRequestsSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(fetchEventParticipationRequestsFail(statusId, error)); dispatch(fetchEventParticipationRequestsFail(statusId, error));
@ -346,7 +346,7 @@ const expandEventParticipationRequests = (statusId: string) =>
dispatch(expandEventParticipationRequestsRequest(statusId)); dispatch(expandEventParticipationRequestsRequest(statusId));
return next().then(response => { return next().then(response => {
dispatch(importFetchedAccounts(response.items.map(({ account }) => account))); dispatch(importEntities({ accounts: response.items.map(({ account }) => account) }));
return dispatch(expandEventParticipationRequestsSuccess(statusId, response.items, response.next)); return dispatch(expandEventParticipationRequestsSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(expandEventParticipationRequestsFail(statusId, error)); dispatch(expandEventParticipationRequestsFail(statusId, error));
@ -479,7 +479,7 @@ const fetchRecentEvents = () =>
return getClient(getState()).timelines.publicTimeline({ return getClient(getState()).timelines.publicTimeline({
only_events: true, only_events: true,
}).then(response => { }).then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
dispatch({ dispatch({
type: RECENT_EVENTS_FETCH_SUCCESS, type: RECENT_EVENTS_FETCH_SUCCESS,
statuses: response.items, statuses: response.items,
@ -499,7 +499,7 @@ const fetchJoinedEvents = () =>
dispatch({ type: JOINED_EVENTS_FETCH_REQUEST }); dispatch({ type: JOINED_EVENTS_FETCH_REQUEST });
getClient(getState).events.getJoinedEvents().then(response => { getClient(getState).events.getJoinedEvents().then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
dispatch({ dispatch({
type: JOINED_EVENTS_FETCH_SUCCESS, type: JOINED_EVENTS_FETCH_SUCCESS,
statuses: response.items, statuses: response.items,

View file

@ -3,7 +3,7 @@ import { AppDispatch, RootState } from 'pl-fe/store';
import { getClient } from '../api'; import { getClient } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
const FAMILIAR_FOLLOWERS_FETCH_REQUEST = 'FAMILIAR_FOLLOWERS_FETCH_REQUEST' as const; const FAMILIAR_FOLLOWERS_FETCH_REQUEST = 'FAMILIAR_FOLLOWERS_FETCH_REQUEST' as const;
const FAMILIAR_FOLLOWERS_FETCH_SUCCESS = 'FAMILIAR_FOLLOWERS_FETCH_SUCCESS' as const; const FAMILIAR_FOLLOWERS_FETCH_SUCCESS = 'FAMILIAR_FOLLOWERS_FETCH_SUCCESS' as const;
@ -19,7 +19,7 @@ const fetchAccountFamiliarFollowers = (accountId: string) => (dispatch: AppDispa
.then((data) => { .then((data) => {
const accounts = data.find(({ id }: { id: string }) => id === accountId)!.accounts; const accounts = data.find(({ id }: { id: string }) => id === accountId)!.accounts;
dispatch(importFetchedAccounts(accounts)); dispatch(importEntities({ accounts }));
dispatch(fetchRelationships(accounts.map((item) => item.id))); dispatch(fetchRelationships(accounts.map((item) => item.id)));
dispatch({ dispatch({
type: FAMILIAR_FOLLOWERS_FETCH_SUCCESS, type: FAMILIAR_FOLLOWERS_FETCH_SUCCESS,

View file

@ -2,7 +2,7 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import type { PaginatedResponse, Status } from 'pl-api'; import type { PaginatedResponse, Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -34,7 +34,7 @@ const fetchFavouritedStatuses = () =>
dispatch(fetchFavouritedStatusesRequest()); dispatch(fetchFavouritedStatusesRequest());
return getClient(getState()).myAccount.getFavourites().then(response => { return getClient(getState()).myAccount.getFavourites().then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
dispatch(fetchFavouritedStatusesSuccess(response.items, response.next)); dispatch(fetchFavouritedStatusesSuccess(response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(fetchFavouritedStatusesFail(error)); dispatch(fetchFavouritedStatusesFail(error));
@ -69,7 +69,7 @@ const expandFavouritedStatuses = () =>
dispatch(expandFavouritedStatusesRequest()); dispatch(expandFavouritedStatusesRequest());
return next().then(response => { return next().then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
dispatch(expandFavouritedStatusesSuccess(response.items, response.next)); dispatch(expandFavouritedStatusesSuccess(response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(expandFavouritedStatusesFail(error)); dispatch(expandFavouritedStatusesFail(error));
@ -102,7 +102,7 @@ const fetchAccountFavouritedStatuses = (accountId: string) =>
dispatch(fetchAccountFavouritedStatusesRequest(accountId)); dispatch(fetchAccountFavouritedStatusesRequest(accountId));
return getClient(getState).accounts.getAccountFavourites(accountId).then(response => { return getClient(getState).accounts.getAccountFavourites(accountId).then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
dispatch(fetchAccountFavouritedStatusesSuccess(accountId, response.items, response.next)); dispatch(fetchAccountFavouritedStatusesSuccess(accountId, response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(fetchAccountFavouritedStatusesFail(accountId, error)); dispatch(fetchAccountFavouritedStatusesFail(accountId, error));
@ -140,7 +140,7 @@ const expandAccountFavouritedStatuses = (accountId: string) =>
dispatch(expandAccountFavouritedStatusesRequest(accountId)); dispatch(expandAccountFavouritedStatusesRequest(accountId));
return next().then(response => { return next().then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
dispatch(expandAccountFavouritedStatusesSuccess(accountId, response.items, response.next)); dispatch(expandAccountFavouritedStatusesSuccess(accountId, response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(expandAccountFavouritedStatusesFail(accountId, error)); dispatch(expandAccountFavouritedStatusesFail(accountId, error));

View file

@ -1,6 +1,6 @@
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import type { Account, PaginatedResponse } from 'pl-api'; import type { Account, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -23,7 +23,7 @@ const fetchGroupBlocks = (groupId: string) =>
dispatch(fetchGroupBlocksRequest(groupId)); dispatch(fetchGroupBlocksRequest(groupId));
return getClient(getState).experimental.groups.getGroupBlocks(groupId).then(response => { return getClient(getState).experimental.groups.getGroupBlocks(groupId).then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
dispatch(fetchGroupBlocksSuccess(groupId, response.items, response.next)); dispatch(fetchGroupBlocksSuccess(groupId, response.items, response.next));
}).catch(error => { }).catch(error => {
dispatch(fetchGroupBlocksFail(groupId, error)); dispatch(fetchGroupBlocksFail(groupId, error));

View file

@ -1,6 +1,6 @@
import { getClient } from 'pl-fe/api'; import { getClient } from 'pl-fe/api';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import type { StatusEdit } from 'pl-api'; import type { StatusEdit } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -18,7 +18,7 @@ const fetchHistory = (statusId: string) =>
dispatch(fetchHistoryRequest(statusId)); dispatch(fetchHistoryRequest(statusId));
return getClient(getState()).statuses.getStatusHistory(statusId).then(data => { return getClient(getState()).statuses.getStatusHistory(statusId).then(data => {
dispatch(importFetchedAccounts(data.map((x) => x.account))); dispatch(importEntities({ accounts: data.map((x) => x.account) }));
dispatch(fetchHistorySuccess(statusId, data)); dispatch(fetchHistorySuccess(statusId, data));
}).catch(error => dispatch(fetchHistoryFail(statusId, error))); }).catch(error => dispatch(fetchHistoryFail(statusId, error)));
}; };

View file

@ -0,0 +1,121 @@
import { importEntities as importEntityStoreEntities } from 'pl-fe/entity-store/actions';
import { Entities } from 'pl-fe/entity-store/entities';
import { normalizeGroup } from 'pl-fe/normalizers/group';
import type { Account as BaseAccount, Group as BaseGroup, Poll as BasePoll, Relationship as BaseRelationship, Status as BaseStatus } from 'pl-api';
import type { AppDispatch } from 'pl-fe/store';
const STATUS_IMPORT = 'STATUS_IMPORT' as const;
const STATUSES_IMPORT = 'STATUSES_IMPORT' as const;
const POLLS_IMPORT = 'POLLS_IMPORT' as const;
// Sometimes Pleroma can return an empty account,
// or a repost can appear of a deleted account. Skip these statuses.
const isBroken = (status: BaseStatus) => {
try {
if (status.scheduled_at !== null) return true;
// Skip empty accounts
// https://gitlab.com/soapbox-pub/soapbox/-/issues/424
if (!status.account.id) return true;
// Skip broken reposts
// https://gitlab.com/soapbox-pub/rebased/-/issues/28
if (status.reblog && !status.reblog.account.id) return true;
return false;
} catch (e) {
return true;
}
};
const isEmpty = (object: Record<string, any>) => !Object.values(object).some(value => value);
interface ImportStatusAction {
type: typeof STATUS_IMPORT;
status: BaseStatus;
idempotencyKey?: string;
}
interface ImportStatusesAction {
type: typeof STATUSES_IMPORT;
statuses: Array<BaseStatus>;
}
interface ImportPollAction {
type: typeof POLLS_IMPORT;
polls: Array<BasePoll>;
}
const importEntities = (entities: {
accounts?: Array<BaseAccount | undefined | null>;
groups?: Array<BaseGroup | undefined | null>;
polls?: Array<BasePoll | undefined | null>;
statuses?: Array<BaseStatus & { expectsCard?: boolean } | undefined | null>;
relationships?: Array<BaseRelationship | undefined | null>;
}, options: {
withParents?: boolean;
idempotencyKey?: string;
} = {
withParents: true,
}) => (dispatch: AppDispatch) => {
const accounts: Record<string, BaseAccount> = {};
const groups: Record<string, BaseGroup> = {};
const polls: Record<string, BasePoll> = {};
const relationships: Record<string, BaseRelationship> = {};
const statuses: Record<string, BaseStatus> = {};
const processAccount = (account: BaseAccount, withSelf = true) => {
if (withSelf) accounts[account.id] = account;
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 (isBroken(status)) return;
if (withSelf) statuses[status.id] = status;
if (status.account) {
processAccount(status.account);
}
if (status.quote) processStatus(status.quote);
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<ImportStatusAction>({
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).map(normalizeGroup), Entities.GROUPS));
if (!isEmpty(polls)) dispatch<ImportPollAction>(({ type: POLLS_IMPORT, polls: Object.values(polls) }));
if (!isEmpty(relationships)) dispatch(importEntityStoreEntities(Object.values(relationships), Entities.RELATIONSHIPS));
if (!isEmpty(statuses)) dispatch<ImportStatusesAction>({ type: STATUSES_IMPORT, statuses: Object.values(statuses) });
};
type ImporterAction = ImportStatusAction | ImportStatusesAction | ImportPollAction;
export {
STATUS_IMPORT,
STATUSES_IMPORT,
POLLS_IMPORT,
importEntities,
type ImporterAction,
};

View file

@ -1,169 +0,0 @@
import { importEntities } from 'pl-fe/entity-store/actions';
import { Entities } from 'pl-fe/entity-store/entities';
import { normalizeAccount } from 'pl-fe/normalizers/account';
import { normalizeGroup } from 'pl-fe/normalizers/group';
import type { Account as BaseAccount, Group, Poll, Status as BaseStatus } from 'pl-api';
import type { AppDispatch } from 'pl-fe/store';
const STATUS_IMPORT = 'STATUS_IMPORT' as const;
const STATUSES_IMPORT = 'STATUSES_IMPORT' as const;
const POLLS_IMPORT = 'POLLS_IMPORT' as const;
const importAccounts = (data: Array<BaseAccount>) => (dispatch: AppDispatch) => {
try {
const accounts = data.map(normalizeAccount);
const relationships = accounts.map(account => account.relationship).filter(relationship => !!relationship);
dispatch(importEntities(accounts, Entities.ACCOUNTS));
dispatch(importEntities(relationships, Entities.RELATIONSHIPS));
} catch (e) {
//
}
};
const importGroup = (data: Group) => importGroups([data]);
const importGroups = (data: Array<Group>) => (dispatch: AppDispatch) => {
try {
const groups = data.map(normalizeGroup);
dispatch(importEntities(groups, Entities.GROUPS));
} catch (e) {
//
}
};
const importStatus = (status: BaseStatus & { expectsCard?: boolean }, idempotencyKey?: string) => ({ type: STATUS_IMPORT, status, idempotencyKey });
const importStatuses = (statuses: Array<BaseStatus>) => ({ type: STATUSES_IMPORT, statuses });
const importPolls = (polls: Array<Poll>) => ({ type: POLLS_IMPORT, polls });
const importFetchedAccount = (account: BaseAccount) =>
importFetchedAccounts([account]);
const importFetchedAccounts = (accounts: Array<BaseAccount>) => {
const normalAccounts: Array<BaseAccount> = [];
const processAccount = (account: BaseAccount) => {
if (!account.id) return;
normalAccounts.push(account);
if (account.moved) {
processAccount(account.moved);
}
};
accounts.forEach(processAccount);
return importAccounts(normalAccounts);
};
const importFetchedStatus = (status: BaseStatus & { expectsCard?: boolean }, idempotencyKey?: string) =>
(dispatch: AppDispatch) => {
// Skip broken statuses
if (isBroken(status)) return;
if (status.reblog?.id) {
dispatch(importFetchedStatus(status.reblog as BaseStatus));
}
// Fedibird quotes
if (status.quote?.id) {
dispatch(importFetchedStatus(status.quote as BaseStatus));
}
// Fedibird quote from reblog
if (status.reblog?.quote?.id) {
dispatch(importFetchedStatus(status.reblog.quote));
}
if (status.poll?.id) {
dispatch(importFetchedPoll(status.poll));
}
if (status.group?.id) {
dispatch(importGroup(status.group));
}
dispatch(importFetchedAccount(status.account));
dispatch(importStatus(status, idempotencyKey));
};
// Sometimes Pleroma can return an empty account,
// or a repost can appear of a deleted account. Skip these statuses.
const isBroken = (status: BaseStatus) => {
try {
if (status.scheduled_at !== null) return true;
// Skip empty accounts
// https://gitlab.com/soapbox-pub/soapbox/-/issues/424
if (!status.account.id) return true;
// Skip broken reposts
// https://gitlab.com/soapbox-pub/rebased/-/issues/28
if (status.reblog && !status.reblog.account.id) return true;
return false;
} catch (e) {
return true;
}
};
const importFetchedStatuses = (statuses: Array<Omit<BaseStatus, 'account'> & { account: BaseAccount | null }>) => (dispatch: AppDispatch) => {
const accounts: Record<string, BaseAccount> = {};
const normalStatuses: Array<BaseStatus> = [];
const polls: Array<Poll> = [];
const processStatus = (status: BaseStatus) => {
if (status.account === null) return;
// Skip broken statuses
if (isBroken(status)) return;
normalStatuses.push(status);
accounts[status.account.id] = status.account;
// if (status.accounts) {
// accounts.push(...status.accounts);
// }
if (status.reblog?.id) {
processStatus(status.reblog as BaseStatus);
}
// Fedibird quotes
if (status.quote?.id) {
processStatus(status.quote as BaseStatus);
}
if (status.poll?.id) {
polls.push(status.poll);
}
};
(statuses as Array<BaseStatus>).forEach(processStatus);
dispatch(importPolls(polls));
dispatch(importFetchedAccounts(Object.values(accounts)));
dispatch(importStatuses(normalStatuses));
};
const importFetchedPoll = (poll: Poll) =>
(dispatch: AppDispatch) => {
dispatch(importPolls([poll]));
};
type ImporterAction =
| ReturnType<typeof importStatus>
| ReturnType<typeof importStatuses>
| ReturnType<typeof importPolls>;
export {
STATUS_IMPORT,
STATUSES_IMPORT,
POLLS_IMPORT,
importFetchedAccount,
importFetchedAccounts,
importFetchedStatus,
importFetchedStatuses,
importFetchedPoll,
type ImporterAction,
};

View file

@ -7,7 +7,7 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatus } from './importer'; import { importEntities } from './importer';
import type { Account, EmojiReaction, PaginatedResponse, Status } from 'pl-api'; import type { Account, EmojiReaction, PaginatedResponse, Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -97,7 +97,7 @@ const reblog = (status: Pick<Status, 'id'>) =>
return getClient(getState()).statuses.reblogStatus(status.id).then((response) => { return getClient(getState()).statuses.reblogStatus(status.id).then((response) => {
// The reblog API method returns a new status wrapped around the original. In this case we are only // The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper // interested in how the original is modified, hence passing it skipping the wrapper
if (response.reblog) dispatch(importFetchedStatus(response.reblog as Status)); if (response.reblog) dispatch(importEntities({ statuses: [response.reblog] }));
dispatch(reblogSuccess(response)); dispatch(reblogSuccess(response));
}).catch(error => { }).catch(error => {
dispatch(reblogFail(status.id, error)); dispatch(reblogFail(status.id, error));
@ -307,7 +307,7 @@ const bookmark = (status: Pick<Status, 'id'>, folderId?: string) =>
dispatch(bookmarkRequest(status.id)); dispatch(bookmarkRequest(status.id));
return getClient(getState()).statuses.bookmarkStatus(status.id, folderId).then((response) => { return getClient(getState()).statuses.bookmarkStatus(status.id, folderId).then((response) => {
dispatch(importFetchedStatus(response)); dispatch(importEntities({ statuses: [response] }));
dispatch(bookmarkSuccess(response)); dispatch(bookmarkSuccess(response));
let opts: IToastOptions = { let opts: IToastOptions = {
@ -335,7 +335,7 @@ const unbookmark = (status: Pick<Status, 'id'>) =>
dispatch(unbookmarkRequest(status.id)); dispatch(unbookmarkRequest(status.id));
return getClient(getState()).statuses.unbookmarkStatus(status.id).then(response => { return getClient(getState()).statuses.unbookmarkStatus(status.id).then(response => {
dispatch(importFetchedStatus(response)); dispatch(importEntities({ statuses: [response] }));
dispatch(unbookmarkSuccess(response)); dispatch(unbookmarkSuccess(response));
toast.success(messages.bookmarkRemoved); toast.success(messages.bookmarkRemoved);
}).catch(error => { }).catch(error => {
@ -391,7 +391,7 @@ const fetchReblogs = (statusId: string) =>
dispatch(fetchReblogsRequest(statusId)); dispatch(fetchReblogsRequest(statusId));
return getClient(getState()).statuses.getRebloggedBy(statusId).then(response => { return getClient(getState()).statuses.getRebloggedBy(statusId).then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
dispatch(fetchRelationships(response.items.map((item) => item.id))); dispatch(fetchRelationships(response.items.map((item) => item.id)));
dispatch(fetchReblogsSuccess(statusId, response.items, response.next)); dispatch(fetchReblogsSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
@ -420,7 +420,7 @@ const fetchReblogsFail = (statusId: string, error: unknown) => ({
const expandReblogs = (statusId: string, next: AccountListLink) => const expandReblogs = (statusId: string, next: AccountListLink) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
next().then(response => { next().then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
dispatch(fetchRelationships(response.items.map((item) => item.id))); dispatch(fetchRelationships(response.items.map((item) => item.id)));
dispatch(expandReblogsSuccess(statusId, response.items, response.next)); dispatch(expandReblogsSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
@ -446,7 +446,7 @@ const fetchFavourites = (statusId: string) =>
dispatch(fetchFavouritesRequest(statusId)); dispatch(fetchFavouritesRequest(statusId));
return getClient(getState()).statuses.getFavouritedBy(statusId).then(response => { return getClient(getState()).statuses.getFavouritedBy(statusId).then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
dispatch(fetchRelationships(response.items.map((item) => item.id))); dispatch(fetchRelationships(response.items.map((item) => item.id)));
dispatch(fetchFavouritesSuccess(statusId, response.items, response.next)); dispatch(fetchFavouritesSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
@ -475,7 +475,7 @@ const fetchFavouritesFail = (statusId: string, error: unknown) => ({
const expandFavourites = (statusId: string, next: AccountListLink) => const expandFavourites = (statusId: string, next: AccountListLink) =>
(dispatch: AppDispatch) => { (dispatch: AppDispatch) => {
next().then(response => { next().then(response => {
dispatch(importFetchedAccounts(response.items)); dispatch(importEntities({ accounts: response.items }));
dispatch(fetchRelationships(response.items.map((item) => item.id))); dispatch(fetchRelationships(response.items.map((item) => item.id)));
dispatch(expandFavouritesSuccess(statusId, response.items, response.next)); dispatch(expandFavouritesSuccess(statusId, response.items, response.next));
}).catch(error => { }).catch(error => {
@ -501,7 +501,7 @@ const fetchDislikes = (statusId: string) =>
dispatch(fetchDislikesRequest(statusId)); dispatch(fetchDislikesRequest(statusId));
return getClient(getState).statuses.getDislikedBy(statusId).then(response => { return getClient(getState).statuses.getDislikedBy(statusId).then(response => {
dispatch(importFetchedAccounts(response)); dispatch(importEntities({ accounts: response }));
dispatch(fetchRelationships(response.map((item) => item.id))); dispatch(fetchRelationships(response.map((item) => item.id)));
dispatch(fetchDislikesSuccess(statusId, response)); dispatch(fetchDislikesSuccess(statusId, response));
}).catch(error => { }).catch(error => {
@ -531,7 +531,7 @@ const fetchReactions = (statusId: string) =>
dispatch(fetchReactionsRequest(statusId)); dispatch(fetchReactionsRequest(statusId));
return getClient(getState).statuses.getStatusReactions(statusId).then(response => { return getClient(getState).statuses.getStatusReactions(statusId).then(response => {
dispatch(importFetchedAccounts((response).map(({ accounts }) => accounts).flat())); dispatch(importEntities({ accounts: (response).map(({ accounts }) => accounts).flat() }));
dispatch(fetchReactionsSuccess(statusId, response)); dispatch(fetchReactionsSuccess(statusId, response));
}).catch(error => { }).catch(error => {
dispatch(fetchReactionsFail(statusId, error)); dispatch(fetchReactionsFail(statusId, error));
@ -562,7 +562,7 @@ const pin = (status: Pick<Status, 'id'>, accountId: string) =>
dispatch(pinRequest(status.id, accountId)); dispatch(pinRequest(status.id, accountId));
return getClient(getState()).statuses.pinStatus(status.id).then(response => { return getClient(getState()).statuses.pinStatus(status.id).then(response => {
dispatch(importFetchedStatus(response)); dispatch(importEntities({ statuses: [response] }));
dispatch(pinSuccess(response, accountId)); dispatch(pinSuccess(response, accountId));
}).catch(error => { }).catch(error => {
dispatch(pinFail(status.id, error, accountId)); dispatch(pinFail(status.id, error, accountId));
@ -596,7 +596,7 @@ const unpin = (status: Pick<Status, 'id'>, accountId: string) =>
dispatch(unpinRequest(status.id, accountId)); dispatch(unpinRequest(status.id, accountId));
return getClient(getState()).statuses.unpinStatus(status.id).then(response => { return getClient(getState()).statuses.unpinStatus(status.id).then(response => {
dispatch(importFetchedStatus(response)); dispatch(importEntities({ statuses: [response] }));
dispatch(unpinSuccess(response, accountId)); dispatch(unpinSuccess(response, accountId));
}).catch(error => { }).catch(error => {
dispatch(unpinFail(status.id, error, accountId)); dispatch(unpinFail(status.id, error, accountId));

View file

@ -4,7 +4,7 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import type { Account, List, PaginatedResponse } from 'pl-api'; import type { Account, List, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -229,7 +229,7 @@ const fetchListAccounts = (listId: string) => (dispatch: AppDispatch, getState:
dispatch(fetchListAccountsRequest(listId)); dispatch(fetchListAccountsRequest(listId));
return getClient(getState()).lists.getListAccounts(listId).then(({ items, next }) => { return getClient(getState()).lists.getListAccounts(listId).then(({ items, next }) => {
dispatch(importFetchedAccounts(items)); dispatch(importEntities({ accounts: items }));
dispatch(fetchListAccountsSuccess(listId, items, next)); dispatch(fetchListAccountsSuccess(listId, items, next));
}).catch(err => dispatch(fetchListAccountsFail(listId, err))); }).catch(err => dispatch(fetchListAccountsFail(listId, err)));
}; };
@ -256,7 +256,7 @@ const fetchListSuggestions = (q: string) => (dispatch: AppDispatch, getState: ()
if (!isLoggedIn(getState)) return; if (!isLoggedIn(getState)) return;
return getClient(getState()).accounts.searchAccounts(q, { resolve: false, limit: 4, following: true }).then((data) => { return getClient(getState()).accounts.searchAccounts(q, { resolve: false, limit: 4, following: true }).then((data) => {
dispatch(importFetchedAccounts(data)); dispatch(importEntities({ accounts: data }));
dispatch(fetchListSuggestionsReady(q, data)); dispatch(fetchListSuggestionsReady(q, data));
}).catch(error => toast.showAlertForError(error)); }).catch(error => toast.showAlertForError(error));
}; };

View file

@ -7,7 +7,7 @@ import { getAuthUserId, getAuthUserUrl } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { loadCredentials } from './auth'; import { loadCredentials } from './auth';
import { importFetchedAccount } from './importer'; import { importEntities } from './importer';
import { FE_NAME } from './settings'; import { FE_NAME } from './settings';
import type { CredentialAccount, UpdateCredentialsParams } from 'pl-api'; import type { CredentialAccount, UpdateCredentialsParams } from 'pl-api';
@ -127,7 +127,7 @@ const patchMeSuccess = (me: CredentialAccount) =>
me, me,
}; };
dispatch(importFetchedAccount(me)); dispatch(importEntities({ accounts: [me] }));
dispatch(action); dispatch(action);
}; };

View file

@ -14,12 +14,7 @@ import { EXCLUDE_TYPES, NOTIFICATION_TYPES } from 'pl-fe/utils/notification';
import { joinPublicPath } from 'pl-fe/utils/static'; import { joinPublicPath } from 'pl-fe/utils/static';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { import { importEntities } from './importer';
importFetchedAccount,
importFetchedAccounts,
importFetchedStatus,
importFetchedStatuses,
} from './importer';
import { saveMarker } from './markers'; import { saveMarker } from './markers';
import { saveSettings } from './settings'; import { saveSettings } from './settings';
@ -76,20 +71,10 @@ const updateNotifications = (notification: BaseNotification) =>
const selectedFilter = useSettingsStore.getState().settings.notifications.quickFilter.active; const selectedFilter = useSettingsStore.getState().settings.notifications.quickFilter.active;
const showInColumn = selectedFilter === 'all' ? true : (FILTER_TYPES[selectedFilter as FilterType] || [notification.type]).includes(notification.type); const showInColumn = selectedFilter === 'all' ? true : (FILTER_TYPES[selectedFilter as FilterType] || [notification.type]).includes(notification.type);
if (notification.account) { dispatch(importEntities({
dispatch(importFetchedAccount(notification.account)); accounts: [notification.account, notification.type === 'move' ? notification.target : undefined],
} statuses: [getNotificationStatus(notification)],
}));
// Used by Move notification
if (notification.type === 'move' && notification.target) {
dispatch(importFetchedAccount(notification.target));
}
const status = getNotificationStatus(notification);
if (status) {
dispatch(importFetchedStatus(status));
}
if (showInColumn) { if (showInColumn) {
dispatch({ dispatch({
@ -256,8 +241,10 @@ const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => an
return acc; return acc;
}, { accounts: {}, statuses: {} } as { accounts: Record<string, Account>; statuses: Record<string, Status> }); }, { accounts: {}, statuses: {} } as { accounts: Record<string, Account>; statuses: Record<string, Status> });
dispatch(importFetchedAccounts(Object.values(entries.accounts))); dispatch(importEntities({
dispatch(importFetchedStatuses(Object.values(entries.statuses))); accounts: Object.values(entries.accounts),
statuses: Object.values(entries.statuses),
}));
const deduplicatedNotifications = normalizeNotifications(response.items, state.notifications.items); const deduplicatedNotifications = normalizeNotifications(response.items, state.notifications.items);

View file

@ -2,7 +2,7 @@ import { isLoggedIn } from 'pl-fe/utils/auth';
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import type { Status } from 'pl-api'; import type { Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -19,7 +19,7 @@ const fetchPinnedStatuses = () =>
dispatch(fetchPinnedStatusesRequest()); dispatch(fetchPinnedStatusesRequest());
return getClient(getState()).accounts.getAccountStatuses(me as string, { pinned: true }).then(response => { return getClient(getState()).accounts.getAccountStatuses(me as string, { pinned: true }).then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
dispatch(fetchPinnedStatusesSuccess(response.items, null)); dispatch(fetchPinnedStatusesSuccess(response.items, null));
}).catch(error => { }).catch(error => {
dispatch(fetchPinnedStatusesFail(error)); dispatch(fetchPinnedStatusesFail(error));

View file

@ -1,6 +1,6 @@
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedPoll } from './importer'; import { importEntities } from './importer';
import type { Poll } from 'pl-api'; import type { Poll } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -18,7 +18,7 @@ const vote = (pollId: string, choices: number[]) =>
dispatch(voteRequest()); dispatch(voteRequest());
return getClient(getState()).polls.vote(pollId, choices).then((data) => { return getClient(getState()).polls.vote(pollId, choices).then((data) => {
dispatch(importFetchedPoll(data)); dispatch(importEntities({ polls: [data] }));
dispatch(voteSuccess(data)); dispatch(voteSuccess(data));
}).catch(err => dispatch(voteFail(err))); }).catch(err => dispatch(voteFail(err)));
}; };
@ -28,7 +28,7 @@ const fetchPoll = (pollId: string) =>
dispatch(fetchPollRequest()); dispatch(fetchPollRequest());
return getClient(getState()).polls.getPoll(pollId).then((data) => { return getClient(getState()).polls.getPoll(pollId).then((data) => {
dispatch(importFetchedPoll(data)); dispatch(importEntities({ polls: [data] }));
dispatch(fetchPollSuccess(data)); dispatch(fetchPollSuccess(data));
}).catch(err => dispatch(fetchPollFail(err))); }).catch(err => dispatch(fetchPollFail(err)));
}; };

View file

@ -1,7 +1,7 @@
import mapValues from 'lodash/mapValues'; import mapValues from 'lodash/mapValues';
import { verifyCredentials } from './auth'; import { verifyCredentials } from './auth';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import type { AppDispatch } from 'pl-fe/store'; import type { AppDispatch } from 'pl-fe/store';
@ -54,7 +54,7 @@ const preloadMastodon = (data: Record<string, any>) =>
const { me, access_token } = data.meta; const { me, access_token } = data.meta;
const { url } = data.accounts[me]; const { url } = data.accounts[me];
dispatch(importFetchedAccounts(Object.values(data.accounts))); dispatch(importEntities({ accounts: Object.values(data.accounts) }));
dispatch(verifyCredentials(access_token, url)); dispatch(verifyCredentials(access_token, url));
dispatch({ type: MASTODON_PRELOAD_IMPORT, data }); dispatch({ type: MASTODON_PRELOAD_IMPORT, data });
}; };

View file

@ -3,7 +3,7 @@ import { useSettingsStore } from 'pl-fe/stores/settings';
import { getClient } from '../api'; import { getClient } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import type { Search } from 'pl-api'; import type { Search } from 'pl-api';
import type { SearchFilter } from 'pl-fe/reducers/search'; import type { SearchFilter } from 'pl-fe/reducers/search';
@ -54,13 +54,7 @@ const submitSearch = (value: string, filter?: SearchFilter) =>
if (accountId) params.account_id = accountId; if (accountId) params.account_id = accountId;
return getClient(getState()).search.search(value, params).then(response => { return getClient(getState()).search.search(value, params).then(response => {
if (response.accounts) { dispatch(importEntities({ accounts: response.accounts, statuses: response.statuses }));
dispatch(importFetchedAccounts(response.accounts));
}
if (response.statuses) {
dispatch(importFetchedStatuses(response.statuses));
}
dispatch(fetchSearchSuccess(response, value, type)); dispatch(fetchSearchSuccess(response, value, type));
dispatch(fetchRelationships(response.accounts.map((item) => item.id))); dispatch(fetchRelationships(response.accounts.map((item) => item.id)));
@ -113,13 +107,7 @@ const expandSearch = (type: SearchFilter) => (dispatch: AppDispatch, getState: (
if (accountId) params.account_id = accountId; if (accountId) params.account_id = accountId;
return getClient(getState()).search.search(value, params).then(response => { return getClient(getState()).search.search(value, params).then(response => {
if (response.accounts) { dispatch(importEntities({ accounts: response.accounts, statuses: response.statuses }));
dispatch(importFetchedAccounts(response.accounts));
}
if (response.statuses) {
dispatch(importFetchedStatuses(response.statuses));
}
dispatch(expandSearchSuccess(response, value, type)); dispatch(expandSearchSuccess(response, value, type));
dispatch(fetchRelationships(response.accounts.map((item) => item.id))); dispatch(fetchRelationships(response.accounts.map((item) => item.id)));

View file

@ -1,6 +1,6 @@
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import type { Status as BaseStatus, PaginatedResponse } from 'pl-api'; import type { Status as BaseStatus, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -43,7 +43,7 @@ const fetchStatusQuotes = (statusId: string) =>
dispatch(action); dispatch(action);
return getClient(getState).statuses.getStatusQuotes(statusId).then(response => { return getClient(getState).statuses.getStatusQuotes(statusId).then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
const action: FetchStatusQuotesSuccessAction = { const action: FetchStatusQuotesSuccessAction = {
type: STATUS_QUOTES_FETCH_SUCCESS, type: STATUS_QUOTES_FETCH_SUCCESS,
statusId, statusId,
@ -94,7 +94,7 @@ const expandStatusQuotes = (statusId: string) =>
dispatch(action); dispatch(action);
return next().then(response => { return next().then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
const action: ExpandStatusQuotesSuccessAction = { const action: ExpandStatusQuotesSuccessAction = {
type: STATUS_QUOTES_EXPAND_SUCCESS, type: STATUS_QUOTES_EXPAND_SUCCESS,
statusId, statusId,

View file

@ -6,7 +6,7 @@ import { shouldHaveCard } from 'pl-fe/utils/status';
import { getClient } from '../api'; import { getClient } from '../api';
import { setComposeToStatus } from './compose'; import { setComposeToStatus } from './compose';
import { importFetchedStatus, importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import { deleteFromTimelines } from './timelines'; import { deleteFromTimelines } from './timelines';
import type { CreateStatusParams, Status as BaseStatus } from 'pl-api'; import type { CreateStatusParams, Status as BaseStatus } from 'pl-api';
@ -66,7 +66,7 @@ const createStatus = (params: CreateStatusParams, idempotencyKey: string, status
// The backend might still be processing the rich media attachment // The backend might still be processing the rich media attachment
const expectsCard = status.scheduled_at === null && !status.card && shouldHaveCard(status); const expectsCard = status.scheduled_at === null && !status.card && shouldHaveCard(status);
if (status.scheduled_at === null) dispatch(importFetchedStatus({ ...status, expectsCard }, idempotencyKey)); if (status.scheduled_at === null) dispatch(importEntities({ statuses: [{ ...status, expectsCard }] }, { idempotencyKey, withParents: true }));
dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!statusId }); dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!statusId });
// Poll the backend for the updated card // Poll the backend for the updated card
@ -76,7 +76,7 @@ const createStatus = (params: CreateStatusParams, idempotencyKey: string, status
const poll = (retries = 5) => { const poll = (retries = 5) => {
return getClient(getState()).statuses.getStatus(status.id).then(response => { return getClient(getState()).statuses.getStatus(status.id).then(response => {
if (response.card) { if (response.card) {
dispatch(importFetchedStatus(response)); dispatch(importEntities({ statuses: [response] }));
} else if (retries > 0 && response) { } else if (retries > 0 && response) {
setTimeout(() => poll(retries - 1), delay); setTimeout(() => poll(retries - 1), delay);
} }
@ -119,7 +119,7 @@ const fetchStatus = (statusId: string, intl?: IntlShape) =>
} : undefined; } : undefined;
return getClient(getState()).statuses.getStatus(statusId, params).then(status => { return getClient(getState()).statuses.getStatus(statusId, params).then(status => {
dispatch(importFetchedStatus(status)); dispatch(importEntities({ statuses: [status] }));
dispatch({ type: STATUS_FETCH_SUCCESS, status }); dispatch({ type: STATUS_FETCH_SUCCESS, status });
return status; return status;
}).catch(error => { }).catch(error => {
@ -153,7 +153,7 @@ const deleteStatus = (statusId: string, withRedraft = false) =>
}; };
const updateStatus = (status: BaseStatus) => (dispatch: AppDispatch) => const updateStatus = (status: BaseStatus) => (dispatch: AppDispatch) =>
dispatch(importFetchedStatus(status)); dispatch(importEntities({ statuses: [status] }));
const fetchContext = (statusId: string, intl?: IntlShape) => const fetchContext = (statusId: string, intl?: IntlShape) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
@ -164,14 +164,10 @@ const fetchContext = (statusId: string, intl?: IntlShape) =>
} : undefined; } : undefined;
return getClient(getState()).statuses.getContext(statusId, params).then(context => { return getClient(getState()).statuses.getContext(statusId, params).then(context => {
if (typeof context === 'object') { const { ancestors, descendants } = context;
const { ancestors, descendants } = context; const statuses = ancestors.concat(descendants);
const statuses = ancestors.concat(descendants); dispatch(importEntities({ statuses }));
dispatch(importFetchedStatuses(statuses)); dispatch({ type: CONTEXT_FETCH_SUCCESS, statusId, ancestors, descendants });
dispatch({ type: CONTEXT_FETCH_SUCCESS, statusId, ancestors, descendants });
} else {
throw context;
}
return context; return context;
}).catch(error => { }).catch(error => {
if (error.response?.status === 404) { if (error.response?.status === 404) {

View file

@ -1,7 +1,7 @@
import { getClient } from '../api'; import { getClient } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer'; import { importEntities } from './importer';
import { insertSuggestionsIntoTimeline } from './timelines'; import { insertSuggestionsIntoTimeline } from './timelines';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -24,7 +24,7 @@ const fetchSuggestions = (limit = 50) =>
return getClient(getState).myAccount.getSuggestions(limit).then((suggestions) => { return getClient(getState).myAccount.getSuggestions(limit).then((suggestions) => {
const accounts = suggestions.map(({ account }) => account); const accounts = suggestions.map(({ account }) => account);
dispatch(importFetchedAccounts(accounts)); dispatch(importEntities({ accounts }));
dispatch({ type: SUGGESTIONS_FETCH_SUCCESS, suggestions }); dispatch({ type: SUGGESTIONS_FETCH_SUCCESS, suggestions });
dispatch(fetchRelationships(accounts.map(({ id }) => id))); dispatch(fetchRelationships(accounts.map(({ id }) => id)));

View file

@ -6,7 +6,7 @@ import { shouldFilter } from 'pl-fe/utils/timelines';
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedStatus, importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import type { PaginatedResponse, Status as BaseStatus, PublicTimelineParams, HomeTimelineParams, ListTimelineParams, HashtagTimelineParams, GetAccountStatusesParams, GroupTimelineParams } from 'pl-api'; import type { PaginatedResponse, Status as BaseStatus, PublicTimelineParams, HomeTimelineParams, ListTimelineParams, HashtagTimelineParams, GetAccountStatusesParams, GroupTimelineParams } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -46,7 +46,7 @@ const processTimelineUpdate = (timeline: string, status: BaseStatus) =>
return; return;
} }
dispatch(importFetchedStatus(status)); dispatch(importEntities({ statuses: [status] }));
if (shouldSkipQueue) { if (shouldSkipQueue) {
dispatch(updateTimeline(timeline, status.id)); dispatch(updateTimeline(timeline, status.id));
@ -159,10 +159,10 @@ const handleTimelineExpand = (timelineId: string, fn: Promise<PaginatedResponse<
dispatch(expandTimelineRequest(timelineId)); dispatch(expandTimelineRequest(timelineId));
return fn.then(response => { return fn.then(response => {
dispatch(importFetchedStatuses(response.items)); dispatch(importEntities({ statuses: response.items }));
const statuses = deduplicateStatuses(response.items); const statuses = deduplicateStatuses(response.items);
dispatch(importFetchedStatuses(statuses.filter(status => status.accounts))); dispatch(importEntities({ statuses: statuses.filter(status => status.accounts) }));
dispatch(expandTimelineSuccess( dispatch(expandTimelineSuccess(
timelineId, timelineId,

View file

@ -1,6 +1,6 @@
import { getClient } from '../api'; import { getClient } from '../api';
import { importFetchedStatuses } from './importer'; import { importEntities } from './importer';
import type { AppDispatch, RootState } from 'pl-fe/store'; import type { AppDispatch, RootState } from 'pl-fe/store';
@ -18,7 +18,7 @@ const fetchTrendingStatuses = () =>
dispatch({ type: TRENDING_STATUSES_FETCH_REQUEST }); dispatch({ type: TRENDING_STATUSES_FETCH_REQUEST });
return client.trends.getTrendingStatuses().then((statuses) => { return client.trends.getTrendingStatuses().then((statuses) => {
dispatch(importFetchedStatuses(statuses)); dispatch(importEntities({ statuses }));
dispatch({ type: TRENDING_STATUSES_FETCH_SUCCESS, statuses }); dispatch({ type: TRENDING_STATUSES_FETCH_SUCCESS, statuses });
return statuses; return statuses;
}).catch(error => { }).catch(error => {

View file

@ -230,7 +230,7 @@ const Status: React.FC<IStatus> = (props) => {
<Link key={account.acct} to={`/@${account.acct}`} className='hover:underline'> <Link key={account.acct} to={`/@${account.acct}`} className='hover:underline'>
<bdi className='truncate'> <bdi className='truncate'>
<strong className='text-gray-800 dark:text-gray-200'> <strong className='text-gray-800 dark:text-gray-200'>
<Emojify text={status.account.display_name} emojis={status.account.emojis} /> <Emojify text={account.display_name} emojis={account.emojis} />
</strong> </strong>
</bdi> </bdi>
</Link> </Link>

View file

@ -121,7 +121,7 @@ const Modal = React.forwardRef<HTMLDivElement, IModal>(({
/> />
)} )}
<h3 className='grow truncate text-lg font-bold leading-6 text-gray-900 dark:text-white'> <h3 className='grow text-lg font-bold leading-6 text-gray-900 dark:text-white'>
{title} {title}
</h3> </h3>

View file

@ -154,10 +154,9 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
if (!canSubmit) return; if (!canSubmit) return;
e?.preventDefault(); e?.preventDefault();
dispatch(submitCompose(id, { history, onSubmit, propagate: fullScreen, onSuccess: () => {
dispatch(submitCompose(id, { history, onSubmit, propagate: fullScreen })).then(() => {
editorRef.current?.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined); editorRef.current?.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);
}).catch(() => {}); } }));
}; };
const onSuggestionsClearRequested = () => { const onSuggestionsClearRequested = () => {

View file

@ -8,9 +8,9 @@ import { fetchDraftStatuses } from 'pl-fe/actions/draft-statuses';
import { fetchStatus } from 'pl-fe/actions/statuses'; import { fetchStatus } from 'pl-fe/actions/statuses';
import Stack from 'pl-fe/components/ui/stack'; import Stack from 'pl-fe/components/ui/stack';
import ComposeForm from 'pl-fe/features/compose/components/compose-form'; import ComposeForm from 'pl-fe/features/compose/components/compose-form';
import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch'; import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useCompose } from 'pl-fe/hooks/useCompose'; import { useCompose } from 'pl-fe/hooks/use-compose';
import { useFeatures } from 'pl-fe/hooks/useFeatures'; import { useFeatures } from 'pl-fe/hooks/use-features';
import { makeGetStatus, selectOwnAccount } from 'pl-fe/selectors'; import { makeGetStatus, selectOwnAccount } from 'pl-fe/selectors';
import { useSettingsStore } from 'pl-fe/stores/settings'; import { useSettingsStore } from 'pl-fe/stores/settings';

View file

@ -9,6 +9,7 @@ import {
} from 'pl-fe/actions/import-data'; } from 'pl-fe/actions/import-data';
import Column from 'pl-fe/components/ui/column'; import Column from 'pl-fe/components/ui/column';
import { useFeatures } from 'pl-fe/hooks/use-features'; import { useFeatures } from 'pl-fe/hooks/use-features';
import { useInstance } from 'pl-fe/hooks/use-instance';
import DataImporter from './components/data-importer'; import DataImporter from './components/data-importer';

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { importFetchedStatuses } from 'pl-fe/actions/importer'; import { importEntities } from 'pl-fe/actions/importer';
import { expandTimelineSuccess } from 'pl-fe/actions/timelines'; import { expandTimelineSuccess } from 'pl-fe/actions/timelines';
import Column from 'pl-fe/components/ui/column'; import Column from 'pl-fe/components/ui/column';
import Timeline from 'pl-fe/features/ui/components/timeline'; import Timeline from 'pl-fe/features/ui/components/timeline';
@ -35,7 +35,7 @@ const TestTimeline: React.FC = () => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
React.useEffect(() => { React.useEffect(() => {
dispatch(importFetchedStatuses(MOCK_STATUSES)); dispatch(importEntities({ statuses: MOCK_STATUSES }));
dispatch(expandTimelineSuccess(timelineId, MOCK_STATUSES, null, null, false, false)); dispatch(expandTimelineSuccess(timelineId, MOCK_STATUSES, null, null, false, false));
}, []); }, []);

View file

@ -25,7 +25,7 @@ const MissingDescriptionModal: React.FC<BaseModalProps & MissingDescriptionModal
confirmationText={intl.formatMessage(messages.post)} confirmationText={intl.formatMessage(messages.post)}
confirmationTheme='danger' confirmationTheme='danger'
cancelText={intl.formatMessage(messages.cancel)} cancelText={intl.formatMessage(messages.cancel)}
cancelAction={onClose} cancelAction={() => onClose('MISSING_DESCRIPTION')}
> >
<p className='text-gray-600 dark:text-gray-300'> <p className='text-gray-600 dark:text-gray-300'>
<FormattedMessage id='missing_description_modal.description' defaultMessage='Continue anyway?' /> <FormattedMessage id='missing_description_modal.description' defaultMessage='Continue anyway?' />

View file

@ -1,7 +1,6 @@
import { QueryClientProvider } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import { HelmetProvider } from 'react-helmet-async'; import { HelmetProvider } from 'react-helmet-async';
import { Toaster } from 'react-hot-toast';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { StatProvider } from 'pl-fe/contexts/stat-context'; import { StatProvider } from 'pl-fe/contexts/stat-context';
@ -40,12 +39,6 @@ const PlFe: React.FC = () => (
</StatProvider> </StatProvider>
</QueryClientProvider> </QueryClientProvider>
</Provider> </Provider>
<div id='toaster'>
<Toaster
position='top-right'
containerClassName='top-4'
/>
</div>
</> </>
); );

View file

@ -3,7 +3,7 @@ import sumBy from 'lodash/sumBy';
import { type Chat, type ChatMessage as BaseChatMessage, type PaginatedResponse, chatMessageSchema, type Relationship } from 'pl-api'; import { type Chat, type ChatMessage as BaseChatMessage, type PaginatedResponse, chatMessageSchema, type Relationship } from 'pl-api';
import * as v from 'valibot'; import * as v from 'valibot';
import { importFetchedAccount, importFetchedAccounts } from 'pl-fe/actions/importer'; import { importEntities } from 'pl-fe/actions/importer';
import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context'; import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context';
import { useStatContext } from 'pl-fe/contexts/stat-context'; import { useStatContext } from 'pl-fe/contexts/stat-context';
import { Entities } from 'pl-fe/entity-store/entities'; import { Entities } from 'pl-fe/entity-store/entities';
@ -72,7 +72,7 @@ const useChats = () => {
// Set the relationships to these users in the redux store. // Set the relationships to these users in the redux store.
fetchRelationships.mutate({ accountIds: items.map((item) => item.account.id) }); fetchRelationships.mutate({ accountIds: items.map((item) => item.account.id) });
dispatch(importFetchedAccounts(items.map((item) => item.account))); dispatch(importEntities({ accounts: items.map((item) => item.account) }));
return response; return response;
}; };
@ -109,7 +109,7 @@ const useChat = (chatId?: string) => {
const data = await client.chats.getChat(chatId); const data = await client.chats.getChat(chatId);
fetchRelationships.mutate({ accountIds: [data.account.id] }); fetchRelationships.mutate({ accountIds: [data.account.id] });
dispatch(importFetchedAccount(data.account)); dispatch(importEntities({ accounts: [data.account] }));
return data; return data;
} }

View file

@ -1,7 +1,7 @@
import { useMutation, keepPreviousData, useQuery } from '@tanstack/react-query'; import { useMutation, keepPreviousData, useQuery } from '@tanstack/react-query';
import { fetchRelationships } from 'pl-fe/actions/accounts'; import { fetchRelationships } from 'pl-fe/actions/accounts';
import { importFetchedAccounts } from 'pl-fe/actions/importer'; import { importEntities } from 'pl-fe/actions/importer';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useClient } from 'pl-fe/hooks/use-client'; import { useClient } from 'pl-fe/hooks/use-client';
@ -20,7 +20,7 @@ const useSuggestions = () => {
const accounts = response.map(({ account }) => account); const accounts = response.map(({ account }) => account);
const accountIds = accounts.map((account) => account.id); const accountIds = accounts.map((account) => account.id);
dispatch(importFetchedAccounts(accounts)); dispatch(importEntities({ accounts }));
dispatch(fetchRelationships(accountIds)); dispatch(fetchRelationships(accountIds));
return response.map(({ account, ...x }) => ({ ...x, account_id: account.id })); return response.map(({ account, ...x }) => ({ ...x, account_id: account.id }));
@ -60,7 +60,7 @@ const useOnboardingSuggestions = () => {
const accounts = response.map(({ account }) => account); const accounts = response.map(({ account }) => account);
const accountIds = accounts.map((account) => account.id); const accountIds = accounts.map((account) => account.id);
dispatch(importFetchedAccounts(accounts)); dispatch(importEntities({ accounts }));
dispatch(fetchRelationships(accountIds)); dispatch(fetchRelationships(accountIds));
return response; return response;

View file

@ -5,17 +5,18 @@ import * as v from 'valibot';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client'; import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { usePlHooksQueryClient } from 'pl-hooks/contexts/query-client'; import { usePlHooksQueryClient } from 'pl-hooks/contexts/query-client';
const placeholderData = v.parse(instanceSchema, {}); const initialData = v.parse(instanceSchema, {});
const useInstance = () => { const useInstance = () => {
const { client } = usePlHooksApiClient(); const { client } = usePlHooksApiClient();
const queryClient = usePlHooksQueryClient(); const queryClient = usePlHooksQueryClient();
return useQuery({ const query = useQuery({
queryKey: ['instance'], queryKey: ['instance'],
queryFn: client.instance.getInstance, queryFn: client.instance.getInstance,
placeholderData,
}, queryClient); }, queryClient);
return { ...query, data: query.data || initialData };
}; };
export { useInstance }; export { useInstance };