mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-08-19 20:57:57 +01:00
237 lines
7.1 KiB
TypeScript
237 lines
7.1 KiB
TypeScript
// Copyright 2019 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import {
|
|
memo,
|
|
useState,
|
|
useCallback,
|
|
type MouseEvent,
|
|
type KeyboardEvent,
|
|
type ReactNode,
|
|
type JSX,
|
|
} from 'react';
|
|
import type { LocalizerType } from '../../types/Util.std.ts';
|
|
import type { StickerPackType } from '../../state/ducks/stickers.preload.ts';
|
|
import { UserText } from '../UserText.dom.tsx';
|
|
import { AxoConfirmDialog } from '../../axo/AxoConfirmDialog.dom.tsx';
|
|
import { AxoIconButton } from '../../axo/AxoIconButton.dom.tsx';
|
|
import { tw } from '../../axo/tw.dom.tsx';
|
|
import { OfficialChatInlineBadge } from '../conversation/OfficialChatInlineBadge.dom.tsx';
|
|
import { AxoContextMenu } from '../../axo/AxoContextMenu.dom.tsx';
|
|
import { AxoSymbol } from '../../axo/AxoSymbol.dom.tsx';
|
|
import { ListBoxItem } from 'react-aria-components';
|
|
|
|
export type OwnProps = {
|
|
readonly i18n: LocalizerType;
|
|
readonly pack: StickerPackType;
|
|
readonly controlType: StickerManagerPackRowControlType;
|
|
readonly onClickPreview?: (packId: string) => unknown;
|
|
readonly installStickerPack?: (
|
|
packId: string,
|
|
packKey: string,
|
|
options: { actionSource: 'ui' }
|
|
) => unknown;
|
|
readonly uninstallStickerPack?: (
|
|
packId: string,
|
|
packKey: string,
|
|
options: { actionSource: 'ui' }
|
|
) => unknown;
|
|
};
|
|
|
|
export type Props = OwnProps;
|
|
|
|
export type StickerManagerPackRowControlType = 'install-button' | 'drag-handle';
|
|
|
|
export const StickerManagerPackRow = memo(function StickerManagerPackRowInner({
|
|
controlType,
|
|
i18n,
|
|
pack,
|
|
installStickerPack,
|
|
uninstallStickerPack,
|
|
onClickPreview,
|
|
}: Props) {
|
|
const { id, key, isBlessed, title } = pack;
|
|
const [uninstalling, setUninstalling] = useState(false);
|
|
|
|
const clearUninstalling = useCallback(() => {
|
|
setUninstalling(false);
|
|
}, [setUninstalling]);
|
|
|
|
const handleInstall = useCallback(
|
|
(e: Event | MouseEvent) => {
|
|
e.stopPropagation();
|
|
if (installStickerPack) {
|
|
installStickerPack(id, key, { actionSource: 'ui' });
|
|
}
|
|
},
|
|
[id, installStickerPack, key]
|
|
);
|
|
|
|
const handleUninstall = useCallback(
|
|
(e: Event) => {
|
|
e.stopPropagation();
|
|
if (isBlessed && uninstallStickerPack) {
|
|
uninstallStickerPack(id, key, { actionSource: 'ui' });
|
|
} else {
|
|
setUninstalling(true);
|
|
}
|
|
},
|
|
[id, isBlessed, key, setUninstalling, uninstallStickerPack]
|
|
);
|
|
|
|
const handleConfirmUninstall = useCallback(() => {
|
|
clearUninstalling();
|
|
if (uninstallStickerPack) {
|
|
uninstallStickerPack(id, key, { actionSource: 'ui' });
|
|
}
|
|
}, [id, key, clearUninstalling, uninstallStickerPack]);
|
|
|
|
const handleKeyDown = useCallback(
|
|
(event: KeyboardEvent) => {
|
|
if (onClickPreview && (event.key === 'Enter' || event.key === 'Space')) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
onClickPreview(id);
|
|
}
|
|
},
|
|
[onClickPreview, id]
|
|
);
|
|
|
|
const handleClickPreview = useCallback(
|
|
(event: MouseEvent) => {
|
|
if (onClickPreview) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
onClickPreview(id);
|
|
}
|
|
},
|
|
[onClickPreview, id]
|
|
);
|
|
|
|
return (
|
|
<ListBoxItem id={id} textValue={title}>
|
|
<AxoConfirmDialog.Root
|
|
open={uninstalling}
|
|
onOpenChange={setUninstalling}
|
|
// @ts-expect-error ConfirmationDialog migration: Needs title
|
|
title={null}
|
|
description={i18n('icu:stickers--StickerManager--UninstallWarning')}
|
|
>
|
|
<AxoConfirmDialog.Cancel />
|
|
<AxoConfirmDialog.Action
|
|
variant="strong-destructive"
|
|
onClick={handleConfirmUninstall}
|
|
>
|
|
{i18n('icu:stickers--StickerManager--Uninstall')}
|
|
</AxoConfirmDialog.Action>
|
|
</AxoConfirmDialog.Root>
|
|
<PackContextMenu
|
|
i18n={i18n}
|
|
handleInstall={handleInstall}
|
|
handleUninstall={handleUninstall}
|
|
isInstalled={pack.status === 'installed'}
|
|
>
|
|
<div
|
|
tabIndex={0}
|
|
// This can't be a button because we have buttons as descendants
|
|
role="button"
|
|
onKeyDown={handleKeyDown}
|
|
onClick={handleClickPreview}
|
|
className="module-sticker-manager__pack-row"
|
|
data-testid={id}
|
|
>
|
|
{pack.cover ? (
|
|
<img
|
|
src={pack.cover.url}
|
|
alt={pack.title}
|
|
className="module-sticker-manager__pack-row__cover"
|
|
/>
|
|
) : (
|
|
<div className="module-sticker-manager__pack-row__cover-placeholder" />
|
|
)}
|
|
<div className="module-sticker-manager__pack-row__meta">
|
|
<div
|
|
className={tw('mb-0.5 flex flex-1 type-body-medium text-primary')}
|
|
>
|
|
<UserText text={pack.title} />
|
|
{pack.isBlessed ? (
|
|
<span className={tw('ms-1')}>
|
|
<OfficialChatInlineBadge />
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
<div className={tw('flex flex-1 type-body-small text-secondary')}>
|
|
{pack.author}
|
|
</div>
|
|
</div>
|
|
{controlType === 'install-button' && (
|
|
<div className="module-sticker-manager__pack-row__controls">
|
|
{pack.status === 'installed' ? (
|
|
<AxoIconButton.Root
|
|
variant="strong-secondary"
|
|
size="md"
|
|
symbol="check"
|
|
label={i18n('icu:stickers--StickerManager--Installed')}
|
|
tooltip={false}
|
|
disabled
|
|
/>
|
|
) : (
|
|
<AxoIconButton.Root
|
|
variant="strong-secondary"
|
|
size="md"
|
|
symbol="plus"
|
|
label={i18n('icu:stickers--StickerManager--Install')}
|
|
onClick={handleInstall}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
{controlType === 'drag-handle' && <ItemDragHandle i18n={i18n} />}
|
|
</div>
|
|
</PackContextMenu>
|
|
</ListBoxItem>
|
|
);
|
|
});
|
|
|
|
function PackContextMenu(props: {
|
|
i18n: LocalizerType;
|
|
isInstalled: boolean;
|
|
handleInstall: (e: Event) => void;
|
|
handleUninstall: (e: Event) => void;
|
|
children: ReactNode;
|
|
}) {
|
|
const { i18n, isInstalled, handleInstall, handleUninstall } = props;
|
|
|
|
return (
|
|
<AxoContextMenu.Root>
|
|
<AxoContextMenu.Trigger>{props.children}</AxoContextMenu.Trigger>
|
|
<AxoContextMenu.Content>
|
|
{isInstalled ? (
|
|
<AxoContextMenu.Item symbol="minus-circle" onSelect={handleUninstall}>
|
|
{i18n('icu:stickers--StickerManagerPackContextMenu--Remove')}
|
|
</AxoContextMenu.Item>
|
|
) : (
|
|
<AxoContextMenu.Item symbol="plus-circle" onSelect={handleInstall}>
|
|
{i18n('icu:stickers--StickerManagerPackContextMenu--Add')}
|
|
</AxoContextMenu.Item>
|
|
)}
|
|
</AxoContextMenu.Content>
|
|
</AxoContextMenu.Root>
|
|
);
|
|
}
|
|
|
|
function ItemDragHandle(props: { i18n: LocalizerType }): JSX.Element {
|
|
const { i18n } = props;
|
|
return (
|
|
<span className={tw('cursor-grab text-primary')}>
|
|
<AxoSymbol.Icon
|
|
symbol="draghandle-alt"
|
|
size={18}
|
|
label={i18n('icu:stickers--StickerManagerPackDragHandle--Label')}
|
|
/>
|
|
</span>
|
|
);
|
|
}
|