glitch-soc/mastodon

View on GitHub
app/javascript/mastodon/models/account.ts

Summary

Maintainability
F
3 days
Test Coverage
import type { RecordOf } from 'immutable';
import { List, Record as ImmutableRecord } from 'immutable';

import escapeTextContentForBrowser from 'escape-html';

import type {
  ApiAccountFieldJSON,
  ApiAccountRoleJSON,
  ApiAccountJSON,
} from 'mastodon/api_types/accounts';
import type { ApiCustomEmojiJSON } from 'mastodon/api_types/custom_emoji';
import emojify from 'mastodon/features/emoji/emoji';
import { unescapeHTML } from 'mastodon/utils/html';

import { CustomEmojiFactory } from './custom_emoji';
import type { CustomEmoji } from './custom_emoji';

// AccountField
interface AccountFieldShape extends Required<ApiAccountFieldJSON> {
  name_emojified: string;
  value_emojified: string;
  value_plain: string | null;
}

type AccountField = RecordOf<AccountFieldShape>;

const AccountFieldFactory = ImmutableRecord<AccountFieldShape>({
  name: '',
  value: '',
  verified_at: null,
  name_emojified: '',
  value_emojified: '',
  value_plain: null,
});

// AccountRole
export type AccountRoleShape = ApiAccountRoleJSON;
export type AccountRole = RecordOf<AccountRoleShape>;

const AccountRoleFactory = ImmutableRecord<AccountRoleShape>({
  color: '',
  id: '',
  name: '',
});

// Account
export interface AccountShape
  extends Required<
    Omit<ApiAccountJSON, 'emojis' | 'fields' | 'roles' | 'moved'>
  > {
  emojis: List<CustomEmoji>;
  fields: List<AccountField>;
  roles: List<AccountRole>;
  display_name_html: string;
  note_emojified: string;
  note_plain: string | null;
  hidden: boolean;
  moved: string | null;
}

export type Account = RecordOf<AccountShape>;

export const accountDefaultValues: AccountShape = {
  acct: '',
  avatar: '',
  avatar_static: '',
  bot: false,
  created_at: '',
  discoverable: false,
  indexable: false,
  display_name: '',
  display_name_html: '',
  emojis: List<CustomEmoji>(),
  fields: List<AccountField>(),
  group: false,
  header: '',
  header_static: '',
  id: '',
  last_status_at: '',
  locked: false,
  noindex: false,
  note: '',
  note_emojified: '',
  note_plain: 'string',
  roles: List<AccountRole>(),
  uri: '',
  url: '',
  username: '',
  followers_count: 0,
  following_count: 0,
  statuses_count: 0,
  hidden: false,
  suspended: false,
  memorial: false,
  limited: false,
  moved: null,
  hide_collections: false,
};

const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues);

type EmojiMap = Record<string, ApiCustomEmojiJSON>;

function makeEmojiMap(emojis: ApiCustomEmojiJSON[]) {
  return emojis.reduce<EmojiMap>((obj, emoji) => {
    obj[`:${emoji.shortcode}:`] = emoji;
    return obj;
  }, {});
}

function createAccountField(
  jsonField: ApiAccountFieldJSON,
  emojiMap: EmojiMap,
) {
  return AccountFieldFactory({
    ...jsonField,
    name_emojified: emojify(
      escapeTextContentForBrowser(jsonField.name),
      emojiMap,
    ),
    value_emojified: emojify(jsonField.value, emojiMap),
    value_plain: unescapeHTML(jsonField.value),
  });
}

export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
  const { moved, ...accountJSON } = serverJSON;

  const emojiMap = makeEmojiMap(accountJSON.emojis);

  const displayName =
    accountJSON.display_name.trim().length === 0
      ? accountJSON.username
      : accountJSON.display_name;

  return AccountFactory({
    ...accountJSON,
    moved: moved?.id,
    fields: List(
      serverJSON.fields.map((field) => createAccountField(field, emojiMap)),
    ),
    emojis: List(serverJSON.emojis.map((emoji) => CustomEmojiFactory(emoji))),
    roles: List(serverJSON.roles?.map((role) => AccountRoleFactory(role))),
    display_name_html: emojify(
      escapeTextContentForBrowser(displayName),
      emojiMap,
    ),
    note_emojified: emojify(accountJSON.note, emojiMap),
    note_plain: unescapeHTML(accountJSON.note),
  });
}