Files
gameyfin/app/src/main/frontend/components/general/ScanProgressPopover.tsx
T
Simon 8d8dca32d8 Overhaul startpage (#821)
* chore: bump version to v2.3.0-preview

* Customize start page (#803)

* Update ConfigService to support complex Objects
Implemented tests for ConfigService

* Added DB migration for config table

* Fixed version in banner.txt not being displayed

* Implement Library ordering
Implement "Show recently added games on homepage"

* Fix build.gradle.kts

* FIx bug when creating libraries

* Fix TypeScript errors
Fix library sorting

* Bump actions/checkout from 5 to 6 (#811)

Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Added automatic scanning using file system watchers (#813)

* Implement collections (#814)

* Backend implementation for collections

* Fix database schema and migration script

* Refactor some config values
Fix ArrayInput not being deactivatable

* Remove "AutoRegisterNewUsers" config option

* Fix bug when removing ignored paths

* Add UI for collections (WIP)

* Fix table actions not synced with state
Fix tests

* Finish implementation of collection feature

* Fix tests

* Bump actions/checkout from 5 to 6 (#815)

Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix "allow guests to create game requests" not being enabled when guest access is activated

* Fix: Disable loading of EditGameMetadataModal and MatchGameModal in GameView when user is not admin

* WIP: Update start page layout

* Performance improvements (lazy loading and virtualized grids/lists)
Fix various smaller issues

* Implement use of blurhash for all images in backend and covers in frontend

* Fix bugs and test

* Fix code analysis issues

* Remove "UI settings" since they have been made obsolete

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-10 00:22:58 +01:00

118 lines
6.2 KiB
TypeScript

import {
Button,
Divider,
Link,
Popover,
PopoverContent,
PopoverTrigger,
Progress,
ScrollShadow,
Spinner
} from "@heroui/react";
import {useSnapshot} from "valtio/react";
import {scanState} from "Frontend/state/ScanState";
import {libraryState} from "Frontend/state/LibraryState";
import {TargetIcon, WarningIcon} from "@phosphor-icons/react";
import {timeBetween, timeUntil, toTitleCase} from "Frontend/util/utils";
import LibraryScanStatus from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryScanStatus";
import {useEffect, useState} from "react";
export default function ScanProgressPopover() {
const libraries = useSnapshot(libraryState).state;
const scans = useSnapshot(scanState).sortedByStartTime;
const scanInProgress = useSnapshot(scanState).isScanning;
// Add state to track current time and force re-renders
const [currentTime, setCurrentTime] = useState(Date.now());
// Set up an interval to update the time every second
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(Date.now());
}, 1000);
// Clean up the interval when component unmounts
return () => clearInterval(intervalId);
}, []);
return (
<Popover placement="bottom-end" showArrow={true}>
<PopoverTrigger>
<Button isIconOnly variant="light">
{scanInProgress ?
<Spinner size="sm" color="default" variant="spinner"
classNames={{
spinnerBars: "bg-foreground-500",
}}/> :
<TargetIcon className="fill-foreground-500"/>
}
</Button>
</PopoverTrigger>
<PopoverContent>
<div className="flex flex-col gap-2 m-2 min-w-md">
{scans.length === 0 ?
<p className="flex h-12 items-center justify-center text-sm text-default-500">
No scans in progress or in history.
</p> :
<ScrollShadow hideScrollBar className="max-h-96">
{scans.map((scan, index) =>
<div className="flex flex-col" key={scan.scanId}>
<div
className="flex flex-row gap-4 justify-between items-center text-default-500 mb-1">
<p>{toTitleCase(scan.type)} scan for library&nbsp;
<Link underline="always"
color="foreground"
size="sm"
href={`/administration/games/library/${scan.libraryId}`}>
{libraries[scan.libraryId].name}
</Link>
</p>
{scan.finishedAt ?
<p className="text-default-500">
Finished {timeUntil(scan.finishedAt)}
</p> :
<p className="text-default-500">
Started {timeUntil(scan.startedAt)}
</p>
}
</div>
{scan.status === LibraryScanStatus.IN_PROGRESS &&
(scan.currentStep.current && scan.currentStep.total ?
<div className="flex flex-col gap-1">
<p className="text-default-500">
{`${scan.currentStep.description} (${scan.currentStep.current}/${scan.currentStep.total})`}
</p>
<Progress
value={scan.currentStep.current / scan.currentStep.total * 100}
size="sm"/>
</div> :
<div className="flex flex-col gap-1">
<p className="text-default-500">{scan.currentStep.description}</p>
<Progress isIndeterminate size="sm"/>
</div>
)
}
{scan.status === LibraryScanStatus.COMPLETED &&
<p>
{scan.result?.new} new /&nbsp;
{(scan as any).result?.updated != null && `${(scan as any).result.updated} updated / `}
{scan.result?.removed} removed /&nbsp;
{scan.result?.unmatched} unmatched&nbsp;
(in {timeBetween(scan.startedAt, scan.finishedAt!)})
</p>
}
{scan.status === LibraryScanStatus.FAILED &&
<p className="text-danger flex flex-row gap-1"><WarningIcon weight="fill"/>
Scan failed (check logs for details)
</p>
}
{scans.length > 1 && index < (scans.length - 1) && <Divider className="my-2"/>}
</div>
)}
</ScrollShadow>
}
</div>
</PopoverContent>
</Popover>
);
}