diff --git a/.changeset/fix-various-visual-fixes.md b/.changeset/fix-various-visual-fixes.md new file mode 100644 index 0000000000..88a49fd85d --- /dev/null +++ b/.changeset/fix-various-visual-fixes.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Various visual tweaks diff --git a/src/app/components/emoji-board/components/Item.tsx b/src/app/components/emoji-board/components/Item.tsx index ea70226357..21af5ed1b7 100644 --- a/src/app/components/emoji-board/components/Item.tsx +++ b/src/app/components/emoji-board/components/Item.tsx @@ -253,7 +253,7 @@ export function GifItem({ > {menuIcon(Star, { weight: favorited ? 'fill' : 'regular', - color: favorited ? color.Warning.MainHover : color.Surface.OnContainer, + color: favorited ? color.Warning.MainHover : color.Secondary.OnContainer, })} ( favoritedContent.gifs.find((v) => v.url == url) != undefined ); - const isGif = - info?.mimetype === 'image/gif' || - info?.mimetype === 'image/apng' || - info?.mimetype === 'image/webp' || - (body ?? '').toLowerCase().endsWith('.gif') || - (body ?? '').toLowerCase().endsWith('.apng') || - (body ?? '').toLowerCase().endsWith('.webp') || - url.toLowerCase().endsWith('.gif') || - url.toLowerCase().endsWith('.apng') || - url.toLowerCase().endsWith('.webp'); + const isGif = checkIfGif(url, info?.mimetype, body); const [srcState, loadSrc] = useAsyncCallback( useCallback(async () => { @@ -464,7 +473,7 @@ export const ImageContent = as<'div', ImageContentProps>( > {menuIcon(Star, { weight: favorited ? 'fill' : 'regular', - color: favorited ? color.Warning.MainHover : color.Surface.OnContainer, + color: favorited ? color.Warning.MainHover : color.Secondary.OnContainer, })} )} diff --git a/src/app/components/message/modals/Options.tsx b/src/app/components/message/modals/Options.tsx index fd08a0f02a..fca2b8b523 100644 --- a/src/app/components/message/modals/Options.tsx +++ b/src/app/components/message/modals/Options.tsx @@ -51,6 +51,10 @@ import { } from '$features/bookmarks'; import { CopyIcon } from '@phosphor-icons/react'; import * as OptionsCss from './Options.css'; +import { MATRIX_SABLE_UNSTABLE_FAVORITE_GIFS } from '$unstable/prefixes'; +import { useFavoriteGifs } from '$hooks/useFavoriteGifs'; +import type { IImageInfo } from '$types/matrix/common'; +import { getIncomingMediaMxcUrl } from '../MsgTypeRenderers'; function WrappedMessage({ isModal, @@ -239,7 +243,9 @@ export const MessageBookmarkItem = as< return ( void; + } +>(({ room, mEvent, onClose, ...props }, ref) => { + const mx = useMatrixClient(); + const content = mEvent.getContent(); + const url = getIncomingMediaMxcUrl(content.file?.url ?? content.url) ?? ''; + const favoritedContent = useFavoriteGifs(); + const [favorited, setFavorited] = useState( + favoritedContent.gifs.find((v) => v.url == url) != undefined + ); + const handleClick = async () => { + if (!favorited) { + const info: IImageInfo | undefined = content?.info; + const body = content?.body; + setFavorited(true); + await mx + .setAccountData(MATRIX_SABLE_UNSTABLE_FAVORITE_GIFS, { + gifs: [ + ...favoritedContent.gifs, + { + title: body ?? '', + url: url, + width: info?.w, + height: info?.h, + size: info?.size, + mimetype: info?.mimetype, + }, + ], + }) + .catch(() => setFavorited(false)); + } else { + setFavorited(false); + await mx + .setAccountData(MATRIX_SABLE_UNSTABLE_FAVORITE_GIFS, { + gifs: favoritedContent.gifs.filter((v) => v.url != url), + }) + .catch(() => setFavorited(true)); + } + + onClose?.(); + }; + return ( + + + {favorited ? 'Unfavorite Gif' : 'Favorite Gif'} + + + ); +}); + export type OptionEmojiMenuProps = { mEvent: MatrixEvent; closeMenu: () => void; @@ -334,6 +404,7 @@ export function OptionQuickMenu({ menuAnchor, imagePackRooms, setIsEmoji, + isGif, }: OptionMenuProps) { const mx = useMatrixClient(); const isThreadedMessage = isThreadRelationEvent(mEvent, mEvent.threadRootId); @@ -438,6 +509,7 @@ export function OptionQuickMenu({ setIsEmoji={setIsEmoji} emojiBoardAnchor={menuAnchor} canSendReaction={canSendReaction} + isGif={isGif} /> } > @@ -484,6 +556,7 @@ export type OptionMenuProps = { canDelete?: boolean; handleOpenMenu?: MouseEventHandler; menuAnchor?: RectCords | undefined; + isGif?: boolean; emojiBoardAnchor?: RectCords; imagePackRooms?: Room[]; @@ -511,6 +584,7 @@ export function OptionMenu({ ActualMessage, isModal, dragOpts, + isGif, }: OptionMenuProps) { const setModal = useSetAtom(modalAtom); const store = useStore(); @@ -665,6 +739,9 @@ export function OptionMenu({ {relations && ( )} + {isGif && isModal && ( + + )} diff --git a/src/app/components/user-profile/UserRoomProfile.tsx b/src/app/components/user-profile/UserRoomProfile.tsx index d5f529518a..e5df6ec398 100644 --- a/src/app/components/user-profile/UserRoomProfile.tsx +++ b/src/app/components/user-profile/UserRoomProfile.tsx @@ -114,9 +114,10 @@ function UserExtendedSection({ const catStatusText = useMemo(() => { if (!renderAnimals) return null; - if (isAnimal && hasAnimal) return `${isAnimal} with ${hasAnimal}, give ${animalNeed}!`; - if (isAnimal) return `Is ${isAnimal}, give ${animalNeed}!`; - if (hasAnimal) return `Has ${hasAnimal}, give ${animalNeed}!`; + const animalGive = animalNeed ? `, give ${animalNeed}!` : '!'; + if (isAnimal && hasAnimal) return `${isAnimal} with ${hasAnimal}${animalGive}`; + if (isAnimal) return `Is ${isAnimal}${animalGive}`; + if (hasAnimal) return `Has ${hasAnimal}${animalGive}`; return null; }, [renderAnimals, isAnimal, hasAnimal, animalNeed]); diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index 81293f9a19..4536740abf 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -22,6 +22,7 @@ import { useSetAtom } from 'jotai'; import { AvatarBase, BubbleLayout, + checkIfGif, CompactLayout, MessageBase, ModernLayout, @@ -381,6 +382,12 @@ function MessageInternal( const setModal = useSetAtom(modalAtom); const [contentVersion, setContentVersion] = useState(0); + const isGif = useMemo(() => { + const content = mEvent.getContent(); + if (content.msgtype !== 'm.image') return false; + return checkIfGif(content?.info?.url ?? '', content?.info?.mimetype, content?.body); + }, [mEvent]); + useEffect(() => { const triggerTimelineRegroup = () => { // A Local Echo update seems to trigger a visual refresh without @@ -848,6 +855,7 @@ function MessageInternal( ), canSendReaction: canSendReaction, + isGif: isGif, }, }); }; @@ -944,6 +952,7 @@ function MessageInternal( imagePackRooms={imagePackRooms} setIsEmoji={setIsEmoji} canSendReaction={canSendReaction} + isGif={isGif} /> )} diff --git a/src/app/pages/client/profile/Profile.tsx b/src/app/pages/client/profile/Profile.tsx index 30be9a695e..705df9b7b6 100644 --- a/src/app/pages/client/profile/Profile.tsx +++ b/src/app/pages/client/profile/Profile.tsx @@ -242,8 +242,8 @@ export function ProfileMobile() { setLogout(true)} > @@ -267,7 +267,7 @@ export function ProfileMobile() { )} -
+
diff --git a/src/app/pages/client/sidebar/UserMenuTab.tsx b/src/app/pages/client/sidebar/UserMenuTab.tsx index e561fd0964..7bb62c3228 100644 --- a/src/app/pages/client/sidebar/UserMenuTab.tsx +++ b/src/app/pages/client/sidebar/UserMenuTab.tsx @@ -277,7 +277,7 @@ export function AccountMenuOption({ isMobile, isRight }: { isMobile: boolean; is background: isMobile ? color.Background.Container : isOpen - ? color.Secondary.Container + ? color.Surface.ContainerHover : color.Surface.Container, }} onClick={() => isMobile && setIsOpen(!isOpen)} @@ -501,7 +501,7 @@ export function PresenceMenuOption({ background: isMobile ? color.Background.Container : isOpen - ? color.Secondary.Container + ? color.Surface.ContainerHover : color.Surface.Container, }} onClick={() => isMobile && setIsOpen(!isOpen)}