import React, { FormEventHandler, useCallback, useMemo, useState } from 'react'; import { Box, Text, Button, Icon, Icons, Avatar, AvatarImage, AvatarFallback, toRem, config, Input, Spinner, color, IconButton, Menu, } from 'folds'; import { MatrixError } from 'matrix-js-sdk'; import { SequenceCard } from '../../../components/sequence-card'; import { ImagePack, ImageUsage, PackAddress, packAddressEqual, PackContent, } from '../../../plugins/custom-emoji'; import { useRoom } from '../../../hooks/useRoom'; import { useRoomImagePacks } from '../../../hooks/useImagePacks'; import { LineClamp2 } from '../../../styles/Text.css'; import { SettingTile } from '../../../components/setting-tile'; import { SequenceCardStyle } from '../styles.css'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { mxcUrlToHttp } from '../../../utils/matrix'; import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication'; import { usePowerLevels } from '../../../hooks/usePowerLevels'; import { StateEvent } from '../../../../types/matrix/room'; import { suffixRename } from '../../../utils/common'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { useAlive } from '../../../hooks/useAlive'; import { useRoomCreators } from '../../../hooks/useRoomCreators'; import { useRoomPermissions } from '../../../hooks/useRoomPermissions'; type CreatePackTileProps = { packs: ImagePack[]; roomId: string; }; function CreatePackTile({ packs, roomId }: CreatePackTileProps) { const mx = useMatrixClient(); const alive = useAlive(); const [addState, addPack] = useAsyncCallback( useCallback( async (stateKey, name) => { const content: PackContent = { pack: { display_name: name, }, }; await mx.sendStateEvent(roomId, StateEvent.PoniesRoomEmotes as any, content, stateKey); }, [mx, roomId] ) ); const creating = addState.status === AsyncStatus.Loading; const handleSubmit: FormEventHandler = (evt) => { evt.preventDefault(); if (creating) return; const target = evt.target as HTMLFormElement | undefined; const nameInput = target?.nameInput as HTMLInputElement | undefined; if (!nameInput) return; const name = nameInput?.value.trim(); if (!name) return; let packKey = name.replace(/\s/g, '-'); const hasPack = (k: string): boolean => !!packs.find((pack) => pack.address?.stateKey === k); if (hasPack(packKey)) { packKey = suffixRename(packKey, hasPack); } addPack(packKey, name).then(() => { if (alive()) { nameInput.value = ''; } }); }; return ( Name {addState.status === AsyncStatus.Error && ( {addState.error.message} )} ); } type RoomPacksProps = { onViewPack: (imagePack: ImagePack) => void; }; export function RoomPacks({ onViewPack }: RoomPacksProps) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); const room = useRoom(); const alive = useAlive(); const powerLevels = usePowerLevels(room); const creators = useRoomCreators(room); const permissions = useRoomPermissions(creators, powerLevels); const canEdit = permissions.stateEvent(StateEvent.PoniesRoomEmotes, mx.getSafeUserId()); const unfilteredPacks = useRoomImagePacks(room); const packs = useMemo(() => unfilteredPacks.filter((pack) => !pack.deleted), [unfilteredPacks]); const [removedPacks, setRemovedPacks] = useState([]); const hasChanges = removedPacks.length > 0; const [applyState, applyChanges] = useAsyncCallback( useCallback(async () => { for (let i = 0; i < removedPacks.length; i += 1) { const addr = removedPacks[i]; // eslint-disable-next-line no-await-in-loop await mx.sendStateEvent(room.roomId, StateEvent.PoniesRoomEmotes as any, {}, addr.stateKey); } }, [mx, room, removedPacks]) ); const applyingChanges = applyState.status === AsyncStatus.Loading; const handleRemove = (address: PackAddress) => { setRemovedPacks((addresses) => [...addresses, address]); }; const handleUndoRemove = (address: PackAddress) => { setRemovedPacks((addresses) => addresses.filter((addr) => !packAddressEqual(addr, address))); }; const handleCancelChanges = () => setRemovedPacks([]); const handleApplyChanges = () => { applyChanges().then(() => { if (alive()) { setRemovedPacks([]); } }); }; const renderPack = (pack: ImagePack) => { const avatarMxc = pack.getAvatarUrl(ImageUsage.Emoticon); const avatarUrl = avatarMxc ? mxcUrlToHttp(mx, avatarMxc, useAuthentication) : undefined; const { address } = pack; if (!address) return null; const removed = !!removedPacks.find((addr) => packAddressEqual(addr, address)); return ( {pack.meta.name ?? 'Unknown'} } description={{pack.meta.attribution}} before={ {canEdit && (removed ? ( handleUndoRemove(address)} disabled={applyingChanges} > ) : ( handleRemove(address)} disabled={applyingChanges} > ))} {avatarUrl ? ( ) : ( )} } after={ !removed && ( ) } /> ); }; return ( <> Packs {canEdit && } {packs.map(renderPack)} {packs.length === 0 && ( No Packs There are no emoji or sticker packs to display at the moment. )} {hasChanges && ( {applyState.status === AsyncStatus.Error ? ( Failed to remove packs! Please try again. ) : ( Delete selected packs. ({removedPacks.length} selected) )} )} ); }