[Glitch] Require following accounts before being able to add them to a collection
Port 68a7cd404d to glitch-soc
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
@@ -6,6 +6,9 @@ import { useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
import CancelIcon from '@/material-icons/400-24px/cancel.svg?react';
|
||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||
import { showAlertForError } from 'flavours/glitch/actions/alerts';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { apiFollowAccount } from 'flavours/glitch/api/accounts';
|
||||
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
|
||||
import { Account } from 'flavours/glitch/components/account';
|
||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||
@@ -21,11 +24,13 @@ import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import ScrollableList from 'flavours/glitch/components/scrollable_list';
|
||||
import { useSearchAccounts } from 'flavours/glitch/features/lists/use_search_accounts';
|
||||
import { useAccount } from 'flavours/glitch/hooks/useAccount';
|
||||
import { me } from 'flavours/glitch/initial_state';
|
||||
import {
|
||||
addCollectionItem,
|
||||
removeCollectionItem,
|
||||
} from 'flavours/glitch/reducers/slices/collections';
|
||||
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
|
||||
import { store, useAppDispatch, useAppSelector } from 'flavours/glitch/store';
|
||||
|
||||
import type { TempCollectionState } from './state';
|
||||
import { getCollectionEditorState } from './state';
|
||||
@@ -69,7 +74,7 @@ interface SuggestionItem {
|
||||
}
|
||||
|
||||
const SuggestedAccountItem: React.FC<SuggestionItem> = ({ id, isSelected }) => {
|
||||
const account = useAppSelector((state) => state.accounts.get(id));
|
||||
const account = useAccount(id);
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
@@ -133,6 +138,7 @@ export const CollectionAccounts: React.FC<{
|
||||
isLoading: isLoadingSuggestions,
|
||||
searchAccounts,
|
||||
} = useSearchAccounts({
|
||||
withRelationships: true,
|
||||
filterResults: (account) =>
|
||||
// Only suggest accounts who allow being featured/recommended
|
||||
account.feature_approval.current_user === 'automatic',
|
||||
@@ -160,14 +166,67 @@ export const CollectionAccounts: React.FC<{
|
||||
[],
|
||||
);
|
||||
|
||||
const toggleAccountItem = useCallback((item: SuggestionItem) => {
|
||||
setAccountIds((ids) =>
|
||||
ids.includes(item.id)
|
||||
? ids.filter((id) => id !== item.id)
|
||||
: [...ids, item.id],
|
||||
);
|
||||
const relationships = useAppSelector((state) => state.relationships);
|
||||
|
||||
const confirmFollowStatus = useCallback(
|
||||
(accountId: string, onFollowing: () => void) => {
|
||||
const relationship = relationships.get(accountId);
|
||||
|
||||
if (!relationship) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
accountId === me ||
|
||||
relationship.following ||
|
||||
relationship.requested
|
||||
) {
|
||||
onFollowing();
|
||||
} else {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'CONFIRM_FOLLOW_TO_COLLECTION',
|
||||
modalProps: {
|
||||
accountId,
|
||||
onConfirm: () => {
|
||||
apiFollowAccount(accountId)
|
||||
.then(onFollowing)
|
||||
.catch((err: unknown) => {
|
||||
store.dispatch(showAlertForError(err));
|
||||
});
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
[dispatch, relationships],
|
||||
);
|
||||
|
||||
const removeAccountItem = useCallback((accountId: string) => {
|
||||
setAccountIds((ids) => ids.filter((id) => id !== accountId));
|
||||
}, []);
|
||||
|
||||
const addAccountItem = useCallback(
|
||||
(accountId: string) => {
|
||||
confirmFollowStatus(accountId, () => {
|
||||
setAccountIds((ids) => [...ids, accountId]);
|
||||
});
|
||||
},
|
||||
[confirmFollowStatus],
|
||||
);
|
||||
|
||||
const toggleAccountItem = useCallback(
|
||||
(item: SuggestionItem) => {
|
||||
if (addedAccountIds.includes(item.id)) {
|
||||
removeAccountItem(item.id);
|
||||
} else {
|
||||
addAccountItem(item.id);
|
||||
}
|
||||
},
|
||||
[addAccountItem, addedAccountIds, removeAccountItem],
|
||||
);
|
||||
|
||||
const instantRemoveAccountItem = useCallback(
|
||||
(accountId: string) => {
|
||||
const itemId = collectionItems?.find(
|
||||
@@ -190,19 +249,24 @@ export const CollectionAccounts: React.FC<{
|
||||
[collectionItems, dispatch, id, intl],
|
||||
);
|
||||
|
||||
const instantAddAccountItem = useCallback(
|
||||
(collectionId: string, accountId: string) => {
|
||||
confirmFollowStatus(accountId, () => {
|
||||
void dispatch(addCollectionItem({ collectionId, accountId }));
|
||||
});
|
||||
},
|
||||
[confirmFollowStatus, dispatch],
|
||||
);
|
||||
|
||||
const instantToggleAccountItem = useCallback(
|
||||
(item: SuggestionItem) => {
|
||||
if (accountIds.includes(item.id)) {
|
||||
instantRemoveAccountItem(item.id);
|
||||
} else {
|
||||
if (id) {
|
||||
void dispatch(
|
||||
addCollectionItem({ collectionId: id, accountId: item.id }),
|
||||
);
|
||||
}
|
||||
} else if (id) {
|
||||
instantAddAccountItem(id, item.id);
|
||||
}
|
||||
},
|
||||
[accountIds, dispatch, id, instantRemoveAccountItem],
|
||||
[accountIds, id, instantAddAccountItem, instantRemoveAccountItem],
|
||||
);
|
||||
|
||||
const handleRemoveAccountItem = useCallback(
|
||||
@@ -210,10 +274,10 @@ export const CollectionAccounts: React.FC<{
|
||||
if (isEditMode) {
|
||||
instantRemoveAccountItem(accountId);
|
||||
} else {
|
||||
setAccountIds((ids) => ids.filter((id) => id !== accountId));
|
||||
removeAccountItem(accountId);
|
||||
}
|
||||
},
|
||||
[isEditMode, instantRemoveAccountItem],
|
||||
[isEditMode, instantRemoveAccountItem, removeAccountItem],
|
||||
);
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
|
||||
@@ -2,19 +2,22 @@ import { useRef, useState } from 'react';
|
||||
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
import { fetchRelationships } from 'flavours/glitch/actions/accounts';
|
||||
import { importFetchedAccounts } from 'flavours/glitch/actions/importer';
|
||||
import { apiRequest } from 'flavours/glitch/api';
|
||||
import type { ApiAccountJSON } from 'flavours/glitch/api_types/accounts';
|
||||
import { useAppDispatch } from 'flavours/glitch/store';
|
||||
|
||||
export function useSearchAccounts({
|
||||
resetOnInputClear = true,
|
||||
onSettled,
|
||||
filterResults,
|
||||
resetOnInputClear = true,
|
||||
withRelationships = false,
|
||||
}: {
|
||||
onSettled?: (value: string) => void;
|
||||
filterResults?: (account: ApiAccountJSON) => boolean;
|
||||
resetOnInputClear?: boolean;
|
||||
withRelationships?: boolean;
|
||||
} = {}) {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -52,8 +55,12 @@ export function useSearchAccounts({
|
||||
})
|
||||
.then((data) => {
|
||||
const accounts = filterResults ? data.filter(filterResults) : data;
|
||||
const accountIds = accounts.map((a) => a.id);
|
||||
dispatch(importFetchedAccounts(accounts));
|
||||
setAccountIds(accounts.map((a) => a.id));
|
||||
if (withRelationships) {
|
||||
dispatch(fetchRelationships(accountIds));
|
||||
}
|
||||
setAccountIds(accountIds);
|
||||
setLoadingState('idle');
|
||||
onSettled?.(value);
|
||||
})
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { useAccount } from 'flavours/glitch/hooks/useAccount';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: {
|
||||
id: 'confirmations.follow_to_collection.title',
|
||||
defaultMessage: 'Follow account?',
|
||||
},
|
||||
confirm: {
|
||||
id: 'confirmations.follow_to_collection.confirm',
|
||||
defaultMessage: 'Follow and add to collection',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmFollowToCollectionModal: React.FC<
|
||||
{
|
||||
accountId: string;
|
||||
onConfirm: () => void;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ accountId, onConfirm, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const account = useAccount(accountId);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.title)}
|
||||
message={
|
||||
<FormattedMessage
|
||||
id='confirmations.follow_to_collection.message'
|
||||
defaultMessage='You need to be following {name} to add them to a collection.'
|
||||
values={{ name: <strong>@{account?.acct}</strong> }}
|
||||
/>
|
||||
}
|
||||
confirm={intl.formatMessage(messages.confirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { useAppSelector } from 'flavours/glitch/store';
|
||||
import { useAccount } from 'flavours/glitch/hooks/useAccount';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
@@ -23,7 +23,7 @@ export const ConfirmFollowToListModal: React.FC<
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ accountId, onConfirm, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const account = useAppSelector((state) => state.accounts.get(accountId));
|
||||
const account = useAccount(accountId);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
|
||||
@@ -13,6 +13,7 @@ export { ConfirmUnblockModal } from './unblock';
|
||||
export { ConfirmClearNotificationsModal } from './clear_notifications';
|
||||
export { ConfirmLogOutModal } from './log_out';
|
||||
export { ConfirmFollowToListModal } from './follow_to_list';
|
||||
export { ConfirmFollowToCollectionModal } from './follow_to_collection';
|
||||
export { ConfirmMissingAltTextModal } from './missing_alt_text';
|
||||
export { ConfirmRevokeQuoteModal } from './revoke_quote';
|
||||
export { QuietPostQuoteInfoModal } from './quiet_post_quote_info';
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
ConfirmClearNotificationsModal,
|
||||
ConfirmLogOutModal,
|
||||
ConfirmFollowToListModal,
|
||||
ConfirmFollowToCollectionModal,
|
||||
ConfirmMissingAltTextModal,
|
||||
ConfirmRevokeQuoteModal,
|
||||
QuietPostQuoteInfoModal,
|
||||
@@ -73,6 +74,7 @@ export const MODAL_COMPONENTS = {
|
||||
'CONFIRM_CLEAR_NOTIFICATIONS': () => Promise.resolve({ default: ConfirmClearNotificationsModal }),
|
||||
'CONFIRM_LOG_OUT': () => Promise.resolve({ default: ConfirmLogOutModal }),
|
||||
'CONFIRM_FOLLOW_TO_LIST': () => Promise.resolve({ default: ConfirmFollowToListModal }),
|
||||
'CONFIRM_FOLLOW_TO_COLLECTION': () => Promise.resolve({ default: ConfirmFollowToCollectionModal }),
|
||||
'CONFIRM_MISSING_ALT_TEXT': () => Promise.resolve({ default: ConfirmMissingAltTextModal }),
|
||||
'CONFIRM_PRIVATE_QUOTE_NOTIFY': () => Promise.resolve({ default: PrivateQuoteNotify }),
|
||||
'CONFIRM_REVOKE_QUOTE': () => Promise.resolve({ default: ConfirmRevokeQuoteModal }),
|
||||
|
||||
Reference in New Issue
Block a user