Mor component refactoring

v2-context
Alex Gleason 2023-05-23 16:05:56 -05:00
rodzic fe0b5d9109
commit 811063c283
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
32 zmienionych plików z 134 dodań i 143 usunięć

Wyświetl plik

@ -10,7 +10,7 @@ export const useAccount = (accountId: string, refetch = false) => {
const { entity: account, ...result } = useEntity(
[Entities.ACCOUNTS, accountId],
() => api.get(`/api/v1/accounts/${accountId}`),
{ schema: accountSchema, refetch },
{ schema: accountSchema, refetch, enabled: !!accountId },
);
const { relationship } = useRelationship(accountId);

Wyświetl plik

@ -14,11 +14,10 @@ import RelativeTimestamp from './relative-timestamp';
import { Avatar, Emoji, HStack, Icon, IconButton, Stack, Text } from './ui';
import type { StatusApprovalStatus } from 'soapbox/normalizers/status';
import type { Account as AccountSchema } from 'soapbox/schemas';
import type { Account as AccountEntity } from 'soapbox/types/entities';
import type { Account as AccountEntity } from 'soapbox/schemas';
interface IInstanceFavicon {
account: AccountEntity | AccountSchema
account: AccountEntity
disabled?: boolean
}
@ -68,7 +67,7 @@ const ProfilePopper: React.FC<IProfilePopper> = ({ condition, wrapper, children
};
export interface IAccount {
account: AccountEntity | AccountSchema
account: AccountEntity
action?: React.ReactElement
actionAlignment?: 'center' | 'top'
actionIcon?: string

Wyświetl plik

@ -96,7 +96,7 @@ const SoapboxMount = () => {
const features = useFeatures();
const { pepeEnabled } = useRegistrationStatus();
const waitlisted = account && !account.source.get('approved', true);
const waitlisted = account && !(account.source?.approved ?? true);
const needsOnboarding = useAppSelector(state => state.onboarding.needsOnboarding);
const showOnboarding = account && !waitlisted && needsOnboarding;
const { redirectRootNoLogin } = soapboxConfig;

Wyświetl plik

@ -19,7 +19,7 @@ const ChatPage: React.FC<IChatPage> = ({ chatId }) => {
const account = useOwnAccount();
const history = useHistory();
const isOnboarded = account?.chats_onboarded;
const isOnboarded = account?.source?.chats_onboarded !== false;
const path = history.location.pathname;
const isSidebarHidden = matchPath(path, {

Wyświetl plik

@ -33,7 +33,7 @@ const ChatPageSettings = () => {
const [data, setData] = useState<FormData>({
chats_onboarded: true,
accepts_chat_messages: account?.accepts_chat_messages,
accepts_chat_messages: account?.pleroma?.accepts_chat_messages,
});
const onToggleChange = (key: string[], checked: boolean) => {

Wyświetl plik

@ -13,7 +13,7 @@ const ChatWidget = () => {
const path = history.location.pathname;
const shouldHideWidget = Boolean(path.match(/^\/chats/));
if (!account?.chats_onboarded || shouldHideWidget) {
if (account?.source?.chats_onboarded === false || shouldHideWidget) {
return null;
}

Wyświetl plik

@ -1,21 +1,17 @@
import React, { useCallback } from 'react';
import React from 'react';
import { useAccount } from 'soapbox/api/hooks';
import Account from 'soapbox/components/account';
import { useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
interface IAutosuggestAccount {
id: string
}
const AutosuggestAccount: React.FC<IAutosuggestAccount> = ({ id }) => {
const getAccount = useCallback(makeGetAccount(), []);
const account = useAppSelector((state) => getAccount(state, id));
const { account } = useAccount(id);
if (!account) return null;
return <Account account={account} hideActions showProfileHoverCard={false} />;
};
export default AutosuggestAccount;

Wyświetl plik

@ -46,7 +46,6 @@ const Conversation: React.FC<IConversation> = ({ conversationId, onMoveUp, onMov
}
return (
// @ts-ignore
<StatusContainer
id={lastStatusId}
unread={unread}

Wyświetl plik

@ -3,29 +3,27 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
import { getSettings } from 'soapbox/actions/settings';
import { useAccount } from 'soapbox/api/hooks';
import Account from 'soapbox/components/account';
import Badge from 'soapbox/components/badge';
import RelativeTimestamp from 'soapbox/components/relative-timestamp';
import { Stack, Text } from 'soapbox/components/ui';
import ActionButton from 'soapbox/features/ui/components/action-button';
import { useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
import { shortNumberFormat } from 'soapbox/utils/numbers';
const getAccount = makeGetAccount();
interface IAccountCard {
id: string
}
const AccountCard: React.FC<IAccountCard> = ({ id }) => {
const me = useAppSelector((state) => state.me);
const account = useAppSelector((state) => getAccount(state, id));
const { account, relationship } = useAccount(id);
const autoPlayGif = useAppSelector((state) => getSettings(state).get('autoPlayGif'));
if (!account) return null;
const followedBy = me !== account.id && account.relationship?.followed_by;
const followedBy = me !== account.id && relationship?.followed_by;
return (
<div className='flex flex-col divide-y divide-gray-200 rounded-lg bg-white text-center shadow dark:divide-primary-700 dark:bg-primary-800'>
@ -39,9 +37,11 @@ const AccountCard: React.FC<IAccountCard> = ({ id }) => {
</div>
)}
<div className='absolute bottom-2.5 right-2.5'>
<ActionButton account={account} small />
</div>
{relationship && (
<div className='absolute bottom-2.5 right-2.5'>
<ActionButton account={account} relationship={relationship} small />
</div>
)}
<img
src={autoPlayGif ? account.header : account.header_static}
@ -87,10 +87,10 @@ const AccountCard: React.FC<IAccountCard> = ({ id }) => {
<Stack>
<Text theme='primary' size='md' weight='medium'>
{account.last_status_at === null ? (
<FormattedMessage id='account.never_active' defaultMessage='Never' />
) : (
{account.last_status_at ? (
<RelativeTimestamp theme='inherit' timestamp={account.last_status_at} />
) : (
<FormattedMessage id='account.never_active' defaultMessage='Never' />
)}
</Text>

Wyświetl plik

@ -1,4 +1,3 @@
import { List as ImmutableList } from 'immutable';
import React, { useState, useEffect, useMemo } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
@ -20,7 +19,7 @@ import {
Toggle,
} from 'soapbox/components/ui';
import { useAppDispatch, useOwnAccount, useFeatures, useInstance } from 'soapbox/hooks';
import { normalizeAccount } from 'soapbox/normalizers';
import { accountSchema } from 'soapbox/schemas';
import toast from 'soapbox/toast';
import resizeImage from 'soapbox/utils/resize-image';
@ -34,7 +33,8 @@ import type { Account } from 'soapbox/types/entities';
* Pleroma's config is granular, but we simplify it into one setting.
*/
const hidesNetwork = (account: Account): boolean => {
const { hide_followers, hide_follows, hide_followers_count, hide_follows_count } = account.pleroma.toJS();
if (!account.pleroma) return false;
const { hide_followers, hide_follows, hide_followers_count, hide_follows_count } = account.pleroma;
return Boolean(hide_followers && hide_follows && hide_followers_count && hide_follows_count);
};
@ -124,18 +124,18 @@ const accountToCredentials = (account: Account): AccountCredentials => {
discoverable: account.discoverable,
bot: account.bot,
display_name: account.display_name,
note: account.source.get('note', ''),
note: account.source?.note || '',
locked: account.locked,
fields_attributes: [...account.source.get<Iterable<AccountCredentialsField>>('fields', ImmutableList()).toJS()],
stranger_notifications: account.getIn(['pleroma', 'notification_settings', 'block_from_strangers']) === true,
accepts_email_list: account.getIn(['pleroma', 'accepts_email_list']) === true,
fields_attributes: account.source?.fields?.map(({ name, value }) => ({ name, value })) ?? [],
stranger_notifications: account.pleroma?.notification_settings?.block_from_strangers === true,
accepts_email_list: account.pleroma?.accepts_email_list === true,
hide_followers: hideNetwork,
hide_follows: hideNetwork,
hide_followers_count: hideNetwork,
hide_follows_count: hideNetwork,
website: account.website,
location: account.location,
birthday: account.birthday,
birthday: account.pleroma?.birthday || undefined,
};
};
@ -185,7 +185,7 @@ const EditProfile: React.FC = () => {
useEffect(() => {
if (account) {
const credentials = accountToCredentials(account);
const strangerNotifications = account.getIn(['pleroma', 'notification_settings', 'block_from_strangers']) === true;
const strangerNotifications = account.pleroma?.notification_settings?.block_from_strangers === true;
setData(credentials);
setMuteStrangers(strangerNotifications);
}
@ -298,13 +298,13 @@ const EditProfile: React.FC = () => {
}, [data.header, account?.header]);
/** Preview account data. */
const previewAccount = useMemo(() => {
return normalizeAccount({
...account?.toJS(),
const previewAccount = useMemo((): Account => {
return accountSchema.parse({
...account,
...data,
avatar: avatarUrl,
header: headerUrl,
}) as Account;
});
}, [account?.id, data.display_name, avatarUrl, headerUrl]);
return (

Wyświetl plik

@ -275,7 +275,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
icon: require('@tabler/icons/at.svg'),
});
if (status.getIn(['account', 'pleroma', 'accepts_chat_messages']) === true) {
if (status.account?.pleroma?.accepts_chat_messages) {
menu.push({
text: intl.formatMessage(messages.chat, { name: username }),
action: handleChatClick,
@ -467,7 +467,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
<HStack alignItems='center' space={2}>
<Icon src={require('@tabler/icons/map-pin.svg')} />
<span>
{event.location.get('name')}
{event.location.name}
</span>
</HStack>
)}

Wyświetl plik

@ -67,14 +67,14 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
<HStack space={2} alignItems='center'>
<Icon src={require('@tabler/icons/map-pin.svg')} />
<Text>
{event.location.get('name')}
{event.location.name}
<br />
{!!event.location.get('street')?.trim() && (<>
{event.location.get('street')}
{!!event.location.street?.trim() && (<>
{event.location.street}
<br />
</>)}
{[event.location.get('postalCode'), event.location.get('locality'), event.location.get('country')].filter(text => text).join(', ')}
{tileServer && event.location.get('latitude') && (<>
{[event.location.postal_code, event.location.locality, event.location.country].filter(text => text).join(', ')}
{tileServer && event.location.latitude && (<>
<br />
<a href='#' className='text-primary-600 hover:underline dark:text-accent-blue' onClick={handleShowMap}>
<FormattedMessage id='event.show_on_map' defaultMessage='Show on map' />
@ -132,7 +132,7 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
}, [status]);
const renderLinks = useCallback(() => {
if (!status.event?.links.size) return null;
if (!status.event?.links?.length) return null;
return (
<Stack space={1}>
@ -178,8 +178,8 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
onToggleVisibility={handleToggleMediaVisibility}
/>
{status.quote && status.pleroma.get('quote_visible', true) && (
<QuotedStatus statusId={status.quote as string} />
{status.quote && status.pleroma?.quote_visible !== false && (
<QuotedStatus status={status.quote} />
)}
{renderEventLocation()}

Wyświetl plik

@ -3,21 +3,23 @@ import { defineMessages, useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import VerificationBadge from 'soapbox/components/verification-badge';
import { useAccount, useAppSelector } from 'soapbox/hooks';
import { useAppSelector } from 'soapbox/hooks';
import { Card, CardBody, CardTitle, HStack, Stack, Text } from '../../components/ui';
import ActionButton from '../ui/components/action-button';
import type { Account } from 'soapbox/types/entities';
import type { Account } from 'soapbox/schemas';
const messages = defineMessages({
heading: { id: 'feed_suggestions.heading', defaultMessage: 'Suggested Profiles' },
viewAll: { id: 'feed_suggestions.view_all', defaultMessage: 'View all' },
});
const SuggestionItem = ({ accountId }: { accountId: string }) => {
const account = useAccount(accountId) as Account;
interface ISuggestionItem {
account: Account
}
const SuggestionItem: React.FC<ISuggestionItem> = ({ account }) => {
return (
<Stack space={3} className='w-52 shrink-0 rounded-md border border-solid border-gray-300 p-4 dark:border-gray-800 md:w-full md:shrink md:border-transparent md:p-0 dark:md:border-transparent'>
<Link

Wyświetl plik

@ -1,10 +1,10 @@
import React, { useCallback } from 'react';
import React from 'react';
import { authorizeFollowRequest, rejectFollowRequest } from 'soapbox/actions/accounts';
import { useAccount } from 'soapbox/api/hooks';
import Account from 'soapbox/components/account';
import { AuthorizeRejectButtons } from 'soapbox/components/authorize-reject-buttons';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
import { useAppDispatch } from 'soapbox/hooks';
interface IAccountAuthorize {
id: string
@ -12,9 +12,7 @@ interface IAccountAuthorize {
const AccountAuthorize: React.FC<IAccountAuthorize> = ({ id }) => {
const dispatch = useAppDispatch();
const getAccount = useCallback(makeGetAccount(), []);
const account = useAppSelector((state) => getAccount(state, id));
const { account } = useAccount(id);
const onAuthorize = () => dispatch(authorizeFollowRequest(id));
const onReject = () => dispatch(rejectFollowRequest(id));

Wyświetl plik

@ -18,7 +18,7 @@ const BioStep = ({ onNext }: { onNext: () => void }) => {
const dispatch = useAppDispatch();
const account = useOwnAccount();
const [value, setValue] = React.useState<string>(account?.source.get('note') || '');
const [value, setValue] = React.useState<string>(account?.source?.note || '');
const [isSubmitting, setSubmitting] = React.useState<boolean>(false);
const [errors, setErrors] = React.useState<string[]>([]);

Wyświetl plik

@ -53,14 +53,14 @@ const ScheduledStatus: React.FC<IScheduledStatus> = ({ statusId, ...other }) =>
collapsable
/>
{status.media_attachments.size > 0 && (
{status.media_attachments.length > 0 && (
<AttachmentThumbs
media={status.media_attachments}
sensitive={status.sensitive}
/>
)}
{status.poll && <PollPreview pollId={status.poll as string} />}
{status.poll && <PollPreview poll={status.poll} />}
<ScheduledStatusActionBar status={status} {...other} />
</div>

Wyświetl plik

@ -29,7 +29,7 @@ const MessagesSettings = () => {
label={intl.formatMessage(messages.label)}
>
<Toggle
checked={account.accepts_chat_messages}
checked={account.pleroma.accepts_chat_messages}
onChange={handleChange}
/>
</ListItem>

Wyświetl plik

@ -95,14 +95,14 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
let quote;
if (actualStatus.quote) {
if (actualStatus.pleroma.get('quote_visible', true) === false) {
if (actualStatus.pleroma?.quote_visible === false) {
quote = (
<div className='quoted-actualStatus-tombstone'>
<p><FormattedMessage id='actualStatuses.quote_tombstone' defaultMessage='Post is unavailable.' /></p>
</div>
);
} else {
quote = <QuotedStatus statusId={actualStatus.quote as string} />;
quote = <QuotedStatus status={actualStatus.quote} />;
}
}
@ -151,7 +151,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
<TranslateButton status={actualStatus} />
{(quote || actualStatus.card || actualStatus.media_attachments.size > 0) && (
{(quote || actualStatus.card || actualStatus.media_attachments.length > 0) && (
<Stack space={4}>
<StatusMedia
status={actualStatus}

Wyświetl plik

@ -1,5 +1,4 @@
import clsx from 'clsx';
import { List as ImmutableList } from 'immutable';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
@ -62,10 +61,10 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
const getNormalizedReacts = () => {
return reduceEmoji(
ImmutableList(status.pleroma.get('emoji_reactions') as any),
status.pleroma?.emoji_reactions || [],
status.favourites_count,
status.favourited,
allowedEmoji,
allowedEmoji.toArray(),
);
};
@ -95,7 +94,7 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
const navigateToQuotes: React.EventHandler<React.MouseEvent> = (e) => {
e.preventDefault();
history.push(`/@${status.getIn(['account', 'acct'])}/posts/${status.id}/quotes`);
history.push(`/@${status.account.acct}/posts/${status.id}/quotes`);
};
const getQuotes = () => {
@ -173,20 +172,20 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
const getEmojiReacts = () => {
const emojiReacts = getNormalizedReacts();
const count = emojiReacts.reduce((acc, cur) => (
acc + cur.get('count')
acc + (cur.count || 0)
), 0);
if (count) {
return (
<InteractionCounter count={count} onClick={features.exposableReactions ? handleOpenReactionsModal : undefined}>
<HStack space={0.5} alignItems='center'>
{emojiReacts.take(3).map((e, i) => {
{emojiReacts.slice(0, 3).map(({ name, url }, i) => {
return (
<Emoji
key={i}
className='h-4.5 w-4.5 flex-none'
emoji={e.get('name')}
src={e.get('url')}
emoji={name}
src={url}
/>
);
})}

Wyświetl plik

@ -74,7 +74,7 @@ const CompareHistoryModal: React.FC<ICompareHistoryModal> = ({ onClose, statusId
</div>
)}
{version.media_attachments.size > 0 && (
{version.media_attachments.length > 0 && (
<AttachmentThumbs media={version.media_attachments} />
)}

Wyświetl plik

@ -31,12 +31,12 @@ const EventMapModal: React.FC<IEventMapModal> = ({ onClose, statusId }) => {
const map = useRef<L.Map>();
useEffect(() => {
const latlng: [number, number] = [+location.get('latitude'), +location.get('longitude')];
const latlng: [number, number] = [+location.latitude, +location.longitude];
map.current = L.map('event-map').setView(latlng, 15);
L.marker(latlng, {
title: location.get('name'),
title: location.name,
}).addTo(map.current);
L.tileLayer(tileServer, {
@ -53,7 +53,7 @@ const EventMapModal: React.FC<IEventMapModal> = ({ onClose, statusId }) => {
};
const onClickNavigate = () => {
window.open(`https://www.openstreetmap.org/directions?from=&to=${location.get('latitude')},${location.get('longitude')}#map=14/${location.get('latitude')}/${location.get('longitude')}`, '_blank');
window.open(`https://www.openstreetmap.org/directions?from=&to=${location.latitude},${location.longitude}#map=14/${location.latitude}/${location.longitude}`, '_blank');
};
return (

Wyświetl plik

@ -14,7 +14,7 @@ import { useAppDispatch } from 'soapbox/hooks';
import ImageLoader from '../image-loader';
import type { List as ImmutableList } from 'immutable';
import type { Attachment, Status } from 'soapbox/types/entities';
import type { Attachment, Status } from 'soapbox/schemas';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
@ -161,9 +161,6 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
const isMultiMedia = media.map((image) => image.type !== 'image').toArray();
const content = media.map((attachment, i) => {
const width = (attachment.meta.getIn(['original', 'width']) || undefined) as number | undefined;
const height = (attachment.meta.getIn(['original', 'height']) || undefined) as number | undefined;
const link = (status && (
<a href={status.url} onClick={handleStatusClick}>
<FormattedMessage id='lightbox.view_context' defaultMessage='View context' />
@ -175,8 +172,8 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
<ImageLoader
previewSrc={attachment.preview_url}
src={attachment.url}
width={width}
height={height}
width={attachment.meta?.original?.width}
height={attachment.meta?.original?.height}
alt={attachment.description}
key={attachment.url}
onClick={toggleNavigation}
@ -188,8 +185,8 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
preview={attachment.preview_url}
blurhash={attachment.blurhash}
src={attachment.url}
width={width}
height={height}
width={attachment.meta?.original?.width}
height={attachment.meta?.original?.height}
startTime={time}
detailed
autoFocus={i === getIndex()}
@ -204,11 +201,11 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
<Audio
src={attachment.url}
alt={attachment.description}
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : (status?.getIn(['account', 'avatar_static'])) as string | undefined}
backgroundColor={attachment.meta.getIn(['colors', 'background']) as string | undefined}
foregroundColor={attachment.meta.getIn(['colors', 'foreground']) as string | undefined}
accentColor={attachment.meta.getIn(['colors', 'accent']) as string | undefined}
duration={attachment.meta.getIn(['original', 'duration'], 0) as number | undefined}
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : status?.account.avatar_static}
backgroundColor={attachment.meta?.colors?.background}
foregroundColor={attachment.meta?.colors?.foreground}
accentColor={attachment.meta?.colors?.accent}
duration={attachment.meta?.duration || 0}
key={attachment.url}
/>
);
@ -218,8 +215,8 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
src={attachment.url}
muted
controls={false}
width={width}
height={height}
width={attachment.meta?.original?.width}
height={attachment.meta?.original?.height}
key={attachment.preview_url}
alt={attachment.description}
onClick={toggleNavigation}

Wyświetl plik

@ -13,7 +13,7 @@ import { makeGetAccount } from 'soapbox/selectors';
import ThemeToggle from './theme-toggle';
import type { Account as AccountEntity } from 'soapbox/types/entities';
import type { Account as AccountEntity } from 'soapbox/schemas';
const messages = defineMessages({
add: { id: 'profile_dropdown.add_account', defaultMessage: 'Add an existing account' },
@ -71,7 +71,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
menu.push({ text: renderAccount(account), to: `/@${account.acct}` });
otherAccounts.forEach((otherAccount: AccountEntity) => {
otherAccounts.forEach((otherAccount) => {
if (otherAccount && otherAccount.id !== account.id) {
menu.push({
text: renderAccount(otherAccount),

Wyświetl plik

@ -13,8 +13,8 @@ const WaitlistPage = () => {
const dispatch = useAppDispatch();
const instance = useInstance();
const me = useOwnAccount();
const isSmsVerified = me?.source.get('sms_verified');
const account = useOwnAccount();
const isSmsVerified = account?.source?.sms_verified;
const onClickLogOut: React.MouseEventHandler = (event) => {
event.preventDefault();

Wyświetl plik

@ -1,21 +1,11 @@
import { useCallback } from 'react';
import { useAccount } from 'soapbox/api/hooks';
import { useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
import type { Account } from 'soapbox/types/entities';
/** Get the logged-in account from the store, if any. */
export const useOwnAccount = (): Account | null => {
const getAccount = useCallback(makeGetAccount(), []);
return useAppSelector((state) => {
const { me } = state;
if (typeof me === 'string') {
return getAccount(state, me);
} else {
return null;
}
});
export const useOwnAccount = (): Account | undefined => {
const accountId = useAppSelector((state) => state.me || '');
const { account } = useAccount(accountId);
return account;
};

Wyświetl plik

@ -41,7 +41,7 @@ const useUpdateCredentials = () => {
return useMutation((data: UpdateCredentialsData) => api.patch('/api/v1/accounts/update_credentials', data), {
onMutate(variables) {
const cachedAccount = account?.toJS();
const cachedAccount = account;
dispatch(patchMeSuccess({ ...cachedAccount, ...variables }));
return { cachedAccount };

Wyświetl plik

@ -273,7 +273,7 @@ const deleteToken = (state: State, token: string) => {
};
const deleteUser = (state: State, account: AccountEntity) => {
const accountUrl = account.get('url');
const accountUrl = account.url;
return state.withMutations(state => {
state.update('users', users => users.delete(accountUrl));

Wyświetl plik

@ -128,7 +128,7 @@ export const statusToMentionsArray = (status: ImmutableMap<string, any>, account
return ImmutableOrderedSet([author])
.concat(mentions)
.delete(account.get('acct')) as ImmutableOrderedSet<string>;
.delete(account.acct) as ImmutableOrderedSet<string>;
};
export const statusToMentionsAccountIdsArray = (status: StatusEntity, account: AccountEntity) => {

Wyświetl plik

@ -51,21 +51,33 @@ const baseAccountSchema = z.object({
}).optional().catch(undefined),
pleroma: z.object({
accepts_chat_messages: z.boolean().catch(false),
accepts_email_list: z.boolean().catch(false),
birthday: birthdaySchema.nullish().catch(undefined),
deactivated: z.boolean().catch(false),
favicon: z.string().url().optional().catch(undefined),
hide_favorites: z.boolean().catch(false),
hide_followers: z.boolean().catch(false),
hide_followers_count: z.boolean().catch(false),
hide_follows: z.boolean().catch(false),
hide_follows_count: z.boolean().catch(false),
is_admin: z.boolean().catch(false),
is_moderator: z.boolean().catch(false),
is_suggested: z.boolean().catch(false),
favicon: z.string().url().optional().catch(undefined),
location: z.string().optional().catch(undefined),
notification_settings: z.object({
block_from_strangers: z.boolean().catch(false),
}).optional().catch(undefined),
tags: z.array(z.string()).catch([]),
}).optional().catch(undefined),
source: z.object({
approved: z.boolean().catch(true),
chats_onboarded: z.boolean().catch(true),
fields: filteredArray(fieldSchema),
note: z.string().catch(''),
pleroma: z.object({
discoverable: z.boolean().catch(true),
}).optional().catch(undefined),
sms_verified: z.boolean().catch(false),
}).optional().catch(undefined),
statuses_count: z.number().catch(0),
suspended: z.boolean().catch(false),
@ -79,6 +91,14 @@ const baseAccountSchema = z.object({
type BaseAccount = z.infer<typeof baseAccountSchema>;
type TransformableAccount = Omit<BaseAccount, 'moved'>;
const getDomain = (url: string) => {
try {
return new URL(url).host;
} catch (e) {
return '';
}
};
/** Add internal fields to the account. */
const transformAccount = <T extends TransformableAccount>({ pleroma, other_settings, fields, ...account }: T) => {
const customEmojiMap = makeCustomEmojiMap(account.emojis);
@ -90,6 +110,12 @@ const transformAccount = <T extends TransformableAccount>({ pleroma, other_setti
value_plain: unescapeHTML(field.value),
}));
const domain = getDomain(account.url || account.uri);
if (pleroma) {
pleroma.birthday = pleroma.birthday || other_settings?.birthday;
}
return {
...account,
admin: pleroma?.is_admin || false,
@ -97,19 +123,15 @@ const transformAccount = <T extends TransformableAccount>({ pleroma, other_setti
discoverable: account.discoverable || account.source?.pleroma?.discoverable || false,
display_name: account.display_name.trim().length === 0 ? account.username : account.display_name,
display_name_html: emojify(escapeTextContentForBrowser(account.display_name), customEmojiMap),
domain,
fields: newFields,
fqn: account.fqn || (account.acct.includes('@') ? account.acct : `${account.acct}@${new URL(account.url).host}`),
fqn: account.fqn || (account.acct.includes('@') ? account.acct : `${account.acct}@${domain}`),
header_static: account.header_static || account.header,
moderator: pleroma?.is_moderator || false,
location: account.location || pleroma?.location || other_settings?.location || '',
note_emojified: emojify(account.note, customEmojiMap),
pleroma: {
accepts_chat_messages: pleroma?.accepts_chat_messages || false,
birthday: pleroma?.birthday || other_settings?.birthday,
hide_favorites: pleroma?.hide_favorites || false,
is_suggested: pleroma?.is_suggested || false,
tags: pleroma?.tags || [],
},
pleroma,
staff: pleroma?.is_admin || pleroma?.is_moderator || false,
suspended: account.suspended || pleroma?.deactivated || false,
verified: account.verified || pleroma?.tags.includes('verified') || false,
};

Wyświetl plik

@ -12,6 +12,9 @@ const locationSchema = z.object({
origin_provider: z.string().catch(''),
type: z.string().catch(''),
timezone: z.string().catch(''),
name: z.string().catch(''),
latitude: z.number().catch(0),
longitude: z.number().catch(0),
geom: z.object({
coordinates: z.tuple([z.number(), z.number()]).nullable().catch(null),
srid: z.string().catch(''),

Wyświetl plik

@ -1,7 +1,6 @@
import {
AdminAccountRecord,
AdminReportRecord,
AccountRecord,
AnnouncementRecord,
AnnouncementReactionRecord,
AttachmentRecord,
@ -19,7 +18,6 @@ import {
MentionRecord,
NotificationRecord,
StatusEditRecord,
StatusRecord,
TagRecord,
} from 'soapbox/normalizers';
import { LogEntryRecord } from 'soapbox/reducers/admin-log';
@ -48,18 +46,6 @@ type Notification = ReturnType<typeof NotificationRecord>;
type StatusEdit = ReturnType<typeof StatusEditRecord>;
type Tag = ReturnType<typeof TagRecord>;
interface Account extends ReturnType<typeof AccountRecord> {
// HACK: we can't do a circular reference in the Record definition itself,
// so do it here.
moved: EmbeddedEntity<Account>
}
interface Status extends ReturnType<typeof StatusRecord> {
// HACK: same as above
quote: EmbeddedEntity<Status>
reblog: EmbeddedEntity<Status>
}
// Utility types
type APIEntity = Record<string, any>;
type EmbeddedEntity<T extends object> = null | string | ReturnType<ImmutableRecord.Factory<T>>;
@ -68,7 +54,6 @@ export {
AdminAccount,
AdminLog,
AdminReport,
Account,
Announcement,
AnnouncementReaction,
Attachment,
@ -85,7 +70,6 @@ export {
Location,
Mention,
Notification,
Status,
StatusEdit,
Tag,
@ -95,6 +79,7 @@ export {
};
export type {
Account,
Card,
EmojiReaction,
Group,
@ -103,4 +88,5 @@ export type {
Poll,
PollOption,
Relationship,
Status,
} from 'soapbox/schemas';

Wyświetl plik

@ -40,7 +40,7 @@ const filterBadges = (tags: string[]): Badge[] => {
/** Get badges from an account. */
const getBadges = (account: Account): Badge[] => {
return filterBadges(account.pleroma.tags);
return filterBadges(account.pleroma?.tags ?? []);
};
export {