pl-fe: migrate account notes
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -65,6 +65,7 @@
|
|||||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
|
"@tanstack/react-pacer": "^0.16.4",
|
||||||
"@tanstack/react-query": "^5.62.11",
|
"@tanstack/react-query": "^5.62.11",
|
||||||
"@transfem-org/sfm-js": "^0.24.6",
|
"@transfem-org/sfm-js": "^0.24.6",
|
||||||
"@twemoji/svg": "^15.0.0",
|
"@twemoji/svg": "^15.0.0",
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
import { __stub } from 'pl-fe/api';
|
|
||||||
import { mockStore, rootState } from 'pl-fe/jest/test-helpers';
|
|
||||||
|
|
||||||
import { submitAccountNote } from './account-notes';
|
|
||||||
|
|
||||||
describe('submitAccountNote()', () => {
|
|
||||||
let store: ReturnType<typeof mockStore>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
store = mockStore(rootState);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with a successful API request', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
__stub((mock) => {
|
|
||||||
mock.onPost('/api/v1/accounts/1/note').reply(200, {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('post the note to the API', async() => {
|
|
||||||
const expectedActions = [
|
|
||||||
{ type: 'ACCOUNT_NOTE_SUBMIT_REQUEST' },
|
|
||||||
{ type: 'ACCOUNT_NOTE_SUBMIT_SUCCESS', relationship: {} },
|
|
||||||
];
|
|
||||||
await store.dispatch(submitAccountNote('1', 'hello'));
|
|
||||||
const actions = store.getActions();
|
|
||||||
|
|
||||||
expect(actions).toEqual(expectedActions);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with an unsuccessful API request', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
__stub((mock) => {
|
|
||||||
mock.onPost('/api/v1/accounts/1/note').networkError();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should dispatch failed action', async() => {
|
|
||||||
const expectedActions = [
|
|
||||||
{ type: 'ACCOUNT_NOTE_SUBMIT_REQUEST' },
|
|
||||||
{
|
|
||||||
type: 'ACCOUNT_NOTE_SUBMIT_FAIL',
|
|
||||||
error: new Error('Network Error'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
await store.dispatch(submitAccountNote('1', 'hello'));
|
|
||||||
const actions = store.getActions();
|
|
||||||
|
|
||||||
expect(actions).toEqual(expectedActions);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import { importEntities } from 'pl-fe/actions/importer';
|
|
||||||
|
|
||||||
import { getClient } from '../api';
|
|
||||||
|
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
|
||||||
|
|
||||||
const submitAccountNote = (accountId: string, value: string) =>
|
|
||||||
(dispatch: AppDispatch, getState: () => RootState) =>
|
|
||||||
getClient(getState).accounts.updateAccountNote(accountId, value)
|
|
||||||
.then(response => dispatch(importEntities({ relationships: [response] })));
|
|
||||||
|
|
||||||
export { submitAccountNote };
|
|
||||||
@ -3,7 +3,7 @@ import { useSettingsStore } from 'pl-fe/stores/settings';
|
|||||||
|
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||||
|
|
||||||
const toggleMainWindow = () =>
|
const toggleChatPane = () =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const main = useSettingsStore.getState().settings.chats.mainWindow;
|
const main = useSettingsStore.getState().settings.chats.mainWindow;
|
||||||
const state = main === 'minimized' ? 'open' : 'minimized';
|
const state = main === 'minimized' ? 'open' : 'minimized';
|
||||||
@ -11,5 +11,5 @@ const toggleMainWindow = () =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
toggleMainWindow,
|
toggleChatPane,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { toggleMainWindow } from 'pl-fe/actions/chats';
|
import { toggleChatPane } from 'pl-fe/actions/chats';
|
||||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||||
import { useChat } from 'pl-fe/queries/chats';
|
import { useChat } from 'pl-fe/queries/chats';
|
||||||
import { useSettings } from 'pl-fe/stores/settings';
|
import { useSettings } from 'pl-fe/stores/settings';
|
||||||
@ -45,13 +45,13 @@ const ChatProvider: React.FC<IChatProvider> = ({ children }) => {
|
|||||||
setScreen(screen);
|
setScreen(screen);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleChatPane = () => dispatch(toggleMainWindow());
|
const handleChatPaneToggle = () => dispatch(toggleChatPane());
|
||||||
|
|
||||||
const value = useMemo(() => ({
|
const value = useMemo(() => ({
|
||||||
chat,
|
chat,
|
||||||
isOpen,
|
isOpen,
|
||||||
isUsingMainChatPage,
|
isUsingMainChatPage,
|
||||||
toggleChatPane,
|
toggleChatPane: handleChatPaneToggle,
|
||||||
screen,
|
screen,
|
||||||
changeScreen,
|
changeScreen,
|
||||||
currentChatId,
|
currentChatId,
|
||||||
|
|||||||
@ -1,23 +1,15 @@
|
|||||||
import debounce from 'lodash/debounce';
|
import { debounce } from '@tanstack/react-pacer/debouncer';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { submitAccountNote } from 'pl-fe/actions/account-notes';
|
|
||||||
import HStack from 'pl-fe/components/ui/hstack';
|
import HStack from 'pl-fe/components/ui/hstack';
|
||||||
import Text from 'pl-fe/components/ui/text';
|
import Text from 'pl-fe/components/ui/text';
|
||||||
import Textarea from 'pl-fe/components/ui/textarea';
|
import Textarea from 'pl-fe/components/ui/textarea';
|
||||||
import Widget from 'pl-fe/components/ui/widget';
|
import Widget from 'pl-fe/components/ui/widget';
|
||||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
|
||||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||||
|
import { useUpdateAccountNoteMutation } from 'pl-fe/queries/accounts/use-relationship';
|
||||||
|
|
||||||
import type { Account as AccountEntity } from 'pl-fe/normalizers/account';
|
import type { Account as AccountEntity } from 'pl-fe/normalizers/account';
|
||||||
import type { AppDispatch } from 'pl-fe/store';
|
|
||||||
|
|
||||||
const onSave = debounce(
|
|
||||||
(dispatch: AppDispatch, accountId: string, value: string, callback: () => void) =>
|
|
||||||
dispatch(submitAccountNote(accountId, value)).then(() => callback()),
|
|
||||||
900,
|
|
||||||
);
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
placeholder: { id: 'account_note.placeholder', defaultMessage: 'Click to add a note' },
|
placeholder: { id: 'account_note.placeholder', defaultMessage: 'Click to add a note' },
|
||||||
@ -30,9 +22,12 @@ interface IAccountNotePanel {
|
|||||||
|
|
||||||
const AccountNotePanel: React.FC<IAccountNotePanel> = ({ account }) => {
|
const AccountNotePanel: React.FC<IAccountNotePanel> = ({ account }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const me = useAppSelector((state) => state.me);
|
const me = useAppSelector((state) => state.me);
|
||||||
|
|
||||||
|
const { mutate: updateAccountNote } = useUpdateAccountNoteMutation(account.id);
|
||||||
|
|
||||||
|
const debouncedUpdateAccountNote = useCallback(debounce(updateAccountNote, { wait: 900 }), []);
|
||||||
|
|
||||||
const textarea = useRef<HTMLTextAreaElement>(null);
|
const textarea = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
const [value, setValue] = useState<string | undefined>(account.relationship?.note);
|
const [value, setValue] = useState<string | undefined>(account.relationship?.note);
|
||||||
@ -41,9 +36,11 @@ const AccountNotePanel: React.FC<IAccountNotePanel> = ({ account }) => {
|
|||||||
const handleChange: React.ChangeEventHandler<HTMLTextAreaElement> = e => {
|
const handleChange: React.ChangeEventHandler<HTMLTextAreaElement> = e => {
|
||||||
setValue(e.target.value);
|
setValue(e.target.value);
|
||||||
|
|
||||||
onSave(dispatch, account.id, e.target.value, () => {
|
debouncedUpdateAccountNote(e.target.value, {
|
||||||
setSaved(true);
|
onSuccess: () => {
|
||||||
setTimeout(() => setSaved(false), 2000);
|
setSaved(true);
|
||||||
|
setTimeout(() => setSaved(false), 2000);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -236,6 +236,23 @@ const useRemoveAccountFromFollowersMutation = (accountId: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useUpdateAccountNoteMutation = (accountId: string) => {
|
||||||
|
const client = useClient();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ['accountNote', accountId],
|
||||||
|
mutationFn: (note: string) => client.accounts.updateAccountNote(accountId, note),
|
||||||
|
onMutate: (note) => updateRelationship(accountId, {
|
||||||
|
note,
|
||||||
|
}, queryClient),
|
||||||
|
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
|
||||||
|
onSuccess: (data) => {
|
||||||
|
queryClient.setQueryData(['accountRelationships', accountId], data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useRelationshipQuery,
|
useRelationshipQuery,
|
||||||
useFollowAccountMutation,
|
useFollowAccountMutation,
|
||||||
@ -247,4 +264,5 @@ export {
|
|||||||
usePinAccountMutation,
|
usePinAccountMutation,
|
||||||
useUnpinAccountMutation,
|
useUnpinAccountMutation,
|
||||||
useRemoveAccountFromFollowersMutation,
|
useRemoveAccountFromFollowersMutation,
|
||||||
|
useUpdateAccountNoteMutation,
|
||||||
};
|
};
|
||||||
|
|||||||
52
pnpm-lock.yaml
generated
52
pnpm-lock.yaml
generated
@ -196,6 +196,9 @@ importers:
|
|||||||
'@tailwindcss/typography':
|
'@tailwindcss/typography':
|
||||||
specifier: ^0.5.16
|
specifier: ^0.5.16
|
||||||
version: 0.5.16(tailwindcss@3.4.17)
|
version: 0.5.16(tailwindcss@3.4.17)
|
||||||
|
'@tanstack/react-pacer':
|
||||||
|
specifier: ^0.16.4
|
||||||
|
version: 0.16.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@tanstack/react-query':
|
'@tanstack/react-query':
|
||||||
specifier: ^5.62.11
|
specifier: ^5.62.11
|
||||||
version: 5.84.1(react@18.3.1)
|
version: 5.84.1(react@18.3.1)
|
||||||
@ -2322,14 +2325,38 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
|
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
|
||||||
|
|
||||||
|
'@tanstack/devtools-event-client@0.3.3':
|
||||||
|
resolution: {integrity: sha512-RfV+OPV/M3CGryYqTue684u10jUt55PEqeBOnOtCe6tAmHI9Iqyc8nHeDhWPEV9715gShuauFVaMc9RiUVNdwg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@tanstack/pacer@0.15.4':
|
||||||
|
resolution: {integrity: sha512-vGY+CWsFZeac3dELgB6UZ4c7OacwsLb8hvL2gLS6hTgy8Fl0Bm/aLokHaeDIP+q9F9HUZTnp360z9uv78eg8pg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@tanstack/query-core@5.83.1':
|
'@tanstack/query-core@5.83.1':
|
||||||
resolution: {integrity: sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==}
|
resolution: {integrity: sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==}
|
||||||
|
|
||||||
|
'@tanstack/react-pacer@0.16.4':
|
||||||
|
resolution: {integrity: sha512-nuQLE8bx0rYMiJau4jOTPZFp3XC/GnIHDKfKVVWeKUHNF4grRdVHPgTlJ8EV/nt/HJxSUnIcy+IIKX+Bj0bLSw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8'
|
||||||
|
react-dom: '>=16.8'
|
||||||
|
|
||||||
'@tanstack/react-query@5.84.1':
|
'@tanstack/react-query@5.84.1':
|
||||||
resolution: {integrity: sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==}
|
resolution: {integrity: sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18 || ^19
|
react: ^18 || ^19
|
||||||
|
|
||||||
|
'@tanstack/react-store@0.7.7':
|
||||||
|
resolution: {integrity: sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
|
'@tanstack/store@0.7.7':
|
||||||
|
resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==}
|
||||||
|
|
||||||
'@testing-library/dom@10.4.1':
|
'@testing-library/dom@10.4.1':
|
||||||
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@ -8625,13 +8652,36 @@ snapshots:
|
|||||||
postcss-selector-parser: 6.0.10
|
postcss-selector-parser: 6.0.10
|
||||||
tailwindcss: 3.4.17
|
tailwindcss: 3.4.17
|
||||||
|
|
||||||
|
'@tanstack/devtools-event-client@0.3.3': {}
|
||||||
|
|
||||||
|
'@tanstack/pacer@0.15.4':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/devtools-event-client': 0.3.3
|
||||||
|
'@tanstack/store': 0.7.7
|
||||||
|
|
||||||
'@tanstack/query-core@5.83.1': {}
|
'@tanstack/query-core@5.83.1': {}
|
||||||
|
|
||||||
|
'@tanstack/react-pacer@0.16.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/pacer': 0.15.4
|
||||||
|
'@tanstack/react-store': 0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
'@tanstack/react-query@5.84.1(react@18.3.1)':
|
'@tanstack/react-query@5.84.1(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tanstack/query-core': 5.83.1
|
'@tanstack/query-core': 5.83.1
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
|
'@tanstack/react-store@0.7.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/store': 0.7.7
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
use-sync-external-store: 1.5.0(react@18.3.1)
|
||||||
|
|
||||||
|
'@tanstack/store@0.7.7': {}
|
||||||
|
|
||||||
'@testing-library/dom@10.4.1':
|
'@testing-library/dom@10.4.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.27.1
|
'@babel/code-frame': 7.27.1
|
||||||
@ -10299,7 +10349,7 @@ snapshots:
|
|||||||
tinyglobby: 0.2.14
|
tinyglobby: 0.2.14
|
||||||
unrs-resolver: 1.11.1
|
unrs-resolver: 1.11.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1)
|
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user