From 9d79b6013480211529a211142818c8c208937ba0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 19 Mar 2022 14:41:16 -0500 Subject: [PATCH] Typescript: reducers/statuses.ts --- app/soapbox/normalizers/status.ts | 3 +- .../reducers/{statuses.js => statuses.ts} | 73 +++++++++++-------- 2 files changed, 43 insertions(+), 33 deletions(-) rename app/soapbox/reducers/{statuses.js => statuses.ts} (69%) diff --git a/app/soapbox/normalizers/status.ts b/app/soapbox/normalizers/status.ts index 8db44ba39..21cdb42bd 100644 --- a/app/soapbox/normalizers/status.ts +++ b/app/soapbox/normalizers/status.ts @@ -15,7 +15,6 @@ import { normalizeCard } from 'soapbox/normalizers/card'; import { normalizeEmoji } from 'soapbox/normalizers/emoji'; import { normalizeMention } from 'soapbox/normalizers/mention'; import { normalizePoll } from 'soapbox/normalizers/poll'; -import { IStatus } from 'soapbox/types'; // https://docs.joinmastodon.org/entities/status/ export const StatusRecord = ImmutableRecord({ @@ -136,7 +135,7 @@ const fixQuote = (status: ImmutableMap) => { }); }; -export const normalizeStatus = (status: Record): IStatus => { +export const normalizeStatus = (status: Record) => { return StatusRecord( ImmutableMap(fromJS(status)).withMutations(status => { normalizeAttachments(status); diff --git a/app/soapbox/reducers/statuses.js b/app/soapbox/reducers/statuses.ts similarity index 69% rename from app/soapbox/reducers/statuses.js rename to app/soapbox/reducers/statuses.ts index 957640c9c..0005adb80 100644 --- a/app/soapbox/reducers/statuses.js +++ b/app/soapbox/reducers/statuses.ts @@ -1,5 +1,6 @@ import escapeTextContentForBrowser from 'escape-html'; -import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { AnyAction } from 'redux'; import emojify from 'soapbox/features/emoji/emoji'; import { normalizeStatus } from 'soapbox/normalizers/status'; @@ -33,7 +34,13 @@ import { TIMELINE_DELETE } from '../actions/timelines'; const domParser = new DOMParser(); -const minifyStatus = status => { +type StatusRecord = ReturnType; +type APIEntity = Record; +type APIEntities = Array; + +type State = ImmutableMap; + +const minifyStatus = (status: StatusRecord): StatusRecord => { return status.mergeWith((o, n) => n || o, { account: status.getIn(['account', 'id']), reblog: status.getIn(['reblog', 'id']), @@ -44,46 +51,50 @@ const minifyStatus = status => { // Only calculate these values when status first encountered // Otherwise keep the ones already in the reducer -export const calculateStatus = (status, oldStatus, expandSpoilers = false) => { +export const calculateStatus = ( + status: StatusRecord, + oldStatus: StatusRecord, + expandSpoilers: boolean = false, +): StatusRecord => { if (oldStatus) { return status.merge({ - search_index: oldStatus.get('search_index'), - contentHtml: oldStatus.get('contentHtml'), - spoilerHtml: oldStatus.get('spoilerHtml'), - hidden: oldStatus.get('hidden'), + search_index: oldStatus.search_index, + contentHtml: oldStatus.contentHtml, + spoilerHtml: oldStatus.spoilerHtml, + hidden: oldStatus.hidden, }); } else { - const spoilerText = status.get('spoiler_text') || ''; - const searchContent = (ImmutableList([spoilerText, status.get('content')]).concat(status.getIn(['poll', 'options'], ImmutableList()).map(option => option.get('title')))).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); - const emojiMap = makeEmojiMap(status.get('emojis')); + const spoilerText = status.spoiler_text; + const searchContent = (ImmutableList([spoilerText, status.content]).concat(status.poll?.options).map(option => option.title)).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); + const emojiMap = makeEmojiMap(status.emojis); return status.merge({ search_index: domParser.parseFromString(searchContent, 'text/html').documentElement.textContent, - contentHtml: stripCompatibilityFeatures(emojify(status.get('content'), emojiMap)), + contentHtml: stripCompatibilityFeatures(emojify(status.content, emojiMap)), spoilerHtml: emojify(escapeTextContentForBrowser(spoilerText), emojiMap), - hidden: expandSpoilers ? false : spoilerText.length > 0 || status.get('sensitive'), + hidden: expandSpoilers ? false : spoilerText.length > 0 || status.sensitive, }); } }; // Check whether a status is a quote by secondary characteristics -const isQuote = status => { - return Boolean(status.get('quote_id') || status.getIn(['pleroma', 'quote_url'])); +const isQuote = (status: StatusRecord) => { + return Boolean(status.getIn(['pleroma', 'quote_url'])); }; // Preserve quote if an existing status already has it -const fixQuote = (status, oldStatus) => { - if (oldStatus && !status.get('quote') && isQuote(status)) { +const fixQuote = (status: StatusRecord, oldStatus: StatusRecord): StatusRecord => { + if (oldStatus && !status.quote && isQuote(status)) { return status - .set('quote', oldStatus.get('quote')) + .set('quote', oldStatus.quote) .updateIn(['pleroma', 'quote_visible'], visible => visible || oldStatus.getIn(['pleroma', 'quote_visible'])); } else { return status; } }; -const fixStatus = (state, status, expandSpoilers) => { - const oldStatus = state.get(status.get('id')); +const fixStatus = (state: State, status: APIEntity, expandSpoilers: boolean): StatusRecord => { + const oldStatus: StatusRecord = state.get(status.id); return normalizeStatus(status).withMutations(status => { fixQuote(status, oldStatus); @@ -92,13 +103,13 @@ const fixStatus = (state, status, expandSpoilers) => { }); }; -const importStatus = (state, status, expandSpoilers) => - state.set(status.id, fixStatus(state, fromJS(status), expandSpoilers)); +const importStatus = (state: State, status: APIEntity, expandSpoilers: boolean): State => + state.set(status.id, fixStatus(state, status, expandSpoilers)); -const importStatuses = (state, statuses, expandSpoilers) => +const importStatuses = (state: State, statuses: APIEntities, expandSpoilers: boolean): State => state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status, expandSpoilers))); -const deleteStatus = (state, id, references) => { +const deleteStatus = (state: State, id: string, references: Array) => { references.forEach(ref => { state = deleteStatus(state, ref[0], []); }); @@ -106,25 +117,25 @@ const deleteStatus = (state, id, references) => { return state.delete(id); }; -const importPendingStatus = (state, { in_reply_to_id }) => { +const importPendingStatus = (state: State, { in_reply_to_id }: APIEntity) => { if (in_reply_to_id) { - return state.updateIn([in_reply_to_id, 'replies_count'], 0, count => count + 1); + return state.updateIn([in_reply_to_id, 'replies_count'], 0, (count: number) => count + 1); } else { return state; } }; -const deletePendingStatus = (state, { in_reply_to_id }) => { +const deletePendingStatus = (state: State, { in_reply_to_id }: APIEntity) => { if (in_reply_to_id) { - return state.updateIn([in_reply_to_id, 'replies_count'], 0, count => Math.max(0, count - 1)); + return state.updateIn([in_reply_to_id, 'replies_count'], 0, (count: number) => Math.max(0, count - 1)); } else { return state; } }; -const initialState = ImmutableMap(); +const initialState: State = ImmutableMap(); -export default function statuses(state = initialState, action) { +export default function statuses(state = initialState, action: AnyAction): State { switch(action.type) { case STATUS_IMPORT: return importStatus(state, action.status, action.expandSpoilers); @@ -172,7 +183,7 @@ export default function statuses(state = initialState, action) { return state.setIn([action.id, 'muted'], false); case STATUS_REVEAL: return state.withMutations(map => { - action.ids.forEach(id => { + action.ids.forEach((id: string) => { if (!(state.get(id) === undefined)) { map.setIn([id, 'hidden'], false); } @@ -180,7 +191,7 @@ export default function statuses(state = initialState, action) { }); case STATUS_HIDE: return state.withMutations(map => { - action.ids.forEach(id => { + action.ids.forEach((id: string) => { if (!(state.get(id) === undefined)) { map.setIn([id, 'hidden'], true); }