pl-fe: account relationships migration
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@@ -5,7 +5,6 @@ import {
|
||||
type Relationship,
|
||||
} from 'pl-api';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { selectAccount } from 'pl-fe/selectors';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
@@ -177,8 +176,7 @@ const fetchRelationships = (accountIds: string[]) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return null;
|
||||
|
||||
const loadedRelationships = getState().entities[Entities.RELATIONSHIPS]?.store;
|
||||
const newAccountIds = accountIds.filter(id => !loadedRelationships?.[id]);
|
||||
const newAccountIds = accountIds.filter(id => !queryClient.getQueryData(['accountRelationships', id]));
|
||||
|
||||
if (newAccountIds.length === 0) {
|
||||
return null;
|
||||
|
||||
@@ -120,7 +120,11 @@ const importEntities = (entities: {
|
||||
queryClient.setQueryData<BasePoll>(['statuses', 'polls', poll.id], poll);
|
||||
}
|
||||
}
|
||||
if (!isEmpty(relationships)) dispatch(importEntityStoreEntities(Object.values(relationships), Entities.RELATIONSHIPS));
|
||||
if (!isEmpty(relationships)) {
|
||||
for (const relationship of Object.values(relationships)) {
|
||||
queryClient.setQueryData<BaseRelationship>(['accountRelationships', relationship.id], relationship);
|
||||
}
|
||||
}
|
||||
if (!isEmpty(statuses)) dispatch<ImportStatusesAction>({ type: STATUSES_IMPORT, statuses: Object.values(statuses) });
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
import { type Account, normalizeAccount } from 'pl-fe/normalizers/account';
|
||||
|
||||
import { useRelationship } from './use-relationship';
|
||||
import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship';
|
||||
|
||||
import type { Account as BaseAccount } from 'pl-api';
|
||||
|
||||
@@ -29,9 +28,9 @@ const useAccountLookup = (acct: string | undefined, opts: UseAccountLookupOpts =
|
||||
);
|
||||
|
||||
const {
|
||||
relationship,
|
||||
data: relationship,
|
||||
isLoading: isRelationshipLoading,
|
||||
} = useRelationship(entity?.id, { enabled: withRelationship });
|
||||
} = useRelationshipQuery(withRelationship ? entity?.id : undefined);
|
||||
|
||||
const isBlocked = entity?.relationship?.blocked_by === true;
|
||||
const isUnavailable = (me === entity?.id) ? false : (isBlocked && !features.blockersVisible);
|
||||
|
||||
@@ -7,8 +7,7 @@ import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
import { type Account, normalizeAccount } from 'pl-fe/normalizers/account';
|
||||
|
||||
import { useRelationship } from './use-relationship';
|
||||
import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship';
|
||||
|
||||
import type { Account as BaseAccount } from 'pl-api';
|
||||
|
||||
@@ -31,9 +30,9 @@ const useAccount = (accountId?: string, opts: UseAccountOpts = {}) => {
|
||||
const meta = useAppSelector((state) => accountId ? state.accounts_meta[accountId] : undefined);
|
||||
|
||||
const {
|
||||
relationship,
|
||||
data: relationship,
|
||||
isLoading: isRelationshipLoading,
|
||||
} = useRelationship(accountId, { enabled: withRelationship });
|
||||
} = useRelationshipQuery(withRelationship ? entity?.id : undefined);
|
||||
|
||||
const isBlocked = entity?.relationship?.blocked_by === true;
|
||||
const isUnavailable = (me === entity?.id) ? false : (isBlocked && !features.blockersVisible);
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import { importEntities } from 'pl-fe/entity-store/actions';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useTransaction } from 'pl-fe/entity-store/hooks/use-transaction';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
|
||||
interface FollowOpts {
|
||||
reblogs?: boolean;
|
||||
notify?: boolean;
|
||||
languages?: string[];
|
||||
}
|
||||
|
||||
const useFollow = () => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
const { isLoggedIn } = useLoggedIn();
|
||||
const { transaction } = useTransaction();
|
||||
|
||||
const followEffect = (accountId: string) => {
|
||||
transaction({
|
||||
Accounts: {
|
||||
[accountId]: (account) => ({
|
||||
...account,
|
||||
followers_count: account.followers_count + 1,
|
||||
}),
|
||||
},
|
||||
Relationships: {
|
||||
[accountId]: (relationship) => ({
|
||||
...relationship,
|
||||
following: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const unfollowEffect = (accountId: string) => {
|
||||
transaction({
|
||||
Accounts: {
|
||||
[accountId]: (account) => ({
|
||||
...account,
|
||||
followers_count: Math.max(0, account.followers_count - 1),
|
||||
}),
|
||||
},
|
||||
Relationships: {
|
||||
[accountId]: (relationship) => ({
|
||||
...relationship,
|
||||
following: false,
|
||||
requested: false,
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const follow = async (accountId: string, options: FollowOpts = {}) => {
|
||||
if (!isLoggedIn) return;
|
||||
followEffect(accountId);
|
||||
|
||||
try {
|
||||
const response = await client.accounts.followAccount(accountId, options);
|
||||
if (response.id) {
|
||||
dispatch(importEntities([response], Entities.RELATIONSHIPS));
|
||||
}
|
||||
} catch (e) {
|
||||
unfollowEffect(accountId);
|
||||
}
|
||||
};
|
||||
|
||||
const unfollow = async (accountId: string) => {
|
||||
if (!isLoggedIn) return;
|
||||
unfollowEffect(accountId);
|
||||
|
||||
try {
|
||||
await client.accounts.unfollowAccount(accountId);
|
||||
} catch (e) {
|
||||
followEffect(accountId);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
follow,
|
||||
unfollow,
|
||||
followEffect,
|
||||
unfollowEffect,
|
||||
};
|
||||
};
|
||||
|
||||
export { useFollow };
|
||||
@@ -1,31 +0,0 @@
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useEntity } from 'pl-fe/entity-store/hooks/use-entity';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
|
||||
import type { Relationship } from 'pl-api';
|
||||
|
||||
interface UseRelationshipOpts {
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
const useRelationship = (accountId: string | undefined, opts: UseRelationshipOpts = {}) => {
|
||||
const client = useClient();
|
||||
const { isLoggedIn } = useLoggedIn();
|
||||
const { enabled = false } = opts;
|
||||
|
||||
const { entity: relationship, ...result } = useEntity<Relationship>(
|
||||
[Entities.RELATIONSHIPS, accountId!],
|
||||
() => client.accounts.getRelationships([accountId!]),
|
||||
{
|
||||
enabled: enabled && isLoggedIn && !!accountId,
|
||||
schema: v.pipe(v.any(), v.transform(arr => arr[0])),
|
||||
},
|
||||
);
|
||||
|
||||
return { relationship, ...result };
|
||||
};
|
||||
|
||||
export { useRelationship };
|
||||
@@ -8,9 +8,6 @@ import { getLocale } from 'pl-fe/actions/settings';
|
||||
import { updateStatus } from 'pl-fe/actions/statuses';
|
||||
import { deleteFromTimelines, processTimelineUpdate } from 'pl-fe/actions/timelines';
|
||||
import { useStatContext } from 'pl-fe/contexts/stat-context';
|
||||
import { importEntities } from 'pl-fe/entity-store/actions';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { selectEntity } from 'pl-fe/entity-store/selectors';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
import messages from 'pl-fe/messages';
|
||||
@@ -73,16 +70,12 @@ const updateFollowRelationships = (update: FollowRelationshipUpdate) =>
|
||||
const state = getState();
|
||||
|
||||
const me = state.me;
|
||||
const relationship = selectEntity<Relationship>(state, Entities.RELATIONSHIPS, update.following.id);
|
||||
|
||||
if (update.follower.id === me && relationship) {
|
||||
const updated = {
|
||||
if (update.follower.id === me) {
|
||||
queryClient.setQueryData<Relationship>(['accountRelationships', update.following.id], (relationship) => relationship ? ({
|
||||
...relationship,
|
||||
...followStateToRelationship(update.state),
|
||||
};
|
||||
|
||||
// Add a small delay to deal with API race conditions.
|
||||
setTimeout(() => dispatch(importEntities([updated], Entities.RELATIONSHIPS)), 300);
|
||||
}) : undefined);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GroupMember, GroupRelationship, Relationship } from 'pl-api';
|
||||
import type { GroupMember, GroupRelationship } from 'pl-api';
|
||||
import type { Account } from 'pl-fe/normalizers/account';
|
||||
import type { Group } from 'pl-fe/normalizers/group';
|
||||
|
||||
@@ -7,7 +7,6 @@ enum Entities {
|
||||
GROUPS = 'Groups',
|
||||
GROUP_MEMBERSHIPS = 'GroupMemberships',
|
||||
GROUP_RELATIONSHIPS = 'GroupRelationships',
|
||||
RELATIONSHIPS = 'Relationships',
|
||||
}
|
||||
|
||||
interface EntityTypes {
|
||||
@@ -15,7 +14,6 @@ interface EntityTypes {
|
||||
[Entities.GROUPS]: Group;
|
||||
[Entities.GROUP_MEMBERSHIPS]: GroupMember;
|
||||
[Entities.GROUP_RELATIONSHIPS]: GroupRelationship;
|
||||
[Entities.RELATIONSHIPS]: Relationship;
|
||||
}
|
||||
|
||||
export { Entities, type EntityTypes };
|
||||
|
||||
@@ -9,7 +9,6 @@ import * as v from 'valibot';
|
||||
import { biteAccount, blockAccount, pinAccount, removeFromFollowers, unblockAccount, unmuteAccount, unpinAccount } from 'pl-fe/actions/accounts';
|
||||
import { mentionCompose, directCompose } from 'pl-fe/actions/compose';
|
||||
import { initReport, ReportableEntities } from 'pl-fe/actions/reports';
|
||||
import { useFollow } from 'pl-fe/api/hooks/accounts/use-follow';
|
||||
import Account from 'pl-fe/components/account';
|
||||
import AltIndicator from 'pl-fe/components/alt-indicator';
|
||||
import Badge from 'pl-fe/components/badge';
|
||||
@@ -30,6 +29,7 @@ import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
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';
|
||||
import { useFollowMutation } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import { useChats } from 'pl-fe/queries/chats';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { blockDomainMutationOptions, unblockDomainMutationOptions } from 'pl-fe/queries/settings/domain-blocks';
|
||||
@@ -134,7 +134,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||
|
||||
const features = useFeatures();
|
||||
const { account: ownAccount } = useOwnAccount();
|
||||
const { follow } = useFollow();
|
||||
const { mutate: follow } = useFollowMutation(account?.id!);
|
||||
const { openModal } = useModalsActions();
|
||||
const settings = useSettings();
|
||||
|
||||
@@ -207,9 +207,9 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||
|
||||
const onReblogToggle = () => {
|
||||
if (account.relationship?.showing_reblogs) {
|
||||
follow(account.id, { reblogs: false });
|
||||
follow({ reblogs: false });
|
||||
} else {
|
||||
follow(account.id, { reblogs: true });
|
||||
follow({ reblogs: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState } from 'react';
|
||||
import { defineMessages, IntlShape, useIntl } from 'react-intl';
|
||||
|
||||
import { unblockAccount } from 'pl-fe/actions/accounts';
|
||||
import { useRelationship } from 'pl-fe/api/hooks/accounts/use-relationship';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import Combobox, { ComboboxInput, ComboboxList, ComboboxOption, ComboboxPopover } from 'pl-fe/components/ui/combobox';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
@@ -14,6 +13,7 @@ import UploadButton from 'pl-fe/features/compose/components/upload-button';
|
||||
import emojiSearch from 'pl-fe/features/emoji/search';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useInstance } from 'pl-fe/hooks/use-instance';
|
||||
import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import { useModalsActions } from 'pl-fe/stores/modals';
|
||||
import { textAtCursorMatchesToken } from 'pl-fe/utils/suggestions';
|
||||
|
||||
@@ -80,7 +80,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
|
||||
|
||||
const { openModal } = useModalsActions();
|
||||
const { chat } = useChatContext();
|
||||
const { relationship } = useRelationship(chat?.account.id, { enabled: !!chat });
|
||||
const { data: relationship } = useRelationshipQuery(chat?.account.id);
|
||||
|
||||
const isBlocked = relationship?.blocked_by && false;
|
||||
const isBlocking = relationship?.blocking && false;
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { useRelationship } from 'pl-fe/api/hooks/accounts/use-relationship';
|
||||
import DropdownMenu from 'pl-fe/components/dropdown-menu';
|
||||
import { ParsedContent } from 'pl-fe/components/parsed-content';
|
||||
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
|
||||
@@ -14,6 +13,7 @@ import Text from 'pl-fe/components/ui/text';
|
||||
import VerificationBadge from 'pl-fe/components/verification-badge';
|
||||
import { useChatContext } from 'pl-fe/contexts/chat-context';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import { useChatActions } from 'pl-fe/queries/chats';
|
||||
import { useModalsActions } from 'pl-fe/stores/modals';
|
||||
|
||||
@@ -41,7 +41,7 @@ const ChatListItem: React.FC<IChatListItemInterface> = ({ chat, onClick }) => {
|
||||
|
||||
const { isUsingMainChatPage } = useChatContext();
|
||||
const { deleteChat } = useChatActions(chat?.id as string);
|
||||
const { relationship } = useRelationship(chat?.account.id, { enabled: !!chat });
|
||||
const { data: relationship } = useRelationshipQuery(chat?.account.id);
|
||||
|
||||
const isBlocked = relationship?.blocked_by && false;
|
||||
const isBlocking = relationship?.blocking && false;
|
||||
|
||||
@@ -8,14 +8,13 @@ import Divider from 'pl-fe/components/ui/divider';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import PlaceholderChatMessage from 'pl-fe/features/placeholder/components/placeholder-chat-message';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import { useChatActions, useChatMessages } from 'pl-fe/queries/chats';
|
||||
|
||||
import ChatMessage from './chat-message';
|
||||
|
||||
import type { Chat, Relationship } from 'pl-api';
|
||||
import type { Chat } from 'pl-api';
|
||||
import type { ChatMessage as ChatMessageEntity } from 'pl-fe/normalizers/chat-message';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -86,9 +85,9 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat }) => {
|
||||
refetch,
|
||||
} = useChatMessages(chat);
|
||||
|
||||
const formattedChatMessages = chatMessages || [];
|
||||
const isBlocked = !!useRelationshipQuery(chat.account.id).data?.blocked_by;
|
||||
|
||||
const isBlocked = !!useAppSelector((state) => (state.entities[Entities.RELATIONSHIPS]?.store[chat.account.id] as Relationship)?.blocked_by);
|
||||
const formattedChatMessages = chatMessages || [];
|
||||
|
||||
const lastChatMessage = chatMessages ? chatMessages[chatMessages.length - 1] : null;
|
||||
|
||||
|
||||
@@ -11,10 +11,9 @@ import Stack from 'pl-fe/components/ui/stack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import VerificationBadge from 'pl-fe/components/verification-badge';
|
||||
import { useChatContext } from 'pl-fe/contexts/chat-context';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
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 { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import { useChat, useChatActions, useChats } from 'pl-fe/queries/chats';
|
||||
import { useModalsActions } from 'pl-fe/stores/modals';
|
||||
|
||||
@@ -23,8 +22,6 @@ import Chat from '../../chat';
|
||||
import BlankslateEmpty from './blankslate-empty';
|
||||
import BlankslateWithChats from './blankslate-with-chats';
|
||||
|
||||
import type { Relationship } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
blockMessage: { id: 'chat_settings.block.message', defaultMessage: 'Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.' },
|
||||
blockHeading: { id: 'chat_settings.block.heading', defaultMessage: 'Block @{acct}' },
|
||||
@@ -57,7 +54,7 @@ const ChatPageMain = () => {
|
||||
|
||||
const { deleteChat } = useChatActions(chat?.id as string);
|
||||
|
||||
const isBlocking = !!useAppSelector((state) => chat?.account?.id && (state.entities[Entities.RELATIONSHIPS]?.store[chat.account.id] as Relationship)?.blocked_by);
|
||||
const isBlocked = !!useRelationshipQuery(chat?.account.id).data?.blocked_by;
|
||||
|
||||
const handleBlockUser = () => {
|
||||
openModal('CONFIRM', {
|
||||
@@ -114,8 +111,8 @@ const ChatPageMain = () => {
|
||||
const menuItems: Menu = [
|
||||
{
|
||||
icon: require('@phosphor-icons/core/regular/prohibit.svg'),
|
||||
text: intl.formatMessage(isBlocking ? messages.unblockUser : messages.blockUser, { acct: chat.account.acct }),
|
||||
action: isBlocking ? handleUnblockUser : handleBlockUser,
|
||||
text: intl.formatMessage(isBlocked ? messages.unblockUser : messages.blockUser, { acct: chat.account.acct }),
|
||||
action: isBlocked ? handleUnblockUser : handleBlockUser,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -8,17 +8,14 @@ import Icon from 'pl-fe/components/ui/icon';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
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 { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import { useChatActions } from 'pl-fe/queries/chats';
|
||||
import { useModalsActions } from 'pl-fe/stores/modals';
|
||||
|
||||
import ChatPaneHeader from './chat-pane-header';
|
||||
|
||||
import type { Relationship } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
blockMessage: { id: 'chat_settings.block.message', defaultMessage: 'Blocking will prevent this profile from direct messaging you and viewing your content. You can unblock later.' },
|
||||
blockHeading: { id: 'chat_settings.block.heading', defaultMessage: 'Block @{acct}' },
|
||||
@@ -44,7 +41,7 @@ const ChatSettings = () => {
|
||||
const { chat, changeScreen, toggleChatPane } = useChatContext();
|
||||
const { deleteChat } = useChatActions(chat?.id as string);
|
||||
|
||||
const isBlocking = !!useAppSelector((state) => chat?.account?.id && (state.entities[Entities.RELATIONSHIPS]?.store[chat.account.id] as Relationship)?.blocked_by);
|
||||
const isBlocked = !!useRelationshipQuery(chat?.account.id).data?.blocked_by;
|
||||
|
||||
const closeSettings = () => {
|
||||
changeScreen(ChatWidgetScreens.CHAT, chat?.id);
|
||||
@@ -121,9 +118,9 @@ const ChatSettings = () => {
|
||||
</HStack>
|
||||
|
||||
<Stack space={5}>
|
||||
<button onClick={isBlocking ? handleUnblockUser : handleBlockUser} className='flex w-full items-center space-x-2 text-sm font-bold text-primary-600 dark:text-accent-blue'>
|
||||
<button onClick={isBlocked ? handleUnblockUser : handleBlockUser} className='flex w-full items-center space-x-2 text-sm font-bold text-primary-600 dark:text-accent-blue'>
|
||||
<Icon src={require('@phosphor-icons/core/regular/prohibit.svg')} className='size-5' />
|
||||
<span>{intl.formatMessage(isBlocking ? messages.unblockUser : messages.blockUser, { acct: chat.account.acct })}</span>
|
||||
<span>{intl.formatMessage(isBlocked ? messages.unblockUser : messages.blockUser, { acct: chat.account.acct })}</span>
|
||||
</button>
|
||||
|
||||
{features.chatsDelete && (
|
||||
|
||||
@@ -5,7 +5,7 @@ import { changeSetting, saveSettings } from 'pl-fe/actions/settings';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useTheme } from 'pl-fe/hooks/use-theme';
|
||||
import { useCustomEmojis } from 'pl-fe/queries/instance/use-custom-emojis';
|
||||
import { useSettings , useSettingsStoreActions } from 'pl-fe/stores/settings';
|
||||
import { useSettings, useSettingsStoreActions } from 'pl-fe/stores/settings';
|
||||
|
||||
import { buildCustomEmojiCategories } from '../../emoji';
|
||||
import { EmojiPicker } from '../../ui/util/async-components';
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
unmuteAccount,
|
||||
biteAccount,
|
||||
} from 'pl-fe/actions/accounts';
|
||||
import { useFollow } from 'pl-fe/api/hooks/accounts/use-follow';
|
||||
import { useRelationship } from 'pl-fe/api/hooks/accounts/use-relationship';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
@@ -17,6 +15,7 @@ import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
import { useAcceptFollowRequestMutation, useRejectFollowRequestMutation } from 'pl-fe/queries/accounts/use-follow-requests';
|
||||
import { useRelationshipQuery, useFollowMutation, useUnfollowMutation } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import { useModalsActions } from 'pl-fe/stores/modals';
|
||||
import toast from 'pl-fe/toast';
|
||||
|
||||
@@ -62,18 +61,20 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small = tr
|
||||
|
||||
const { openModal } = useModalsActions();
|
||||
const { isLoggedIn, me } = useLoggedIn();
|
||||
const { follow, unfollow } = useFollow();
|
||||
|
||||
const { relationship, isLoading } = useRelationship(account.id, { enabled: true });
|
||||
const { mutate: follow, isPending: isPendingFollow } = useFollowMutation(account.id);
|
||||
const { mutate: unfollow, isPending: isPendingUnfollow } = useUnfollowMutation(account.id);
|
||||
|
||||
const { data: relationship, isLoading } = useRelationshipQuery(account.id);
|
||||
|
||||
const { mutate: authorizeFollowRequest } = useAcceptFollowRequestMutation(account.id);
|
||||
const { mutate: rejectFollowRequest } = useRejectFollowRequestMutation(account.id);
|
||||
|
||||
const handleFollow = () => {
|
||||
if (relationship?.following || relationship?.requested) {
|
||||
unfollow(account.id);
|
||||
unfollow();
|
||||
} else {
|
||||
follow(account.id);
|
||||
follow(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,9 +241,9 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small = tr
|
||||
}
|
||||
}
|
||||
|
||||
if (!relationship && !isLoading) return null;
|
||||
|
||||
if (!relationship) {
|
||||
if (!isLoading) return null;
|
||||
// Wait until the relationship is loaded
|
||||
return (
|
||||
<Button
|
||||
size='xs'
|
||||
@@ -267,7 +268,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small = tr
|
||||
return (
|
||||
<Button
|
||||
size='sm'
|
||||
disabled={blockedBy}
|
||||
disabled={blockedBy || isPendingFollow || isPendingUnfollow}
|
||||
theme={isFollowing ? 'secondary' : 'primary'}
|
||||
icon={blockedBy ? require('@phosphor-icons/core/regular/prohibit.svg') : (!isFollowing && require('@phosphor-icons/core/regular/plus.svg'))}
|
||||
onClick={handleFollow}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { useFollow } from 'pl-fe/api/hooks/accounts/use-follow';
|
||||
import IconButton from 'pl-fe/components/ui/icon-button';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useFollowMutation } from 'pl-fe/queries/accounts/use-relationship';
|
||||
import toast from 'pl-fe/toast';
|
||||
|
||||
import type { Account as AccountEntity } from 'pl-fe/normalizers/account';
|
||||
@@ -24,7 +24,7 @@ interface ISubscriptionButton {
|
||||
const SubscriptionButton = ({ account }: ISubscriptionButton) => {
|
||||
const features = useFeatures();
|
||||
const intl = useIntl();
|
||||
const { follow } = useFollow();
|
||||
const { mutate: follow, isPending } = useFollowMutation(account.id);
|
||||
|
||||
const isFollowing = account.relationship?.following;
|
||||
const isRequested = account.relationship?.requested;
|
||||
@@ -35,13 +35,15 @@ const SubscriptionButton = ({ account }: ISubscriptionButton) => {
|
||||
|
||||
const onNotifyToggle = () => {
|
||||
if (account.relationship?.notifying) {
|
||||
follow(account.id, { notify: false })
|
||||
?.then(() => toast.success(intl.formatMessage(messages.unsubscribeSuccess)))
|
||||
.catch(() => toast.error(intl.formatMessage(messages.unsubscribeFailure)));
|
||||
follow({ notify: false }, {
|
||||
onSuccess: () => toast.success(intl.formatMessage(messages.unsubscribeSuccess)),
|
||||
onError: () => toast.error(intl.formatMessage(messages.unsubscribeFailure)),
|
||||
});
|
||||
} else {
|
||||
follow(account.id, { notify: true })
|
||||
?.then(() => toast.success(intl.formatMessage(messages.subscribeSuccess)))
|
||||
.catch(() => toast.error(intl.formatMessage(messages.subscribeFailure)));
|
||||
follow({ notify: true }, {
|
||||
onSuccess: () => toast.success(intl.formatMessage(messages.subscribeSuccess)),
|
||||
onError: () => toast.error(intl.formatMessage(messages.subscribeFailure)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,6 +60,7 @@ const SubscriptionButton = ({ account }: ISubscriptionButton) => {
|
||||
<IconButton
|
||||
src={isSubscribed ? require('@phosphor-icons/core/regular/bell-simple-ringing.svg') : require('@phosphor-icons/core/regular/bell-simple.svg')}
|
||||
onClick={handleToggle}
|
||||
disabled={isPending}
|
||||
title={title}
|
||||
theme='outlined'
|
||||
className='px-2'
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import * as plapi from 'pl-api';
|
||||
import { type Features } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useAppSelector } from './use-app-selector';
|
||||
|
||||
(window as any).v = v;
|
||||
(window as any).plapi = plapi;
|
||||
|
||||
/** Get features for the current instance. */
|
||||
const useFeatures = (): Features => ({ ...useAppSelector(state => state.auth.client.features), filtersV2BlurAction: true });
|
||||
|
||||
|
||||
87
packages/pl-fe/src/queries/accounts/use-relationship.ts
Normal file
87
packages/pl-fe/src/queries/accounts/use-relationship.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
|
||||
import type { FollowAccountParams, Relationship } from 'pl-api';
|
||||
|
||||
const useRelationshipQuery = (accountId?: string) => {
|
||||
const client = useClient();
|
||||
const { isLoggedIn } = useLoggedIn();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['accountRelationships', accountId],
|
||||
queryFn: () => client.accounts.getRelationships([accountId!]).then(arr => arr[0]),
|
||||
enabled: isLoggedIn && !!accountId,
|
||||
});
|
||||
};
|
||||
|
||||
const useFollowMutation = (accountId: string) => {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['accountRelationships', accountId],
|
||||
mutationFn: (params?: FollowAccountParams) => client.accounts.followAccount(accountId, params),
|
||||
onMutate: (params) => {
|
||||
const previousRelationship = queryClient.getQueryData<Relationship>(['accountRelationships', accountId])!;
|
||||
|
||||
if (!previousRelationship) return;
|
||||
|
||||
const newRelationship: Relationship = {
|
||||
...previousRelationship,
|
||||
requested: !previousRelationship.following,
|
||||
notifying: params?.notify ?? previousRelationship.notifying,
|
||||
showing_reblogs: params?.reblogs ?? previousRelationship.showing_reblogs,
|
||||
};
|
||||
|
||||
queryClient.setQueryData(['accountRelationships', accountId], newRelationship);
|
||||
|
||||
return { previousRelationship };
|
||||
},
|
||||
onError: (_err, _variables, context) => {
|
||||
if (context?.previousRelationship) {
|
||||
queryClient.setQueryData(['accountRelationships', accountId], context.previousRelationship);
|
||||
}
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(['accountRelationships', accountId], data);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const useUnfollowMutation = (accountId: string) => {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['accountRelationships', accountId],
|
||||
mutationFn: () => client.accounts.unfollowAccount(accountId),
|
||||
onMutate: () => {
|
||||
const previousRelationship = queryClient.getQueryData<Relationship>(['accountRelationships', accountId])!;
|
||||
|
||||
if (!previousRelationship) return;
|
||||
const newRelationship: Relationship = {
|
||||
...previousRelationship,
|
||||
following: false,
|
||||
requested: false,
|
||||
notifying: false,
|
||||
showing_reblogs: false,
|
||||
};
|
||||
|
||||
queryClient.setQueryData(['accountRelationships', accountId], newRelationship);
|
||||
|
||||
return { previousRelationship };
|
||||
},
|
||||
onError: (_err, _variables, context) => {
|
||||
if (context?.previousRelationship) {
|
||||
queryClient.setQueryData(['accountRelationships', accountId], context.previousRelationship);
|
||||
}
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(['accountRelationships', accountId], data);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export { useRelationshipQuery, useFollowMutation, useUnfollowMutation };
|
||||
@@ -1,14 +1,12 @@
|
||||
import { InfiniteData, keepPreviousData, useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
||||
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 } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { importEntities } from 'pl-fe/actions/importer';
|
||||
import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context';
|
||||
import { useStatContext } from 'pl-fe/contexts/stat-context';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
@@ -17,6 +15,7 @@ import { type ChatMessage, normalizeChatMessage } from 'pl-fe/normalizers/chat-m
|
||||
import { reOrderChatListItems } from 'pl-fe/utils/chats';
|
||||
import { flattenPages, updatePageItem } from 'pl-fe/utils/queries';
|
||||
|
||||
import { useRelationshipQuery } from './accounts/use-relationship';
|
||||
import { queryClient } from './client';
|
||||
import { useFetchRelationships } from './relationships';
|
||||
|
||||
@@ -27,7 +26,7 @@ const ChatKeys = {
|
||||
|
||||
const useChatMessages = (chat: Chat) => {
|
||||
const client = useClient();
|
||||
const isBlocked = useAppSelector((state) => (state.entities[Entities.RELATIONSHIPS]?.store[chat.account.id] as Relationship)?.blocked_by);
|
||||
const isBlocked = !!useRelationshipQuery(chat?.account.id).data?.blocked_by;
|
||||
|
||||
const getChatMessages = async (chatId: string, pageParam?: Pick<PaginatedResponse<BaseChatMessage>, 'next'>) => {
|
||||
const response = await (pageParam?.next ? pageParam.next() : client.chats.getChatMessages(chatId));
|
||||
|
||||
Reference in New Issue
Block a user