pl-fe: remove some immutable usage
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
66fbdf2ad9
commit
865130c55a
15 changed files with 197 additions and 169 deletions
|
@ -59,7 +59,7 @@ const Aliases = () => {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<Search />
|
<Search />
|
||||||
{
|
{
|
||||||
loaded && searchAccountIds.size === 0 ? (
|
loaded && searchAccountIds.length === 0 ? (
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
<FormattedMessage id='empty_column.aliases.suggestions' defaultMessage='There are no account suggestions available for the provided term.' />
|
<FormattedMessage id='empty_column.aliases.suggestions' defaultMessage='There are no account suggestions available for the provided term.' />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
@ -19,7 +18,7 @@ interface ICaptchaField {
|
||||||
name?: string;
|
name?: string;
|
||||||
value: string;
|
value: string;
|
||||||
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
||||||
onFetch?: (captcha: ImmutableMap<string, any>) => void;
|
onFetch?: (captcha: Record<string, any>) => void;
|
||||||
onFetchFail?: (error: Error) => void;
|
onFetchFail?: (error: Error) => void;
|
||||||
onClick?: React.MouseEventHandler;
|
onClick?: React.MouseEventHandler;
|
||||||
refreshInterval?: number;
|
refreshInterval?: number;
|
||||||
|
@ -38,12 +37,11 @@ const CaptchaField: React.FC<ICaptchaField> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const [captcha, setCaptcha] = useState(ImmutableMap<string, any>());
|
const [captcha, setCaptcha] = useState<Record<string, any>>({});
|
||||||
const [refresh, setRefresh] = useState<NodeJS.Timeout | undefined>(undefined);
|
const [refresh, setRefresh] = useState<NodeJS.Timeout | undefined>(undefined);
|
||||||
|
|
||||||
const getCaptcha = () => {
|
const getCaptcha = () => {
|
||||||
dispatch(fetchCaptcha()).then((response) => {
|
dispatch(fetchCaptcha()).then((captcha) => {
|
||||||
const captcha = ImmutableMap(response);
|
|
||||||
setCaptcha(captcha);
|
setCaptcha(captcha);
|
||||||
onFetch(captcha);
|
onFetch(captcha);
|
||||||
}).catch((error: Error) => {
|
}).catch((error: Error) => {
|
||||||
|
@ -98,7 +96,7 @@ const CaptchaField: React.FC<ICaptchaField> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
interface INativeCaptchaField {
|
interface INativeCaptchaField {
|
||||||
captcha: ImmutableMap<string, any>;
|
captcha: Record<string, any>;
|
||||||
onChange: React.ChangeEventHandler<HTMLInputElement>;
|
onChange: React.ChangeEventHandler<HTMLInputElement>;
|
||||||
onClick: React.MouseEventHandler;
|
onClick: React.MouseEventHandler;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -111,7 +109,7 @@ const NativeCaptchaField: React.FC<INativeCaptchaField> = ({ captcha, onChange,
|
||||||
return (
|
return (
|
||||||
<Stack space={2}>
|
<Stack space={2}>
|
||||||
<div className='flex w-full items-center justify-center rounded-md border border-solid border-gray-300 bg-white dark:border-gray-600'>
|
<div className='flex w-full items-center justify-center rounded-md border border-solid border-gray-300 bg-white dark:border-gray-600'>
|
||||||
<img alt={intl.formatMessage(messages.captcha)} src={captcha.get('url')} onClick={onClick} />
|
<img alt={intl.formatMessage(messages.captcha)} src={captcha.url} onClick={onClick} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import React, { useState, useRef, useCallback } from 'react';
|
import React, { useState, useRef, useCallback } from 'react';
|
||||||
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||||
|
@ -221,12 +220,12 @@ const RegistrationForm: React.FC<IRegistrationForm> = ({ inviteToken }) => {
|
||||||
refreshCaptcha();
|
refreshCaptcha();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFetchCaptcha = (captcha: ImmutableMap<string, any>) => {
|
const onFetchCaptcha = (captcha: Record<string, any>) => {
|
||||||
setCaptchaLoading(false);
|
setCaptchaLoading(false);
|
||||||
setParams(params => ({
|
setParams(params => ({
|
||||||
...params,
|
...params,
|
||||||
captcha_token: captcha.get('token'),
|
captcha_token: captcha.token,
|
||||||
captcha_answer_data: captcha.get('answer_data'),
|
captcha_answer_data: captcha.answer_data,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
// See: https://github.com/spothq/cryptocurrency-icons/blob/master/manifest.json
|
// See: https://github.com/spothq/cryptocurrency-icons/blob/master/manifest.json
|
||||||
|
|
||||||
import manifest from 'cryptocurrency-icons/manifest.json';
|
import manifest from 'cryptocurrency-icons/manifest.json';
|
||||||
import { List as ImmutableList, Map as ImmutableMap, fromJS } from 'immutable';
|
|
||||||
|
|
||||||
const manifestMap = (fromJS(manifest) as ImmutableList<ImmutableMap<string, string>>).reduce(
|
const manifestMap = manifest.reduce((acc: Record<string, typeof manifest[0]>, entry) => {
|
||||||
(acc: ImmutableMap<string, ImmutableMap<string, string>>, entry: ImmutableMap<string, string>) =>
|
acc[entry.symbol.toLowerCase()] = entry;
|
||||||
acc.set(entry.get('symbol')!.toLowerCase(), entry),
|
return acc;
|
||||||
ImmutableMap(),
|
}, {});
|
||||||
).toJS();
|
|
||||||
|
|
||||||
export { manifestMap as default };
|
export { manifestMap as default };
|
||||||
|
|
|
@ -11,8 +11,6 @@ import { federationRestrictionsDisclosed } from 'pl-fe/utils/state';
|
||||||
|
|
||||||
import RestrictedInstance from './components/restricted-instance';
|
import RestrictedInstance from './components/restricted-instance';
|
||||||
|
|
||||||
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'column.federation_restrictions', defaultMessage: 'Federation Restrictions' },
|
heading: { id: 'column.federation_restrictions', defaultMessage: 'Federation Restrictions' },
|
||||||
boxTitle: { id: 'federation_restrictions.explanation_box.title', defaultMessage: 'Instance-specific policies' },
|
boxTitle: { id: 'federation_restrictions.explanation_box.title', defaultMessage: 'Instance-specific policies' },
|
||||||
|
@ -27,7 +25,7 @@ const FederationRestrictions = () => {
|
||||||
|
|
||||||
const getHosts = useCallback(makeGetHosts(), []);
|
const getHosts = useCallback(makeGetHosts(), []);
|
||||||
|
|
||||||
const hosts = useAppSelector((state) => getHosts(state)) as ImmutableOrderedSet<string>;
|
const hosts = useAppSelector((state) => getHosts(state));
|
||||||
const disclosed = useAppSelector((state) => federationRestrictionsDisclosed(state));
|
const disclosed = useAppSelector((state) => federationRestrictionsDisclosed(state));
|
||||||
|
|
||||||
const [explanationBoxExpanded, setExplanationBoxExpanded] = useState(true);
|
const [explanationBoxExpanded, setExplanationBoxExpanded] = useState(true);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { List as ImmutableList } from 'immutable';
|
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
@ -31,8 +30,8 @@ const messages = defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
const getNotifications = createSelector([
|
const getNotifications = createSelector([
|
||||||
(state: RootState) => state.notifications.items.toList(),
|
(state: RootState) => state.notifications.items.toArray(),
|
||||||
], (notifications) => notifications.filter(item => item !== null));
|
], (notifications) => notifications.map(([_, notification]) => notification).filter(item => item !== null));
|
||||||
|
|
||||||
const Notifications = () => {
|
const Notifications = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
@ -47,7 +46,7 @@ const Notifications = () => {
|
||||||
const hasMore = useAppSelector(state => state.notifications.hasMore);
|
const hasMore = useAppSelector(state => state.notifications.hasMore);
|
||||||
const totalQueuedNotificationsCount = useAppSelector(state => state.notifications.totalQueuedNotificationsCount || 0);
|
const totalQueuedNotificationsCount = useAppSelector(state => state.notifications.totalQueuedNotificationsCount || 0);
|
||||||
|
|
||||||
const scrollableContentRef = useRef<ImmutableList<JSX.Element> | null>(null);
|
const scrollableContentRef = useRef<Array<JSX.Element> | null>(null);
|
||||||
|
|
||||||
// const handleLoadGap = (maxId) => {
|
// const handleLoadGap = (maxId) => {
|
||||||
// dispatch(expandNotifications({ maxId }));
|
// dispatch(expandNotifications({ maxId }));
|
||||||
|
@ -105,7 +104,7 @@ const Notifications = () => {
|
||||||
? <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />
|
? <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />
|
||||||
: <FormattedMessage id='empty_column.notifications_filtered' defaultMessage="You don't have any notifications of this type yet." />;
|
: <FormattedMessage id='empty_column.notifications_filtered' defaultMessage="You don't have any notifications of this type yet." />;
|
||||||
|
|
||||||
let scrollableContent: ImmutableList<JSX.Element> | null = null;
|
let scrollableContent: Array<JSX.Element> | null = null;
|
||||||
|
|
||||||
const filterBarContainer = showFilterBar
|
const filterBarContainer = showFilterBar
|
||||||
? (<FilterBar />)
|
? (<FilterBar />)
|
||||||
|
@ -113,7 +112,7 @@ const Notifications = () => {
|
||||||
|
|
||||||
if (isLoading && scrollableContentRef.current) {
|
if (isLoading && scrollableContentRef.current) {
|
||||||
scrollableContent = scrollableContentRef.current;
|
scrollableContent = scrollableContentRef.current;
|
||||||
} else if (notifications.size > 0 || hasMore) {
|
} else if (notifications.length > 0 || hasMore) {
|
||||||
scrollableContent = notifications.map((item) => (
|
scrollableContent = notifications.map((item) => (
|
||||||
<Notification
|
<Notification
|
||||||
key={item.group_key}
|
key={item.group_key}
|
||||||
|
@ -131,7 +130,7 @@ const Notifications = () => {
|
||||||
const scrollContainer = (
|
const scrollContainer = (
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
showLoading={isLoading && notifications.size === 0}
|
showLoading={isLoading && notifications.length === 0}
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
placeholderComponent={PlaceholderNotification}
|
placeholderComponent={PlaceholderNotification}
|
||||||
|
@ -139,7 +138,7 @@ const Notifications = () => {
|
||||||
onLoadMore={handleLoadOlder}
|
onLoadMore={handleLoadOlder}
|
||||||
onScroll={handleScroll}
|
onScroll={handleScroll}
|
||||||
listClassName={clsx('divide-y divide-solid divide-gray-200 black:divide-gray-800 dark:divide-primary-800', {
|
listClassName={clsx('divide-y divide-solid divide-gray-200 black:divide-gray-800 dark:divide-primary-800', {
|
||||||
'animate-pulse': notifications.size === 0,
|
'animate-pulse': notifications.length === 0,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{scrollableContent!}
|
{scrollableContent!}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Record as ImmutableRecord } from 'immutable';
|
|
||||||
import { MediaAttachment } from 'pl-api';
|
import { MediaAttachment } from 'pl-api';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
@ -7,12 +6,12 @@ interface IPlaceholderMediaGallery {
|
||||||
defaultWidth?: number;
|
defaultWidth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SizeData = ImmutableRecord({
|
interface SizeData {
|
||||||
style: {} as React.CSSProperties,
|
style: React.CSSProperties;
|
||||||
itemsDimensions: [] as Record<string, string>[],
|
itemsDimensions: Record<string, string>[];
|
||||||
size: 1 as number,
|
size: number;
|
||||||
width: 0 as number,
|
width: number;
|
||||||
});
|
}
|
||||||
|
|
||||||
const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, defaultWidth }) => {
|
const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, defaultWidth }) => {
|
||||||
const [width, setWidth] = useState(defaultWidth);
|
const [width, setWidth] = useState(defaultWidth);
|
||||||
|
@ -23,7 +22,7 @@ const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, de
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSizeData = (size: number) => {
|
const getSizeData = (size: number): SizeData => {
|
||||||
const style: React.CSSProperties = {};
|
const style: React.CSSProperties = {};
|
||||||
let itemsDimensions: Record<string, string>[] = [];
|
let itemsDimensions: Record<string, string>[] = [];
|
||||||
|
|
||||||
|
@ -59,12 +58,12 @@ const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, de
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return SizeData({
|
return {
|
||||||
style,
|
style,
|
||||||
itemsDimensions,
|
itemsDimensions,
|
||||||
size,
|
size,
|
||||||
width,
|
width: width || 0,
|
||||||
});
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderItem = (dimensions: Record<string, string>, i: number) => {
|
const renderItem = (dimensions: Record<string, string>, i: number) => {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Set as ImmutableSet } from 'immutable';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
@ -106,7 +105,7 @@ const Preferences = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelectMultiple = (selectedList: string[], path: string[]) => {
|
const onSelectMultiple = (selectedList: string[], path: string[]) => {
|
||||||
dispatch(changeSetting(path, ImmutableSet(selectedList.sort((a, b) => a.localeCompare(b))), { showAlert: true }));
|
dispatch(changeSetting(path, selectedList.toSorted((a, b) => a.localeCompare(b)), { showAlert: true }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onToggleChange = (key: string[], checked: boolean) => {
|
const onToggleChange = (key: string[], checked: boolean) => {
|
||||||
|
|
|
@ -54,7 +54,7 @@ const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ list
|
||||||
<EditListForm />
|
<EditListForm />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
{accountIds.size > 0 && (
|
{accountIds.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle title={intl.formatMessage(messages.removeFromList)} />
|
<CardTitle title={intl.formatMessage(messages.removeFromList)} />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
import { create } from 'mutative';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ALIASES_SUGGESTIONS_READY,
|
ALIASES_SUGGESTIONS_READY,
|
||||||
|
@ -8,37 +8,52 @@ import {
|
||||||
AliasesAction,
|
AliasesAction,
|
||||||
} from '../actions/aliases';
|
} from '../actions/aliases';
|
||||||
|
|
||||||
const ReducerRecord = ImmutableRecord({
|
interface State {
|
||||||
aliases: ImmutableRecord({
|
aliases: {
|
||||||
items: ImmutableList<string>(),
|
items: Array<string>;
|
||||||
|
loaded: boolean;
|
||||||
|
};
|
||||||
|
suggestions: {
|
||||||
|
items: Array<string>;
|
||||||
|
value: string;
|
||||||
|
loaded: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: State = {
|
||||||
|
aliases: {
|
||||||
|
items: [],
|
||||||
loaded: false,
|
loaded: false,
|
||||||
})(),
|
},
|
||||||
suggestions: ImmutableRecord({
|
suggestions: {
|
||||||
items: ImmutableList<string>(),
|
items: [],
|
||||||
value: '',
|
value: '',
|
||||||
loaded: false,
|
loaded: false,
|
||||||
})(),
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
const aliasesReducer = (state = ReducerRecord(), action: AliasesAction) => {
|
const aliasesReducer = (state = initialState, action: AliasesAction): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ALIASES_FETCH_SUCCESS:
|
case ALIASES_FETCH_SUCCESS:
|
||||||
return state
|
return create(state, (draft) => {
|
||||||
.setIn(['aliases', 'items'], action.value);
|
draft.aliases.items = action.value;
|
||||||
|
});
|
||||||
case ALIASES_SUGGESTIONS_CHANGE:
|
case ALIASES_SUGGESTIONS_CHANGE:
|
||||||
return state
|
return create(state, (draft) => {
|
||||||
.setIn(['suggestions', 'value'], action.value)
|
draft.suggestions.value = action.value;
|
||||||
.setIn(['suggestions', 'loaded'], false);
|
draft.suggestions.loaded = false;
|
||||||
|
});
|
||||||
case ALIASES_SUGGESTIONS_READY:
|
case ALIASES_SUGGESTIONS_READY:
|
||||||
return state
|
return create(state, (draft) => {
|
||||||
.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map((item) => item.id)))
|
draft.suggestions.items = action.accounts.map((item) => item.id);
|
||||||
.setIn(['suggestions', 'loaded'], true);
|
draft.suggestions.loaded = true;
|
||||||
|
});
|
||||||
case ALIASES_SUGGESTIONS_CLEAR:
|
case ALIASES_SUGGESTIONS_CLEAR:
|
||||||
return state.update('suggestions', suggestions => suggestions.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('items', ImmutableList());
|
draft.suggestions.items = [];
|
||||||
map.set('value', '');
|
draft.suggestions.value = '';
|
||||||
map.set('loaded', false);
|
draft.suggestions.loaded = false;
|
||||||
}));
|
});
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
import { create } from 'mutative';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LIST_ADDER_RESET,
|
LIST_ADDER_RESET,
|
||||||
|
@ -12,42 +12,54 @@ import {
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
|
|
||||||
const ListsRecord = ImmutableRecord({
|
interface State {
|
||||||
items: ImmutableList<string>(),
|
accountId: string | null;
|
||||||
loaded: false,
|
lists: {
|
||||||
isLoading: false,
|
items: Array<string>;
|
||||||
});
|
loaded: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const ReducerRecord = ImmutableRecord({
|
const initialState: State = {
|
||||||
accountId: null as string | null,
|
accountId: null,
|
||||||
|
lists: {
|
||||||
|
items: [],
|
||||||
|
loaded: false,
|
||||||
|
isLoading: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
lists: ListsRecord(),
|
const listAdderReducer = (state: State = initialState, action: AnyAction): State => {
|
||||||
});
|
|
||||||
|
|
||||||
type State = ReturnType<typeof ReducerRecord>;
|
|
||||||
|
|
||||||
const listAdderReducer = (state: State = ReducerRecord(), action: AnyAction) => {
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case LIST_ADDER_RESET:
|
case LIST_ADDER_RESET:
|
||||||
return ReducerRecord();
|
return initialState;
|
||||||
case LIST_ADDER_SETUP:
|
case LIST_ADDER_SETUP:
|
||||||
return state.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('accountId', action.account.id);
|
draft.accountId = action.account.id;
|
||||||
});
|
});
|
||||||
case LIST_ADDER_LISTS_FETCH_REQUEST:
|
case LIST_ADDER_LISTS_FETCH_REQUEST:
|
||||||
return state.setIn(['lists', 'isLoading'], true);
|
return create(state, (draft) => {
|
||||||
|
draft.lists.isLoading = true;
|
||||||
|
});
|
||||||
case LIST_ADDER_LISTS_FETCH_FAIL:
|
case LIST_ADDER_LISTS_FETCH_FAIL:
|
||||||
return state.setIn(['lists', 'isLoading'], false);
|
return create(state, (draft) => {
|
||||||
|
draft.lists.isLoading = false;
|
||||||
|
});
|
||||||
case LIST_ADDER_LISTS_FETCH_SUCCESS:
|
case LIST_ADDER_LISTS_FETCH_SUCCESS:
|
||||||
return state.update('lists', lists => lists.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('isLoading', false);
|
draft.lists.isLoading = false;
|
||||||
map.set('loaded', true);
|
draft.lists.loaded = true;
|
||||||
map.set('items', ImmutableList(action.lists.map((item: { id: string }) => item.id)));
|
draft.lists.items = action.lists.map((item: { id: string }) => item.id);
|
||||||
}));
|
});
|
||||||
case LIST_EDITOR_ADD_SUCCESS:
|
case LIST_EDITOR_ADD_SUCCESS:
|
||||||
return state.updateIn(['lists', 'items'], list => (list as ImmutableList<string>).unshift(action.listId));
|
return create(state, (draft) => {
|
||||||
|
draft.lists.items = [action.listId, ...draft.lists.items];
|
||||||
|
});
|
||||||
case LIST_EDITOR_REMOVE_SUCCESS:
|
case LIST_EDITOR_REMOVE_SUCCESS:
|
||||||
return state.updateIn(['lists', 'items'], list => (list as ImmutableList<string>).filterNot(item => item === action.listId));
|
return create(state, (draft) => {
|
||||||
|
draft.lists.items = draft.lists.items.filter(id => id !== action.listId);
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
import { create } from 'mutative';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LIST_CREATE_REQUEST,
|
LIST_CREATE_REQUEST,
|
||||||
|
@ -22,83 +22,109 @@ import {
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
|
|
||||||
const AccountsRecord = ImmutableRecord({
|
interface State {
|
||||||
items: ImmutableList<string>(),
|
listId: string | null;
|
||||||
loaded: false,
|
isSubmitting: boolean;
|
||||||
isLoading: false,
|
isChanged: boolean;
|
||||||
});
|
title: string;
|
||||||
|
|
||||||
const SuggestionsRecord = ImmutableRecord({
|
accounts: {
|
||||||
value: '',
|
items: Array<string>;
|
||||||
items: ImmutableList<string>(),
|
loaded: boolean;
|
||||||
});
|
isLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
const ReducerRecord = ImmutableRecord({
|
suggestions: {
|
||||||
listId: null as string | null,
|
value: string;
|
||||||
|
items: Array<string>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: State = {
|
||||||
|
listId: null,
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
isChanged: false,
|
isChanged: false,
|
||||||
title: '',
|
title: '',
|
||||||
|
|
||||||
accounts: AccountsRecord(),
|
accounts: {
|
||||||
|
items: [],
|
||||||
|
loaded: false,
|
||||||
|
isLoading: false,
|
||||||
|
},
|
||||||
|
|
||||||
suggestions: SuggestionsRecord(),
|
suggestions: {
|
||||||
});
|
value: '',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
type State = ReturnType<typeof ReducerRecord>;
|
const listEditorReducer = (state: State = initialState, action: AnyAction): State => {
|
||||||
|
|
||||||
const listEditorReducer = (state: State = ReducerRecord(), action: AnyAction) => {
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case LIST_EDITOR_RESET:
|
case LIST_EDITOR_RESET:
|
||||||
return ReducerRecord();
|
return initialState;
|
||||||
case LIST_EDITOR_SETUP:
|
case LIST_EDITOR_SETUP:
|
||||||
return state.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('listId', action.list.id);
|
draft.listId = action.list.id;
|
||||||
map.set('title', action.list.title);
|
draft.title = action.list.title;
|
||||||
map.set('isSubmitting', false);
|
draft.isSubmitting = false;
|
||||||
});
|
});
|
||||||
case LIST_EDITOR_TITLE_CHANGE:
|
case LIST_EDITOR_TITLE_CHANGE:
|
||||||
return state.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('title', action.value);
|
draft.title = action.value;
|
||||||
map.set('isChanged', true);
|
draft.isChanged = true;
|
||||||
});
|
});
|
||||||
case LIST_CREATE_REQUEST:
|
case LIST_CREATE_REQUEST:
|
||||||
case LIST_UPDATE_REQUEST:
|
case LIST_UPDATE_REQUEST:
|
||||||
return state.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('isSubmitting', true);
|
draft.isSubmitting = true;
|
||||||
map.set('isChanged', false);
|
draft.isChanged = false;
|
||||||
});
|
});
|
||||||
case LIST_CREATE_FAIL:
|
case LIST_CREATE_FAIL:
|
||||||
case LIST_UPDATE_FAIL:
|
case LIST_UPDATE_FAIL:
|
||||||
return state.set('isSubmitting', false);
|
return create(state, (draft) => {
|
||||||
|
draft.isSubmitting = false;
|
||||||
|
});
|
||||||
case LIST_CREATE_SUCCESS:
|
case LIST_CREATE_SUCCESS:
|
||||||
case LIST_UPDATE_SUCCESS:
|
case LIST_UPDATE_SUCCESS:
|
||||||
return state.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('isSubmitting', false);
|
draft.isSubmitting = false;
|
||||||
map.set('listId', action.list.id);
|
draft.listId = action.list.id;
|
||||||
});
|
});
|
||||||
case LIST_ACCOUNTS_FETCH_REQUEST:
|
case LIST_ACCOUNTS_FETCH_REQUEST:
|
||||||
return state.setIn(['accounts', 'isLoading'], true);
|
return create(state, (draft) => {
|
||||||
|
draft.accounts.isLoading = true;
|
||||||
|
});
|
||||||
case LIST_ACCOUNTS_FETCH_FAIL:
|
case LIST_ACCOUNTS_FETCH_FAIL:
|
||||||
return state.setIn(['accounts', 'isLoading'], false);
|
return create(state, (draft) => {
|
||||||
|
draft.accounts.isLoading = false;
|
||||||
|
});
|
||||||
case LIST_ACCOUNTS_FETCH_SUCCESS:
|
case LIST_ACCOUNTS_FETCH_SUCCESS:
|
||||||
return state.update('accounts', accounts => accounts.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('isLoading', false);
|
draft.accounts.isLoading = false;
|
||||||
map.set('loaded', true);
|
draft.accounts.loaded = true;
|
||||||
map.set('items', ImmutableList(action.accounts.map((item: { id: string }) => item.id)));
|
draft.accounts.items = action.accounts.map((item: { id: string }) => item.id);
|
||||||
}));
|
});
|
||||||
case LIST_EDITOR_SUGGESTIONS_CHANGE:
|
case LIST_EDITOR_SUGGESTIONS_CHANGE:
|
||||||
return state.setIn(['suggestions', 'value'], action.value);
|
return create(state, (draft) => {
|
||||||
|
draft.suggestions.value = action.value;
|
||||||
|
});
|
||||||
case LIST_EDITOR_SUGGESTIONS_READY:
|
case LIST_EDITOR_SUGGESTIONS_READY:
|
||||||
return state.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map((item: { id: string }) => item.id)));
|
return create(state, (draft) => {
|
||||||
|
draft.suggestions.items = action.accounts.map((item: { id: string }) => item.id);
|
||||||
|
});
|
||||||
case LIST_EDITOR_SUGGESTIONS_CLEAR:
|
case LIST_EDITOR_SUGGESTIONS_CLEAR:
|
||||||
return state.update('suggestions', suggestions => suggestions.withMutations(map => {
|
return create(state, (draft) => {
|
||||||
map.set('items', ImmutableList());
|
draft.suggestions.items = [];
|
||||||
map.set('value', '');
|
draft.suggestions.value = '';
|
||||||
}));
|
});
|
||||||
case LIST_EDITOR_ADD_SUCCESS:
|
case LIST_EDITOR_ADD_SUCCESS:
|
||||||
return state.updateIn(['accounts', 'items'], list => (list as ImmutableList<string>).unshift(action.accountId));
|
return create(state, (draft) => {
|
||||||
|
draft.accounts.items = [action.accountId, ...draft.accounts.items];
|
||||||
|
});
|
||||||
case LIST_EDITOR_REMOVE_SUCCESS:
|
case LIST_EDITOR_REMOVE_SUCCESS:
|
||||||
return state.updateIn(['accounts', 'items'], list => (list as ImmutableList<string>).filterNot((item) => item === action.accountId));
|
return create(state, (draft) => {
|
||||||
|
draft.accounts.items = draft.accounts.items.filter(id => id !== action.accoundId);
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
// import { getLocale } from 'pl-fe/actions/settings';
|
// import { getLocale } from 'pl-fe/actions/settings';
|
||||||
|
@ -262,14 +261,11 @@ const makeGetReport = () => {
|
||||||
|
|
||||||
const getAuthUserIds = createSelector(
|
const getAuthUserIds = createSelector(
|
||||||
[(state: RootState) => state.auth.users],
|
[(state: RootState) => state.auth.users],
|
||||||
authUsers => authUsers.reduce((userIds: ImmutableOrderedSet<string>, authUser) => {
|
authUsers => authUsers.reduce((userIds: Array<string>, authUser) => {
|
||||||
try {
|
const userId = authUser?.id;
|
||||||
const userId = authUser.id;
|
if (validId(userId)) userIds.push(userId);
|
||||||
return validId(userId) ? userIds.add(userId) : userIds;
|
return userIds;
|
||||||
} catch {
|
}, []));
|
||||||
return userIds;
|
|
||||||
}
|
|
||||||
}, ImmutableOrderedSet<string>()));
|
|
||||||
|
|
||||||
const makeGetOtherAccounts = () => createSelector([
|
const makeGetOtherAccounts = () => createSelector([
|
||||||
(state: RootState) => state.entities[Entities.ACCOUNTS]?.store as EntityStore<Account>,
|
(state: RootState) => state.entities[Entities.ACCOUNTS]?.store as EntityStore<Account>,
|
||||||
|
@ -314,9 +310,7 @@ const makeGetHosts = () =>
|
||||||
createSelector([getSimplePolicy], (simplePolicy) => {
|
createSelector([getSimplePolicy], (simplePolicy) => {
|
||||||
const { accept, reject_deletes, report_removal, ...rest } = simplePolicy;
|
const { accept, reject_deletes, report_removal, ...rest } = simplePolicy;
|
||||||
|
|
||||||
return Object.values(rest)
|
return [...new Set(Object.values(rest).reduce((acc, hosts) => (acc.push(...hosts), acc), []))].toSorted();
|
||||||
.reduce((acc, hosts) => acc.union(hosts), ImmutableOrderedSet())
|
|
||||||
.sort();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
interface RemoteInstance {
|
interface RemoteInstance {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { List as ImmutableList } from 'immutable';
|
|
||||||
|
|
||||||
import { selectAccount, selectOwnAccount } from 'pl-fe/selectors';
|
import { selectAccount, selectOwnAccount } from 'pl-fe/selectors';
|
||||||
|
|
||||||
import type { RootState } from 'pl-fe/store';
|
import type { RootState } from 'pl-fe/store';
|
||||||
|
@ -43,19 +41,19 @@ const getAccessToken = (state: RootState) => {
|
||||||
const getAuthUserId = (state: RootState) => {
|
const getAuthUserId = (state: RootState) => {
|
||||||
const me = state.auth.me;
|
const me = state.auth.me;
|
||||||
|
|
||||||
return ImmutableList([
|
return [
|
||||||
state.auth.users.get(me!)?.id,
|
state.auth.users.get(me!)?.id,
|
||||||
me,
|
me,
|
||||||
].filter(id => id)).find(validId);
|
].filter(id => id).find(validId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAuthUserUrl = (state: RootState) => {
|
const getAuthUserUrl = (state: RootState) => {
|
||||||
const me = state.auth.me;
|
const me = state.auth.me;
|
||||||
|
|
||||||
return ImmutableList([
|
return [
|
||||||
state.auth.users.get(me!)?.url,
|
state.auth.users.get(me!)?.url,
|
||||||
me,
|
me,
|
||||||
].filter(url => url)).find(isURL);
|
].filter(url => url).find(isURL);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Get the VAPID public key. */
|
/** Get the VAPID public key. */
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
|
|
||||||
import type { Account } from 'pl-fe/normalizers/account';
|
import type { Account } from 'pl-fe/normalizers/account';
|
||||||
|
|
||||||
/** Convert a plain tag into a badge. */
|
/** Convert a plain tag into a badge. */
|
||||||
|
@ -17,15 +15,10 @@ interface TagDiff {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the differences between two sets of tags. */
|
/** Returns the differences between two sets of tags. */
|
||||||
const getTagDiff = (oldTags: string[], newTags: string[]): TagDiff => {
|
const getTagDiff = (oldTags: string[], newTags: string[]): TagDiff => ({
|
||||||
const o = ImmutableOrderedSet(oldTags);
|
added: newTags.filter(tag => !oldTags.includes(tag)),
|
||||||
const n = ImmutableOrderedSet(newTags);
|
removed: oldTags.filter(tag => !newTags.includes(tag)),
|
||||||
|
});
|
||||||
return {
|
|
||||||
added: n.subtract(o).toArray(),
|
|
||||||
removed: o.subtract(n).toArray(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Returns only tags which are badges. */
|
/** Returns only tags which are badges. */
|
||||||
const filterBadges = (tags: string[]): string[] => tags.filter(tag => tag.startsWith('badge:'));
|
const filterBadges = (tags: string[]): string[] => tags.filter(tag => tag.startsWith('badge:'));
|
||||||
|
|
Loading…
Reference in a new issue