diff --git a/fluxer_app/src/features/channel/components/StickersPicker.tsx b/fluxer_app/src/features/channel/components/StickersPicker.tsx index 44279893a..b7eda5379 100644 --- a/fluxer_app/src/features/channel/components/StickersPicker.tsx +++ b/fluxer_app/src/features/channel/components/StickersPicker.tsx @@ -14,10 +14,11 @@ import {useVirtualRows} from '@app/features/channel/components/sticker_picker/ho import {StickerPickerCategoryList} from '@app/features/channel/components/sticker_picker/StickerPickerCategoryList'; import { buildStickerRowOffsets, - getStickerRowHeight, + getFixedStickerRowHeight, + getStickerGridColumns, getStickerRowWindow, + STICKER_GRID_TRACK_WIDTH, STICKER_SECTION_GAP, - STICKERS_PER_ROW, type StickerRowKind, } from '@app/features/channel/components/sticker_picker/StickerPickerConstants'; import {StickerPickerInspector} from '@app/features/channel/components/sticker_picker/StickerPickerInspector'; @@ -74,12 +75,12 @@ export const StickersPicker = observer( const [selectedColumn, setSelectedColumn] = useState(-1); const [shouldScrollOnSelection, setShouldScrollOnSelection] = useState(false); const scrollerRef = useRef(null); - const {scrollTop, handleScroll, handleResize} = useScrollerViewport(scrollerRef); + const {viewportSize, scrollTop, handleScroll, handleResize} = useScrollerViewport(scrollerRef); const searchInputRef = useRef(null); const stickerRefs = useRef>(new Map()); const channel = channelId ? (Channels.getChannel(channelId) ?? null) : null; const rowListRef = useRef(null); - const [listMetrics, setListMetrics] = useState({origin: 0, viewportHeight: 0, gridWidth: 0}); + const [listMetrics, setListMetrics] = useState({origin: 0, viewportHeight: 0}); const [stickerDataVersion, setStickerDataVersion] = useState(0); const permissionVersion = useSyncExternalStore(Permission.subscribe.bind(Permission), () => Permission.version); const {shouldAnimate: shouldAnimateStickerPreview} = useStickerAnimation(); @@ -138,13 +139,26 @@ export const StickersPicker = observer( allUpsell.accessibleItems, renderedStickers, ); + const zoomLevel = Accessibility.zoomLevel; + const gridColumns = useMemo(() => getStickerGridColumns(viewportSize.width), [viewportSize.width, zoomLevel]); + const previousGridColumnsRef = useRef(gridColumns); + useLayoutEffect(() => { + if (previousGridColumnsRef.current === gridColumns) { + return; + } + previousGridColumnsRef.current = gridColumns; + setHoveredSticker(null); + setSelectedRow(-1); + setSelectedColumn(-1); + setShouldScrollOnSelection(false); + }, [gridColumns]); const pickerRows = useVirtualRows( searchTerm, renderedStickers, favoriteStickers, frequentlyUsedStickers, stickersByGuildId, - STICKERS_PER_ROW, + gridColumns, ); const hasNoStickersAtAll = allItems.length === 0; const isSearching = searchTerm.trim().length > 0; @@ -198,12 +212,8 @@ export const StickersPicker = observer( } return {stickerRowIndexes: indexes, stickerRowStarts: starts, categoryRowIndexes: categories}; }, [pickerRows]); - const zoomLevel = Accessibility.zoomLevel; const remScale = getAppRemScale(); - const stickerRowHeight = useMemo( - () => getStickerRowHeight(listMetrics.gridWidth, STICKERS_PER_ROW, remScale), - [listMetrics.gridWidth, remScale, zoomLevel], - ); + const stickerRowHeight = useMemo(() => getFixedStickerRowHeight(remScale), [remScale, zoomLevel]); const rowOffsets = useMemo(() => { const rowKinds = pickerRows.map((row): StickerRowKind => row.type); return buildStickerRowOffsets(rowKinds, {remScale, stickerRowHeight, sectionGap: STICKER_SECTION_GAP}); @@ -219,11 +229,8 @@ export const StickersPicker = observer( rowListNode.getBoundingClientRect().top - scrollerNode.getBoundingClientRect().top + scrollerNode.scrollTop, ); const viewportHeight = scrollerNode.clientHeight; - const gridWidth = rowListNode.clientWidth; setListMetrics((current) => - current.origin === origin && current.viewportHeight === viewportHeight && current.gridWidth === gridWidth - ? current - : {origin, viewportHeight, gridWidth}, + current.origin === origin && current.viewportHeight === viewportHeight ? current : {origin, viewportHeight}, ); }); const rowWindow = useMemo( @@ -394,7 +401,8 @@ export const StickersPicker = observer( row={row} handleHover={handleHover} handleSelect={handleStickerSelect} - gridColumns={STICKERS_PER_ROW} + gridColumns={gridColumns} + cellTrackWidth={STICKER_GRID_TRACK_WIDTH} selectedRow={selectedRow} selectedColumn={selectedColumn} stickerRowIndex={stickerRowIndexes[rowIndex]!} diff --git a/fluxer_app/src/features/channel/components/sticker_picker/StickerPickerConstants.ts b/fluxer_app/src/features/channel/components/sticker_picker/StickerPickerConstants.ts index 5bfca2e3c..297c24bd3 100644 --- a/fluxer_app/src/features/channel/components/sticker_picker/StickerPickerConstants.ts +++ b/fluxer_app/src/features/channel/components/sticker_picker/StickerPickerConstants.ts @@ -1,9 +1,12 @@ // SPDX-License-Identifier: AGPL-3.0-or-later +import {getRemScaleForDocument} from '@app/features/theme/layout/RemFromPx'; + export const STICKERS_PER_ROW = 4; export const STICKERS_PER_ROW_MOBILE = 5; export const STICKER_CATEGORY_HEADER_HEIGHT = 32; export const STICKER_SECTION_GAP = 12; +export const STICKER_GRID_TRACK_WIDTH = 120; const STICKER_CATEGORY_HEADER_GAP = 8; const STICKER_GRID_GAP = 8; @@ -23,6 +26,20 @@ export interface StickerRowWindow { lastRow: number; } +export function getStickerGridColumns(containerWidth: number): number { + if (containerWidth <= 0) { + return STICKERS_PER_ROW; + } + const remScale = getRemScaleForDocument(typeof document === 'undefined' ? null : document); + const trackWidth = STICKER_GRID_TRACK_WIDTH * remScale; + const gap = STICKER_GRID_GAP * remScale; + return Math.max(1, Math.floor((containerWidth + gap) / (trackWidth + gap))); +} + +export function getFixedStickerRowHeight(remScale: number): number { + return (STICKER_GRID_TRACK_WIDTH + STICKER_GRID_BLOCK_END_PADDING) * remScale; +} + export function getStickerRowHeight(gridWidth: number, gridColumns: number, remScale: number): number { if (!(gridWidth > 0) || gridColumns <= 0) { return 0; diff --git a/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.module.css b/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.module.css index ed0c31cd3..55e7de189 100644 --- a/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.module.css +++ b/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.module.css @@ -20,6 +20,11 @@ padding: 0 0 0.5rem; } +.stickerGridFixedTrack { + grid-auto-flow: column; + justify-content: start; +} + .stickerButton { position: relative; display: flex; diff --git a/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.tsx b/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.tsx index e01dc43f7..fd6ee6572 100644 --- a/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.tsx +++ b/fluxer_app/src/features/channel/components/sticker_picker/VirtualRow.tsx @@ -27,6 +27,7 @@ interface VirtualRowRendererProps { handleHover: (sticker: GuildSticker | null, row?: number, column?: number) => void; handleSelect: (sticker: GuildSticker, shiftKey?: boolean) => void; gridColumns?: number; + cellTrackWidth?: number; selectedRow: number; selectedColumn: number; stickerRowIndex: number; @@ -126,6 +127,7 @@ const VirtualRowRendererBase: React.FC = React.memo( handleHover, handleSelect, gridColumns = 4, + cellTrackWidth, selectedRow, selectedColumn, stickerRowIndex, @@ -220,11 +222,14 @@ const VirtualRowRendererBase: React.FC = React.memo( ); } if (row.type === 'sticker-row') { + const hasFixedTrack = cellTrackWidth != null && cellTrackWidth > 0; return (
@@ -286,6 +291,7 @@ interface VirtualRowWrapperProps { handleHover: (sticker: GuildSticker | null, row?: number, column?: number) => void; handleSelect: (sticker: GuildSticker, shiftKey?: boolean) => void; gridColumns?: number; + cellTrackWidth?: number; selectedRow: number; selectedColumn: number; stickerRowIndex: number; @@ -300,6 +306,7 @@ export const VirtualRowWrapper: React.FC = observer( handleHover, handleSelect, gridColumns, + cellTrackWidth, selectedRow, selectedColumn, stickerRowIndex, @@ -313,6 +320,7 @@ export const VirtualRowWrapper: React.FC = observer( handleHover={handleHover} handleSelect={handleSelect} gridColumns={gridColumns} + cellTrackWidth={cellTrackWidth} selectedRow={selectedRow} selectedColumn={selectedColumn} stickerRowIndex={stickerRowIndex}