From d20cbffbd7a874225f1191e4b5b2c1e587eebe0c Mon Sep 17 00:00:00 2001 From: Matthew Yung <117509016+myung03@users.noreply.github.com> Date: Wed, 26 Jun 2024 00:49:10 -0700 Subject: [PATCH] Bug fixes (#2566) * changed tag assign mode behaviour * added min/max zoom and filekinds logic * changed colour of system data bar --- .../$libraryId/Explorer/ExplorerTagBar.tsx | 83 +++++++++++++------ .../Explorer/QuickPreview/index.tsx | 32 +++---- .../app/$libraryId/overview/FileKindStats.tsx | 60 +++++++++----- .../app/$libraryId/overview/LibraryStats.tsx | 8 +- interface/locales/en/common.json | 3 +- 5 files changed, 121 insertions(+), 65 deletions(-) diff --git a/interface/app/$libraryId/Explorer/ExplorerTagBar.tsx b/interface/app/$libraryId/Explorer/ExplorerTagBar.tsx index 101c65cc9..38060d43e 100644 --- a/interface/app/$libraryId/Explorer/ExplorerTagBar.tsx +++ b/interface/app/$libraryId/Explorer/ExplorerTagBar.tsx @@ -1,6 +1,9 @@ import { Circle } from '@phosphor-icons/react'; +import clsx from 'clsx'; +import { useEffect, useRef, useState } from 'react'; import { ExplorerItem, + getItemObject, Tag, Target, useLibraryMutation, @@ -9,8 +12,6 @@ import { useSelector } from '@sd/client'; import { Shortcut, toast } from '@sd/ui'; -import clsx from 'clsx'; -import { useEffect, useRef, useState } from 'react'; import { useIsDark, useKeybind, useLocale, useOperatingSystem } from '~/hooks'; import { keybindForOs } from '~/util/keybinds'; @@ -87,27 +88,30 @@ export const ExplorerTagBar = () => { const element = tagsRef.current; if (element) { setIsTagsOverflowing( - element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth + element.scrollHeight > element.clientHeight || + element.scrollWidth > element.clientWidth ); } - } + }; useEffect(() => { const element = tagsRef.current; if (!element) return; //handles initial render when not resizing - setIsTagsOverflowing(element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth) + setIsTagsOverflowing( + element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth + ); //make sure state updates when window resizing window.addEventListener('resize', () => { updateOverflowState(); - }) + }); //remove listeners on unmount return () => { window.removeEventListener('resize', () => { updateOverflowState(); - }) - } - }, [tagsRef]) + }); + }; + }, [tagsRef]); const [tagListeningForKeyPress, setTagListeningForKeyPress] = useState(); @@ -147,22 +151,51 @@ export const ExplorerTagBar = () => { if (!tag) return; - try { - await mutation.mutateAsync({ - targets, - tag_id: tag.id, - unassign: false - }); + // extract the list of tags from each object in the selected items + const targetsTagList = Array.from(explorer.selectedItems.entries()).map( + // issues with type here. unsure as to why, and not causing any noticeable errors, so ignoring for now with as any + (item) => (item[0] as any).object.item.tags + ); - toast( - t('tags_bulk_assigned', { - tag_name: tag.name, - file_count: targets.length - }), - { - type: 'success' - } - ); + // iterate through each tag in the selected items and check if the tag we want to assign is already assigned + const areAllAssigned = targetsTagList.every((tags) => { + return tags.some((t: { tag_id: any }) => t.tag_id === tag.id); + }); + + try { + if (areAllAssigned) { + await mutation.mutateAsync({ + targets, + tag_id: tag.id, + unassign: true + }); + + toast( + t('tags_bulk_unassigned', { + tag_name: tag.name, + file_count: targets.length + }), + { + type: 'success' + } + ); + } else { + await mutation.mutateAsync({ + targets, + tag_id: tag.id, + unassign: false + }); + + toast( + t('tags_bulk_assigned', { + tag_name: tag.name, + file_count: targets.length + }), + { + type: 'success' + } + ); + } } catch (err) { let msg: string = t('error_unknown'); @@ -199,7 +232,7 @@ export const ExplorerTagBar = () => { return (
{t('tags_bulk_instructions')} diff --git a/interface/app/$libraryId/Explorer/QuickPreview/index.tsx b/interface/app/$libraryId/Explorer/QuickPreview/index.tsx index e274162c6..78138cfe3 100644 --- a/interface/app/$libraryId/Explorer/QuickPreview/index.tsx +++ b/interface/app/$libraryId/Explorer/QuickPreview/index.tsx @@ -432,14 +432,15 @@ export const QuickPreview = () => {
- setMagnification( - (currentMagnification) => - currentMagnification + - currentMagnification * 0.2 - ) - } - // this is same formula as intrest calculation + onClick={() => { + magnification < 2 && + setMagnification( + (currentMagnification) => + currentMagnification + + currentMagnification * 0.2 + ); + }} + // this is same formula as interest calculation > @@ -447,13 +448,14 @@ export const QuickPreview = () => { - setMagnification( - (currentMagnification) => - currentMagnification / (1 + 0.2) - ) - } - // this is same formula as intrest calculation + onClick={() => { + magnification > 0.5 && + setMagnification( + (currentMagnification) => + currentMagnification / (1 + 0.2) + ); + }} + // this is same formula as interest calculation > diff --git a/interface/app/$libraryId/overview/FileKindStats.tsx b/interface/app/$libraryId/overview/FileKindStats.tsx index 1249bb969..300c7182b 100644 --- a/interface/app/$libraryId/overview/FileKindStats.tsx +++ b/interface/app/$libraryId/overview/FileKindStats.tsx @@ -8,14 +8,20 @@ import { KindStatistic, uint32ArrayToBigInt, useLibraryQuery } from '@sd/client' import { Card, Tooltip } from '@sd/ui'; import { useIsDark, useLocale } from '~/hooks'; -const INFO_ICON_CLASSLIST = 'inline size-3 text-ink-faint opacity-0'; +const INFO_ICON_CLASSLIST = + 'inline size-3 text-ink-faint opacity-0 ml-1 transition-opacity duration-300 group-hover:opacity-70'; const TOTAL_FILES_CLASSLIST = 'flex items-center justify-between whitespace-nowrap text-sm font-medium text-ink-dull mt-2 px-1'; const UNIDENTIFIED_FILES_CLASSLIST = 'relative flex items-center text-xs text-ink-faint'; +const BARS_CONTAINER_CLASSLIST = + 'relative mx-2.5 grid grow grid-cols-[repeat(auto-fit,_minmax(0,_1fr))] grid-rows-[136px_12px] items-end justify-items-center gap-x-1.5 gap-y-1 self-stretch'; const mapFractionalValue = (numerator: bigint, denominator: bigint, maxValue: bigint): string => { - const result = ((numerator * maxValue) / denominator).toString(); - return result; + if (denominator === 0n) return '0'; + const result = (numerator * maxValue) / denominator; + // ensures min width except for empty bars (numerator = 0) + if (numerator != 0n && result < 1) return '1'; + return result.toString(); }; const formatNumberWithCommas = (number: number | bigint) => number.toLocaleString(); @@ -40,6 +46,27 @@ interface FileKind { interface FileKindStatsProps {} +const defaultFileKinds: FileKind[] = [ + { kind: 'Package', count: 0n, id: 4 }, + { kind: 'Archive', count: 0n, id: 8 }, + { kind: 'Executable', count: 0n, id: 9 }, + { kind: 'Encrypted', count: 0n, id: 11 }, + { kind: 'Key', count: 0n, id: 12 }, + { kind: 'Link', count: 0n, id: 13 }, + { kind: 'WebPageArchive', count: 0n, id: 14 }, + { kind: 'Widget', count: 0n, id: 15 }, + { kind: 'Album', count: 0n, id: 16 }, + { kind: 'Collection', count: 0n, id: 17 }, + { kind: 'Font', count: 0n, id: 18 }, + { kind: 'Mesh', count: 0n, id: 19 }, + { kind: 'Code', count: 0n, id: 20 }, + { kind: 'Database', count: 0n, id: 21 }, + { kind: 'Book', count: 0n, id: 22 }, + { kind: 'Config', count: 0n, id: 23 }, + { kind: 'Dotfile', count: 0n, id: 24 }, + { kind: 'Screenshot', count: 0n, id: 25 } +]; + const FileKindStats: React.FC = () => { const isDark = useIsDark(); const navigate = useNavigate(); @@ -111,8 +138,15 @@ const FileKindStats: React.FC = () => { id: item.kind })) ); + if (statistics.length < 10) { + const additionalKinds = defaultFileKinds.filter( + (defaultKind) => !statistics.some((stat) => stat.kind === defaultKind.id) + ); + const kindsToAdd = additionalKinds.slice(0, 10 - statistics.length); + setFileKinds((prevKinds) => [...prevKinds, ...kindsToAdd]); + } - statistics.forEach((item) => { + data.statistics.forEach((item: { name: string }) => { const iconName = item.name; if (!iconsRef.current[iconName]) { const img = new Image(); @@ -129,10 +163,7 @@ const FileKindStats: React.FC = () => { }); const maxFileCount = sortedFileKinds && sortedFileKinds[0] ? sortedFileKinds[0].count : 0n; - - const barGap = 12; const barCount = sortedFileKinds.length; - const totalGapWidth = barGap * (barCount - 5); const makeBarClickHandler = (fileKind: FileKind): MouseEventHandler | undefined => () => { @@ -166,10 +197,7 @@ const FileKindStats: React.FC = () => {
{t('total_files')} - +
@@ -184,7 +212,7 @@ const FileKindStats: React.FC = () => {
-
+
{sortedFileKinds.map((fileKind, index) => { const iconImage = iconsRef.current[fileKind.kind]; const barColor = interpolateHexColor( @@ -226,18 +254,12 @@ const FileKindStats: React.FC = () => { transition={{ duration: 0.4, ease: [0.42, 0, 0.58, 1] }} style={{ height: barHeight, - minHeight: '2px', backgroundColor: barColor }} >
-
+
{formatCount(fileKind.count)}
diff --git a/interface/app/$libraryId/overview/LibraryStats.tsx b/interface/app/$libraryId/overview/LibraryStats.tsx index c04fcdb9d..26ec57fd4 100644 --- a/interface/app/$libraryId/overview/LibraryStats.tsx +++ b/interface/app/$libraryId/overview/LibraryStats.tsx @@ -78,8 +78,6 @@ const LibraryStats = () => { const { library } = useLibraryContext(); const stats = useLibraryQuery(['library.statistics']); const storageBarData = useLibraryQuery(['library.kindStatistics']).data?.statistics; - console.log(storageBarData); - console.log(stats); const { t } = useLocale(); useEffect(() => { @@ -87,17 +85,17 @@ const LibraryStats = () => { }, [stats.isLoading]); const StatItemNames: Partial> = { + total_library_bytes: t('library_bytes'), total_local_bytes_capacity: t('total_bytes_capacity'), total_local_bytes_free: t('total_bytes_free'), - total_library_bytes: t('library_bytes'), library_db_size: t('library_db_size'), total_library_preview_media_bytes: t('preview_media_bytes') }; const StatDescriptions: Partial> = { + total_library_bytes: t('library_bytes_description'), total_local_bytes_capacity: t('total_bytes_capacity_description'), total_local_bytes_free: t('total_bytes_free_description'), - total_library_bytes: t('library_bytes_description'), library_db_size: t('library_db_size_description'), total_library_preview_media_bytes: t('preview_media_bytes_description') }; @@ -161,7 +159,7 @@ const LibraryStats = () => { sections.push({ name: 'System Data', value: systemDataBytes, - color: '#707070', // Gray for System Data + color: '#2F3038', // Gray for System Data tooltip: 'System data that exists outside of your Spacedrive library' }); diff --git a/interface/locales/en/common.json b/interface/locales/en/common.json index 4dbc1f733..ee746b5de 100644 --- a/interface/locales/en/common.json +++ b/interface/locales/en/common.json @@ -678,8 +678,9 @@ "tags_bulk_assigned": "Assigned tag \"{{tag_name}}\" to {{file_count}} $t(file, { \"count\": {{file_count}} }).", "tags_bulk_failed_with_tag": "Could not assign tag \"{{tag_name}}\" to {{file_count}} $t(file, { \"count\": {{file_count}} }): {{error_message}}", "tags_bulk_failed_without_tag": "Could not tag {{file_count}} $t(file, { \"count\": {{file_count}} }): {{error_message}}", - "tags_bulk_instructions": "Select one or more files and press a key to assign the corresponding tag.", + "tags_bulk_instructions": "Select one or more files and press a number key to assign/unassign the corresponding tag.", "tags_bulk_mode_active": "Tag assign mode is enabled.", + "tags_bulk_unassigned": "Unassigned tag \"{{tag_name}}\" to {{file_count}} $t(file, { \"count\": {{file_count}} }).", "tags_description": "Manage your tags.", "tags_notice_message": "No items assigned to this tag.", "task": "task",