Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-gif-slash-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
sable: minor
---

Add a `/gif` composer command that opens the GIF picker.
12 changes: 11 additions & 1 deletion src/app/components/emoji-board/EmojiBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ type EmojiBoardProps = {
onCustomEmojiSelect?: (mxc: string, shortcode: string) => void;
onStickerSelect?: (mxc: string, shortcode: string, label: string) => void;
onGifSelect?: (gif: GifData, spoiler?: boolean) => void;
initialGifSearch?: string;
allowTextCustomEmoji?: boolean;
addToRecentEmoji?: boolean;
isFullWidth?: boolean;
Expand All @@ -474,6 +475,7 @@ export function EmojiBoard({
onCustomEmojiSelect,
onStickerSelect,
onGifSelect,
initialGifSearch,
allowTextCustomEmoji,
addToRecentEmoji = true,
isFullWidth,
Expand Down Expand Up @@ -576,6 +578,13 @@ export function EmojiBoard({
{ wait: 200 }
);

useEffect(() => {
if (!gifTab || initialGifSearch === undefined) return;
setShowFavoritesOnly(initialGifSearch.length === 0);
if (initialGifSearch) searchGifs(initialGifSearch);
else resetGifSearch();
}, [gifTab, initialGifSearch, searchGifs, resetGifSearch]);

const contentScrollRef = useRef<HTMLDivElement>(null);
const virtualBaseRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
Expand Down Expand Up @@ -677,10 +686,11 @@ export function EmojiBoard({
<Box direction="Column" gap="200">
{onTabChange && <EmojiBoardTabs tab={tab} onTabChange={onTabChange} />}
<SearchInput
key={tab}
key={gifTab ? `${tab}-${initialGifSearch ?? ''}` : tab}
query={emojiResult?.query}
onChange={handleOnChange}
tab={tab}
defaultValue={gifTab ? initialGifSearch : undefined}
allowTextCustomEmoji={allowTextCustomEmoji}
onTextCustomEmojiSelect={handleTextCustomEmojiSelect}
/>
Expand Down
3 changes: 3 additions & 0 deletions src/app/components/emoji-board/components/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { EmojiBoardTab } from '../types';

type SearchInputProps = {
query?: string;
defaultValue?: string;
onChange: ChangeEventHandler<HTMLInputElement>;
allowTextCustomEmoji?: boolean;
onTextCustomEmojiSelect?: (text: string) => void;
tab?: EmojiBoardTab;
};
export function SearchInput({
query,
defaultValue,
onChange,
allowTextCustomEmoji,
onTextCustomEmojiSelect,
Expand All @@ -40,6 +42,7 @@ export function SearchInput({
: 'Search'
}
maxLength={50}
defaultValue={defaultValue}
after={
allowTextCustomEmoji && query && tab !== EmojiBoardTab.Gif ? (
<Chip
Expand Down
10 changes: 8 additions & 2 deletions src/app/features/room/CommandAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useKeyDown } from '$hooks/useKeyDown';
import { onTabPress } from '$utils/keyboard';

type CommandAutoCompleteHandler = (commandName: string) => void;
const GIF_COMMAND = 'gif';

type CommandAutocompleteProps = {
room: Room;
Expand All @@ -41,7 +42,10 @@ export function CommandAutocomplete({
}: CommandAutocompleteProps) {
const mx = useMatrixClient();
const commands = useCommands(mx, room);
const commandNames = useMemo(() => Object.keys(commands) as Command[], [commands]);
const commandNames = useMemo(
() => [GIF_COMMAND, ...(Object.keys(commands) as Command[])],
[commands]
);

const [result, search, resetSearch] = useAsyncSearch(
commandNames,
Expand Down Expand Up @@ -106,7 +110,9 @@ export function CommandAutocomplete({
{`/${commandName}`}
</Text>
<Text truncate priority="300" size="T200">
{commands[commandName].description}
{commandName === GIF_COMMAND
? 'Search and send a GIF: /gif <search>'
: commands[commandName as Command].description}
</Text>
</Box>
</MenuItem>
Expand Down
19 changes: 18 additions & 1 deletion src/app/features/room/RoomInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -521,11 +521,13 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
const [sendError, setSendError] = useState<string | undefined>();
const isEncrypted = room.hasEncryptionStateEvent();
const [emojiBoardTab, setEmojiBoardTab] = useState<EmojiBoardTab | undefined>(undefined);
const [initialGifSearch, setInitialGifSearch] = useState<string>();
const closeEmojiBoard = useCallback(() => {
if (isMobileOrTablet()) {
const activeElement = document.activeElement;
if (activeElement instanceof HTMLElement) activeElement.blur();
}
setInitialGifSearch(undefined);
setEmojiBoardTab(undefined);
}, []);
const toggleEmojiBoardTab = useCallback((tab: EmojiBoardTab) => {
Expand Down Expand Up @@ -1212,6 +1214,8 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
room,
})
);
const rawGifCommand =
commandName === undefined ? plainText.match(/^\/gif(?:\s+(.*))?$/i) : undefined;

let msgType = MsgType.Text;

Expand All @@ -1228,6 +1232,15 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
return; // don't do anything besides handling the command
}

if (rawGifCommand) {
setInitialGifSearch(rawGifCommand[1] ?? '');
setEmojiBoardTab(EmojiBoardTab.Gif);
resetEditor(editor);
resetEditorHistory(editor);
sendTypingStatus(false);
return;
}

if (commandName) {
plainText = trimCommand(commandName, plainText);
customHtml = trimCommand(commandName, customHtml);
Expand All @@ -1249,7 +1262,10 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
if ((commandName as Command) === Command.Poll) setShowPollPicker(true);
else if ((commandName as Command) === Command.Location && plainText.trim().length === 0)
setShowLocationPicker(true);
else {
else if (commandName === 'gif') {
setInitialGifSearch(plainText);
setEmojiBoardTab(EmojiBoardTab.Gif);
} else {
const commandContent = commands[commandName as Command];
if (commandContent) {
commandContent.exe(plainText, customHtml);
Expand Down Expand Up @@ -2221,6 +2237,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
onCustomEmojiSelect={handleEmoticonSelect}
onStickerSelect={handleStickerSelect}
onGifSelect={handleGifSelect}
initialGifSearch={initialGifSearch}
requestClose={closeEmojiBoard}
/>
);
Expand Down
Loading