From e64aabe9fc6d36938cbaa40b7acb3356729a6686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino=20=28aider=29?= Date: Mon, 25 Nov 2024 13:11:21 +0100 Subject: [PATCH] feat: Migrate emoji_actions.js to TypeScript with full type support --- webapp/channels/src/actions/emoji_actions.ts | 259 +++++++++++++++++++ 1 file changed, 259 insertions(+) diff --git a/webapp/channels/src/actions/emoji_actions.ts b/webapp/channels/src/actions/emoji_actions.ts index e69de29bb2d..fd57f4d91a3 100644 --- a/webapp/channels/src/actions/emoji_actions.ts +++ b/webapp/channels/src/actions/emoji_actions.ts @@ -0,0 +1,259 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {AnyAction} from 'redux'; +import {ThunkAction} from 'redux-thunk'; + +import * as EmojiActions from 'mattermost-redux/actions/emojis'; +import {savePreferences} from 'mattermost-redux/actions/preferences'; +import {Preferences as ReduxPreferences} from 'mattermost-redux/constants'; +import {getCustomEmojisByName as selectCustomEmojisByName, getCustomEmojisEnabled} from 'mattermost-redux/selectors/entities/emojis'; +import {get} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; +import {getEmojiName} from 'mattermost-redux/utils/emoji_utils'; + +import {getEmojiMap, getRecentEmojisData, getRecentEmojisNames, isCustomEmojiEnabled} from 'selectors/emojis'; +import {isCustomStatusEnabled, makeGetCustomStatus} from 'selectors/views/custom_status'; +import LocalStorageStore from 'stores/local_storage_store'; + +import Constants, {ActionTypes, Preferences} from 'utils/constants'; +import {EmojiIndicesByAlias} from 'utils/emoji'; + +import {GlobalState} from 'types/store'; +import {Post} from '@mattermost/types/posts'; + +interface RecentEmojiData { + name: string; + usageCount: number; +} + +type ThunkResult = ThunkAction; + +export function loadRecentlyUsedCustomEmojis(): ThunkResult<{data: boolean}> { + return (dispatch, getState) => { + const state = getState(); + + if (!getCustomEmojisEnabled(state)) { + return {data: true}; + } + + const recentEmojiNames = getRecentEmojisNames(state); + + return dispatch(EmojiActions.getCustomEmojisByName(recentEmojiNames)); + }; +} + +export function incrementEmojiPickerPage(): ThunkResult<{data: boolean}> { + return async (dispatch) => { + dispatch({ + type: ActionTypes.INCREMENT_EMOJI_PICKER_PAGE, + }); + + return {data: true}; + }; +} + +export function setUserSkinTone(skin: string): ThunkResult { + return async (dispatch, getState) => { + const state = getState(); + const currentUserId = getCurrentUserId(state); + const skinTonePreference = [{ + user_id: currentUserId, + name: Preferences.EMOJI_SKINTONE, + category: Preferences.CATEGORY_EMOJI, + value: skin, + }]; + dispatch(savePreferences(currentUserId, skinTonePreference)); + }; +} + +export function addRecentEmoji(alias: string): ThunkResult<{data: boolean}> { + return addRecentEmojis([alias]); +} + +export const MAXIMUM_RECENT_EMOJI = 27; + +export function addRecentEmojis(aliases: string[]): ThunkResult<{data: boolean}> { + return (dispatch, getState) => { + const state = getState(); + const currentUserId = getCurrentUserId(state); + const recentEmojis = getRecentEmojisData(state); + const emojiMap = getEmojiMap(state); + + let updatedRecentEmojis: RecentEmojiData[] = [...recentEmojis]; + for (const alias of aliases) { + const emoji = emojiMap.get(alias); + if (!emoji) { + continue; + } + + const name = getEmojiName(emoji); + + const currentEmojiIndexInRecentList = updatedRecentEmojis.findIndex((recentEmoji) => recentEmoji.name === name); + if (currentEmojiIndexInRecentList > -1) { + const currentEmojiInRecentList = updatedRecentEmojis[currentEmojiIndexInRecentList]; + + const updatedCurrentEmojiData: RecentEmojiData = { + name, + usageCount: currentEmojiInRecentList.usageCount + 1, + }; + updatedRecentEmojis.splice(currentEmojiIndexInRecentList, 1); + updatedRecentEmojis = [...updatedRecentEmojis, updatedCurrentEmojiData].slice(-MAXIMUM_RECENT_EMOJI); + } else { + const currentEmojiData: RecentEmojiData = { + name, + usageCount: 1, + }; + updatedRecentEmojis = [...updatedRecentEmojis, currentEmojiData].slice(-MAXIMUM_RECENT_EMOJI); + } + } + + updatedRecentEmojis.sort( + (emojiA, emojiB) => emojiA.usageCount - emojiB.usageCount, + ); + + dispatch(savePreferences(currentUserId, [{category: Constants.Preferences.RECENT_EMOJIS, name: currentUserId, user_id: currentUserId, value: JSON.stringify(updatedRecentEmojis)}])); + + return {data: true}; + }; +} + +export function loadCustomEmojisForCustomStatusesByUserIds(userIds: Set): ThunkResult<{data: boolean}> { + const getCustomStatus = makeGetCustomStatus(); + return (dispatch, getState) => { + const state = getState(); + const customEmojiEnabled = isCustomEmojiEnabled(state); + const customStatusEnabled = isCustomStatusEnabled(state); + if (!customEmojiEnabled || !customStatusEnabled) { + return {data: false}; + } + + const emojisToLoad = new Set(); + + userIds.forEach((userId) => { + const customStatus = getCustomStatus(state, userId); + if (!customStatus || !customStatus.emoji) { + return; + } + + emojisToLoad.add(customStatus.emoji); + }); + + return dispatch(loadCustomEmojisIfNeeded(Array.from(emojisToLoad))); + }; +} + +export function loadCustomEmojisForRecentCustomStatuses(): ThunkResult<{data: boolean}> { + return (dispatch, getState) => { + const state = getState(); + const customEmojiEnabled = isCustomEmojiEnabled(state); + const customStatusEnabled = isCustomStatusEnabled(state); + if (!customEmojiEnabled || !customStatusEnabled) { + return {data: false}; + } + + const recentCustomStatusesValue = get(state, ReduxPreferences.CATEGORY_CUSTOM_STATUS, ReduxPreferences.NAME_RECENT_CUSTOM_STATUSES); + if (!recentCustomStatusesValue) { + return {data: false}; + } + + const recentCustomStatuses = JSON.parse(recentCustomStatusesValue); + const emojisToLoad = new Set(); + + for (const customStatus of recentCustomStatuses) { + if (!customStatus || !customStatus.emoji) { + continue; + } + + emojisToLoad.add(customStatus.emoji); + } + + return dispatch(loadCustomEmojisIfNeeded(Array.from(emojisToLoad))); + }; +} + +export function loadCustomEmojisIfNeeded(emojis: string[]): ThunkResult<{data: boolean}> { + return (dispatch, getState) => { + if (!emojis || emojis.length === 0) { + return {data: false}; + } + + const state = getState(); + const customEmojiEnabled = isCustomEmojiEnabled(state); + if (!customEmojiEnabled) { + return {data: false}; + } + + const systemEmojis = EmojiIndicesByAlias; + const customEmojisByName = selectCustomEmojisByName(state); + const nonExistentCustomEmoji = state.entities.emojis.nonExistentEmoji; + const emojisToLoad: string[] = []; + + emojis.forEach((emoji) => { + if (!emoji) { + return; + } + + if (systemEmojis.has(emoji)) { + return; + } + + if (nonExistentCustomEmoji.has(emoji)) { + return; + } + + if (customEmojisByName.has(emoji)) { + return; + } + + emojisToLoad.push(emoji); + }); + + return dispatch(EmojiActions.getCustomEmojisByName(emojisToLoad)); + }; +} + +export function loadCustomStatusEmojisForPostList(posts: Record): ThunkResult<{data: boolean}> { + return (dispatch) => { + if (!posts || Object.keys(posts).length === 0) { + return {data: false}; + } + + const userIds = new Set(); + Object.keys(posts).forEach((postId) => { + const post = posts[postId]; + if (post.user_id) { + userIds.add(post.user_id); + } + }); + return dispatch(loadCustomEmojisForCustomStatusesByUserIds(userIds)); + }; +} + +export function migrateRecentEmojis(): ThunkResult<{data: RecentEmojiData[] | string[]}> { + return async (dispatch, getState) => { + const state = getState(); + const currentUserId = getCurrentUserId(state); + const recentEmojisFromPreference = getRecentEmojisData(state); + if (recentEmojisFromPreference.length === 0) { + const recentEmojisFromLocalStorage = LocalStorageStore.getRecentEmojis(currentUserId); + if (recentEmojisFromLocalStorage) { + const parsedRecentEmojisFromLocalStorage = JSON.parse(recentEmojisFromLocalStorage); + const toSetRecentEmojiData: RecentEmojiData[] = parsedRecentEmojisFromLocalStorage.map((emojiName: string) => ({ + name: emojiName, + usageCount: 1, + })); + if (toSetRecentEmojiData.length > 0) { + dispatch(savePreferences(currentUserId, [{ + category: Constants.Preferences.RECENT_EMOJIS, + name: currentUserId, + user_id: currentUserId, + value: JSON.stringify(toSetRecentEmojiData), + }])); + } + return {data: parsedRecentEmojisFromLocalStorage}; + } + } + return {data: recentEmojisFromPreference}; + }; +}