Convert selectors/index to Typescript
This commit is contained in:
parent
830fb67215
commit
dddba516fb
|
@ -13,7 +13,6 @@ import {
|
||||||
|
|
||||||
import emojify from 'soapbox/features/emoji/emoji';
|
import emojify from 'soapbox/features/emoji/emoji';
|
||||||
import { normalizeEmoji } from 'soapbox/normalizers/emoji';
|
import { normalizeEmoji } from 'soapbox/normalizers/emoji';
|
||||||
import { acctFull } from 'soapbox/utils/accounts';
|
|
||||||
import { unescapeHTML } from 'soapbox/utils/html';
|
import { unescapeHTML } from 'soapbox/utils/html';
|
||||||
import { mergeDefined, makeEmojiMap } from 'soapbox/utils/normalizers';
|
import { mergeDefined, makeEmojiMap } from 'soapbox/utils/normalizers';
|
||||||
|
|
||||||
|
@ -197,8 +196,29 @@ const addInternalFields = (account: ImmutableMap<string, any>) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDomainFromURL = (account: ImmutableMap<string, any>): string => {
|
||||||
|
try {
|
||||||
|
const url = account.get('url');
|
||||||
|
return new URL(url).host;
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const guessFqn = (account: ImmutableMap<string, any>): string => {
|
||||||
|
const acct = account.get('acct', '');
|
||||||
|
const [user, domain] = acct.split('@');
|
||||||
|
|
||||||
|
if (domain) {
|
||||||
|
return acct;
|
||||||
|
} else {
|
||||||
|
return [user, getDomainFromURL(account)].join('@');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeFqn = (account: ImmutableMap<string, any>) => {
|
const normalizeFqn = (account: ImmutableMap<string, any>) => {
|
||||||
return account.set('fqn', acctFull(account));
|
const fqn = account.get('fqn') || guessFqn(account);
|
||||||
|
return account.set('fqn', fqn);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const normalizeAccount = (account: Record<string, any>) => {
|
export const normalizeAccount = (account: Record<string, any>) => {
|
||||||
|
|
|
@ -26,8 +26,8 @@ export const AttachmentRecord = ImmutableRecord({
|
||||||
|
|
||||||
// Internal fields
|
// Internal fields
|
||||||
// TODO: Remove these? They're set in selectors/index.js
|
// TODO: Remove these? They're set in selectors/index.js
|
||||||
account: null,
|
account: null as any,
|
||||||
status: null,
|
status: null as any,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure attachments have required fields
|
// Ensure attachments have required fields
|
||||||
|
|
|
@ -9,16 +9,30 @@ import {
|
||||||
fromJS,
|
fromJS,
|
||||||
} from 'immutable';
|
} from 'immutable';
|
||||||
|
|
||||||
|
import type { Account, Status, EmbeddedEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
type NotificationType = ''
|
||||||
|
| 'follow'
|
||||||
|
| 'follow_request'
|
||||||
|
| 'mention'
|
||||||
|
| 'reblog'
|
||||||
|
| 'favourite'
|
||||||
|
| 'poll'
|
||||||
|
| 'status'
|
||||||
|
| 'move'
|
||||||
|
| 'pleroma:chat_mention'
|
||||||
|
| 'pleroma:emoji_reaction';
|
||||||
|
|
||||||
// https://docs.joinmastodon.org/entities/notification/
|
// https://docs.joinmastodon.org/entities/notification/
|
||||||
export const NotificationRecord = ImmutableRecord({
|
export const NotificationRecord = ImmutableRecord({
|
||||||
account: null,
|
account: null as EmbeddedEntity<Account>,
|
||||||
chat_message: null, // pleroma:chat_mention
|
chat_message: null as ImmutableMap<string, any> | string | null, // pleroma:chat_mention
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
emoji: null, // pleroma:emoji_reaction
|
emoji: null as string | null, // pleroma:emoji_reaction
|
||||||
id: '',
|
id: '',
|
||||||
status: null,
|
status: null as EmbeddedEntity<Status>,
|
||||||
target: null, // move
|
target: null as EmbeddedEntity<Account>, // move
|
||||||
type: '',
|
type: '' as NotificationType,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const normalizeNotification = (notification: Record<string, any>) => {
|
export const normalizeNotification = (notification: Record<string, any>) => {
|
||||||
|
|
|
@ -16,13 +16,14 @@ import { normalizeEmoji } from 'soapbox/normalizers/emoji';
|
||||||
import { normalizeMention } from 'soapbox/normalizers/mention';
|
import { normalizeMention } from 'soapbox/normalizers/mention';
|
||||||
import { normalizePoll } from 'soapbox/normalizers/poll';
|
import { normalizePoll } from 'soapbox/normalizers/poll';
|
||||||
|
|
||||||
|
import type { ReducerAccount } from 'soapbox/reducers/accounts';
|
||||||
import type { Account, Attachment, Card, Emoji, Mention, Poll, EmbeddedEntity } from 'soapbox/types/entities';
|
import type { Account, Attachment, Card, Emoji, Mention, Poll, EmbeddedEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct';
|
type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct';
|
||||||
|
|
||||||
// https://docs.joinmastodon.org/entities/status/
|
// https://docs.joinmastodon.org/entities/status/
|
||||||
export const StatusRecord = ImmutableRecord({
|
export const StatusRecord = ImmutableRecord({
|
||||||
account: null as EmbeddedEntity<Account>,
|
account: null as EmbeddedEntity<Account | ReducerAccount>,
|
||||||
application: null as ImmutableMap<string, any> | null,
|
application: null as ImmutableMap<string, any> | null,
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
card: null as Card | null,
|
card: null as Card | null,
|
||||||
|
|
|
@ -45,14 +45,18 @@ type AccountMap = ImmutableMap<string, any>;
|
||||||
type APIEntity = Record<string, any>;
|
type APIEntity = Record<string, any>;
|
||||||
type APIEntities = Array<APIEntity>;
|
type APIEntities = Array<APIEntity>;
|
||||||
|
|
||||||
type State = ImmutableMap<string | number, AccountRecord>;
|
export interface ReducerAccount extends AccountRecord {
|
||||||
|
moved: string | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = ImmutableMap<string | number, ReducerAccount>;
|
||||||
|
|
||||||
const initialState: State = ImmutableMap();
|
const initialState: State = ImmutableMap();
|
||||||
|
|
||||||
const minifyAccount = (account: AccountRecord): AccountRecord => {
|
const minifyAccount = (account: AccountRecord): ReducerAccount => {
|
||||||
return account.mergeWith((o, n) => n || o, {
|
return account.mergeWith((o, n) => n || o, {
|
||||||
moved: normalizeId(account.getIn(['moved', 'id'])),
|
moved: normalizeId(account.getIn(['moved', 'id'])),
|
||||||
});
|
}) as ReducerAccount;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fixAccount = (state: State, account: APIEntity) => {
|
const fixAccount = (state: State, account: APIEntity) => {
|
||||||
|
@ -194,9 +198,9 @@ const importAdminUser = (state: State, adminUser: ImmutableMap<string, any>): St
|
||||||
const account = state.get(id);
|
const account = state.get(id);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return state.set(id, buildAccount(adminUser));
|
return state.set(id, minifyAccount(buildAccount(adminUser)));
|
||||||
} else {
|
} else {
|
||||||
return state.set(id, mergeAdminUser(account, adminUser));
|
return state.set(id, minifyAccount(mergeAdminUser(account, adminUser)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -223,7 +227,7 @@ export default function accounts(state: State = initialState, action: AnyAction)
|
||||||
case ACCOUNTS_IMPORT:
|
case ACCOUNTS_IMPORT:
|
||||||
return normalizeAccounts(state, action.accounts);
|
return normalizeAccounts(state, action.accounts);
|
||||||
case ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP:
|
case ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP:
|
||||||
return state.set(-1, normalizeAccount({ username: action.username }));
|
return fixAccount(state, { id: -1, username: action.username });
|
||||||
case CHATS_FETCH_SUCCESS:
|
case CHATS_FETCH_SUCCESS:
|
||||||
case CHATS_EXPAND_SUCCESS:
|
case CHATS_EXPAND_SUCCESS:
|
||||||
return importAccountsFromChats(state, action.chats);
|
return importAccountsFromChats(state, action.chats);
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { List as ImmutableList, fromJS } from 'immutable';
|
|
||||||
|
|
||||||
import { FILTERS_FETCH_SUCCESS } from '../actions/filters';
|
|
||||||
|
|
||||||
export default function filters(state = ImmutableList(), action) {
|
|
||||||
switch(action.type) {
|
|
||||||
case FILTERS_FETCH_SUCCESS:
|
|
||||||
return fromJS(action.filters);
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import {
|
||||||
|
Map as ImmutableMap,
|
||||||
|
List as ImmutableList,
|
||||||
|
fromJS,
|
||||||
|
} from 'immutable';
|
||||||
|
|
||||||
|
import { FILTERS_FETCH_SUCCESS } from '../actions/filters';
|
||||||
|
|
||||||
|
import type { AnyAction } from 'redux';
|
||||||
|
|
||||||
|
type Filter = ImmutableMap<string, any>;
|
||||||
|
type State = ImmutableList<Filter>;
|
||||||
|
|
||||||
|
const importFilters = (_state: State, filters: unknown): State => {
|
||||||
|
return ImmutableList(fromJS(filters)).map(filter => ImmutableMap(fromJS(filter)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function filters(state: State = ImmutableList<Filter>(), action: AnyAction): State {
|
||||||
|
switch(action.type) {
|
||||||
|
case FILTERS_FETCH_SUCCESS:
|
||||||
|
return importFilters(state, action.filters);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,15 +39,22 @@ type StatusRecord = ReturnType<typeof normalizeStatus>;
|
||||||
type APIEntity = Record<string, any>;
|
type APIEntity = Record<string, any>;
|
||||||
type APIEntities = Array<APIEntity>;
|
type APIEntities = Array<APIEntity>;
|
||||||
|
|
||||||
type State = ImmutableMap<string, StatusRecord>;
|
type State = ImmutableMap<string, ReducerStatus>;
|
||||||
|
|
||||||
const minifyStatus = (status: StatusRecord): StatusRecord => {
|
export interface ReducerStatus extends StatusRecord {
|
||||||
|
account: string | null,
|
||||||
|
reblog: string | null,
|
||||||
|
poll: string | null,
|
||||||
|
quote: string | null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const minifyStatus = (status: StatusRecord): ReducerStatus => {
|
||||||
return status.mergeWith((o, n) => n || o, {
|
return status.mergeWith((o, n) => n || o, {
|
||||||
account: normalizeId(status.getIn(['account', 'id'])),
|
account: normalizeId(status.getIn(['account', 'id'])),
|
||||||
reblog: normalizeId(status.getIn(['reblog', 'id'])),
|
reblog: normalizeId(status.getIn(['reblog', 'id'])),
|
||||||
poll: normalizeId(status.getIn(['poll', 'id'])),
|
poll: normalizeId(status.getIn(['poll', 'id'])),
|
||||||
quote: normalizeId(status.getIn(['quote', 'id'])),
|
quote: normalizeId(status.getIn(['quote', 'id'])),
|
||||||
});
|
}) as ReducerStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gets titles of poll options from status
|
// Gets titles of poll options from status
|
||||||
|
@ -121,14 +128,14 @@ const fixQuote = (status: StatusRecord, oldStatus?: StatusRecord): StatusRecord
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fixStatus = (state: State, status: APIEntity, expandSpoilers: boolean): StatusRecord => {
|
const fixStatus = (state: State, status: APIEntity, expandSpoilers: boolean): ReducerStatus => {
|
||||||
const oldStatus = state.get(status.id);
|
const oldStatus = state.get(status.id);
|
||||||
|
|
||||||
return normalizeStatus(status).withMutations(status => {
|
return normalizeStatus(status).withMutations(status => {
|
||||||
fixQuote(status, oldStatus);
|
fixQuote(status, oldStatus);
|
||||||
calculateStatus(status, oldStatus, expandSpoilers);
|
calculateStatus(status, oldStatus, expandSpoilers);
|
||||||
minifyStatus(status);
|
minifyStatus(status);
|
||||||
});
|
}) as ReducerStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
const importStatus = (state: State, status: APIEntity, expandSpoilers: boolean): State =>
|
const importStatus = (state: State, status: APIEntity, expandSpoilers: boolean): State =>
|
||||||
|
|
|
@ -1,332 +0,0 @@
|
||||||
import {
|
|
||||||
Map as ImmutableMap,
|
|
||||||
List as ImmutableList,
|
|
||||||
OrderedSet as ImmutableOrderedSet,
|
|
||||||
} from 'immutable';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
|
||||||
import { getDomain } from 'soapbox/utils/accounts';
|
|
||||||
import { validId } from 'soapbox/utils/auth';
|
|
||||||
import ConfigDB from 'soapbox/utils/config_db';
|
|
||||||
import { shouldFilter } from 'soapbox/utils/timelines';
|
|
||||||
|
|
||||||
const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
|
|
||||||
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
|
|
||||||
const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null);
|
|
||||||
const getAccountMoved = (state, id) => state.getIn(['accounts', state.getIn(['accounts', id, 'moved'])]);
|
|
||||||
const getAccountMeta = (state, id) => state.getIn(['accounts_meta', id], ImmutableMap());
|
|
||||||
const getAccountAdminData = (state, id) => state.getIn(['admin', 'users', id]);
|
|
||||||
const getAccountPatron = (state, id) => {
|
|
||||||
const url = state.getIn(['accounts', id, 'url']);
|
|
||||||
return state.getIn(['patron', 'accounts', url]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeGetAccount = () => {
|
|
||||||
return createSelector([
|
|
||||||
getAccountBase,
|
|
||||||
getAccountCounters,
|
|
||||||
getAccountRelationship,
|
|
||||||
getAccountMoved,
|
|
||||||
getAccountMeta,
|
|
||||||
getAccountAdminData,
|
|
||||||
getAccountPatron,
|
|
||||||
], (base, counters, relationship, moved, meta, admin, patron) => {
|
|
||||||
if (base === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.withMutations(map => {
|
|
||||||
map.merge(counters);
|
|
||||||
map.merge(meta);
|
|
||||||
map.set('pleroma', meta.get('pleroma', ImmutableMap()).merge(base.get('pleroma', ImmutableMap()))); // Lol, thanks Pleroma
|
|
||||||
map.set('relationship', relationship);
|
|
||||||
map.set('moved', moved);
|
|
||||||
map.set('patron', patron);
|
|
||||||
map.setIn(['pleroma', 'admin'], admin);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const findAccountsByUsername = (state, username) => {
|
|
||||||
const accounts = state.get('accounts');
|
|
||||||
|
|
||||||
return accounts.filter(account => {
|
|
||||||
return username.toLowerCase() === account.getIn(['acct'], '').toLowerCase();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findAccountByUsername = (state, username) => {
|
|
||||||
const accounts = findAccountsByUsername(state, username);
|
|
||||||
|
|
||||||
if (accounts.size > 1) {
|
|
||||||
const me = state.get('me');
|
|
||||||
const meURL = state.getIn(['accounts', me, 'url']);
|
|
||||||
|
|
||||||
return accounts.find(account => {
|
|
||||||
try {
|
|
||||||
// If more than one account has the same username, try matching its host
|
|
||||||
const { host } = new URL(account.get('url'));
|
|
||||||
const { host: meHost } = new URL(meURL);
|
|
||||||
return host === meHost;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return accounts.first();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toServerSideType = columnType => {
|
|
||||||
switch (columnType) {
|
|
||||||
case 'home':
|
|
||||||
case 'notifications':
|
|
||||||
case 'public':
|
|
||||||
case 'thread':
|
|
||||||
return columnType;
|
|
||||||
default:
|
|
||||||
if (columnType.indexOf('list:') > -1) {
|
|
||||||
return 'home';
|
|
||||||
} else {
|
|
||||||
return 'public'; // community, account, hashtag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getFilters = (state, { contextType }) => state.get('filters', ImmutableList()).filter(filter => contextType && filter.get('context').includes(toServerSideType(contextType)) && (filter.get('expires_at') === null || Date.parse(filter.get('expires_at')) > (new Date())));
|
|
||||||
|
|
||||||
const escapeRegExp = string =>
|
|
||||||
string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
||||||
|
|
||||||
export const regexFromFilters = filters => {
|
|
||||||
if (filters.size === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RegExp(filters.map(filter => {
|
|
||||||
let expr = escapeRegExp(filter.get('phrase'));
|
|
||||||
|
|
||||||
if (filter.get('whole_word')) {
|
|
||||||
if (/^[\w]/.test(expr)) {
|
|
||||||
expr = `\\b${expr}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/[\w]$/.test(expr)) {
|
|
||||||
expr = `${expr}\\b`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expr;
|
|
||||||
}).join('|'), 'i');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeGetStatus = () => {
|
|
||||||
return createSelector(
|
|
||||||
[
|
|
||||||
(state, { id }) => state.getIn(['statuses', id]),
|
|
||||||
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]),
|
|
||||||
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
|
|
||||||
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
|
|
||||||
(state, { username }) => username,
|
|
||||||
getFilters,
|
|
||||||
(state) => state.get('me'),
|
|
||||||
],
|
|
||||||
|
|
||||||
(statusBase, statusReblog, accountBase, accountReblog, username, filters, me) => {
|
|
||||||
if (!statusBase) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountUsername = accountBase.get('acct');
|
|
||||||
//Must be owner of status if username exists
|
|
||||||
if (accountUsername !== username && username !== undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statusReblog) {
|
|
||||||
statusReblog = statusReblog.set('account', accountReblog);
|
|
||||||
} else {
|
|
||||||
statusReblog = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const regex = (accountReblog || accountBase).get('id') !== me && regexFromFilters(filters);
|
|
||||||
const filtered = regex && regex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'));
|
|
||||||
|
|
||||||
return statusBase.withMutations(map => {
|
|
||||||
map.set('reblog', statusReblog);
|
|
||||||
map.set('account', accountBase);
|
|
||||||
map.set('filtered', filtered);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAlertsBase = state => state.get('alerts');
|
|
||||||
|
|
||||||
export const getAlerts = createSelector([getAlertsBase], (base) => {
|
|
||||||
const arr = [];
|
|
||||||
|
|
||||||
base.forEach(item => {
|
|
||||||
arr.push({
|
|
||||||
message: item.get('message'),
|
|
||||||
title: item.get('title'),
|
|
||||||
actionLabel: item.get('actionLabel'),
|
|
||||||
actionLink: item.get('actionLink'),
|
|
||||||
key: item.get('key'),
|
|
||||||
className: `notification-bar-${item.get('severity', 'info')}`,
|
|
||||||
activeClassName: 'snackbar--active',
|
|
||||||
dismissAfter: 6000,
|
|
||||||
style: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
});
|
|
||||||
|
|
||||||
export const makeGetNotification = () => {
|
|
||||||
return createSelector([
|
|
||||||
(state, notification) => notification,
|
|
||||||
(state, notification) => state.getIn(['accounts', notification.get('account')]),
|
|
||||||
(state, notification) => state.getIn(['accounts', notification.get('target')]),
|
|
||||||
(state, notification) => state.getIn(['statuses', notification.get('status')]),
|
|
||||||
], (notification, account, target, status) => {
|
|
||||||
return notification.merge({ account, target, status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAccountGallery = createSelector([
|
|
||||||
(state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()),
|
|
||||||
state => state.get('statuses'),
|
|
||||||
state => state.get('accounts'),
|
|
||||||
], (statusIds, statuses, accounts) => {
|
|
||||||
|
|
||||||
return statusIds.reduce((medias, statusId) => {
|
|
||||||
const status = statuses.get(statusId);
|
|
||||||
const account = accounts.get(status.get('account'));
|
|
||||||
if (status.get('reblog')) return medias;
|
|
||||||
return medias.concat(status.get('media_attachments')
|
|
||||||
.map(media => media.merge({ status, account })));
|
|
||||||
}, ImmutableList());
|
|
||||||
});
|
|
||||||
|
|
||||||
export const makeGetChat = () => {
|
|
||||||
return createSelector(
|
|
||||||
[
|
|
||||||
(state, { id }) => state.getIn(['chats', 'items', id]),
|
|
||||||
(state, { id }) => state.getIn(['accounts', state.getIn(['chats', 'items', id, 'account'])]),
|
|
||||||
(state, { last_message }) => state.getIn(['chat_messages', last_message]),
|
|
||||||
],
|
|
||||||
|
|
||||||
(chat, account, lastMessage) => {
|
|
||||||
if (!chat) return null;
|
|
||||||
|
|
||||||
return chat.withMutations(map => {
|
|
||||||
map.set('account', account);
|
|
||||||
map.set('last_message', lastMessage);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeGetReport = () => {
|
|
||||||
const getStatus = makeGetStatus();
|
|
||||||
|
|
||||||
return createSelector(
|
|
||||||
[
|
|
||||||
(state, id) => state.getIn(['admin', 'reports', id]),
|
|
||||||
(state, id) => state.getIn(['admin', 'reports', id, 'statuses']).map(
|
|
||||||
statusId => state.getIn(['statuses', statusId]))
|
|
||||||
.filter(s => s)
|
|
||||||
.map(s => getStatus(state, s.toJS())),
|
|
||||||
],
|
|
||||||
|
|
||||||
(report, statuses) => {
|
|
||||||
if (!report) return null;
|
|
||||||
return report.set('statuses', statuses);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAuthUserIds = createSelector([
|
|
||||||
state => state.getIn(['auth', 'users'], ImmutableMap()),
|
|
||||||
], authUsers => {
|
|
||||||
return authUsers.reduce((ids, authUser) => {
|
|
||||||
try {
|
|
||||||
const id = authUser.get('id');
|
|
||||||
return validId(id) ? ids.add(id) : ids;
|
|
||||||
} catch {
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
}, ImmutableOrderedSet());
|
|
||||||
});
|
|
||||||
|
|
||||||
export const makeGetOtherAccounts = () => {
|
|
||||||
return createSelector([
|
|
||||||
state => state.get('accounts'),
|
|
||||||
getAuthUserIds,
|
|
||||||
state => state.get('me'),
|
|
||||||
],
|
|
||||||
(accounts, authUserIds, me) => {
|
|
||||||
return authUserIds
|
|
||||||
.reduce((list, id) => {
|
|
||||||
if (id === me) return list;
|
|
||||||
const account = accounts.get(id);
|
|
||||||
return account ? list.push(account) : list;
|
|
||||||
}, ImmutableList());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSimplePolicy = createSelector([
|
|
||||||
state => state.getIn(['admin', 'configs'], ImmutableMap()),
|
|
||||||
state => state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()),
|
|
||||||
], (configs, instancePolicy) => {
|
|
||||||
return instancePolicy.merge(ConfigDB.toSimplePolicy(configs));
|
|
||||||
});
|
|
||||||
|
|
||||||
const getRemoteInstanceFavicon = (state, host) => (
|
|
||||||
state.get('accounts')
|
|
||||||
.find(account => getDomain(account) === host, null, ImmutableMap())
|
|
||||||
.getIn(['pleroma', 'favicon'])
|
|
||||||
);
|
|
||||||
|
|
||||||
const getRemoteInstanceFederation = (state, host) => (
|
|
||||||
getSimplePolicy(state)
|
|
||||||
.map(hosts => hosts.includes(host))
|
|
||||||
);
|
|
||||||
|
|
||||||
export const makeGetHosts = () => {
|
|
||||||
return createSelector([getSimplePolicy], (simplePolicy) => {
|
|
||||||
return simplePolicy
|
|
||||||
.deleteAll(['accept', 'reject_deletes', 'report_removal'])
|
|
||||||
.reduce((acc, hosts) => acc.union(hosts), ImmutableOrderedSet())
|
|
||||||
.sort();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeGetRemoteInstance = () => {
|
|
||||||
return createSelector([
|
|
||||||
(state, host) => host,
|
|
||||||
getRemoteInstanceFavicon,
|
|
||||||
getRemoteInstanceFederation,
|
|
||||||
], (host, favicon, federation) => {
|
|
||||||
return ImmutableMap({
|
|
||||||
host,
|
|
||||||
favicon,
|
|
||||||
federation,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeGetStatusIds = () => createSelector([
|
|
||||||
(state, { type, prefix }) => getSettings(state).get(prefix || type, ImmutableMap()),
|
|
||||||
(state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableOrderedSet()),
|
|
||||||
(state) => state.get('statuses'),
|
|
||||||
(state) => state.get('me'),
|
|
||||||
], (columnSettings, statusIds, statuses, me) => {
|
|
||||||
return statusIds.filter(id => {
|
|
||||||
const status = statuses.get(id);
|
|
||||||
if (!status) return true;
|
|
||||||
return !shouldFilter(status, columnSettings);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,359 @@
|
||||||
|
import {
|
||||||
|
Map as ImmutableMap,
|
||||||
|
List as ImmutableList,
|
||||||
|
OrderedSet as ImmutableOrderedSet,
|
||||||
|
} from 'immutable';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
|
import { getDomain } from 'soapbox/utils/accounts';
|
||||||
|
import { validId } from 'soapbox/utils/auth';
|
||||||
|
import ConfigDB from 'soapbox/utils/config_db';
|
||||||
|
import { shouldFilter } from 'soapbox/utils/timelines';
|
||||||
|
|
||||||
|
import type { RootState } from 'soapbox/store';
|
||||||
|
import type { Notification } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
const normalizeId = (id: any): string => typeof id === 'string' ? id : '';
|
||||||
|
|
||||||
|
const getAccountBase = (state: RootState, id: string) => state.accounts.get(id);
|
||||||
|
const getAccountCounters = (state: RootState, id: string) => state.accounts_counters.get(id);
|
||||||
|
const getAccountRelationship = (state: RootState, id: string) => state.relationships.get(id);
|
||||||
|
const getAccountMoved = (state: RootState, id: string) => state.accounts.get(state.accounts.get(id)?.moved || '');
|
||||||
|
const getAccountMeta = (state: RootState, id: string) => state.accounts_meta.get(id, ImmutableMap());
|
||||||
|
const getAccountAdminData = (state: RootState, id: string) => state.admin.users.get(id);
|
||||||
|
const getAccountPatron = (state: RootState, id: string) => {
|
||||||
|
const url = state.accounts.get(id)?.url;
|
||||||
|
return state.patron.getIn(['accounts', url]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeGetAccount = () => {
|
||||||
|
return createSelector([
|
||||||
|
getAccountBase,
|
||||||
|
getAccountCounters,
|
||||||
|
getAccountRelationship,
|
||||||
|
getAccountMoved,
|
||||||
|
getAccountMeta,
|
||||||
|
getAccountAdminData,
|
||||||
|
getAccountPatron,
|
||||||
|
], (base, counters, relationship, moved, meta, admin, patron) => {
|
||||||
|
if (!base) return null;
|
||||||
|
|
||||||
|
return base.withMutations(map => {
|
||||||
|
map.merge(counters);
|
||||||
|
map.merge(meta);
|
||||||
|
map.set('pleroma', meta.get('pleroma', ImmutableMap()).merge(base.get('pleroma', ImmutableMap()))); // Lol, thanks Pleroma
|
||||||
|
map.set('relationship', relationship);
|
||||||
|
map.set('moved', moved || null);
|
||||||
|
map.set('patron', patron);
|
||||||
|
map.setIn(['pleroma', 'admin'], admin);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const findAccountsByUsername = (state: RootState, username: string) => {
|
||||||
|
const accounts = state.accounts;
|
||||||
|
|
||||||
|
return accounts.filter(account => {
|
||||||
|
return username.toLowerCase() === account.acct.toLowerCase();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const findAccountByUsername = (state: RootState, username: string) => {
|
||||||
|
const accounts = findAccountsByUsername(state, username);
|
||||||
|
|
||||||
|
if (accounts.size > 1) {
|
||||||
|
const me = state.me;
|
||||||
|
const meURL = state.accounts.get(me)?.url || '';
|
||||||
|
|
||||||
|
return accounts.find(account => {
|
||||||
|
try {
|
||||||
|
// If more than one account has the same username, try matching its host
|
||||||
|
const { host } = new URL(account.url);
|
||||||
|
const { host: meHost } = new URL(meURL);
|
||||||
|
return host === meHost;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return accounts.first();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toServerSideType = (columnType: string): string => {
|
||||||
|
switch (columnType) {
|
||||||
|
case 'home':
|
||||||
|
case 'notifications':
|
||||||
|
case 'public':
|
||||||
|
case 'thread':
|
||||||
|
return columnType;
|
||||||
|
default:
|
||||||
|
if (columnType.indexOf('list:') > -1) {
|
||||||
|
return 'home';
|
||||||
|
} else {
|
||||||
|
return 'public'; // community, account, hashtag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type FilterContext = { contextType: string };
|
||||||
|
|
||||||
|
export const getFilters = (state: RootState, { contextType }: FilterContext) => {
|
||||||
|
return state.filters.filter((filter): boolean => {
|
||||||
|
return contextType
|
||||||
|
&& filter.get('context').includes(toServerSideType(contextType))
|
||||||
|
&& (filter.get('expires_at') === null
|
||||||
|
|| Date.parse(filter.get('expires_at')) > new Date().getTime());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const escapeRegExp = (string: string) =>
|
||||||
|
string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||||
|
|
||||||
|
export const regexFromFilters = (filters: ImmutableList<ImmutableMap<string, any>>) => {
|
||||||
|
if (filters.size === 0) return null;
|
||||||
|
|
||||||
|
return new RegExp(filters.map(filter => {
|
||||||
|
let expr = escapeRegExp(filter.get('phrase'));
|
||||||
|
|
||||||
|
if (filter.get('whole_word')) {
|
||||||
|
if (/^[\w]/.test(expr)) {
|
||||||
|
expr = `\\b${expr}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/[\w]$/.test(expr)) {
|
||||||
|
expr = `${expr}\\b`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}).join('|'), 'i');
|
||||||
|
};
|
||||||
|
|
||||||
|
type APIStatus = { id: string, username: string };
|
||||||
|
|
||||||
|
export const makeGetStatus = () => {
|
||||||
|
return createSelector(
|
||||||
|
[
|
||||||
|
(state: RootState, { id }: APIStatus) => state.statuses.get(id),
|
||||||
|
(state: RootState, { id }: APIStatus) => state.statuses.get(state.statuses.get(id)?.reblog || ''),
|
||||||
|
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(id)?.account || ''),
|
||||||
|
(state: RootState, { id }: APIStatus) => state.accounts.get(state.statuses.get(state.statuses.get(id)?.reblog || '')?.account || ''),
|
||||||
|
(_state: RootState, { username }: APIStatus) => username,
|
||||||
|
getFilters,
|
||||||
|
(state: RootState) => state.me,
|
||||||
|
],
|
||||||
|
|
||||||
|
(statusBase, statusReblog, accountBase, accountReblog, username, filters, me) => {
|
||||||
|
if (!statusBase || !accountBase) return null;
|
||||||
|
|
||||||
|
const accountUsername = accountBase.acct;
|
||||||
|
//Must be owner of status if username exists
|
||||||
|
if (accountUsername !== username && username !== undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusReblog && accountReblog) {
|
||||||
|
// @ts-ignore AAHHHHH
|
||||||
|
statusReblog = statusReblog.set('account', accountReblog);
|
||||||
|
} else {
|
||||||
|
statusReblog = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const regex = (accountReblog || accountBase).id !== me && regexFromFilters(filters);
|
||||||
|
const filtered = regex && regex.test(statusReblog?.search_index || statusBase.search_index);
|
||||||
|
|
||||||
|
return statusBase.withMutations(map => {
|
||||||
|
map.set('reblog', statusReblog || null);
|
||||||
|
// @ts-ignore :(
|
||||||
|
map.set('account', accountBase || null);
|
||||||
|
map.set('filtered', Boolean(filtered));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAlertsBase = (state: RootState) => state.alerts;
|
||||||
|
|
||||||
|
const buildAlert = (item: any) => {
|
||||||
|
return {
|
||||||
|
message: item.message,
|
||||||
|
title: item.title,
|
||||||
|
actionLabel: item.actionLabel,
|
||||||
|
actionLink: item.actionLink,
|
||||||
|
key: item.key,
|
||||||
|
className: `notification-bar-${item.severity}`,
|
||||||
|
activeClassName: 'snackbar--active',
|
||||||
|
dismissAfter: 6000,
|
||||||
|
style: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Alert = ReturnType<typeof buildAlert>;
|
||||||
|
|
||||||
|
export const getAlerts = createSelector([getAlertsBase], (base): Alert[] => {
|
||||||
|
const arr: Alert[] = [];
|
||||||
|
base.forEach((item: any) => arr.push(buildAlert(item)));
|
||||||
|
return arr;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const makeGetNotification = () => {
|
||||||
|
return createSelector([
|
||||||
|
(_state: RootState, notification: Notification) => notification,
|
||||||
|
(state: RootState, notification: Notification) => state.accounts.get(normalizeId(notification.account)),
|
||||||
|
(state: RootState, notification: Notification) => state.accounts.get(normalizeId(notification.target)),
|
||||||
|
(state: RootState, notification: Notification) => state.statuses.get(normalizeId(notification.status)),
|
||||||
|
], (notification, account, target, status) => {
|
||||||
|
return notification.merge({
|
||||||
|
// @ts-ignore
|
||||||
|
account: account || null,
|
||||||
|
// @ts-ignore
|
||||||
|
target: target || null,
|
||||||
|
// @ts-ignore
|
||||||
|
status: status || null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAccountGallery = createSelector([
|
||||||
|
(state: RootState, id: string) => state.timelines.getIn([`account:${id}:media`, 'items'], ImmutableList()),
|
||||||
|
(state: RootState) => state.statuses,
|
||||||
|
(state: RootState) => state.accounts,
|
||||||
|
], (statusIds, statuses, accounts) => {
|
||||||
|
|
||||||
|
return statusIds.reduce((medias: ImmutableList<any>, statusId: string) => {
|
||||||
|
const status = statuses.get(statusId);
|
||||||
|
if (!status) return medias;
|
||||||
|
if (status.reblog) return medias;
|
||||||
|
if (typeof status.account !== 'string') return medias;
|
||||||
|
|
||||||
|
const account = accounts.get(status.account);
|
||||||
|
|
||||||
|
return medias.concat(
|
||||||
|
status.media_attachments.map(media => media.merge({ status, account })));
|
||||||
|
}, ImmutableList());
|
||||||
|
});
|
||||||
|
|
||||||
|
type APIChat = { id: string, last_message: string };
|
||||||
|
|
||||||
|
export const makeGetChat = () => {
|
||||||
|
return createSelector(
|
||||||
|
[
|
||||||
|
(state: RootState, { id }: APIChat) => state.chats.getIn(['items', id]),
|
||||||
|
(state: RootState, { id }: APIChat) => state.accounts.get(state.chats.getIn(['items', id, 'account'])),
|
||||||
|
(state: RootState, { last_message }: APIChat) => state.chat_messages.get(last_message),
|
||||||
|
],
|
||||||
|
|
||||||
|
(chat, account, lastMessage: string) => {
|
||||||
|
if (!chat) return null;
|
||||||
|
|
||||||
|
return chat.withMutations((map: ImmutableMap<string, any>) => {
|
||||||
|
map.set('account', account);
|
||||||
|
map.set('last_message', lastMessage);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeGetReport = () => {
|
||||||
|
const getStatus = makeGetStatus();
|
||||||
|
|
||||||
|
return createSelector(
|
||||||
|
[
|
||||||
|
(state: RootState, id: string) => state.admin.getIn(['reports', id]),
|
||||||
|
(state: RootState, id: string) => state.admin.getIn(['reports', id, 'statuses']).map(
|
||||||
|
(statusId: string) => state.statuses.get(statusId))
|
||||||
|
.filter((s: any) => s)
|
||||||
|
.map((s: any) => getStatus(state, s.toJS())),
|
||||||
|
],
|
||||||
|
|
||||||
|
(report, statuses) => {
|
||||||
|
if (!report) return null;
|
||||||
|
return report.set('statuses', statuses);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAuthUserIds = createSelector([
|
||||||
|
(state: RootState) => state.auth.get('users', ImmutableMap()),
|
||||||
|
], authUsers => {
|
||||||
|
return authUsers.reduce((ids: ImmutableOrderedSet<string>, authUser: ImmutableMap<string, any>) => {
|
||||||
|
try {
|
||||||
|
const id = authUser.get('id');
|
||||||
|
return validId(id) ? ids.add(id) : ids;
|
||||||
|
} catch {
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}, ImmutableOrderedSet());
|
||||||
|
});
|
||||||
|
|
||||||
|
export const makeGetOtherAccounts = () => {
|
||||||
|
return createSelector([
|
||||||
|
(state: RootState) => state.accounts,
|
||||||
|
getAuthUserIds,
|
||||||
|
(state: RootState) => state.me,
|
||||||
|
],
|
||||||
|
(accounts, authUserIds, me) => {
|
||||||
|
return authUserIds
|
||||||
|
.reduce((list: ImmutableList<any>, id: string) => {
|
||||||
|
if (id === me) return list;
|
||||||
|
const account = accounts.get(id);
|
||||||
|
return account ? list.push(account) : list;
|
||||||
|
}, ImmutableList());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSimplePolicy = createSelector([
|
||||||
|
(state: RootState) => state.admin.configs,
|
||||||
|
(state: RootState) => state.instance.pleroma.getIn(['metadata', 'federation', 'mrf_simple'], ImmutableMap()),
|
||||||
|
], (configs, instancePolicy: ImmutableMap<string, any>) => {
|
||||||
|
return instancePolicy.merge(ConfigDB.toSimplePolicy(configs));
|
||||||
|
});
|
||||||
|
|
||||||
|
const getRemoteInstanceFavicon = (state: RootState, host: string) => (
|
||||||
|
(state.accounts.find(account => getDomain(account) === host, null) || ImmutableMap())
|
||||||
|
.getIn(['pleroma', 'favicon'])
|
||||||
|
);
|
||||||
|
|
||||||
|
const getRemoteInstanceFederation = (state: RootState, host: string) => (
|
||||||
|
getSimplePolicy(state)
|
||||||
|
.map(hosts => hosts.includes(host))
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeGetHosts = () => {
|
||||||
|
return createSelector([getSimplePolicy], (simplePolicy) => {
|
||||||
|
return simplePolicy
|
||||||
|
.deleteAll(['accept', 'reject_deletes', 'report_removal'])
|
||||||
|
.reduce((acc, hosts) => acc.union(hosts), ImmutableOrderedSet())
|
||||||
|
.sort();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeGetRemoteInstance = () => {
|
||||||
|
return createSelector([
|
||||||
|
(_state: RootState, host: string) => host,
|
||||||
|
getRemoteInstanceFavicon,
|
||||||
|
getRemoteInstanceFederation,
|
||||||
|
], (host, favicon, federation) => {
|
||||||
|
return ImmutableMap({
|
||||||
|
host,
|
||||||
|
favicon,
|
||||||
|
federation,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
type ColumnQuery = { type: string, prefix?: string };
|
||||||
|
|
||||||
|
export const makeGetStatusIds = () => createSelector([
|
||||||
|
(state: RootState, { type, prefix }: ColumnQuery) => getSettings(state).get(prefix || type, ImmutableMap()),
|
||||||
|
(state: RootState, { type }: ColumnQuery) => state.timelines.getIn([type, 'items'], ImmutableOrderedSet()),
|
||||||
|
(state: RootState) => state.statuses,
|
||||||
|
], (columnSettings, statusIds: string[], statuses) => {
|
||||||
|
return statusIds.filter((id: string) => {
|
||||||
|
const status = statuses.get(id);
|
||||||
|
if (!status) return true;
|
||||||
|
return !shouldFilter(status, columnSettings);
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,7 +2,6 @@ import { fromJS } from 'immutable';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDomain,
|
getDomain,
|
||||||
acctFull,
|
|
||||||
isStaff,
|
isStaff,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isModerator,
|
isModerator,
|
||||||
|
@ -18,28 +17,6 @@ describe('getDomain', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('acctFull', () => {
|
|
||||||
describe('with a local user', () => {
|
|
||||||
const account = fromJS({
|
|
||||||
acct: 'alice',
|
|
||||||
url: 'https://party.com/users/alice',
|
|
||||||
});
|
|
||||||
it('returns the full acct', () => {
|
|
||||||
expect(acctFull(account)).toEqual('alice@party.com');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with a remote user', () => {
|
|
||||||
const account = fromJS({
|
|
||||||
acct: 'bob@pool.com',
|
|
||||||
url: 'https://pool.com/users/bob',
|
|
||||||
});
|
|
||||||
it('returns the full acct', () => {
|
|
||||||
expect(acctFull(account)).toEqual('bob@pool.com');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isStaff', () => {
|
describe('isStaff', () => {
|
||||||
describe('with empty user', () => {
|
describe('with empty user', () => {
|
||||||
const account = fromJS({});
|
const account = fromJS({});
|
||||||
|
|
|
@ -2,26 +2,20 @@ import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutabl
|
||||||
|
|
||||||
import { Account } from 'soapbox/types/entities';
|
import { Account } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const getDomainFromURL = (account: ImmutableMap<string, any>): string => {
|
const getDomainFromURL = (account: Account): string => {
|
||||||
try {
|
try {
|
||||||
const url = account.get('url');
|
const url = account.url;
|
||||||
return new URL(url).host;
|
return new URL(url).host;
|
||||||
} catch {
|
} catch {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDomain = (account: ImmutableMap<string, any>): string => {
|
export const getDomain = (account: Account): string => {
|
||||||
const domain = account.get('acct', '').split('@')[1];
|
const domain = account.acct.split('@')[1];
|
||||||
return domain ? domain : getDomainFromURL(account);
|
return domain ? domain : getDomainFromURL(account);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const guessFqn = (account: ImmutableMap<string, any>): string => {
|
|
||||||
const [user, domain] = account.get('acct', '').split('@');
|
|
||||||
if (!domain) return [user, getDomainFromURL(account)].join('@');
|
|
||||||
return account.get('acct', '');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getBaseURL = (account: ImmutableMap<string, any>): string => {
|
export const getBaseURL = (account: ImmutableMap<string, any>): string => {
|
||||||
try {
|
try {
|
||||||
const url = account.get('url');
|
const url = account.get('url');
|
||||||
|
@ -31,11 +25,6 @@ export const getBaseURL = (account: ImmutableMap<string, any>): string => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// user@domain even for local users
|
|
||||||
export const acctFull = (account: ImmutableMap<string, any>): string => (
|
|
||||||
account.get('fqn') || guessFqn(account) || ''
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getAcct = (account: Account, displayFqn: boolean): string => (
|
export const getAcct = (account: Account, displayFqn: boolean): string => (
|
||||||
displayFqn === true ? account.fqn : account.acct
|
displayFqn === true ? account.fqn : account.acct
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue