pl-fe: WIP migrate settings store to zustand
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
419877adcc
commit
e4615b70f7
31 changed files with 351 additions and 306 deletions
|
@ -1,10 +1,11 @@
|
||||||
import { getSettings, changeSetting } from 'pl-fe/actions/settings';
|
import { changeSetting } from 'pl-fe/actions/settings';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
|
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||||
|
|
||||||
const toggleMainWindow = () =>
|
const toggleMainWindow = () =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const main = getSettings(getState()).getIn(['chats', 'mainWindow']) as 'minimized' | 'open';
|
const main = useSettingsStore.getState().settings.chats.mainWindow;
|
||||||
const state = main === 'minimized' ? 'open' : 'minimized';
|
const state = main === 'minimized' ? 'open' : 'minimized';
|
||||||
return dispatch(changeSetting(['chats', 'mainWindow'], state));
|
return dispatch(changeSetting(['chats', 'mainWindow'], state));
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { Language } from 'pl-fe/features/preferences';
|
||||||
import { selectAccount, selectOwnAccount, makeGetAccount } from 'pl-fe/selectors';
|
import { selectAccount, selectOwnAccount, makeGetAccount } from 'pl-fe/selectors';
|
||||||
import { tagHistory } from 'pl-fe/settings';
|
import { tagHistory } from 'pl-fe/settings';
|
||||||
import { useModalsStore } from 'pl-fe/stores';
|
import { useModalsStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import toast from 'pl-fe/toast';
|
import toast from 'pl-fe/toast';
|
||||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||||
|
|
||||||
|
@ -15,7 +16,6 @@ import { chooseEmoji } from './emojis';
|
||||||
import { importFetchedAccounts } from './importer';
|
import { importFetchedAccounts } from './importer';
|
||||||
import { rememberLanguageUse } from './languages';
|
import { rememberLanguageUse } from './languages';
|
||||||
import { uploadFile, updateMedia } from './media';
|
import { uploadFile, updateMedia } from './media';
|
||||||
import { getSettings } from './settings';
|
|
||||||
import { createStatus } from './statuses';
|
import { createStatus } from './statuses';
|
||||||
|
|
||||||
import type { EditorState } from 'lexical';
|
import type { EditorState } from 'lexical';
|
||||||
|
@ -178,7 +178,7 @@ const replyCompose = (
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const client = getClient(state);
|
const client = getClient(state);
|
||||||
const { createStatusExplicitAddressing: explicitAddressing } = client.features;
|
const { createStatusExplicitAddressing: explicitAddressing } = client.features;
|
||||||
const preserveSpoilers = !!getSettings(state).get('preserveSpoilers');
|
const preserveSpoilers = useSettingsStore.getState().settings.preserveSpoilers;
|
||||||
const account = selectOwnAccount(state);
|
const account = selectOwnAccount(state);
|
||||||
|
|
||||||
if (!account) return;
|
if (!account) return;
|
||||||
|
@ -321,7 +321,7 @@ const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, c
|
||||||
|
|
||||||
const needsDescriptions = (state: RootState, composeId: string) => {
|
const needsDescriptions = (state: RootState, composeId: string) => {
|
||||||
const media = state.compose.get(composeId)!.media_attachments;
|
const media = state.compose.get(composeId)!.media_attachments;
|
||||||
const missingDescriptionModal = getSettings(state).get('missingDescriptionModal');
|
const missingDescriptionModal = useSettingsStore.getState().settings.missingDescriptionModal;
|
||||||
|
|
||||||
const hasMissing = media.filter(item => !item.description).size > 0;
|
const hasMissing = media.filter(item => !item.description).size > 0;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { selectAccount } from 'pl-fe/selectors';
|
import { selectAccount } from 'pl-fe/selectors';
|
||||||
import { setSentryAccount } from 'pl-fe/sentry';
|
import { setSentryAccount } from 'pl-fe/sentry';
|
||||||
import KVStore from 'pl-fe/storage/kv-store';
|
import KVStore from 'pl-fe/storage/kv-store';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { getAuthUserId, getAuthUserUrl } from 'pl-fe/utils/auth';
|
import { getAuthUserId, getAuthUserUrl } from 'pl-fe/utils/auth';
|
||||||
|
|
||||||
import { getClient } from '../api';
|
import { getClient } from '../api';
|
||||||
|
|
||||||
import { loadCredentials } from './auth';
|
import { loadCredentials } from './auth';
|
||||||
import { importFetchedAccount } from './importer';
|
import { importFetchedAccount } from './importer';
|
||||||
|
import { FE_NAME } from './settings';
|
||||||
|
|
||||||
import type { CredentialAccount, UpdateCredentialsParams } from 'pl-api';
|
import type { CredentialAccount, UpdateCredentialsParams } from 'pl-api';
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||||
|
@ -95,6 +97,8 @@ const fetchMeRequest = () => ({
|
||||||
const fetchMeSuccess = (account: CredentialAccount) => {
|
const fetchMeSuccess = (account: CredentialAccount) => {
|
||||||
setSentryAccount(account);
|
setSentryAccount(account);
|
||||||
|
|
||||||
|
useSettingsStore.getState().loadUserSettings(account.settings_store?.[FE_NAME]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: ME_FETCH_SUCCESS,
|
type: ME_FETCH_SUCCESS,
|
||||||
me: account,
|
me: account,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { getClient } from 'pl-fe/api';
|
||||||
import { getNotificationStatus } from 'pl-fe/features/notifications/components/notification';
|
import { getNotificationStatus } from 'pl-fe/features/notifications/components/notification';
|
||||||
import { normalizeNotification, normalizeNotifications, type Notification } from 'pl-fe/normalizers';
|
import { normalizeNotification, normalizeNotifications, type Notification } from 'pl-fe/normalizers';
|
||||||
import { getFilters, regexFromFilters } from 'pl-fe/selectors';
|
import { getFilters, regexFromFilters } from 'pl-fe/selectors';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||||
import { compareId } from 'pl-fe/utils/comparators';
|
import { compareId } from 'pl-fe/utils/comparators';
|
||||||
import { unescapeHTML } from 'pl-fe/utils/html';
|
import { unescapeHTML } from 'pl-fe/utils/html';
|
||||||
|
@ -20,7 +21,7 @@ import {
|
||||||
importFetchedStatuses,
|
importFetchedStatuses,
|
||||||
} from './importer';
|
} from './importer';
|
||||||
import { saveMarker } from './markers';
|
import { saveMarker } from './markers';
|
||||||
import { getSettings, saveSettings } from './settings';
|
import { saveSettings } from './settings';
|
||||||
|
|
||||||
import type { Account, Notification as BaseNotification, PaginatedResponse, Status } from 'pl-api';
|
import type { Account, Notification as BaseNotification, PaginatedResponse, Status } from 'pl-api';
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||||
|
@ -72,7 +73,8 @@ const fetchRelatedRelationships = (dispatch: AppDispatch, notifications: Array<B
|
||||||
|
|
||||||
const updateNotifications = (notification: BaseNotification) =>
|
const updateNotifications = (notification: BaseNotification) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const showInColumn = getSettings(getState()).getIn(['notifications', 'shows', notification.type], true);
|
const selectedFilter = useSettingsStore().settings.notifications.quickFilter.active;
|
||||||
|
const showInColumn = selectedFilter === 'all' ? true : (FILTER_TYPES[selectedFilter as FilterType] || [notification.type]).includes(notification.type);
|
||||||
|
|
||||||
if (notification.account) {
|
if (notification.account) {
|
||||||
dispatch(importFetchedAccount(notification.account));
|
dispatch(importFetchedAccount(notification.account));
|
||||||
|
@ -105,7 +107,7 @@ const updateNotificationsQueue = (notification: BaseNotification, intlMessages:
|
||||||
if (notification.type === 'chat_mention') return; // Drop chat notifications, handle them per-chat
|
if (notification.type === 'chat_mention') return; // Drop chat notifications, handle them per-chat
|
||||||
|
|
||||||
const filters = getFilters(getState(), { contextType: 'notifications' });
|
const filters = getFilters(getState(), { contextType: 'notifications' });
|
||||||
const playSound = getSettings(getState()).getIn(['notifications', 'sounds', notification.type]);
|
const playSound = useSettingsStore.getState().settings.notifications.sounds[notification.type];
|
||||||
|
|
||||||
const status = getNotificationStatus(notification);
|
const status = getNotificationStatus(notification);
|
||||||
|
|
||||||
|
@ -195,7 +197,7 @@ const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => an
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const features = state.auth.client.features;
|
const features = state.auth.client.features;
|
||||||
const activeFilter = getSettings(state).getIn(['notifications', 'quickFilter', 'active']) as FilterType;
|
const activeFilter = useSettingsStore.getState().settings.notifications.quickFilter.active as FilterType;
|
||||||
const notifications = state.notifications;
|
const notifications = state.notifications;
|
||||||
|
|
||||||
if (notifications.isLoading) {
|
if (notifications.isLoading) {
|
||||||
|
@ -291,8 +293,8 @@ const scrollTopNotifications = (top: boolean) =>
|
||||||
};
|
};
|
||||||
|
|
||||||
const setFilter = (filterType: FilterType, abort?: boolean) =>
|
const setFilter = (filterType: FilterType, abort?: boolean) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch) => {
|
||||||
const activeFilter = getSettings(getState()).getIn(['notifications', 'quickFilter', 'active']);
|
const activeFilter = useSettingsStore.getState().settings.notifications.quickFilter.active as FilterType;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATIONS_FILTER_SET,
|
type: NOTIFICATIONS_FILTER_SET,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { createSelector } from 'reselect';
|
||||||
import { getHost } from 'pl-fe/actions/instance';
|
import { getHost } from 'pl-fe/actions/instance';
|
||||||
import { normalizePlFeConfig } from 'pl-fe/normalizers';
|
import { normalizePlFeConfig } from 'pl-fe/normalizers';
|
||||||
import KVStore from 'pl-fe/storage/kv-store';
|
import KVStore from 'pl-fe/storage/kv-store';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
|
|
||||||
import { getClient, staticFetch } from '../api';
|
import { getClient, staticFetch } from '../api';
|
||||||
|
|
||||||
|
@ -77,6 +78,9 @@ const importPlFeConfig = (plFeConfig: APIEntity, host: string | null) => {
|
||||||
if (!plFeConfig.brandColor) {
|
if (!plFeConfig.brandColor) {
|
||||||
plFeConfig.brandColor = '#d80482';
|
plFeConfig.brandColor = '#d80482';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useSettingsStore.getState().loadDefaultSettings(plFeConfig?.defaultSettings);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: PLFE_CONFIG_REQUEST_SUCCESS,
|
type: PLFE_CONFIG_REQUEST_SUCCESS,
|
||||||
plFeConfig,
|
plFeConfig,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { getSettings, changeSetting } from 'pl-fe/actions/settings';
|
import { changeSetting } from 'pl-fe/actions/settings';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
|
|
||||||
import type { List as ImmutableList, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||||
|
|
||||||
const getPinnedHosts = (state: RootState) => {
|
const getPinnedHosts = (state: RootState) => {
|
||||||
const settings = getSettings(state);
|
const { settings } = useSettingsStore.getState();
|
||||||
return settings.getIn(['remote_timeline', 'pinnedHosts']) as ImmutableList<string> | ImmutableOrderedSet<string>;
|
return settings.remote_timeline.pinnedHosts;
|
||||||
};
|
};
|
||||||
|
|
||||||
const pinHost = (host: string) =>
|
const pinHost = (host: string) =>
|
||||||
|
@ -13,7 +13,7 @@ const pinHost = (host: string) =>
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const pinnedHosts = getPinnedHosts(state);
|
const pinnedHosts = getPinnedHosts(state);
|
||||||
|
|
||||||
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.toOrderedSet().add(host)));
|
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], [...pinnedHosts, host]));
|
||||||
};
|
};
|
||||||
|
|
||||||
const unpinHost = (host: string) =>
|
const unpinHost = (host: string) =>
|
||||||
|
@ -21,7 +21,7 @@ const unpinHost = (host: string) =>
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const pinnedHosts = getPinnedHosts(state);
|
const pinnedHosts = getPinnedHosts(state);
|
||||||
|
|
||||||
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.toOrderedSet().remove(host)));
|
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.filter(value => value !== host)));
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
import { defineMessage } from 'react-intl';
|
import { defineMessage } from 'react-intl';
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
|
|
||||||
import { patchMe } from 'pl-fe/actions/me';
|
import { patchMe } from 'pl-fe/actions/me';
|
||||||
import { getClient } from 'pl-fe/api';
|
import { getClient } from 'pl-fe/api';
|
||||||
import messages from 'pl-fe/messages';
|
import messages from 'pl-fe/messages';
|
||||||
import { makeGetAccount } from 'pl-fe/selectors';
|
import { makeGetAccount } from 'pl-fe/selectors';
|
||||||
import KVStore from 'pl-fe/storage/kv-store';
|
import KVStore from 'pl-fe/storage/kv-store';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import toast from 'pl-fe/toast';
|
import toast from 'pl-fe/toast';
|
||||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||||
|
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||||
|
|
||||||
const SETTING_CHANGE = 'SETTING_CHANGE' as const;
|
const SETTING_CHANGE = 'SETTING_CHANGE' as const;
|
||||||
const SETTING_SAVE = 'SETTING_SAVE' as const;
|
|
||||||
const SETTINGS_UPDATE = 'SETTINGS_UPDATE' as const;
|
|
||||||
|
|
||||||
const FE_NAME = 'pl_fe';
|
const FE_NAME = 'pl_fe';
|
||||||
|
|
||||||
|
@ -28,114 +25,109 @@ type SettingOpts = {
|
||||||
|
|
||||||
const saveSuccessMessage = defineMessage({ id: 'settings.save.success', defaultMessage: 'Your preferences have been saved!' });
|
const saveSuccessMessage = defineMessage({ id: 'settings.save.success', defaultMessage: 'Your preferences have been saved!' });
|
||||||
|
|
||||||
const defaultSettings = ImmutableMap({
|
// const defaultSettings = ImmutableMap({
|
||||||
onboarded: false,
|
// onboarded: false,
|
||||||
skinTone: 1,
|
// skinTone: 1,
|
||||||
reduceMotion: false,
|
// reduceMotion: false,
|
||||||
underlineLinks: false,
|
// underlineLinks: false,
|
||||||
autoPlayGif: true,
|
// autoPlayGif: true,
|
||||||
displayMedia: 'default',
|
// displayMedia: 'default',
|
||||||
displaySpoilers: false,
|
// displaySpoilers: false,
|
||||||
unfollowModal: true,
|
// unfollowModal: true,
|
||||||
boostModal: false,
|
// boostModal: false,
|
||||||
deleteModal: true,
|
// deleteModal: true,
|
||||||
missingDescriptionModal: true,
|
// missingDescriptionModal: true,
|
||||||
defaultPrivacy: 'public',
|
// defaultPrivacy: 'public',
|
||||||
defaultContentType: 'text/plain',
|
// defaultContentType: 'text/plain',
|
||||||
themeMode: 'system',
|
// themeMode: 'system',
|
||||||
locale: navigator.language || 'en',
|
// locale: navigator.language || 'en',
|
||||||
showExplanationBox: true,
|
// showExplanationBox: true,
|
||||||
explanationBox: true,
|
// explanationBox: true,
|
||||||
autoloadTimelines: true,
|
// autoloadTimelines: true,
|
||||||
autoloadMore: false,
|
// autoloadMore: false,
|
||||||
preserveSpoilers: true,
|
// preserveSpoilers: true,
|
||||||
autoTranslate: false,
|
// autoTranslate: false,
|
||||||
knownLanguages: ImmutableOrderedSet(),
|
// knownLanguages: ImmutableOrderedSet(),
|
||||||
|
|
||||||
systemFont: false,
|
// systemFont: false,
|
||||||
demetricator: false,
|
// demetricator: false,
|
||||||
|
|
||||||
isDeveloper: false,
|
// isDeveloper: false,
|
||||||
|
|
||||||
chats: ImmutableMap({
|
// chats: ImmutableMap({
|
||||||
panes: ImmutableList(),
|
// panes: ImmutableList(),
|
||||||
mainWindow: 'minimized',
|
// mainWindow: 'minimized',
|
||||||
sound: true,
|
// sound: true,
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
home: ImmutableMap({
|
// home: ImmutableMap({
|
||||||
shows: ImmutableMap({
|
// shows: ImmutableMap({
|
||||||
reblog: true,
|
// reblog: true,
|
||||||
reply: true,
|
// reply: true,
|
||||||
direct: false,
|
// direct: false,
|
||||||
}),
|
// }),
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
notifications: ImmutableMap({
|
// notifications: ImmutableMap({
|
||||||
quickFilter: ImmutableMap({
|
// quickFilter: ImmutableMap({
|
||||||
active: 'all',
|
// active: 'all',
|
||||||
show: true,
|
// show: true,
|
||||||
advanced: false,
|
// advanced: false,
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
sounds: ImmutableMap({
|
// sounds: ImmutableMap({
|
||||||
follow: false,
|
// follow: false,
|
||||||
follow_request: false,
|
// follow_request: false,
|
||||||
favourite: false,
|
// favourite: false,
|
||||||
reblog: false,
|
// reblog: false,
|
||||||
mention: false,
|
// mention: false,
|
||||||
poll: false,
|
// poll: false,
|
||||||
move: false,
|
// move: false,
|
||||||
emoji_reaction: false,
|
// emoji_reaction: false,
|
||||||
}),
|
// }),
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
'public:local': ImmutableMap({
|
// 'public:local': ImmutableMap({
|
||||||
shows: ImmutableMap({
|
// shows: ImmutableMap({
|
||||||
reblog: false,
|
// reblog: false,
|
||||||
reply: true,
|
// reply: true,
|
||||||
direct: false,
|
// direct: false,
|
||||||
}),
|
// }),
|
||||||
other: ImmutableMap({
|
// other: ImmutableMap({
|
||||||
onlyMedia: false,
|
// onlyMedia: false,
|
||||||
}),
|
// }),
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
public: ImmutableMap({
|
// public: ImmutableMap({
|
||||||
shows: ImmutableMap({
|
// shows: ImmutableMap({
|
||||||
reblog: true,
|
// reblog: true,
|
||||||
reply: true,
|
// reply: true,
|
||||||
direct: false,
|
// direct: false,
|
||||||
}),
|
// }),
|
||||||
other: ImmutableMap({
|
// other: ImmutableMap({
|
||||||
onlyMedia: false,
|
// onlyMedia: false,
|
||||||
}),
|
// }),
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
direct: ImmutableMap({
|
// direct: ImmutableMap({
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
account_timeline: ImmutableMap({
|
// account_timeline: ImmutableMap({
|
||||||
shows: ImmutableMap({
|
// shows: ImmutableMap({
|
||||||
reblog: true,
|
// reblog: true,
|
||||||
pinned: true,
|
// pinned: true,
|
||||||
direct: false,
|
// direct: false,
|
||||||
}),
|
// }),
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
trends: ImmutableMap({
|
// trends: ImmutableMap({
|
||||||
show: true,
|
// show: true,
|
||||||
}),
|
// }),
|
||||||
|
|
||||||
remote_timeline: ImmutableMap({
|
// remote_timeline: ImmutableMap({
|
||||||
pinnedHosts: ImmutableList(),
|
// pinnedHosts: ImmutableList(),
|
||||||
}),
|
// }),
|
||||||
});
|
// });
|
||||||
|
|
||||||
const getSettings = createSelector([
|
|
||||||
(state: RootState) => state.plfe.get('defaultSettings'),
|
|
||||||
(state: RootState) => state.settings,
|
|
||||||
], (plFeSettings, settings) => defaultSettings.mergeDeep(plFeSettings).mergeDeep(settings));
|
|
||||||
|
|
||||||
interface SettingChangeAction {
|
interface SettingChangeAction {
|
||||||
type: typeof SETTING_CHANGE;
|
type: typeof SETTING_CHANGE;
|
||||||
|
@ -171,13 +163,13 @@ const saveSettings = (opts?: SettingOpts) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
if (!isLoggedIn(getState)) return;
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
const state = getState();
|
const { userSettings, userSettingsSaving } = useSettingsStore.getState();
|
||||||
if (getSettings(state).getIn(['saved'])) return;
|
if (userSettings.saved) return;
|
||||||
|
|
||||||
const data = state.settings.delete('saved').toJS();
|
const { saved, ...data } = userSettings;
|
||||||
|
|
||||||
dispatch(updateSettingsStore(data)).then(() => {
|
dispatch(updateSettingsStore(data)).then(() => {
|
||||||
dispatch({ type: SETTING_SAVE });
|
userSettingsSaving();
|
||||||
|
|
||||||
if (opts?.showAlert) {
|
if (opts?.showAlert) {
|
||||||
toast.success(saveSuccessMessage);
|
toast.success(saveSuccessMessage);
|
||||||
|
@ -216,23 +208,18 @@ const updateSettingsStore = (settings: any) =>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLocale = (state: RootState, fallback = 'en') => {
|
const getLocale = (fallback = 'en') => {
|
||||||
const localeWithVariant = (getSettings(state).get('locale') as string).replace('_', '-');
|
const localeWithVariant = useSettingsStore.getState().settings.locale.replace('_', '-');
|
||||||
const locale = localeWithVariant.split('-')[0];
|
const locale = localeWithVariant.split('-')[0];
|
||||||
return Object.keys(messages).includes(localeWithVariant) ? localeWithVariant : Object.keys(messages).includes(locale) ? locale : fallback;
|
return Object.keys(messages).includes(localeWithVariant) ? localeWithVariant : Object.keys(messages).includes(locale) ? locale : fallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettingsAction =
|
type SettingsAction =
|
||||||
| SettingChangeAction
|
| SettingChangeAction
|
||||||
| { type: typeof SETTING_SAVE }
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SETTING_CHANGE,
|
SETTING_CHANGE,
|
||||||
SETTING_SAVE,
|
|
||||||
SETTINGS_UPDATE,
|
|
||||||
FE_NAME,
|
FE_NAME,
|
||||||
defaultSettings,
|
|
||||||
getSettings,
|
|
||||||
changeSettingImmediate,
|
changeSettingImmediate,
|
||||||
changeSetting,
|
changeSetting,
|
||||||
saveSettings,
|
saveSettings,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useModalsStore } from 'pl-fe/stores';
|
import { useModalsStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||||
import { shouldHaveCard } from 'pl-fe/utils/status';
|
import { shouldHaveCard } from 'pl-fe/utils/status';
|
||||||
|
|
||||||
|
@ -6,7 +7,6 @@ import { getClient } from '../api';
|
||||||
|
|
||||||
import { setComposeToStatus } from './compose';
|
import { setComposeToStatus } from './compose';
|
||||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||||
import { getSettings } from './settings';
|
|
||||||
import { deleteFromTimelines } from './timelines';
|
import { deleteFromTimelines } from './timelines';
|
||||||
|
|
||||||
import type { CreateStatusParams, Status as BaseStatus } from 'pl-api';
|
import type { CreateStatusParams, Status as BaseStatus } from 'pl-api';
|
||||||
|
@ -114,7 +114,7 @@ const fetchStatus = (statusId: string, intl?: IntlShape) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: STATUS_FETCH_REQUEST, statusId });
|
dispatch({ type: STATUS_FETCH_REQUEST, statusId });
|
||||||
|
|
||||||
const params = intl && getSettings(getState()).get('autoTranslate') ? {
|
const params = intl && useSettingsStore.getState().settings.autoTranslate ? {
|
||||||
language: intl.locale,
|
language: intl.locale,
|
||||||
} : undefined;
|
} : undefined;
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ const fetchContext = (statusId: string, intl?: IntlShape) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: CONTEXT_FETCH_REQUEST, statusId });
|
dispatch({ type: CONTEXT_FETCH_REQUEST, statusId });
|
||||||
|
|
||||||
const params = intl && getSettings(getState()).get('autoTranslate') ? {
|
const params = intl && useSettingsStore.getState().settings.autoTranslate ? {
|
||||||
language: intl.locale,
|
language: intl.locale,
|
||||||
} : undefined;
|
} : undefined;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
import { getLocale, getSettings } from 'pl-fe/actions/settings';
|
import { getLocale } from 'pl-fe/actions/settings';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { shouldFilter } from 'pl-fe/utils/timelines';
|
import { shouldFilter } from 'pl-fe/utils/timelines';
|
||||||
|
|
||||||
import { getClient } from '../api';
|
import { getClient } from '../api';
|
||||||
|
@ -31,12 +32,12 @@ const processTimelineUpdate = (timeline: string, status: BaseStatus) =>
|
||||||
const ownStatus = status.account?.id === me;
|
const ownStatus = status.account?.id === me;
|
||||||
const hasPendingStatuses = !getState().pending_statuses.isEmpty();
|
const hasPendingStatuses = !getState().pending_statuses.isEmpty();
|
||||||
|
|
||||||
const columnSettings = getSettings(getState()).get(timeline, ImmutableMap());
|
const columnSettings = useSettingsStore.getState().settings.timelines[timeline];
|
||||||
const shouldSkipQueue = shouldFilter({
|
const shouldSkipQueue = shouldFilter({
|
||||||
in_reply_to_id: status.in_reply_to_id,
|
in_reply_to_id: status.in_reply_to_id,
|
||||||
visibility: status.visibility,
|
visibility: status.visibility,
|
||||||
reblog_id: status.reblog?.id || null,
|
reblog_id: status.reblog?.id || null,
|
||||||
}, columnSettings as any);
|
}, columnSettings);
|
||||||
|
|
||||||
if (ownStatus && hasPendingStatuses) {
|
if (ownStatus && hasPendingStatuses) {
|
||||||
// WebSockets push statuses without the Idempotency-Key,
|
// WebSockets push statuses without the Idempotency-Key,
|
||||||
|
@ -183,7 +184,7 @@ const fetchHomeTimeline = (expand = false, done = noOp) =>
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const params: HomeTimelineParams = {};
|
const params: HomeTimelineParams = {};
|
||||||
if (getSettings(state).get('autoTranslate')) params.language = getLocale(state);
|
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
|
||||||
|
|
||||||
if (expand && state.timelines.get('home')?.isLoading) return;
|
if (expand && state.timelines.get('home')?.isLoading) return;
|
||||||
|
|
||||||
|
@ -198,7 +199,7 @@ const fetchPublicTimeline = ({ onlyMedia, local, instance }: Record<string, any>
|
||||||
const timelineId = `${instance ? 'remote' : 'public'}${local ? ':local' : ''}${onlyMedia ? ':media' : ''}${instance ? `:${instance}` : ''}`;
|
const timelineId = `${instance ? 'remote' : 'public'}${local ? ':local' : ''}${onlyMedia ? ':media' : ''}${instance ? `:${instance}` : ''}`;
|
||||||
|
|
||||||
const params: PublicTimelineParams = { only_media: onlyMedia, local: instance ? false : local, instance };
|
const params: PublicTimelineParams = { only_media: onlyMedia, local: instance ? false : local, instance };
|
||||||
if (getSettings(state).get('autoTranslate')) params.language = getLocale(state);
|
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
|
||||||
|
|
||||||
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
||||||
|
|
||||||
|
@ -213,7 +214,7 @@ const fetchBubbleTimeline = ({ onlyMedia }: Record<string, any> = {}, expand = f
|
||||||
const timelineId = `bubble${onlyMedia ? ':media' : ''}`;
|
const timelineId = `bubble${onlyMedia ? ':media' : ''}`;
|
||||||
|
|
||||||
const params: PublicTimelineParams = { only_media: onlyMedia };
|
const params: PublicTimelineParams = { only_media: onlyMedia };
|
||||||
if (getSettings(state).get('autoTranslate')) params.language = getLocale(state);
|
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
|
||||||
|
|
||||||
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
||||||
|
|
||||||
|
@ -229,7 +230,7 @@ const fetchAccountTimeline = (accountId: string, { exclude_replies, pinned, only
|
||||||
|
|
||||||
const params: GetAccountStatusesParams = { exclude_replies, pinned, only_media, limit };
|
const params: GetAccountStatusesParams = { exclude_replies, pinned, only_media, limit };
|
||||||
if (pinned || only_media) params.with_muted = true;
|
if (pinned || only_media) params.with_muted = true;
|
||||||
if (getSettings(state).get('autoTranslate')) params.language = getLocale(state);
|
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
|
||||||
|
|
||||||
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
||||||
|
|
||||||
|
@ -244,7 +245,7 @@ const fetchListTimeline = (listId: string, expand = false, done = noOp) =>
|
||||||
const timelineId = `list:${listId}`;
|
const timelineId = `list:${listId}`;
|
||||||
|
|
||||||
const params: ListTimelineParams = {};
|
const params: ListTimelineParams = {};
|
||||||
if (getSettings(state).get('autoTranslate')) params.language = getLocale(state);
|
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
|
||||||
|
|
||||||
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
||||||
|
|
||||||
|
@ -260,7 +261,7 @@ const fetchGroupTimeline = (groupId: string, { only_media, limit }: Record<strin
|
||||||
|
|
||||||
const params: GroupTimelineParams = { only_media, limit };
|
const params: GroupTimelineParams = { only_media, limit };
|
||||||
if (only_media) params.with_muted = true;
|
if (only_media) params.with_muted = true;
|
||||||
if (getSettings(state).get('autoTranslate')) params.language = getLocale(state);
|
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
|
||||||
|
|
||||||
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
||||||
|
|
||||||
|
@ -282,7 +283,7 @@ const fetchHashtagTimeline = (hashtag: string, { tags }: Record<string, any> = {
|
||||||
|
|
||||||
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
if (expand && state.timelines.get(timelineId)?.isLoading) return;
|
||||||
|
|
||||||
if (getSettings(state).get('autoTranslate')) params.language = getLocale(state);
|
if (useSettingsStore.getState().settings.autoTranslate) params.language = getLocale();
|
||||||
|
|
||||||
const fn = (expand && state.timelines.get(timelineId)?.next?.()) || getClient(state).timelines.hashtagTimeline(hashtag, params);
|
const fn = (expand && state.timelines.get(timelineId)?.next?.()) || getClient(state).timelines.hashtagTimeline(hashtag, params);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { updateConversations } from 'pl-fe/actions/conversations';
|
||||||
import { fetchFilters } from 'pl-fe/actions/filters';
|
import { fetchFilters } from 'pl-fe/actions/filters';
|
||||||
import { MARKER_FETCH_SUCCESS } from 'pl-fe/actions/markers';
|
import { MARKER_FETCH_SUCCESS } from 'pl-fe/actions/markers';
|
||||||
import { updateNotificationsQueue } from 'pl-fe/actions/notifications';
|
import { updateNotificationsQueue } from 'pl-fe/actions/notifications';
|
||||||
import { getLocale, getSettings } from 'pl-fe/actions/settings';
|
import { getLocale } from 'pl-fe/actions/settings';
|
||||||
import { updateStatus } from 'pl-fe/actions/statuses';
|
import { updateStatus } from 'pl-fe/actions/statuses';
|
||||||
import { deleteFromTimelines, processTimelineUpdate } from 'pl-fe/actions/timelines';
|
import { deleteFromTimelines, processTimelineUpdate } from 'pl-fe/actions/timelines';
|
||||||
import { useStatContext } from 'pl-fe/contexts/stat-context';
|
import { useStatContext } from 'pl-fe/contexts/stat-context';
|
||||||
|
@ -14,6 +14,7 @@ import { selectEntity } from 'pl-fe/entity-store/selectors';
|
||||||
import { useAppDispatch, useLoggedIn } from 'pl-fe/hooks';
|
import { useAppDispatch, useLoggedIn } from 'pl-fe/hooks';
|
||||||
import messages from 'pl-fe/messages';
|
import messages from 'pl-fe/messages';
|
||||||
import { queryClient } from 'pl-fe/queries/client';
|
import { queryClient } from 'pl-fe/queries/client';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { getUnreadChatsCount, updateChatListItem } from 'pl-fe/utils/chats';
|
import { getUnreadChatsCount, updateChatListItem } from 'pl-fe/utils/chats';
|
||||||
import { play, soundCache } from 'pl-fe/utils/sounds';
|
import { play, soundCache } from 'pl-fe/utils/sounds';
|
||||||
|
|
||||||
|
@ -101,6 +102,7 @@ const useUserStream = () => {
|
||||||
const { isLoggedIn } = useLoggedIn();
|
const { isLoggedIn } = useLoggedIn();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const statContext = useStatContext();
|
const statContext = useStatContext();
|
||||||
|
const { settings } = useSettingsStore();
|
||||||
|
|
||||||
const listener = useCallback((event: StreamingEvent) => {
|
const listener = useCallback((event: StreamingEvent) => {
|
||||||
switch (event.event) {
|
switch (event.event) {
|
||||||
|
@ -114,20 +116,17 @@ const useUserStream = () => {
|
||||||
dispatch(deleteFromTimelines(event.payload));
|
dispatch(deleteFromTimelines(event.payload));
|
||||||
break;
|
break;
|
||||||
case 'notification':
|
case 'notification':
|
||||||
dispatch((dispatch, getState) => {
|
messages[getLocale()]().then(messages => {
|
||||||
const locale = getLocale(getState());
|
dispatch(
|
||||||
messages[locale]().then(messages => {
|
updateNotificationsQueue(
|
||||||
dispatch(
|
event.payload,
|
||||||
updateNotificationsQueue(
|
messages,
|
||||||
event.payload,
|
getLocale(),
|
||||||
messages,
|
window.location.pathname,
|
||||||
locale,
|
),
|
||||||
window.location.pathname,
|
);
|
||||||
),
|
}).catch(error => {
|
||||||
);
|
console.error(error);
|
||||||
}).catch(error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'conversation':
|
case 'conversation':
|
||||||
|
@ -141,13 +140,12 @@ const useUserStream = () => {
|
||||||
const chat = event.payload;
|
const chat = event.payload;
|
||||||
const me = getState().me;
|
const me = getState().me;
|
||||||
const messageOwned = chat.last_message?.account_id === me;
|
const messageOwned = chat.last_message?.account_id === me;
|
||||||
const settings = getSettings(getState());
|
|
||||||
|
|
||||||
// Don't update own messages from streaming
|
// Don't update own messages from streaming
|
||||||
if (!messageOwned) {
|
if (!messageOwned) {
|
||||||
updateChatListItem(chat);
|
updateChatListItem(chat);
|
||||||
|
|
||||||
if (settings.getIn(['chats', 'sound'])) {
|
if (settings.chats.sound) {
|
||||||
play(soundCache.chat);
|
play(soundCache.chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import { fetchOwnAccounts, logOut, switchAccount } from 'pl-fe/actions/auth';
|
import { fetchOwnAccounts, logOut, switchAccount } from 'pl-fe/actions/auth';
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { useAccount } from 'pl-fe/api/hooks';
|
import { useAccount } from 'pl-fe/api/hooks';
|
||||||
import Account from 'pl-fe/components/account';
|
import Account from 'pl-fe/components/account';
|
||||||
import { Stack, Divider, HStack, Icon, Text } from 'pl-fe/components/ui';
|
import { Stack, Divider, HStack, Icon, Text } from 'pl-fe/components/ui';
|
||||||
|
@ -13,6 +12,7 @@ import ProfileStats from 'pl-fe/features/ui/components/profile-stats';
|
||||||
import { useAppDispatch, useAppSelector, useFeatures, useInstance, useRegistrationStatus } from 'pl-fe/hooks';
|
import { useAppDispatch, useAppSelector, useFeatures, useInstance, useRegistrationStatus } from 'pl-fe/hooks';
|
||||||
import { makeGetOtherAccounts } from 'pl-fe/selectors';
|
import { makeGetOtherAccounts } from 'pl-fe/selectors';
|
||||||
import { useUiStore } from 'pl-fe/stores';
|
import { useUiStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import sourceCode from 'pl-fe/utils/code';
|
import sourceCode from 'pl-fe/utils/code';
|
||||||
|
|
||||||
import type { List as ImmutableList } from 'immutable';
|
import type { List as ImmutableList } from 'immutable';
|
||||||
|
@ -86,7 +86,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
||||||
const me = useAppSelector((state) => state.me);
|
const me = useAppSelector((state) => state.me);
|
||||||
const { account } = useAccount(me || undefined);
|
const { account } = useAccount(me || undefined);
|
||||||
const otherAccounts: ImmutableList<AccountEntity> = useAppSelector((state) => getOtherAccounts(state));
|
const otherAccounts: ImmutableList<AccountEntity> = useAppSelector((state) => getOtherAccounts(state));
|
||||||
const settings = useAppSelector((state) => getSettings(state));
|
const { settings } = useSettingsStore();
|
||||||
const followRequestsCount = useAppSelector((state) => state.user_lists.follow_requests.items.count());
|
const followRequestsCount = useAppSelector((state) => state.user_lists.follow_requests.items.count());
|
||||||
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
||||||
const draftCount = useAppSelector((state) => state.draft_statuses.size);
|
const draftCount = useAppSelector((state) => state.draft_statuses.size);
|
||||||
|
@ -343,7 +343,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{settings.get('isDeveloper') && (
|
{settings.isDeveloper && (
|
||||||
<SidebarLink
|
<SidebarLink
|
||||||
to='/developers'
|
to='/developers'
|
||||||
icon={require('@tabler/icons/outline/code.svg')}
|
icon={require('@tabler/icons/outline/code.svg')}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { blockDomain, unblockDomain } from 'pl-fe/actions/domain-blocks';
|
||||||
import { initMuteModal } from 'pl-fe/actions/mutes';
|
import { initMuteModal } from 'pl-fe/actions/mutes';
|
||||||
import { initReport, ReportableEntities } from 'pl-fe/actions/reports';
|
import { initReport, ReportableEntities } from 'pl-fe/actions/reports';
|
||||||
import { setSearchAccount } from 'pl-fe/actions/search';
|
import { setSearchAccount } from 'pl-fe/actions/search';
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { useFollow } from 'pl-fe/api/hooks';
|
import { useFollow } from 'pl-fe/api/hooks';
|
||||||
import Badge from 'pl-fe/components/badge';
|
import Badge from 'pl-fe/components/badge';
|
||||||
import DropdownMenu, { Menu } from 'pl-fe/components/dropdown-menu';
|
import DropdownMenu, { Menu } from 'pl-fe/components/dropdown-menu';
|
||||||
|
@ -24,6 +23,7 @@ import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'pl-f
|
||||||
import { useChats } from 'pl-fe/queries/chats';
|
import { useChats } from 'pl-fe/queries/chats';
|
||||||
import { queryClient } from 'pl-fe/queries/client';
|
import { queryClient } from 'pl-fe/queries/client';
|
||||||
import { useModalsStore } from 'pl-fe/stores';
|
import { useModalsStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import toast from 'pl-fe/toast';
|
import toast from 'pl-fe/toast';
|
||||||
import { isDefaultHeader } from 'pl-fe/utils/accounts';
|
import { isDefaultHeader } from 'pl-fe/utils/accounts';
|
||||||
import copy from 'pl-fe/utils/copy';
|
import copy from 'pl-fe/utils/copy';
|
||||||
|
@ -90,6 +90,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
const { account: ownAccount } = useOwnAccount();
|
const { account: ownAccount } = useOwnAccount();
|
||||||
const { follow } = useFollow();
|
const { follow } = useFollow();
|
||||||
const { openModal } = useModalsStore();
|
const { openModal } = useModalsStore();
|
||||||
|
const { settings } = useSettingsStore();
|
||||||
|
|
||||||
const { software } = useAppSelector((state) => state.auth.client.features.version);
|
const { software } = useAppSelector((state) => state.auth.client.features.version);
|
||||||
|
|
||||||
|
@ -221,19 +222,17 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRemoveFromFollowers = () => {
|
const onRemoveFromFollowers = () => {
|
||||||
dispatch((_, getState) => {
|
const unfollowModal = settings.unfollowModal;
|
||||||
const unfollowModal = getSettings(getState()).get('unfollowModal');
|
if (unfollowModal) {
|
||||||
if (unfollowModal) {
|
openModal('CONFIRM', {
|
||||||
openModal('CONFIRM', {
|
heading: <FormattedMessage id='confirmations.remove_from_followers.heading' defaultMessage='Remove {name} from followers' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
||||||
heading: <FormattedMessage id='confirmations.remove_from_followers.heading' defaultMessage='Remove {name} from followers' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
message: <FormattedMessage id='confirmations.remove_from_followers.message' defaultMessage='Are you sure you want to remove {name} from your followers?' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
||||||
message: <FormattedMessage id='confirmations.remove_from_followers.message' defaultMessage='Are you sure you want to remove {name} from your followers?' values={{ name: <strong className='break-words'>@{account.acct}</strong> }} />,
|
confirm: intl.formatMessage(messages.removeFromFollowersConfirm),
|
||||||
confirm: intl.formatMessage(messages.removeFromFollowersConfirm),
|
onConfirm: () => dispatch(removeFromFollowers(account.id)),
|
||||||
onConfirm: () => dispatch(removeFromFollowers(account.id)),
|
});
|
||||||
});
|
} else {
|
||||||
} else {
|
dispatch(removeFromFollowers(account.id));
|
||||||
dispatch(removeFromFollowers(account.id));
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSearch = () => {
|
const onSearch = () => {
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import fuzzysort from 'fuzzysort';
|
import fuzzysort from 'fuzzysort';
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
|
|
||||||
import { addComposeLanguage, changeComposeLanguage, changeComposeModifiedLanguage, deleteComposeLanguage } from 'pl-fe/actions/compose';
|
import { addComposeLanguage, changeComposeLanguage, changeComposeModifiedLanguage, deleteComposeLanguage } from 'pl-fe/actions/compose';
|
||||||
import DropdownMenu from 'pl-fe/components/dropdown-menu';
|
import DropdownMenu from 'pl-fe/components/dropdown-menu';
|
||||||
import { Button, Icon, Input } from 'pl-fe/components/ui';
|
import { Button, Icon, Input } from 'pl-fe/components/ui';
|
||||||
import { type Language, languages as languagesObject } from 'pl-fe/features/preferences';
|
import { type Language, languages as languagesObject } from 'pl-fe/features/preferences';
|
||||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures } from 'pl-fe/hooks';
|
import { useAppDispatch, useCompose, useFeatures, useSettings } from 'pl-fe/hooks';
|
||||||
|
|
||||||
const getFrequentlyUsedLanguages = createSelector([
|
const getFrequentlyUsedLanguages = (languageCounters: Record<string, number>) => (
|
||||||
state => state.settings.get('frequentlyUsedLanguages', ImmutableMap()),
|
Object.keys(languageCounters)
|
||||||
], (languageCounters: ImmutableMap<Language, number>) => (
|
.toSorted((a, b) => languageCounters[a] - languageCounters[b])
|
||||||
languageCounters.keySeq()
|
.toReversed()
|
||||||
.sort((a, b) => languageCounters.get(a, 0) - languageCounters.get(b, 0))
|
);
|
||||||
.reverse()
|
|
||||||
.toArray()
|
|
||||||
));
|
|
||||||
|
|
||||||
const languages = Object.entries(languagesObject) as Array<[Language, string]>;
|
const languages = Object.entries(languagesObject) as Array<[Language, string]>;
|
||||||
|
|
||||||
|
@ -39,7 +34,8 @@ const getLanguageDropdown = (composeId: string): React.FC<ILanguageDropdown> =>
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const frequentlyUsedLanguages = useAppSelector(getFrequentlyUsedLanguages);
|
const settings = useSettings();
|
||||||
|
const frequentlyUsedLanguages = useMemo(() => getFrequentlyUsedLanguages(settings.frequentlyUsedLanguages), [settings.frequentlyUsedLanguages]);
|
||||||
|
|
||||||
const node = useRef<HTMLDivElement>(null);
|
const node = useRef<HTMLDivElement>(null);
|
||||||
const focusedItem = useRef<HTMLButtonElement>(null);
|
const focusedItem = useRef<HTMLButtonElement>(null);
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { useAppSelector } from 'pl-fe/hooks';
|
|
||||||
|
|
||||||
import DevelopersChallenge from './developers-challenge';
|
import DevelopersChallenge from './developers-challenge';
|
||||||
import DevelopersMenu from './developers-menu';
|
import DevelopersMenu from './developers-menu';
|
||||||
|
|
||||||
const Developers: React.FC = () => {
|
const Developers: React.FC = () => {
|
||||||
const isDeveloper = useAppSelector((state) => getSettings(state).get('isDeveloper'));
|
const { isDeveloper } = useSettingsStore().settings;
|
||||||
|
|
||||||
return isDeveloper ? <DevelopersMenu /> : <DevelopersChallenge />;
|
return isDeveloper ? <DevelopersMenu /> : <DevelopersChallenge />;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { SETTINGS_UPDATE, changeSetting, updateSettingsStore } from 'pl-fe/actions/settings';
|
import { changeSetting, updateSettingsStore } from 'pl-fe/actions/settings';
|
||||||
import List, { ListItem } from 'pl-fe/components/list';
|
import List, { ListItem } from 'pl-fe/components/list';
|
||||||
import {
|
import {
|
||||||
CardHeader,
|
CardHeader,
|
||||||
|
@ -14,7 +14,8 @@ import {
|
||||||
Textarea,
|
Textarea,
|
||||||
} from 'pl-fe/components/ui';
|
} from 'pl-fe/components/ui';
|
||||||
import SettingToggle from 'pl-fe/features/notifications/components/setting-toggle';
|
import SettingToggle from 'pl-fe/features/notifications/components/setting-toggle';
|
||||||
import { useAppSelector, useAppDispatch, useSettings } from 'pl-fe/hooks';
|
import { useAppDispatch } from 'pl-fe/hooks';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import toast from 'pl-fe/toast';
|
import toast from 'pl-fe/toast';
|
||||||
|
|
||||||
const isJSONValid = (text: any): boolean => {
|
const isJSONValid = (text: any): boolean => {
|
||||||
|
@ -35,10 +36,9 @@ const messages = defineMessages({
|
||||||
const SettingsStore: React.FC = () => {
|
const SettingsStore: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const settings = useSettings();
|
const { settings, userSettings, loadUserSettings } = useSettingsStore();
|
||||||
const settingsStore = useAppSelector(state => state.settings);
|
|
||||||
|
|
||||||
const [rawJSON, setRawJSON] = useState<string>(JSON.stringify(settingsStore, null, 2));
|
const [rawJSON, setRawJSON] = useState<string>(JSON.stringify(userSettings, null, 2));
|
||||||
const [jsonValid, setJsonValid] = useState(true);
|
const [jsonValid, setJsonValid] = useState(true);
|
||||||
const [isLoading, setLoading] = useState(false);
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ const SettingsStore: React.FC = () => {
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
dispatch(updateSettingsStore(settings)).then(() => {
|
dispatch(updateSettingsStore(settings)).then(() => {
|
||||||
dispatch({ type: SETTINGS_UPDATE, settings });
|
loadUserSettings(settings);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
toast.showAlertForError(error);
|
toast.showAlertForError(error);
|
||||||
|
@ -66,9 +66,9 @@ const SettingsStore: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRawJSON(JSON.stringify(settingsStore, null, 2));
|
setRawJSON(JSON.stringify(userSettings, null, 2));
|
||||||
setJsonValid(true);
|
setJsonValid(true);
|
||||||
}, [settingsStore]);
|
}, [userSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(messages.heading)} backHref='/developers'>
|
<Column label={intl.formatMessage(messages.heading)} backHref='/developers'>
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { useAccount } from 'pl-fe/api/hooks';
|
import { useAccount } from 'pl-fe/api/hooks';
|
||||||
import Account from 'pl-fe/components/account';
|
import Account from 'pl-fe/components/account';
|
||||||
import Badge from 'pl-fe/components/badge';
|
import Badge from 'pl-fe/components/badge';
|
||||||
|
@ -12,6 +11,7 @@ import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
|
||||||
import { Avatar, Stack, Text } from 'pl-fe/components/ui';
|
import { Avatar, Stack, Text } from 'pl-fe/components/ui';
|
||||||
import ActionButton from 'pl-fe/features/ui/components/action-button';
|
import ActionButton from 'pl-fe/features/ui/components/action-button';
|
||||||
import { useAppSelector } from 'pl-fe/hooks';
|
import { useAppSelector } from 'pl-fe/hooks';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { shortNumberFormat } from 'pl-fe/utils/numbers';
|
import { shortNumberFormat } from 'pl-fe/utils/numbers';
|
||||||
|
|
||||||
interface IAccountCard {
|
interface IAccountCard {
|
||||||
|
@ -21,7 +21,7 @@ interface IAccountCard {
|
||||||
const AccountCard: React.FC<IAccountCard> = ({ id }) => {
|
const AccountCard: React.FC<IAccountCard> = ({ id }) => {
|
||||||
const me = useAppSelector((state) => state.me);
|
const me = useAppSelector((state) => state.me);
|
||||||
const { account } = useAccount(id);
|
const { account } = useAccount(id);
|
||||||
const autoPlayGif = useAppSelector((state) => getSettings(state).get('autoPlayGif'));
|
const { autoPlayGif } = useSettingsStore().settings;
|
||||||
|
|
||||||
if (!account) return null;
|
if (!account) return null;
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,10 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { setComposeToStatus } from 'pl-fe/actions/compose';
|
import { setComposeToStatus } from 'pl-fe/actions/compose';
|
||||||
import { cancelDraftStatus } from 'pl-fe/actions/draft-statuses';
|
import { cancelDraftStatus } from 'pl-fe/actions/draft-statuses';
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { Button, HStack } from 'pl-fe/components/ui';
|
import { Button, HStack } from 'pl-fe/components/ui';
|
||||||
import { useAppDispatch } from 'pl-fe/hooks';
|
import { useAppDispatch } from 'pl-fe/hooks';
|
||||||
import { useModalsStore } from 'pl-fe/stores';
|
import { useModalsStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
|
|
||||||
import type { Status as StatusEntity } from 'pl-fe/normalizers';
|
import type { Status as StatusEntity } from 'pl-fe/normalizers';
|
||||||
import type { DraftStatus } from 'pl-fe/reducers/draft-statuses';
|
import type { DraftStatus } from 'pl-fe/reducers/draft-statuses';
|
||||||
|
@ -26,12 +26,13 @@ const DraftStatusActionBar: React.FC<IDraftStatusActionBar> = ({ source, status
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const { openModal } = useModalsStore();
|
const { openModal } = useModalsStore();
|
||||||
|
const { settings } = useSettingsStore();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const handleCancelClick = () => {
|
const handleCancelClick = () => {
|
||||||
dispatch((_, getState) => {
|
dispatch((_, getState) => {
|
||||||
|
|
||||||
const deleteModal = getSettings(getState()).get('deleteModal');
|
const deleteModal = settings.deleteModal;
|
||||||
if (!deleteModal) {
|
if (!deleteModal) {
|
||||||
dispatch(cancelDraftStatus(source.draft_id));
|
dispatch(cancelDraftStatus(source.draft_id));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import React, { useEffect, useState, useLayoutEffect, Suspense, useMemo } from 'react';
|
||||||
import React, { useEffect, useState, useLayoutEffect, Suspense } from 'react';
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import { chooseEmoji } from 'pl-fe/actions/emojis';
|
import { chooseEmoji } from 'pl-fe/actions/emojis';
|
||||||
import { changeSetting } from 'pl-fe/actions/settings';
|
import { changeSetting } from 'pl-fe/actions/settings';
|
||||||
import { useAppDispatch, useAppSelector, useTheme } from 'pl-fe/hooks';
|
import { useAppDispatch, useAppSelector, useSettings, useTheme } from 'pl-fe/hooks';
|
||||||
import { RootState } from 'pl-fe/store';
|
import { RootState } from 'pl-fe/store';
|
||||||
|
|
||||||
import { buildCustomEmojis } from '../../emoji';
|
import { buildCustomEmojis } from '../../emoji';
|
||||||
|
@ -71,15 +70,11 @@ const DEFAULTS = [
|
||||||
'ok_hand',
|
'ok_hand',
|
||||||
];
|
];
|
||||||
|
|
||||||
const getFrequentlyUsedEmojis = createSelector([
|
const getFrequentlyUsedEmojis = (emojiCounters: Record<string, number>) => {
|
||||||
(state: RootState) => state.settings.get('frequentlyUsedEmojis', ImmutableMap()),
|
let emojis = Object.keys(emojiCounters)
|
||||||
], (emojiCounters: ImmutableMap<string, number>) => {
|
.toSorted((a, b) => emojiCounters[a] - emojiCounters[b])
|
||||||
let emojis = emojiCounters
|
.toReversed()
|
||||||
.keySeq()
|
.slice(0, perLine * lines);
|
||||||
.sort((a, b) => emojiCounters.get(a)! - emojiCounters.get(b)!)
|
|
||||||
.reverse()
|
|
||||||
.slice(0, perLine * lines)
|
|
||||||
.toArray();
|
|
||||||
|
|
||||||
if (emojis.length < DEFAULTS.length) {
|
if (emojis.length < DEFAULTS.length) {
|
||||||
const uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji));
|
const uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji));
|
||||||
|
@ -87,7 +82,7 @@ const getFrequentlyUsedEmojis = createSelector([
|
||||||
}
|
}
|
||||||
|
|
||||||
return emojis;
|
return emojis;
|
||||||
});
|
};
|
||||||
|
|
||||||
const getCustomEmojis = createSelector([
|
const getCustomEmojis = createSelector([
|
||||||
(state: RootState) => state.custom_emojis,
|
(state: RootState) => state.custom_emojis,
|
||||||
|
@ -132,7 +127,9 @@ const EmojiPickerDropdown: React.FC<IEmojiPickerDropdown> = ({
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const customEmojis = useAppSelector((state) => getCustomEmojis(state));
|
const customEmojis = useAppSelector((state) => getCustomEmojis(state));
|
||||||
const frequentlyUsedEmojis = useAppSelector((state) => getFrequentlyUsedEmojis(state));
|
|
||||||
|
const settings = useSettings();
|
||||||
|
const frequentlyUsedEmojis = useMemo(() => getFrequentlyUsedEmojis(settings.frequentlyUsedEmojis), [settings.frequentlyUsedEmojis]);
|
||||||
|
|
||||||
const handlePick = (emoji: any) => {
|
const handlePick = (emoji: any) => {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
@ -238,6 +235,5 @@ const EmojiPickerDropdown: React.FC<IEmojiPickerDropdown> = ({
|
||||||
export {
|
export {
|
||||||
messages,
|
messages,
|
||||||
type IEmojiPickerDropdown,
|
type IEmojiPickerDropdown,
|
||||||
getFrequentlyUsedEmojis,
|
|
||||||
EmojiPickerDropdown as default,
|
EmojiPickerDropdown as default,
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { Link, useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { mentionCompose } from 'pl-fe/actions/compose';
|
import { mentionCompose } from 'pl-fe/actions/compose';
|
||||||
import { reblog, favourite, unreblog, unfavourite } from 'pl-fe/actions/interactions';
|
import { reblog, favourite, unreblog, unfavourite } from 'pl-fe/actions/interactions';
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
|
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
|
||||||
import Icon from 'pl-fe/components/icon';
|
import Icon from 'pl-fe/components/icon';
|
||||||
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
|
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
|
||||||
|
@ -15,6 +14,7 @@ import { HotKeys } from 'pl-fe/features/ui/components/hotkeys';
|
||||||
import { useAppDispatch, useAppSelector, useInstance, useLoggedIn } from 'pl-fe/hooks';
|
import { useAppDispatch, useAppSelector, useInstance, useLoggedIn } from 'pl-fe/hooks';
|
||||||
import { makeGetNotification } from 'pl-fe/selectors';
|
import { makeGetNotification } from 'pl-fe/selectors';
|
||||||
import { useModalsStore } from 'pl-fe/stores';
|
import { useModalsStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { NotificationType } from 'pl-fe/utils/notification';
|
import { NotificationType } from 'pl-fe/utils/notification';
|
||||||
|
|
||||||
import type { Notification as BaseNotification } from 'pl-api';
|
import type { Notification as BaseNotification } from 'pl-api';
|
||||||
|
@ -196,6 +196,7 @@ const Notification: React.FC<INotification> = (props) => {
|
||||||
|
|
||||||
const { me } = useLoggedIn();
|
const { me } = useLoggedIn();
|
||||||
const { openModal } = useModalsStore();
|
const { openModal } = useModalsStore();
|
||||||
|
const { settings } = useSettingsStore();
|
||||||
const notification = useAppSelector((state) => getNotification(state, props.notification));
|
const notification = useAppSelector((state) => getNotification(state, props.notification));
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -252,23 +253,21 @@ const Notification: React.FC<INotification> = (props) => {
|
||||||
|
|
||||||
const handleHotkeyBoost = useCallback((e?: KeyboardEvent) => {
|
const handleHotkeyBoost = useCallback((e?: KeyboardEvent) => {
|
||||||
if (status && typeof status === 'object') {
|
if (status && typeof status === 'object') {
|
||||||
dispatch((_, getState) => {
|
const boostModal = settings.boostModal;
|
||||||
const boostModal = getSettings(getState()).get('boostModal');
|
if (status.reblogged) {
|
||||||
if (status.reblogged) {
|
dispatch(unreblog(status));
|
||||||
dispatch(unreblog(status));
|
} else {
|
||||||
|
if (e?.shiftKey || !boostModal) {
|
||||||
|
dispatch(reblog(status));
|
||||||
} else {
|
} else {
|
||||||
if (e?.shiftKey || !boostModal) {
|
openModal('BOOST', {
|
||||||
dispatch(reblog(status));
|
statusId: status.id,
|
||||||
} else {
|
onReblog: (status) => {
|
||||||
openModal('BOOST', {
|
dispatch(reblog(status));
|
||||||
statusId: status.id,
|
},
|
||||||
onReblog: (status) => {
|
});
|
||||||
dispatch(reblog(status));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ import clsx from 'clsx';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { defaultSettings } from 'pl-fe/actions/settings';
|
|
||||||
import BackgroundShapes from 'pl-fe/features/ui/components/background-shapes';
|
import BackgroundShapes from 'pl-fe/features/ui/components/background-shapes';
|
||||||
import { useSystemTheme } from 'pl-fe/hooks';
|
import { useSystemTheme } from 'pl-fe/hooks';
|
||||||
import { normalizePlFeConfig } from 'pl-fe/normalizers';
|
import { normalizePlFeConfig } from 'pl-fe/normalizers';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { generateThemeCss } from 'pl-fe/utils/theme';
|
import { generateThemeCss } from 'pl-fe/utils/theme';
|
||||||
|
|
||||||
interface ISitePreview {
|
interface ISitePreview {
|
||||||
|
@ -16,9 +16,9 @@ interface ISitePreview {
|
||||||
/** Renders a preview of the website's style with the configuration applied. */
|
/** Renders a preview of the website's style with the configuration applied. */
|
||||||
const SitePreview: React.FC<ISitePreview> = ({ plFe }) => {
|
const SitePreview: React.FC<ISitePreview> = ({ plFe }) => {
|
||||||
const plFeConfig = useMemo(() => normalizePlFeConfig(plFe), [plFe]);
|
const plFeConfig = useMemo(() => normalizePlFeConfig(plFe), [plFe]);
|
||||||
const settings = defaultSettings.mergeDeep(plFeConfig.defaultSettings);
|
const { defaultSettings } = useSettingsStore();
|
||||||
|
|
||||||
const userTheme = settings.get('themeMode');
|
const userTheme = defaultSettings.themeMode;
|
||||||
const systemTheme = useSystemTheme();
|
const systemTheme = useSystemTheme();
|
||||||
|
|
||||||
const dark = ['dark', 'black'].includes(userTheme as string) || (userTheme === 'system' && systemTheme === 'black');
|
const dark = ['dark', 'black'].includes(userTheme as string) || (userTheme === 'system' && systemTheme === 'black');
|
||||||
|
|
|
@ -2,10 +2,10 @@ import React from 'react';
|
||||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { cancelScheduledStatus } from 'pl-fe/actions/scheduled-statuses';
|
import { cancelScheduledStatus } from 'pl-fe/actions/scheduled-statuses';
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { Button, HStack } from 'pl-fe/components/ui';
|
import { Button, HStack } from 'pl-fe/components/ui';
|
||||||
import { useAppDispatch } from 'pl-fe/hooks';
|
import { useAppDispatch } from 'pl-fe/hooks';
|
||||||
import { useModalsStore } from 'pl-fe/stores';
|
import { useModalsStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
|
|
||||||
import type { Status as StatusEntity } from 'pl-fe/normalizers';
|
import type { Status as StatusEntity } from 'pl-fe/normalizers';
|
||||||
|
|
||||||
|
@ -25,11 +25,12 @@ const ScheduledStatusActionBar: React.FC<IScheduledStatusActionBar> = ({ status
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { openModal } = useModalsStore();
|
const { openModal } = useModalsStore();
|
||||||
|
const { settings } = useSettingsStore();
|
||||||
|
|
||||||
const handleCancelClick = () => {
|
const handleCancelClick = () => {
|
||||||
dispatch((_, getState) => {
|
dispatch((_, getState) => {
|
||||||
|
|
||||||
const deleteModal = getSettings(getState()).get('deleteModal');
|
const deleteModal = settings.deleteModal;
|
||||||
if (!deleteModal) {
|
if (!deleteModal) {
|
||||||
dispatch(cancelScheduledStatus(status.id));
|
dispatch(cancelScheduledStatus(status.id));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { type ComposeReplyAction, mentionCompose, replyCompose } from 'pl-fe/actions/compose';
|
import { type ComposeReplyAction, mentionCompose, replyCompose } from 'pl-fe/actions/compose';
|
||||||
import { reblog, toggleFavourite, unreblog } from 'pl-fe/actions/interactions';
|
import { reblog, toggleFavourite, unreblog } from 'pl-fe/actions/interactions';
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
|
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
|
||||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||||
import StatusActionBar from 'pl-fe/components/status-action-bar';
|
import StatusActionBar from 'pl-fe/components/status-action-bar';
|
||||||
|
@ -20,6 +19,7 @@ import PendingStatus from 'pl-fe/features/ui/components/pending-status';
|
||||||
import { useAppDispatch, useAppSelector } from 'pl-fe/hooks';
|
import { useAppDispatch, useAppSelector } from 'pl-fe/hooks';
|
||||||
import { RootState } from 'pl-fe/store';
|
import { RootState } from 'pl-fe/store';
|
||||||
import { useModalsStore } from 'pl-fe/stores';
|
import { useModalsStore } from 'pl-fe/stores';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { textForScreenReader } from 'pl-fe/utils/status';
|
import { textForScreenReader } from 'pl-fe/utils/status';
|
||||||
|
|
||||||
import DetailedStatus from './detailed-status';
|
import DetailedStatus from './detailed-status';
|
||||||
|
@ -93,6 +93,7 @@ const Thread: React.FC<IThread> = ({
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const { openModal } = useModalsStore();
|
const { openModal } = useModalsStore();
|
||||||
|
const { settings } = useSettingsStore();
|
||||||
|
|
||||||
const { ancestorsIds, descendantsIds } = useAppSelector((state) => {
|
const { ancestorsIds, descendantsIds } = useAppSelector((state) => {
|
||||||
let ancestorsIds = ImmutableOrderedSet<string>();
|
let ancestorsIds = ImmutableOrderedSet<string>();
|
||||||
|
@ -136,7 +137,7 @@ const Thread: React.FC<IThread> = ({
|
||||||
|
|
||||||
const handleReblogClick = (status: SelectedStatus, e?: React.MouseEvent) => {
|
const handleReblogClick = (status: SelectedStatus, e?: React.MouseEvent) => {
|
||||||
dispatch((_, getState) => {
|
dispatch((_, getState) => {
|
||||||
const boostModal = getSettings(getState()).get('boostModal');
|
const boostModal = settings.boostModal;
|
||||||
if (status.reblogged) {
|
if (status.reblogged) {
|
||||||
dispatch(unreblog(status));
|
dispatch(unreblog(status));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { getLocale } from 'pl-fe/actions/settings';
|
import { getLocale } from 'pl-fe/actions/settings';
|
||||||
|
|
||||||
import { useAppSelector } from './useAppSelector';
|
|
||||||
|
|
||||||
/** Locales which should be presented in right-to-left. */
|
/** Locales which should be presented in right-to-left. */
|
||||||
const RTL_LOCALES = ['ar', 'ckb', 'fa', 'he'];
|
const RTL_LOCALES = ['ar', 'ckb', 'fa', 'he'];
|
||||||
|
|
||||||
|
@ -12,7 +10,8 @@ interface UseLocaleResult {
|
||||||
|
|
||||||
/** Get valid locale from settings. */
|
/** Get valid locale from settings. */
|
||||||
const useLocale = (fallback = 'en'): UseLocaleResult => {
|
const useLocale = (fallback = 'en'): UseLocaleResult => {
|
||||||
const locale = useAppSelector((state) => getLocale(state, fallback));
|
// TODO use useSettingsStore directly
|
||||||
|
const locale = getLocale(fallback);
|
||||||
|
|
||||||
const direction: 'ltr' | 'rtl' =
|
const direction: 'ltr' | 'rtl' =
|
||||||
RTL_LOCALES.includes(locale)
|
RTL_LOCALES.includes(locale)
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
import { useMemo } from 'react';
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
|
|
||||||
import { getSettings } from 'pl-fe/actions/settings';
|
|
||||||
import { settingsSchema } from 'pl-fe/schemas/pl-fe/settings';
|
|
||||||
|
|
||||||
import { useAppSelector } from './useAppSelector';
|
|
||||||
|
|
||||||
/** Get the user settings from the store */
|
/** Get the user settings from the store */
|
||||||
const useSettings = () => {
|
const useSettings = () => useSettingsStore().settings;
|
||||||
const data = useAppSelector((state) => getSettings(state));
|
|
||||||
return useMemo(() => settingsSchema.parse(data.toJS()), [data]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { useSettings };
|
export { useSettings };
|
||||||
|
|
|
@ -37,7 +37,7 @@ import push_notifications from './push-notifications';
|
||||||
import scheduled_statuses from './scheduled-statuses';
|
import scheduled_statuses from './scheduled-statuses';
|
||||||
import search from './search';
|
import search from './search';
|
||||||
import security from './security';
|
import security from './security';
|
||||||
import settings from './settings';
|
// import settings from './settings';
|
||||||
import status_lists from './status-lists';
|
import status_lists from './status-lists';
|
||||||
import statuses from './statuses';
|
import statuses from './statuses';
|
||||||
import suggestions from './suggestions';
|
import suggestions from './suggestions';
|
||||||
|
@ -81,7 +81,7 @@ const reducers = {
|
||||||
scheduled_statuses,
|
scheduled_statuses,
|
||||||
search,
|
search,
|
||||||
security,
|
security,
|
||||||
settings,
|
// settings,
|
||||||
status_lists,
|
status_lists,
|
||||||
statuses,
|
statuses,
|
||||||
suggestions,
|
suggestions,
|
||||||
|
|
|
@ -46,7 +46,7 @@ const persistPlFeConfig = (plFeConfig: ImmutableMap<string, any>, host: string)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const importPlFeConfig = (state: ImmutableMap<string, any>, plFeConfig: ImmutableMap<string, any>, host: string) => {
|
const importPlFeConfig = (plFeConfig: ImmutableMap<string, any>, host: string) => {
|
||||||
persistPlFeConfig(plFeConfig, host);
|
persistPlFeConfig(plFeConfig, host);
|
||||||
return plFeConfig;
|
return plFeConfig;
|
||||||
};
|
};
|
||||||
|
@ -58,7 +58,7 @@ const plfe = (state = initialState, action: Record<string, any>) => {
|
||||||
case PLFE_CONFIG_REMEMBER_SUCCESS:
|
case PLFE_CONFIG_REMEMBER_SUCCESS:
|
||||||
return fromJS(action.plFeConfig);
|
return fromJS(action.plFeConfig);
|
||||||
case PLFE_CONFIG_REQUEST_SUCCESS:
|
case PLFE_CONFIG_REQUEST_SUCCESS:
|
||||||
return importPlFeConfig(state, fromJS(action.plFeConfig) as ImmutableMap<string, any>, action.host);
|
return importPlFeConfig(fromJS(action.plFeConfig) as ImmutableMap<string, any>, action.host);
|
||||||
case PLFE_CONFIG_REQUEST_FAIL:
|
case PLFE_CONFIG_REQUEST_FAIL:
|
||||||
return fallbackState.mergeDeep(state);
|
return fallbackState.mergeDeep(state);
|
||||||
case ADMIN_CONFIG_UPDATE_SUCCESS:
|
case ADMIN_CONFIG_UPDATE_SUCCESS:
|
||||||
|
|
|
@ -1,61 +1,31 @@
|
||||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
import { produce } from 'immer';
|
||||||
import { AnyAction } from 'redux';
|
import { AnyAction } from 'redux';
|
||||||
|
|
||||||
import { LANGUAGE_USE } from 'pl-fe/actions/languages';
|
import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings';
|
||||||
import { ME_FETCH_SUCCESS } from 'pl-fe/actions/me';
|
|
||||||
|
|
||||||
import { EMOJI_CHOOSE } from '../actions/emojis';
|
|
||||||
import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications';
|
import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications';
|
||||||
import { SEARCH_FILTER_SET } from '../actions/search';
|
import { SEARCH_FILTER_SET } from '../actions/search';
|
||||||
import {
|
import { SETTING_CHANGE } from '../actions/settings';
|
||||||
SETTING_CHANGE,
|
|
||||||
SETTING_SAVE,
|
|
||||||
SETTINGS_UPDATE,
|
|
||||||
FE_NAME,
|
|
||||||
} from '../actions/settings';
|
|
||||||
|
|
||||||
import type { Emoji } from 'pl-fe/features/emoji';
|
type State = Partial<Settings>;
|
||||||
import type { APIEntity } from 'pl-fe/types/entities';
|
|
||||||
|
|
||||||
type State = ImmutableMap<string, any>;
|
|
||||||
|
|
||||||
const updateFrequentEmojis = (state: State, emoji: Emoji) =>
|
|
||||||
state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, (count: number) => count + 1)).set('saved', false);
|
|
||||||
|
|
||||||
const updateFrequentLanguages = (state: State, language: string) =>
|
|
||||||
state.update('frequentlyUsedLanguages', ImmutableMap<string, number>(), map => map.update(language, 0, (count: number) => count + 1)).set('saved', false);
|
|
||||||
|
|
||||||
const importSettings = (state: State, account: APIEntity) => {
|
|
||||||
account = fromJS(account);
|
|
||||||
const prefs = account.getIn(['settings_store', FE_NAME], ImmutableMap());
|
|
||||||
return state.merge(prefs) as State;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Default settings are in action/settings.js
|
// Default settings are in action/settings.js
|
||||||
//
|
//
|
||||||
// Settings should be accessed with `getSettings(getState()).getIn(...)`
|
// Settings should be accessed with `getSettings(getState()).getIn(...)`
|
||||||
// instead of directly from the state.
|
// instead of directly from the state.
|
||||||
const settings = (
|
const settings = (
|
||||||
state: State = ImmutableMap<string, any>({ saved: true }),
|
state: State = settingsSchema.partial().parse({}),
|
||||||
action: AnyAction,
|
action: AnyAction,
|
||||||
): State => {
|
): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ME_FETCH_SUCCESS:
|
|
||||||
return importSettings(state, action.me);
|
|
||||||
case NOTIFICATIONS_FILTER_SET:
|
case NOTIFICATIONS_FILTER_SET:
|
||||||
case SEARCH_FILTER_SET:
|
case SEARCH_FILTER_SET:
|
||||||
case SETTING_CHANGE:
|
case SETTING_CHANGE:
|
||||||
return state
|
return produce(state, draft => {
|
||||||
.setIn(action.path, action.value)
|
// @ts-ignore
|
||||||
.set('saved', false);
|
draft[action.path] = action.value;
|
||||||
case EMOJI_CHOOSE:
|
draft.saved = false;
|
||||||
return updateFrequentEmojis(state, action.emoji);
|
});
|
||||||
case LANGUAGE_USE:
|
|
||||||
return updateFrequentLanguages(state, action.language);
|
|
||||||
case SETTING_SAVE:
|
|
||||||
return state.set('saved', true);
|
|
||||||
case SETTINGS_UPDATE:
|
|
||||||
return ImmutableMap<string, any>(fromJS(action.settings));
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,9 +77,24 @@ const settingsSchema = z.object({
|
||||||
advanced: z.boolean().catch(false),
|
advanced: z.boolean().catch(false),
|
||||||
show: z.boolean().catch(true),
|
show: z.boolean().catch(true),
|
||||||
}),
|
}),
|
||||||
|
sounds: z.record(z.boolean()).catch({}),
|
||||||
}),
|
}),
|
||||||
autoTranslate: z.boolean().catch(false),
|
autoTranslate: z.boolean().catch(false),
|
||||||
knownLanguages: z.array(z.string()).catch([]),
|
knownLanguages: z.array(z.string()).catch([]),
|
||||||
|
frequentlyUsedEmojis: z.record(z.number()).catch({}),
|
||||||
|
frequentlyUsedLanguages: z.record(z.number()).catch({}),
|
||||||
|
timelines: z.record(coerceObject({
|
||||||
|
shows: coerceObject({
|
||||||
|
reblog: z.boolean().catch(true),
|
||||||
|
reply: z.boolean().catch(true),
|
||||||
|
direct: z.boolean().catch(false),
|
||||||
|
}),
|
||||||
|
other: coerceObject({
|
||||||
|
onlyMedia: z.boolean().catch(false),
|
||||||
|
}),
|
||||||
|
})).catch({}),
|
||||||
|
|
||||||
|
saved: z.boolean().catch(true),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Settings = z.infer<typeof settingsSchema>;
|
type Settings = z.infer<typeof settingsSchema>;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import {
|
import {
|
||||||
Map as ImmutableMap,
|
|
||||||
List as ImmutableList,
|
List as ImmutableList,
|
||||||
OrderedSet as ImmutableOrderedSet,
|
OrderedSet as ImmutableOrderedSet,
|
||||||
Record as ImmutableRecord,
|
Record as ImmutableRecord,
|
||||||
} from 'immutable';
|
} from 'immutable';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import { getLocale, getSettings } from 'pl-fe/actions/settings';
|
import { getLocale } from 'pl-fe/actions/settings';
|
||||||
import { Entities } from 'pl-fe/entity-store/entities';
|
import { Entities } from 'pl-fe/entity-store/entities';
|
||||||
|
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||||
import { getDomain } from 'pl-fe/utils/accounts';
|
import { getDomain } from 'pl-fe/utils/accounts';
|
||||||
import { validId } from 'pl-fe/utils/auth';
|
import { validId } from 'pl-fe/utils/auth';
|
||||||
import ConfigDB from 'pl-fe/utils/config-db';
|
import ConfigDB from 'pl-fe/utils/config-db';
|
||||||
|
@ -137,7 +137,7 @@ const makeGetStatus = () => createSelector(
|
||||||
getFilters,
|
getFilters,
|
||||||
(state: RootState) => state.me,
|
(state: RootState) => state.me,
|
||||||
(state: RootState) => state.auth.client.features,
|
(state: RootState) => state.auth.client.features,
|
||||||
(state: RootState) => getLocale(state, 'en'),
|
(state: RootState) => getLocale('en'),
|
||||||
],
|
],
|
||||||
|
|
||||||
(statusBase, statusReblog, statusQuote, statusGroup, poll, username, filters, me, features, locale) => {
|
(statusBase, statusReblog, statusQuote, statusGroup, poll, username, filters, me, features, locale) => {
|
||||||
|
@ -333,7 +333,7 @@ const makeGetRemoteInstance = () =>
|
||||||
type ColumnQuery = { type: string; prefix?: string };
|
type ColumnQuery = { type: string; prefix?: string };
|
||||||
|
|
||||||
const makeGetStatusIds = () => createSelector([
|
const makeGetStatusIds = () => createSelector([
|
||||||
(state: RootState, { type, prefix }: ColumnQuery) => getSettings(state).get(prefix || type, ImmutableMap()),
|
(state: RootState, { type, prefix }: ColumnQuery) => useSettingsStore.getState().settings.timelines[prefix || type],
|
||||||
(state: RootState, { type }: ColumnQuery) => state.timelines.get(type)?.items || ImmutableOrderedSet(),
|
(state: RootState, { type }: ColumnQuery) => state.timelines.get(type)?.items || ImmutableOrderedSet(),
|
||||||
(state: RootState) => state.statuses,
|
(state: RootState) => state.statuses,
|
||||||
], (columnSettings: any, statusIds: ImmutableOrderedSet<string>, statuses) =>
|
], (columnSettings: any, statusIds: ImmutableOrderedSet<string>, statuses) =>
|
||||||
|
|
74
packages/pl-fe/src/stores/settings.ts
Normal file
74
packages/pl-fe/src/stores/settings.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { produce } from 'immer';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings';
|
||||||
|
|
||||||
|
import type { Emoji } from 'pl-fe/features/emoji';
|
||||||
|
import type { APIEntity } from 'pl-fe/types/entities';
|
||||||
|
|
||||||
|
const settingsSchemaPartial = settingsSchema.partial();
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
defaultSettings: Settings;
|
||||||
|
userSettings: Partial<Settings>;
|
||||||
|
|
||||||
|
settings: Settings;
|
||||||
|
|
||||||
|
loadDefaultSettings: (settings: APIEntity) => void;
|
||||||
|
loadUserSettings: (settings: APIEntity) => void;
|
||||||
|
userSettingsSaving: () => void;
|
||||||
|
rememberEmojiUse: (emoji: Emoji) => void;
|
||||||
|
rememberLanguageUse: (language: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergeSettings = (state: State) => state.settings = { ...state.defaultSettings, ...state.userSettings };
|
||||||
|
|
||||||
|
const useSettingsStore = create<State>((set) => ({
|
||||||
|
defaultSettings: settingsSchema.parse({}),
|
||||||
|
userSettings: {},
|
||||||
|
|
||||||
|
settings: settingsSchema.parse({}),
|
||||||
|
|
||||||
|
loadDefaultSettings: (settings: APIEntity) => set(produce((state: State) => {
|
||||||
|
if (typeof settings !== 'object') return;
|
||||||
|
|
||||||
|
state.defaultSettings = settingsSchema.parse(settings);
|
||||||
|
mergeSettings(state);
|
||||||
|
})),
|
||||||
|
|
||||||
|
loadUserSettings: (settings?: APIEntity) => set(produce((state: State) => {
|
||||||
|
if (typeof settings !== 'object') return;
|
||||||
|
|
||||||
|
state.userSettings = settingsSchemaPartial.parse(settings);
|
||||||
|
mergeSettings(state);
|
||||||
|
})),
|
||||||
|
|
||||||
|
userSettingsSaving: () => set(produce((state: State) => {
|
||||||
|
state.userSettings.saved = true;
|
||||||
|
|
||||||
|
mergeSettings(state);
|
||||||
|
})),
|
||||||
|
|
||||||
|
rememberEmojiUse: (emoji: Emoji) => set(produce((state: State) => {
|
||||||
|
const settings = state.userSettings;
|
||||||
|
if (!settings.frequentlyUsedEmojis) settings.frequentlyUsedEmojis = {};
|
||||||
|
|
||||||
|
settings.frequentlyUsedEmojis[emoji.id] = (settings.frequentlyUsedEmojis[emoji.id] || 0) + 1;
|
||||||
|
settings.saved = false;
|
||||||
|
|
||||||
|
mergeSettings(state);
|
||||||
|
})),
|
||||||
|
|
||||||
|
rememberLanguageUse: (language: string) => set(produce((state: State) => {
|
||||||
|
const settings = state.userSettings;
|
||||||
|
if (!settings.frequentlyUsedLanguages) settings.frequentlyUsedLanguages = {};
|
||||||
|
|
||||||
|
settings.frequentlyUsedLanguages[language] = (settings.frequentlyUsedLanguages[language] || 0) + 1;
|
||||||
|
settings.saved = false;
|
||||||
|
|
||||||
|
mergeSettings(state);
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export { useSettingsStore };
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
import { Map as ImmutableMap, type Collection } from 'immutable';
|
import { Settings } from 'pl-fe/schemas/pl-fe/settings';
|
||||||
|
|
||||||
import type { Status } from 'pl-fe/normalizers';
|
import type { Status } from 'pl-fe/normalizers';
|
||||||
|
|
||||||
const shouldFilter = (
|
const shouldFilter = (
|
||||||
status: Pick<Status, 'in_reply_to_id' | 'visibility' | 'reblog_id'>,
|
status: Pick<Status, 'in_reply_to_id' | 'visibility' | 'reblog_id'>,
|
||||||
columnSettings: Collection<any, any>,
|
columnSettings: Settings['timelines'][''],
|
||||||
) => {
|
) => {
|
||||||
const shows = ImmutableMap({
|
const fallback = {
|
||||||
|
reblog: true,
|
||||||
|
reply: true,
|
||||||
|
direct: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const shows = {
|
||||||
reblog: status.reblog_id !== null,
|
reblog: status.reblog_id !== null,
|
||||||
reply: status.in_reply_to_id !== null,
|
reply: status.in_reply_to_id !== null,
|
||||||
direct: status.visibility === 'direct',
|
direct: status.visibility === 'direct',
|
||||||
});
|
};
|
||||||
|
|
||||||
return shows.some((value, key) => columnSettings.getIn(['shows', key]) === false && value);
|
return Object.entries(shows).some(([key, value]) => (columnSettings?.shows || fallback)[key as 'reblog' | 'reply' | 'direct'] === false && value);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { shouldFilter };
|
export { shouldFilter };
|
||||||
|
|
Loading…
Reference in a new issue