pl-fe: migrations: account aliases
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -1,102 +0,0 @@
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import toast from 'pl-fe/toast';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import { getClient } from '../api';
|
||||
|
||||
import { importEntities } from './importer';
|
||||
|
||||
import type { Account as BaseAccount } from 'pl-api';
|
||||
import type { Account } from 'pl-fe/normalizers/account';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
const ALIASES_FETCH_SUCCESS = 'ALIASES_FETCH_SUCCESS' as const;
|
||||
|
||||
const ALIASES_SUGGESTIONS_CHANGE = 'ALIASES_SUGGESTIONS_CHANGE' as const;
|
||||
const ALIASES_SUGGESTIONS_READY = 'ALIASES_SUGGESTIONS_READY' as const;
|
||||
const ALIASES_SUGGESTIONS_CLEAR = 'ALIASES_SUGGESTIONS_CLEAR' as const;
|
||||
|
||||
const messages = defineMessages({
|
||||
createSuccess: { id: 'aliases.success.add', defaultMessage: 'Account alias created successfully' },
|
||||
removeSuccess: { id: 'aliases.success.remove', defaultMessage: 'Account alias removed successfully' },
|
||||
});
|
||||
|
||||
const fetchAliases = (dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
return getClient(getState).settings.getAccountAliases()
|
||||
.then(response => {
|
||||
dispatch(fetchAliasesSuccess(response.aliases));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchAliasesSuccess = (aliases: Array<string>) => ({
|
||||
type: ALIASES_FETCH_SUCCESS,
|
||||
value: aliases,
|
||||
});
|
||||
|
||||
const fetchAliasesSuggestions = (q: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
return getClient(getState()).accounts.searchAccounts(q, { resolve: true, limit: 4 })
|
||||
.then((data) => {
|
||||
dispatch(importEntities({ accounts: data }));
|
||||
dispatch(fetchAliasesSuggestionsReady(q, data));
|
||||
}).catch(error => toast.showAlertForError(error));
|
||||
};
|
||||
|
||||
const fetchAliasesSuggestionsReady = (query: string, accounts: BaseAccount[]) => ({
|
||||
type: ALIASES_SUGGESTIONS_READY,
|
||||
query,
|
||||
accounts,
|
||||
});
|
||||
|
||||
const clearAliasesSuggestions = () => ({
|
||||
type: ALIASES_SUGGESTIONS_CLEAR,
|
||||
});
|
||||
|
||||
const changeAliasesSuggestions = (value: string) => ({
|
||||
type: ALIASES_SUGGESTIONS_CHANGE,
|
||||
value,
|
||||
});
|
||||
|
||||
const addToAliases = (account: Account) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
return getClient(getState).settings.addAccountAlias(account.acct).then(() => {
|
||||
toast.success(messages.createSuccess);
|
||||
dispatch(fetchAliases);
|
||||
});
|
||||
};
|
||||
|
||||
const removeFromAliases = (account: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
return getClient(getState).settings.deleteAccountAlias(account).then(() => {
|
||||
toast.success(messages.removeSuccess);
|
||||
});
|
||||
};
|
||||
|
||||
type AliasesAction =
|
||||
| ReturnType<typeof fetchAliasesSuccess>
|
||||
| ReturnType<typeof fetchAliasesSuggestionsReady>
|
||||
| ReturnType<typeof clearAliasesSuggestions>
|
||||
| ReturnType<typeof changeAliasesSuggestions>
|
||||
|
||||
export {
|
||||
ALIASES_FETCH_SUCCESS,
|
||||
ALIASES_SUGGESTIONS_CHANGE,
|
||||
ALIASES_SUGGESTIONS_READY,
|
||||
ALIASES_SUGGESTIONS_CLEAR,
|
||||
fetchAliases,
|
||||
fetchAliasesSuggestions,
|
||||
clearAliasesSuggestions,
|
||||
changeAliasesSuggestions,
|
||||
addToAliases,
|
||||
removeFromAliases,
|
||||
type AliasesAction,
|
||||
};
|
||||
@ -1,8 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { addToAliases, changeAliasesSuggestions, clearAliasesSuggestions, fetchAliases, fetchAliasesSuggestions, removeFromAliases } from 'pl-fe/actions/aliases';
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import AccountComponent from 'pl-fe/components/account';
|
||||
import Icon from 'pl-fe/components/icon';
|
||||
@ -13,10 +12,10 @@ import { CardHeader, CardTitle } from 'pl-fe/components/ui/card';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
|
||||
import { useSearchAccounts } from 'pl-fe/queries/search/use-search';
|
||||
import { useAccountAliases, useAddAccountAlias, useDeleteAccountAlias } from 'pl-fe/queries/settings/use-account-aliases';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.aliases', defaultMessage: 'Account aliases' },
|
||||
@ -37,17 +36,18 @@ interface IAccount {
|
||||
|
||||
const Account: React.FC<IAccount> = ({ accountId, aliases }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
|
||||
const me = useAppSelector((state) => state.me);
|
||||
const { account } = useAccount(accountId);
|
||||
|
||||
const { mutate: addAccountAlias } = useAddAccountAlias();
|
||||
|
||||
const apId = account?.ap_id;
|
||||
const name = features.accountMoving ? account?.acct : apId;
|
||||
const added = name ? aliases.includes(name) : false;
|
||||
|
||||
const handleOnAdd = () => dispatch(addToAliases(account!));
|
||||
const handleOnAdd = () => addAccountAlias(name!);
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
@ -69,28 +69,31 @@ const Account: React.FC<IAccount> = ({ accountId, aliases }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Search: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
interface IAliasesSearch {
|
||||
onSubmit: (value: string) => void;
|
||||
}
|
||||
|
||||
const Search: React.FC<IAliasesSearch> = ({ onSubmit }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const value = useAppSelector(state => state.aliases.suggestions.value);
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(changeAliasesSuggestions(e.target.value));
|
||||
setValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.keyCode === 13) {
|
||||
dispatch(fetchAliasesSuggestions(value));
|
||||
onSubmit(value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
dispatch(fetchAliasesSuggestions(value));
|
||||
onSubmit(value);
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
dispatch(clearAliasesSuggestions());
|
||||
onSubmit('');
|
||||
};
|
||||
|
||||
const hasValue = value.length > 0;
|
||||
@ -120,27 +123,15 @@ const Search: React.FC = () => {
|
||||
|
||||
const AliasesPage = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
const { account } = useOwnAccount();
|
||||
|
||||
const aliases = useAppSelector((state): Array<string> => {
|
||||
if (features.accountMoving) {
|
||||
return [...state.aliases.aliases.items];
|
||||
} else {
|
||||
return account?.__meta.pleroma?.also_known_as ?? [];
|
||||
}
|
||||
});
|
||||
const [query, setQuery] = useState('');
|
||||
|
||||
const searchAccountIds = useAppSelector((state) => state.aliases.suggestions.items);
|
||||
const loaded = useAppSelector((state) => state.aliases.suggestions.loaded);
|
||||
const { data: aliases = [] } = useAccountAliases();
|
||||
const { data: searchAccountIds = [], isFetched } = useSearchAccounts(query);
|
||||
const { mutate: deleteAccountAlias } = useDeleteAccountAlias();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchAliases);
|
||||
}, []);
|
||||
|
||||
const handleFilterDelete: React.MouseEventHandler<HTMLDivElement> = e => {
|
||||
dispatch(removeFromAliases(e.currentTarget.dataset.value as string));
|
||||
const handleAliasDelete: React.MouseEventHandler<HTMLDivElement> = e => {
|
||||
deleteAccountAlias(e.currentTarget.dataset.value as string);
|
||||
};
|
||||
|
||||
const emptyMessage = <FormattedMessage id='empty_column.aliases' defaultMessage="You haven't created any account alias yet." />;
|
||||
@ -150,14 +141,14 @@ const AliasesPage = () => {
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.subheading_add_new)} />
|
||||
</CardHeader>
|
||||
<Search />
|
||||
<Search onSubmit={setQuery} />
|
||||
{
|
||||
loaded && searchAccountIds.length === 0 ? (
|
||||
isFetched && searchAccountIds.length === 0 ? (
|
||||
<div className='empty-column-indicator'>
|
||||
<FormattedMessage id='empty_column.aliases.suggestions' defaultMessage='There are no account suggestions available for the provided term.' />
|
||||
</div>
|
||||
) : (
|
||||
<div className='mb-4 overflow-y-auto'>
|
||||
<div className='mb-4 max-h-72 overflow-y-auto'>
|
||||
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} aliases={aliases} />)}
|
||||
</div>
|
||||
)
|
||||
@ -177,7 +168,7 @@ const AliasesPage = () => {
|
||||
{' '}
|
||||
<Text tag='span'>{alias}</Text>
|
||||
</div>
|
||||
<div className='flex items-center' role='button' tabIndex={0} onClick={handleFilterDelete} data-value={alias} aria-label={intl.formatMessage(messages.delete)}>
|
||||
<div className='flex items-center' role='button' tabIndex={0} onClick={handleAliasDelete} data-value={alias} aria-label={intl.formatMessage(messages.delete)}>
|
||||
<Icon className='mr-1.5' src={require('@tabler/icons/outline/x.svg')} />
|
||||
<Text weight='bold' theme='muted'><FormattedMessage id='aliases.aliases_list_delete' defaultMessage='Unlink alias' /></Text>
|
||||
</div>
|
||||
|
||||
47
packages/pl-fe/src/queries/settings/use-account-aliases.ts
Normal file
47
packages/pl-fe/src/queries/settings/use-account-aliases.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
|
||||
|
||||
const useAccountAliases = () => {
|
||||
const client = useClient();
|
||||
const features = useFeatures();
|
||||
const { account } = useOwnAccount();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['settings', 'accountAliases'],
|
||||
queryFn: async (): Promise<Array<string>> => {
|
||||
if (features.accountMoving) return (await client.settings.getAccountAliases()).aliases;
|
||||
return account?.__meta.pleroma?.also_known_as ?? [];
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const useAddAccountAlias = () => {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['settings', 'accountAliases'],
|
||||
mutationFn: (acct: string) => client.settings.addAccountAlias(acct),
|
||||
onSettled: () => queryClient.invalidateQueries({
|
||||
queryKey: ['settings', 'accountAliases'],
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const useDeleteAccountAlias = () => {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['settings', 'accountAliases'],
|
||||
mutationFn: (acct: string) => client.settings.deleteAccountAlias(acct),
|
||||
onSettled: () => queryClient.invalidateQueries({
|
||||
queryKey: ['settings', 'accountAliases'],
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export { useAccountAliases, useAddAccountAlias, useDeleteAccountAlias };
|
||||
@ -1,62 +0,0 @@
|
||||
import { create } from 'mutative';
|
||||
|
||||
import {
|
||||
ALIASES_SUGGESTIONS_READY,
|
||||
ALIASES_SUGGESTIONS_CLEAR,
|
||||
ALIASES_SUGGESTIONS_CHANGE,
|
||||
ALIASES_FETCH_SUCCESS,
|
||||
AliasesAction,
|
||||
} from '../actions/aliases';
|
||||
|
||||
interface State {
|
||||
aliases: {
|
||||
items: Array<string>;
|
||||
loaded: boolean;
|
||||
};
|
||||
suggestions: {
|
||||
items: Array<string>;
|
||||
value: string;
|
||||
loaded: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
aliases: {
|
||||
items: [],
|
||||
loaded: false,
|
||||
},
|
||||
suggestions: {
|
||||
items: [],
|
||||
value: '',
|
||||
loaded: false,
|
||||
},
|
||||
};
|
||||
|
||||
const aliasesReducer = (state = initialState, action: AliasesAction): State => {
|
||||
switch (action.type) {
|
||||
case ALIASES_FETCH_SUCCESS:
|
||||
return create(state, (draft) => {
|
||||
draft.aliases.items = action.value;
|
||||
});
|
||||
case ALIASES_SUGGESTIONS_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
draft.suggestions.value = action.value;
|
||||
draft.suggestions.loaded = false;
|
||||
});
|
||||
case ALIASES_SUGGESTIONS_READY:
|
||||
return create(state, (draft) => {
|
||||
draft.suggestions.items = action.accounts.map((item) => item.id);
|
||||
draft.suggestions.loaded = true;
|
||||
});
|
||||
case ALIASES_SUGGESTIONS_CLEAR:
|
||||
return create(state, (draft) => {
|
||||
draft.suggestions.items = [];
|
||||
draft.suggestions.value = '';
|
||||
draft.suggestions.loaded = false;
|
||||
});
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { aliasesReducer as default };
|
||||
@ -7,7 +7,6 @@ import entities from 'pl-fe/entity-store/reducer';
|
||||
import accounts_meta from './accounts-meta';
|
||||
import admin from './admin';
|
||||
import admin_user_index from './admin-user-index';
|
||||
import aliases from './aliases';
|
||||
import auth from './auth';
|
||||
import compose from './compose';
|
||||
import contexts from './contexts';
|
||||
@ -34,7 +33,6 @@ const reducers = {
|
||||
accounts_meta,
|
||||
admin,
|
||||
admin_user_index,
|
||||
aliases,
|
||||
auth,
|
||||
compose,
|
||||
contexts,
|
||||
|
||||
Reference in New Issue
Block a user