[ENG-1778] Total library size statistic (#2477)

* it

* migration

* fix error msg

* fix error msg actually tho

* fix

* ts errors
This commit is contained in:
Jamie Pine 2024-05-13 15:02:49 -07:00 committed by GitHub
parent 9a43df95b9
commit 8bfcf58a0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 200 additions and 96 deletions

View file

@ -57,11 +57,11 @@ const Devices = ({ node, stats }: Props) => {
const totalSpace =
Platform.OS === 'android'
? sizeInfo.totalSpace.toString()
: stats.data?.statistics?.total_bytes_capacity || '0';
: stats.data?.statistics?.total_local_bytes_capacity || '0';
const freeSpace =
Platform.OS === 'android'
? sizeInfo.freeSpace.toString()
: stats.data?.statistics?.total_bytes_free || '0';
: stats.data?.statistics?.total_local_bytes_free || '0';
useEffect(() => {
if (Platform.OS === 'android') {

View file

@ -11,11 +11,11 @@ import { tw, twStyle } from '~/lib/tailwind';
import Card from '../layout/Card';
const StatItemNames: Partial<Record<keyof Statistics, string>> = {
total_bytes_capacity: 'Total capacity',
preview_media_bytes: 'Preview media',
total_local_bytes_capacity: 'Total capacity',
total_library_preview_media_bytes: 'Preview media',
library_db_size: 'Index size',
total_bytes_free: 'Free space',
total_bytes_used: 'Total used space'
total_local_bytes_free: 'Free space',
total_local_bytes_used: 'Total used space'
};
interface StatItemProps {

View file

@ -0,0 +1,29 @@
/*
Warnings:
- You are about to drop the column `preview_media_bytes` on the `statistics` table. All the data in the column will be lost.
- You are about to drop the column `total_bytes_capacity` on the `statistics` table. All the data in the column will be lost.
- You are about to drop the column `total_bytes_free` on the `statistics` table. All the data in the column will be lost.
- You are about to drop the column `total_bytes_used` on the `statistics` table. All the data in the column will be lost.
- You are about to drop the column `total_unique_bytes` on the `statistics` table. All the data in the column will be lost.
*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_statistics" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"date_captured" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"total_object_count" INTEGER NOT NULL DEFAULT 0,
"library_db_size" TEXT NOT NULL DEFAULT '0',
"total_local_bytes_used" TEXT NOT NULL DEFAULT '0',
"total_local_bytes_capacity" TEXT NOT NULL DEFAULT '0',
"total_local_bytes_free" TEXT NOT NULL DEFAULT '0',
"total_library_bytes" TEXT NOT NULL DEFAULT '0',
"total_library_unique_bytes" TEXT NOT NULL DEFAULT '0',
"total_library_preview_media_bytes" TEXT NOT NULL DEFAULT '0'
);
INSERT INTO "new_statistics" ("date_captured", "id", "library_db_size", "total_object_count") SELECT "date_captured", "id", "library_db_size", "total_object_count" FROM "statistics";
DROP TABLE "statistics";
ALTER TABLE "new_statistics" RENAME TO "statistics";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View file

@ -95,15 +95,18 @@ model Instance {
/// @local
model Statistics {
id Int @id @default(autoincrement())
date_captured DateTime @default(now())
total_object_count Int @default(0)
library_db_size String @default("0")
total_bytes_used String @default("0")
total_bytes_capacity String @default("0")
total_unique_bytes String @default("0")
total_bytes_free String @default("0")
preview_media_bytes String @default("0")
id Int @id @default(autoincrement())
date_captured DateTime @default(now())
total_object_count Int @default(0)
library_db_size String @default("0")
// local calulations
total_local_bytes_used String @default("0")
total_local_bytes_capacity String @default("0")
total_local_bytes_free String @default("0")
// library calculations
total_library_bytes String @default("0")
total_library_unique_bytes String @default("0")
total_library_preview_media_bytes String @default("0")
@@map("statistics")
}

View file

@ -443,6 +443,8 @@ impl Libraries {
id, instance_node_id, node_config.id
);
// ensure
db.instance()
.update(
instance::id::equals(instance.id),

View file

@ -3,9 +3,9 @@ use crate::{api::utils::get_size, library::Library, volume::get_volumes, Node};
use sd_prisma::prisma::statistics;
use chrono::Utc;
use tracing::info;
use super::LibraryManagerError;
use tracing::{error, info};
pub async fn update_library_statistics(
node: &Node,
@ -31,6 +31,25 @@ pub async fn update_library_statistics(
.await
.unwrap_or(0);
let total_library_bytes = library
.db
.location()
.find_many(vec![])
.exec()
.await
.unwrap_or_else(|err| {
error!("Failed to get locations: {:#?}", err);
vec![]
})
.into_iter()
.map(|location| {
location
.size_in_bytes
.map(|bytes| bytes.iter().fold(0, |acc, &x| acc * 256 + x as u64))
.unwrap_or(0)
})
.sum::<u64>();
let thumbnail_folder_size = get_size(node.config.data_directory().join("thumbnails"))
.await
.unwrap_or(0);
@ -41,11 +60,11 @@ pub async fn update_library_statistics(
date_captured::set(Utc::now().into()),
total_object_count::set(0),
library_db_size::set(library_db_size.to_string()),
total_bytes_used::set(total_bytes_used.to_string()),
total_bytes_capacity::set(total_capacity.to_string()),
total_unique_bytes::set(0.to_string()),
total_bytes_free::set(available_capacity.to_string()),
preview_media_bytes::set(thumbnail_folder_size.to_string()),
total_library_bytes::set(total_library_bytes.to_string()),
total_local_bytes_used::set(total_bytes_used.to_string()),
total_local_bytes_capacity::set(total_capacity.to_string()),
total_local_bytes_free::set(available_capacity.to_string()),
total_library_preview_media_bytes::set(thumbnail_folder_size.to_string()),
];
let stats = library

View file

@ -3,6 +3,7 @@ import { LibraryContextProvider, useClientContext } from '@sd/client';
import Debug from './sections/Debug';
// sections
import Devices from './sections/Devices';
import Peers from './sections/Peers';
import Library from './sections/Library';
import Local from './sections/Local';
import Locations from './sections/Locations';
@ -26,6 +27,7 @@ export default function Sidebar() {
<LibraryContextProvider library={library}>
<SavedSearches />
<Devices />
<Peers />
<Locations />
<Tags />
</LibraryContextProvider>

View file

@ -1,7 +1,8 @@
import { useBridgeQuery, usePeers } from '@sd/client';
import { HardwareModel, useBridgeQuery, usePeers } from '@sd/client';
import { Button, toast, Tooltip } from '@sd/ui';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
import { hardwareModelToIcon } from '~/util/hardware';
import SidebarLink from '../../SidebarLayout/Link';
import Section from '../../SidebarLayout/Section';
@ -16,16 +17,20 @@ export default function DevicesSection() {
<Section name={t('devices')}>
{node && (
<SidebarLink className="group relative w-full" to={`node/${node.id}`} key={node.id}>
<Icon name="Laptop" className="mr-1 size-5" />
{node.device_model ? (
<Icon
name={hardwareModelToIcon(node.device_model as HardwareModel)}
size={20}
className="mr-1"
/>
) : (
<Icon name="Laptop" className="mr-1" />
)}
<span className="truncate">{node.name}</span>
</SidebarLink>
)}
{Array.from(peers).map(([id, node]) => (
<SidebarLink className="group relative w-full" to={`todo`} key={id} disabled>
<Icon name="Laptop" className="mr-1 size-5" />
<span className="truncate">{node.metadata.name}</span>
</SidebarLink>
))}
<Tooltip label={t('devices_coming_soon_tooltip')} position="right">
<Button
onClick={() => {

View file

@ -33,7 +33,7 @@ export default function Locations() {
</Link>
}
>
<SeeMore>
<SeeMore limit={10}>
{locations?.map((location) => (
<Location
key={location.id}

View file

@ -0,0 +1,33 @@
import { HardwareModel, useBridgeQuery, usePeers } from '@sd/client';
import { Button, toast, Tooltip } from '@sd/ui';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
import { hardwareModelToIcon } from '~/util/hardware';
import SidebarLink from '../../SidebarLayout/Link';
import Section from '../../SidebarLayout/Section';
export default function PeersSection() {
const { t } = useLocale();
const peers = usePeers();
return (
<Section name={t('peers')}>
{Array.from(peers).map(([id, node]) => (
<SidebarLink className="group relative w-full" to={`todo`} key={id}>
{node.metadata.device_model ? (
<Icon
name={hardwareModelToIcon(node.metadata.device_model as HardwareModel)}
size={20}
className="mr-1"
/>
) : (
<Icon name="Laptop" className="mr-1" />
)}
<span className="truncate">{node.metadata.name}</span>
</SidebarLink>
))}
</Section>
);
}

View file

@ -159,7 +159,7 @@ function Node({
onDrop: (files) => onDropped(id, files)
});
const {t} = useLocale()
const { t } = useLocale();
return (
<div

View file

@ -76,19 +76,21 @@ const LibraryStats = () => {
const { t } = useLocale();
const StatItemNames: Partial<Record<keyof Statistics, string>> = {
total_bytes_capacity: t('total_bytes_capacity'),
preview_media_bytes: t('preview_media_bytes'),
total_library_bytes: t('library_bytes'),
library_db_size: t('library_db_size'),
total_bytes_free: t('total_bytes_free'),
total_bytes_used: t('total_bytes_used')
total_local_bytes_capacity: t('total_bytes_capacity'),
total_library_preview_media_bytes: t('preview_media_bytes'),
total_local_bytes_free: t('total_bytes_free'),
total_local_bytes_used: t('total_bytes_used')
};
const StatDescriptions: Partial<Record<keyof Statistics, string>> = {
total_bytes_capacity: t('total_bytes_capacity_description'),
preview_media_bytes: t('preview_media_bytes_description'),
total_local_bytes_capacity: t('total_bytes_capacity_description'),
total_library_preview_media_bytes: t('preview_media_bytes_description'),
total_library_bytes: t('library_bytes_description'),
library_db_size: t('library_db_size_description'),
total_bytes_free: t('total_bytes_free_description'),
total_bytes_used: t('total_bytes_used_description')
total_local_bytes_free: t('total_bytes_free_description'),
total_local_bytes_used: t('total_bytes_used_description')
};
const displayableStatItems = Object.keys(
@ -97,18 +99,24 @@ const LibraryStats = () => {
return (
<div className="flex w-full">
<div className="flex gap-3 overflow-hidden">
{Object.entries(stats?.data?.statistics || []).map(([key, value]) => {
if (!displayableStatItems.includes(key)) return null;
return (
<StatItem
key={`${library.uuid} ${key}`}
title={StatItemNames[key as keyof Statistics]!}
bytes={BigInt(value)}
isLoading={stats.isLoading}
info={StatDescriptions[key as keyof Statistics]}
/>
);
})}
{Object.entries(stats?.data?.statistics || [])
// sort the stats by the order of the displayableStatItems
.sort(
([a], [b]) =>
displayableStatItems.indexOf(a) - displayableStatItems.indexOf(b)
)
.map(([key, value]) => {
if (!displayableStatItems.includes(key)) return null;
return (
<StatItem
key={`${library.uuid} ${key}`}
title={StatItemNames[key as keyof Statistics]!}
bytes={BigInt(value)}
isLoading={stats.isLoading}
info={StatDescriptions[key as keyof Statistics]}
/>
);
})}
</div>
</div>
);

View file

@ -85,8 +85,10 @@ export const Component = () => {
<StatisticItem
name={node.name}
icon={hardwareModelToIcon(node.device_model as any)}
totalSpace={stats.data?.statistics?.total_bytes_capacity || '0'}
freeSpace={stats.data?.statistics?.total_bytes_free || '0'}
totalSpace={
stats.data?.statistics?.total_local_bytes_capacity || '0'
}
freeSpace={stats.data?.statistics?.total_local_bytes_free || '0'}
color="#0362FF"
connectionType={null}
/>

View file

@ -21,10 +21,10 @@ export const Component = () => {
const info = useMemo(() => {
if (locations.data && discoveredPeers) {
const statistics = stats.data?.statistics;
const tb_capacity = humanizeSize(statistics?.total_bytes_capacity);
const free_space = humanizeSize(statistics?.total_bytes_free);
const tb_capacity = humanizeSize(statistics?.total_local_bytes_capacity);
const free_space = humanizeSize(statistics?.total_local_bytes_free);
const library_db_size = humanizeSize(statistics?.library_db_size);
const preview_media = humanizeSize(statistics?.preview_media_bytes);
const preview_media = humanizeSize(statistics?.total_library_preview_media_bytes);
const data: {
icon: keyof typeof iconNames;
title?: string;

View file

@ -34,8 +34,8 @@
"archive_coming_soon": "Archiving locations is coming soon...",
"archive_info": "Extract data from Library as an archive, useful to preserve Location folder structure.",
"are_you_sure": "Are you sure?",
"ask_spacedrive": "Ask Spacedrive",
"ascending": "Ascending",
"ask_spacedrive": "Ask Spacedrive",
"assign_tag": "Assign tag",
"audio": "Audio",
"audio_preview_not_supported": "Audio preview is not supported.",
@ -56,6 +56,8 @@
"chapters": "Chapters",
"checksum": "Checksum",
"clear_finished_jobs": "Clear out finished jobs",
"click_to_hide": "Click to hide",
"click_to_lock": "Click to lock",
"client": "Client",
"close": "Close",
"close_command_palette": "Close command palette",
@ -67,7 +69,6 @@
"collection": "Collection",
"color": "Color",
"coming_soon": "Coming soon",
"contains": "contains",
"compress": "Compress",
"config": "Config",
"configure_location": "Configure Location",
@ -79,6 +80,7 @@
"connected": "Connected",
"contacts": "Contacts",
"contacts_description": "Manage your contacts in Spacedrive.",
"contains": "contains",
"content_id": "Content ID",
"continue": "Continue",
"convert_to": "Convert to",
@ -113,8 +115,8 @@
"cut_object": "Cut object",
"cut_success": "Items cut",
"dark": "Dark",
"database": "Database",
"data_folder": "Data Folder",
"database": "Database",
"date_accessed": "Date Accessed",
"date_created": "Date Created",
"date_indexed": "Date Indexed",
@ -125,16 +127,6 @@
"debug_mode": "Debug mode",
"debug_mode_description": "Enable extra debugging features within the app.",
"default": "Default",
"descending": "Descending",
"duration": "Duration",
"random": "Random",
"ipv4_listeners_error": "Error creating the IPv4 listeners. Please check your firewall settings!",
"ipv4_ipv6_listeners_error": "Error creating the IPv4 and IPv6 listeners. Please check your firewall settings!",
"ipv6": "IPv6 networking",
"ipv6_description": "Allow peer-to-peer communication using IPv6 networking",
"ipv6_listeners_error": "Error creating the IPv6 listeners. Please check your firewall settings!",
"is": "is",
"is_not": "is not",
"default_settings": "Default Settings",
"delete": "Delete",
"delete_dialog_title": "Delete {{prefix}} {{type}}",
@ -150,6 +142,7 @@
"delete_tag": "Delete Tag",
"delete_tag_description": "Are you sure you want to delete this tag? This cannot be undone and tagged files will be unlinked.",
"delete_warning": "This will delete your {{type}}. This action cannot be undone as of right now. If you Move to Trash, you can restore it later. If you Delete Forever, it will be gone forever.",
"descending": "Descending",
"description": "Description",
"deselect": "Deselect",
"details": "Details",
@ -158,8 +151,8 @@
"dialog": "Dialog",
"dialog_shortcut_description": "To perform actions and operations",
"direction": "Direction",
"directory": "directory",
"directories": "directories",
"directory": "directory",
"disabled": "Disabled",
"disconnected": "Disconnected",
"display_formats": "Display Formats",
@ -168,15 +161,17 @@
"do_the_thing": "Do the thing",
"document": "Document",
"done": "Done",
"dont_show_again": "Don't show again",
"dont_have_any": "Looks like you don't have any!",
"dont_show_again": "Don't show again",
"dotfile": "Dotfile",
"double_click_action": "Double click action",
"download": "Download",
"downloading_update": "Downloading Update",
"drag_to_resize": "Drag to resize",
"duplicate": "Duplicate",
"duplicate_object": "Duplicate object",
"duplicate_success": "Items duplicated",
"duration": "Duration",
"edit": "Edit",
"edit_library": "Edit Library",
"edit_location": "Edit Location",
@ -184,12 +179,6 @@
"enable_networking": "Enable Networking",
"enable_networking_description": "Allow your node to communicate with other Spacedrive nodes around you.",
"enable_networking_description_required": "Required for library sync or Spacedrop!",
"spacedrop": "Spacedrop visibility",
"spacedrop_everyone": "Everyone",
"spacedrop_contacts_only": "Contacts Only",
"spacedrop_disabled": "Disabled",
"remote_access": "Enable remote access",
"remote_access_description": "Enable other nodes to directly connect to this node.",
"encrypt": "Encrypt",
"encrypt_library": "Encrypt Library",
"encrypt_library_coming_soon": "Library encryption coming soon",
@ -205,6 +194,7 @@
"error": "Error",
"error_loading_original_file": "Error loading original file",
"error_message": "Error: {{error}}.",
"executable": "Executable",
"expand": "Expand",
"explorer": "Explorer",
"explorer_settings": "Explorer settings",
@ -214,7 +204,6 @@
"export_library": "Export Library",
"export_library_coming_soon": "Export Library coming soon",
"export_library_description": "Export this library to a file.",
"executable": "Executable",
"extension": "Extension",
"extensions": "Extensions",
"extensions_description": "Install extensions to extend the functionality of this client.",
@ -232,8 +221,8 @@
"failed_to_generate_labels": "Failed to generate labels",
"failed_to_generate_thumbnails": "Failed to generate thumbnails",
"failed_to_load_tags": "Failed to load tags",
"failed_to_open_file_title": "Failed to open file",
"failed_to_open_file_body": "Couldn't open file, due to an error: {{error}}",
"failed_to_open_file_title": "Failed to open file",
"failed_to_open_file_with": "Failed to open file, with: {{data}}",
"failed_to_pause_job": "Failed to pause job.",
"failed_to_reindex_location": "Failed to re-index location",
@ -292,6 +281,7 @@
"hide_in_sidebar": "Hide in sidebar",
"hide_in_sidebar_description": "Prevent this tag from showing in the sidebar of the app.",
"hide_location_from_view": "Hide location and contents from view",
"hide_sidebar": "Hide sidebar",
"home": "Home",
"hosted_locations": "Hosted Locations",
"hosted_locations_description": "Augment your local storage with our cloud!",
@ -306,11 +296,18 @@
"indexer_rule_reject_allow_label": "By default, an indexer rule functions as a Reject list, resulting in the exclusion of any files that match its criteria. Enabling this option will transform it into a Allow list, allowing the location to solely index files that meet its specified rules.",
"indexer_rules": "Indexer rules",
"indexer_rules_error": "Error while retrieving indexer rules",
"indexer_rules_not_available": "No indexer rules available",
"indexer_rules_info": "Indexer rules allow you to specify paths to ignore using globs.",
"indexer_rules_not_available": "No indexer rules available",
"install": "Install",
"install_update": "Install Update",
"installed": "Installed",
"ipv4_ipv6_listeners_error": "Error creating the IPv4 and IPv6 listeners. Please check your firewall settings!",
"ipv4_listeners_error": "Error creating the IPv4 listeners. Please check your firewall settings!",
"ipv6": "IPv6 networking",
"ipv6_description": "Allow peer-to-peer communication using IPv6 networking",
"ipv6_listeners_error": "Error creating the IPv6 listeners. Please check your firewall settings!",
"is": "is",
"is_not": "is not",
"item": "item",
"item_size": "Item size",
"item_with_count_one": "{{count}} item",
@ -344,6 +341,8 @@
"libraries": "Libraries",
"libraries_description": "The database contains all library data and file metadata.",
"library": "Library",
"library_bytes": "Library size",
"library_bytes_description": "The total size of all locations in your library.",
"library_db_size": "Index size",
"library_db_size_description": "The size of the library database.",
"library_name": "Library name",
@ -359,13 +358,13 @@
"local_locations": "Local Locations",
"local_node": "Local Node",
"location": "Location",
"location_one": "Location",
"location_other": "Locations",
"location_connected_tooltip": "Location is being watched for changes",
"location_disconnected_tooltip": "Location is not being watched for changes",
"location_display_name_info": "The name of this Location, this is what will be displayed in the sidebar. Will not rename the actual folder on disk.",
"location_empty_notice_message": "No files found here",
"location_is_already_linked": "Location is already linked",
"location_one": "Location",
"location_other": "Locations",
"location_path_info": "The path to this Location, this is where the files will be stored on disk.",
"location_type": "Location Type",
"location_type_managed": "Spacedrive will sort files for you. If Location isn't empty a \"spacedrive\" folder will be created.",
@ -374,6 +373,7 @@
"locations": "Locations",
"locations_description": "Manage your storage locations.",
"lock": "Lock",
"lock_sidebar": "Lock sidebar",
"log_in": "Log in",
"log_in_with_browser": "Log in with browser",
"log_out": "Log out",
@ -430,17 +430,17 @@
"no_jobs": "No jobs.",
"no_labels": "No labels",
"no_nodes_found": "No Spacedrive nodes were found.",
"no_search_selected": "No Search Selected",
"no_tag_selected": "No Tag Selected",
"no_tags": "No tags",
"no_tags_description": "You have not created any tags",
"no_search_selected": "No Search Selected",
"node_name": "Node Name",
"nodes": "Nodes",
"nodes_description": "Manage the nodes connected to this library. A node is an instance of Spacedrive's backend, running on a device or server. Each node carries a copy of the database and synchronizes via peer-to-peer connections in realtime.",
"none": "None",
"normal": "Normal",
"note": "Note",
"not_you": "Not you?",
"note": "Note",
"nothing_selected": "Nothing selected",
"number_of_passes": "# of passes",
"object": "Object",
@ -486,6 +486,7 @@
"quick_preview": "Quick Preview",
"quick_rescan_started": "Quick rescan started",
"quick_view": "Quick view",
"random": "Random",
"recent_jobs": "Recent Jobs",
"recents": "Recents",
"recents_notice_message": "Recents are created when you open a file.",
@ -496,6 +497,8 @@
"reject": "Reject",
"reject_files": "Reject files",
"reload": "Reload",
"remote_access": "Enable remote access",
"remote_access_description": "Enable other nodes to directly connect to this node.",
"remove": "Remove",
"remove_from_recents": "Remove From Recents",
"rename": "Rename",
@ -530,8 +533,8 @@
"secure_delete": "Secure delete",
"security": "Security",
"security_description": "Keep your client safe.",
"see_more": "See more",
"see_less": "See less",
"see_more": "See more",
"send": "Send",
"send_report": "Send Report",
"settings": "Settings",
@ -560,14 +563,18 @@
"spacedrive_account": "Spacedrive Account",
"spacedrive_cloud": "Spacedrive Cloud",
"spacedrive_cloud_description": "Spacedrive is always local first, but we will offer our own optional cloud services in the future. For now, authentication is only used for the Feedback feature, otherwise it is not required.",
"spacedrop": "Spacedrop visibility",
"spacedrop_a_file": "Spacedrop a File",
"spacedrop_already_progress": "Spacedrop already in progress",
"spacedrop_contacts_only": "Contacts Only",
"spacedrop_description": "Share instantly with devices running Spacedrive on your network.",
"spacedrop_disabled": "Disabled",
"spacedrop_everyone": "Everyone",
"spacedrop_rejected": "Spacedrop rejected",
"square_thumbnails": "Square Thumbnails",
"star_on_github": "Star on GitHub",
"starts_with": "starts with",
"start_time": "Start Time",
"starts_with": "starts with",
"stop": "Stop",
"success": "Success",
"support": "Support",
@ -608,11 +615,6 @@
"toggle_path_bar": "Toggle path bar",
"toggle_quick_preview": "Toggle quick preview",
"toggle_sidebar": "Toggle sidebar",
"lock_sidebar": "Lock sidebar",
"hide_sidebar": "Hide sidebar",
"drag_to_resize": "Drag to resize",
"click_to_hide": "Click to hide",
"click_to_lock": "Click to lock",
"tools": "Tools",
"total_bytes_capacity": "Total capacity",
"total_bytes_capacity_description": "The total capacity of all nodes connected to the library. May show incorrect values during alpha.",
@ -624,8 +626,8 @@
"type": "Type",
"ui_animations": "UI Animations",
"ui_animations_description": "Dialogs and other UI elements will animate when opening and closing.",
"unnamed_location": "Unnamed Location",
"unknown": "Unknown",
"unnamed_location": "Unnamed Location",
"update": "Update",
"update_downloaded": "Update Downloaded. Restart Spacedrive to install",
"updated_successfully": "Updated successfully, you're on version {{version}}",

View file

@ -595,7 +595,7 @@ export type SortOrder = "Asc" | "Desc"
export type SpacedropArgs = { identity: RemoteIdentity; file_path: string[] }
export type Statistics = { id: number; date_captured: string; total_object_count: number; library_db_size: string; total_bytes_used: string; total_bytes_capacity: string; total_unique_bytes: string; total_bytes_free: string; preview_media_bytes: string }
export type Statistics = { id: number; date_captured: string; total_object_count: number; library_db_size: string; total_local_bytes_used: string; total_local_bytes_capacity: string; total_local_bytes_free: string; total_library_bytes: string; total_library_unique_bytes: string; total_library_preview_media_bytes: string }
export type StatisticsResponse = { statistics: Statistics | null }

View file

@ -126,11 +126,10 @@ export const humanizeSize = (
unit.from === 0n
? Number(bytes)
: Number((bytes * BigInt(precisionFactor)) / unit.from) / precisionFactor;
const plural = use_plural && value !== 1 ? 's' : '';
return {
unit: (is_bit ? BYTE_TO_BIT[unit.short as keyof typeof BYTE_TO_BIT] : unit.short) + plural,
long: (is_bit ? BYTE_TO_BIT[unit.long as keyof typeof BYTE_TO_BIT] : unit.long) + plural,
unit: is_bit ? BYTE_TO_BIT[unit.short as keyof typeof BYTE_TO_BIT] : unit.short,
long: is_bit ? BYTE_TO_BIT[unit.long as keyof typeof BYTE_TO_BIT] : unit.long,
value: (isNegative ? -1 : 1) * value,
original: value,
toString() {