kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Remove Immutable from accounts reducer
rodzic
12c81d9847
commit
32006407a3
|
@ -1,12 +1,6 @@
|
||||||
import {
|
import { produce } from 'immer';
|
||||||
Map as ImmutableMap,
|
|
||||||
List as ImmutableList,
|
|
||||||
OrderedSet as ImmutableOrderedSet,
|
|
||||||
fromJS,
|
|
||||||
} from 'immutable';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ADMIN_USERS_FETCH_SUCCESS,
|
|
||||||
ADMIN_USERS_TAG_REQUEST,
|
ADMIN_USERS_TAG_REQUEST,
|
||||||
ADMIN_USERS_TAG_SUCCESS,
|
ADMIN_USERS_TAG_SUCCESS,
|
||||||
ADMIN_USERS_TAG_FAIL,
|
ADMIN_USERS_TAG_FAIL,
|
||||||
|
@ -31,204 +25,114 @@ import {
|
||||||
ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP,
|
ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP,
|
||||||
} from 'soapbox/actions/importer';
|
} from 'soapbox/actions/importer';
|
||||||
import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
|
import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
|
||||||
import { normalizeAccount } from 'soapbox/normalizers/account';
|
import { Account, accountSchema } from 'soapbox/schemas';
|
||||||
import { normalizeId } from 'soapbox/utils/normalizers';
|
|
||||||
|
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
import type { APIEntity } from 'soapbox/types/entities';
|
|
||||||
|
|
||||||
type AccountRecord = ReturnType<typeof normalizeAccount>;
|
export interface ReducerAccount extends Omit<Account, 'moved'> {
|
||||||
type AccountMap = ImmutableMap<string, any>;
|
|
||||||
type APIEntities = Array<APIEntity>;
|
|
||||||
|
|
||||||
export interface ReducerAccount extends AccountRecord {
|
|
||||||
moved: string | null;
|
moved: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = ImmutableMap<any, ReducerAccount>;
|
type State = Record<string, ReducerAccount>;
|
||||||
|
|
||||||
const initialState: State = ImmutableMap();
|
const initialState: State = {};
|
||||||
|
|
||||||
const minifyAccount = (account: AccountRecord): ReducerAccount => {
|
/** Convert sub-entities into string IDs (or null). */
|
||||||
return account.mergeWith((o, n) => n || o, {
|
function minifyAccount(account: Account): ReducerAccount {
|
||||||
moved: normalizeId(account.getIn(['moved', 'id'])),
|
return { ...account, moved: account.moved?.id ?? null };
|
||||||
}) as ReducerAccount;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const fixAccount = (state: State, account: APIEntity) => {
|
/** Parse account data, and import valid accounts into the state. */
|
||||||
const normalized = minifyAccount(normalizeAccount(account));
|
function fixAccount(state: State, data: unknown): State {
|
||||||
return state.set(account.id, normalized);
|
const result = accountSchema.safeParse(data);
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeAccounts = (state: State, accounts: ImmutableList<AccountMap>) => {
|
if (!result.success) {
|
||||||
accounts.forEach(account => {
|
return state;
|
||||||
state = fixAccount(state, account);
|
|
||||||
});
|
|
||||||
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
const importAccountFromChat = (
|
|
||||||
state: State,
|
|
||||||
chat: APIEntity,
|
|
||||||
): State => fixAccount(state, chat.account);
|
|
||||||
|
|
||||||
const importAccountsFromChats = (state: State, chats: APIEntities): State =>
|
|
||||||
state.withMutations(mutable =>
|
|
||||||
chats.forEach(chat => importAccountFromChat(mutable, chat)));
|
|
||||||
|
|
||||||
const addTags = (
|
|
||||||
state: State,
|
|
||||||
accountIds: Array<string>,
|
|
||||||
tags: Array<string>,
|
|
||||||
): State => {
|
|
||||||
return state.withMutations(state => {
|
|
||||||
accountIds.forEach(id => {
|
|
||||||
state.updateIn([id, 'pleroma', 'tags'], ImmutableList(), v =>
|
|
||||||
ImmutableOrderedSet(fromJS(v)).union(tags).toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
tags.forEach(tag => {
|
|
||||||
switch (tag) {
|
|
||||||
case 'verified':
|
|
||||||
state.setIn([id, 'verified'], true);
|
|
||||||
break;
|
|
||||||
case 'donor':
|
|
||||||
state.setIn([id, 'donor'], true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeTags = (
|
|
||||||
state: State,
|
|
||||||
accountIds: Array<string>,
|
|
||||||
tags: Array<string>,
|
|
||||||
): State => {
|
|
||||||
return state.withMutations(state => {
|
|
||||||
accountIds.forEach(id => {
|
|
||||||
state.updateIn([id, 'pleroma', 'tags'], ImmutableList(), v =>
|
|
||||||
ImmutableOrderedSet(fromJS(v)).subtract(tags).toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
tags.forEach(tag => {
|
|
||||||
switch (tag) {
|
|
||||||
case 'verified':
|
|
||||||
state.setIn([id, 'verified'], false);
|
|
||||||
break;
|
|
||||||
case 'donor':
|
|
||||||
state.setIn([id, 'donor'], false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const setActive = (state: State, accountIds: Array<string>, active: boolean): State => {
|
|
||||||
return state.withMutations(state => {
|
|
||||||
accountIds.forEach(id => {
|
|
||||||
state.setIn([id, 'pleroma', 'is_active'], active);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const permissionGroupFields: Record<string, string> = {
|
|
||||||
admin: 'is_admin',
|
|
||||||
moderator: 'is_moderator',
|
|
||||||
};
|
|
||||||
|
|
||||||
const addPermission = (
|
|
||||||
state: State,
|
|
||||||
accountIds: Array<string>,
|
|
||||||
permissionGroup: string,
|
|
||||||
): State => {
|
|
||||||
const field = permissionGroupFields[permissionGroup];
|
|
||||||
if (!field) return state;
|
|
||||||
|
|
||||||
return state.withMutations(state => {
|
|
||||||
accountIds.forEach(id => {
|
|
||||||
state.setIn([id, 'pleroma', field], true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const removePermission = (
|
|
||||||
state: State,
|
|
||||||
accountIds: Array<string>,
|
|
||||||
permissionGroup: string,
|
|
||||||
): State => {
|
|
||||||
const field = permissionGroupFields[permissionGroup];
|
|
||||||
if (!field) return state;
|
|
||||||
|
|
||||||
return state.withMutations(state => {
|
|
||||||
accountIds.forEach(id => {
|
|
||||||
state.setIn([id, 'pleroma', field], false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildAccount = (adminUser: ImmutableMap<string, any>): AccountRecord => normalizeAccount({
|
|
||||||
id: adminUser.get('id'),
|
|
||||||
username: adminUser.get('nickname').split('@')[0],
|
|
||||||
acct: adminUser.get('nickname'),
|
|
||||||
display_name: adminUser.get('display_name'),
|
|
||||||
display_name_html: adminUser.get('display_name'),
|
|
||||||
url: adminUser.get('url'),
|
|
||||||
avatar: adminUser.get('avatar'),
|
|
||||||
avatar_static: adminUser.get('avatar'),
|
|
||||||
created_at: adminUser.get('created_at'),
|
|
||||||
pleroma: {
|
|
||||||
is_active: adminUser.get('is_active'),
|
|
||||||
is_confirmed: adminUser.get('is_confirmed'),
|
|
||||||
is_admin: adminUser.getIn(['roles', 'admin']),
|
|
||||||
is_moderator: adminUser.getIn(['roles', 'moderator']),
|
|
||||||
tags: adminUser.get('tags'),
|
|
||||||
},
|
|
||||||
source: {
|
|
||||||
pleroma: {
|
|
||||||
actor_type: adminUser.get('actor_type'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
should_refetch: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mergeAdminUser = (
|
|
||||||
account: AccountRecord,
|
|
||||||
adminUser: ImmutableMap<string, any>,
|
|
||||||
) => {
|
|
||||||
return account.withMutations(account => {
|
|
||||||
account.set('display_name', adminUser.get('display_name'));
|
|
||||||
account.set('avatar', adminUser.get('avatar'));
|
|
||||||
account.set('avatar_static', adminUser.get('avatar'));
|
|
||||||
account.setIn(['pleroma', 'is_active'], adminUser.get('is_active'));
|
|
||||||
account.setIn(['pleroma', 'is_admin'], adminUser.getIn(['roles', 'admin']));
|
|
||||||
account.setIn(['pleroma', 'is_moderator'], adminUser.getIn(['roles', 'moderator']));
|
|
||||||
account.setIn(['pleroma', 'is_confirmed'], adminUser.get('is_confirmed'));
|
|
||||||
account.setIn(['pleroma', 'tags'], adminUser.get('tags'));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const importAdminUser = (state: State, adminUser: ImmutableMap<string, any>): State => {
|
|
||||||
const id = adminUser.get('id');
|
|
||||||
const account = state.get(id);
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
return state.set(id, minifyAccount(buildAccount(adminUser)));
|
|
||||||
} else {
|
|
||||||
return state.set(id, minifyAccount(mergeAdminUser(account, adminUser)));
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const importAdminUsers = (state: State, adminUsers: Array<Record<string, any>>): State => {
|
const account = result.data;
|
||||||
return state.withMutations((state: State) => {
|
const normalized = minifyAccount(account);
|
||||||
adminUsers.filter(adminUser => !adminUser.account).forEach(adminUser => {
|
|
||||||
importAdminUser(state, ImmutableMap(fromJS(adminUser)));
|
return { ...state, [normalized.id]: normalized };
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/** Import valid accounts into the state. */
|
||||||
|
function normalizeAccounts(state: State, data: unknown[]): State {
|
||||||
|
return produce(state, draft => {
|
||||||
|
data.forEach((item) => fixAccount(draft, item));
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function importAccountFromChat(state: State, chat: any): State {
|
||||||
|
return fixAccount(state, chat.account);
|
||||||
|
}
|
||||||
|
|
||||||
|
function importAccountsFromChats(state: State, chats: unknown[]): State {
|
||||||
|
return produce(state, draft => {
|
||||||
|
chats.forEach(chat => importAccountFromChat(draft, chat));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTags(state: State, accountIds: string[], tags: string[]): State {
|
||||||
|
return produce(state, draft => {
|
||||||
|
for (const id of accountIds) {
|
||||||
|
const account = draft[id];
|
||||||
|
if (!account) continue;
|
||||||
|
|
||||||
|
const tagSet = new Set([...account.pleroma.tags, ...tags]);
|
||||||
|
account.pleroma.tags = [...tagSet];
|
||||||
|
|
||||||
|
if (tagSet.has('verified')) {
|
||||||
|
account.verified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTags (state: State, accountIds: string[], tags: string[]): State {
|
||||||
|
return produce(state, draft => {
|
||||||
|
for (const id of accountIds) {
|
||||||
|
const account = draft[id];
|
||||||
|
if (!account) continue;
|
||||||
|
|
||||||
|
const tagSet = new Set(account.pleroma.tags).difference(new Set(tags));
|
||||||
|
account.pleroma.tags = [...tagSet];
|
||||||
|
|
||||||
|
if (tagSet.has('verified')) {
|
||||||
|
account.verified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActive(state: State, accountIds: Array<string>, active: boolean): State {
|
||||||
|
return produce(state, draft => {
|
||||||
|
for (const id of accountIds) {
|
||||||
|
const account = draft[id];
|
||||||
|
if (!account) continue;
|
||||||
|
|
||||||
|
account.pleroma.deactivated = !active;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPermission(state: State, accountIds: string[], permissionGroup: string, value: boolean): State {
|
||||||
|
return produce(state, draft => {
|
||||||
|
for (const id of accountIds) {
|
||||||
|
const account = draft[id];
|
||||||
|
if (!account) continue;
|
||||||
|
|
||||||
|
switch (permissionGroup) {
|
||||||
|
case 'admin':
|
||||||
|
account.pleroma.is_admin = value;
|
||||||
|
break;
|
||||||
|
case 'moderator':
|
||||||
|
account.pleroma.is_moderator = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default function accounts(state: State = initialState, action: AnyAction): State {
|
export default function accounts(state: State = initialState, action: AnyAction): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
@ -255,19 +159,17 @@ export default function accounts(state: State = initialState, action: AnyAction)
|
||||||
case ADMIN_ADD_PERMISSION_GROUP_REQUEST:
|
case ADMIN_ADD_PERMISSION_GROUP_REQUEST:
|
||||||
case ADMIN_ADD_PERMISSION_GROUP_SUCCESS:
|
case ADMIN_ADD_PERMISSION_GROUP_SUCCESS:
|
||||||
case ADMIN_REMOVE_PERMISSION_GROUP_FAIL:
|
case ADMIN_REMOVE_PERMISSION_GROUP_FAIL:
|
||||||
return addPermission(state, action.accountIds, action.permissionGroup);
|
return setPermission(state, action.accountIds, action.permissionGroup, true);
|
||||||
case ADMIN_REMOVE_PERMISSION_GROUP_REQUEST:
|
case ADMIN_REMOVE_PERMISSION_GROUP_REQUEST:
|
||||||
case ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS:
|
case ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS:
|
||||||
case ADMIN_ADD_PERMISSION_GROUP_FAIL:
|
case ADMIN_ADD_PERMISSION_GROUP_FAIL:
|
||||||
return removePermission(state, action.accountIds, action.permissionGroup);
|
return setPermission(state, action.accountIds, action.permissionGroup, false);
|
||||||
case ADMIN_USERS_DELETE_REQUEST:
|
case ADMIN_USERS_DELETE_REQUEST:
|
||||||
case ADMIN_USERS_DEACTIVATE_REQUEST:
|
case ADMIN_USERS_DEACTIVATE_REQUEST:
|
||||||
return setActive(state, action.accountIds, false);
|
return setActive(state, action.accountIds, false);
|
||||||
case ADMIN_USERS_DELETE_FAIL:
|
case ADMIN_USERS_DELETE_FAIL:
|
||||||
case ADMIN_USERS_DEACTIVATE_FAIL:
|
case ADMIN_USERS_DEACTIVATE_FAIL:
|
||||||
return setActive(state, action.accountIds, true);
|
return setActive(state, action.accountIds, true);
|
||||||
case ADMIN_USERS_FETCH_SUCCESS:
|
|
||||||
return importAdminUsers(state, action.users);
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import emojify from 'soapbox/features/emoji';
|
||||||
import { unescapeHTML } from 'soapbox/utils/html';
|
import { unescapeHTML } from 'soapbox/utils/html';
|
||||||
|
|
||||||
import { customEmojiSchema } from './custom-emoji';
|
import { customEmojiSchema } from './custom-emoji';
|
||||||
import { relationshipSchema } from './relationship';
|
|
||||||
import { coerceObject, contentSchema, filteredArray, makeCustomEmojiMap } from './utils';
|
import { coerceObject, contentSchema, filteredArray, makeCustomEmojiMap } from './utils';
|
||||||
|
|
||||||
import type { Resolve } from 'soapbox/utils/types';
|
import type { Resolve } from 'soapbox/utils/types';
|
||||||
|
@ -73,7 +72,7 @@ const baseAccountSchema = z.object({
|
||||||
birthday: birthdaySchema.nullish().catch(undefined),
|
birthday: birthdaySchema.nullish().catch(undefined),
|
||||||
location: z.string().optional().catch(undefined),
|
location: z.string().optional().catch(undefined),
|
||||||
}).optional().catch(undefined),
|
}).optional().catch(undefined),
|
||||||
pleroma: z.object({
|
pleroma: coerceObject({
|
||||||
accepts_chat_messages: z.boolean().catch(false),
|
accepts_chat_messages: z.boolean().catch(false),
|
||||||
accepts_email_list: z.boolean().catch(false),
|
accepts_email_list: z.boolean().catch(false),
|
||||||
also_known_as: z.array(z.string().url()).catch([]),
|
also_known_as: z.array(z.string().url()).catch([]),
|
||||||
|
@ -91,12 +90,11 @@ const baseAccountSchema = z.object({
|
||||||
is_moderator: z.boolean().catch(false),
|
is_moderator: z.boolean().catch(false),
|
||||||
is_suggested: z.boolean().catch(false),
|
is_suggested: z.boolean().catch(false),
|
||||||
location: z.string().optional().catch(undefined),
|
location: z.string().optional().catch(undefined),
|
||||||
notification_settings: z.object({
|
notification_settings: coerceObject({
|
||||||
block_from_strangers: z.boolean().catch(false),
|
block_from_strangers: z.boolean().catch(false),
|
||||||
}).optional().catch(undefined),
|
}),
|
||||||
relationship: relationshipSchema.optional().catch(undefined),
|
|
||||||
tags: z.array(z.string()).catch([]),
|
tags: z.array(z.string()).catch([]),
|
||||||
}).optional().catch(undefined),
|
}),
|
||||||
roles: filteredArray(roleSchema),
|
roles: filteredArray(roleSchema),
|
||||||
source: z.object({
|
source: z.object({
|
||||||
approved: z.boolean().catch(true),
|
approved: z.boolean().catch(true),
|
||||||
|
@ -170,13 +168,8 @@ const transformAccount = <T extends TransformableAccount>({ pleroma, other_setti
|
||||||
local: pleroma?.is_local !== undefined ? pleroma.is_local : account.acct.split('@')[1] === undefined,
|
local: pleroma?.is_local !== undefined ? pleroma.is_local : account.acct.split('@')[1] === undefined,
|
||||||
location: account.location || pleroma?.location || other_settings?.location || '',
|
location: account.location || pleroma?.location || other_settings?.location || '',
|
||||||
note_emojified: DOMPurify.sanitize(emojify(account.note, customEmojiMap), { USE_PROFILES: { html: true } }),
|
note_emojified: DOMPurify.sanitize(emojify(account.note, customEmojiMap), { USE_PROFILES: { html: true } }),
|
||||||
pleroma: (() => {
|
pleroma,
|
||||||
if (!pleroma) return undefined;
|
|
||||||
const { relationship, ...rest } = pleroma;
|
|
||||||
return rest;
|
|
||||||
})(),
|
|
||||||
roles: account.roles.length ? account.roles : filterBadges(pleroma?.tags),
|
roles: account.roles.length ? account.roles : filterBadges(pleroma?.tags),
|
||||||
relationship: pleroma?.relationship,
|
|
||||||
staff: pleroma?.is_admin || pleroma?.is_moderator || false,
|
staff: pleroma?.is_admin || pleroma?.is_moderator || false,
|
||||||
suspended: account.suspended || pleroma?.deactivated || false,
|
suspended: account.suspended || pleroma?.deactivated || false,
|
||||||
verified: account.verified || pleroma?.tags.includes('verified') || false,
|
verified: account.verified || pleroma?.tags.includes('verified') || false,
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const makeEmojiMap = (emojis: any) => emojis.reduce((obj: any, emoji: any
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
/** Normalize entity ID */
|
/** Normalize entity ID */
|
||||||
export const normalizeId = (id: any): string | null => {
|
export const normalizeId = (id: unknown): string | null => {
|
||||||
return z.string().nullable().catch(null).parse(id);
|
return z.string().nullable().catch(null).parse(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue