mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 16:29:12 +00:00

* refactor(web): persist incognito state * feat: merge incognito state into SettingsContext * feat: merge incognito state into SettingsContext
189 lines
5.9 KiB
TypeScript
189 lines
5.9 KiB
TypeScript
/*
|
|
* Copyright (c) 2021 - 2025, Ludvig Lundgren and the autobrr contributors.
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
import {
|
|
useReactTable,
|
|
getCoreRowModel,
|
|
flexRender,
|
|
ColumnDef
|
|
} from "@tanstack/react-table";
|
|
import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid";
|
|
|
|
import { EmptyListState } from "@components/emptystates";
|
|
import * as DataTable from "@components/data-table";
|
|
import { RandomLinuxIsos, RandomIsoTracker } from "@utils";
|
|
import { ReleasesLatestQueryOptions } from "@api/queries";
|
|
import { IndexerCell } from "@components/data-table";
|
|
import { SettingsContext } from "@utils/Context";
|
|
|
|
interface TableProps {
|
|
columns: ColumnDef<Release>[];
|
|
data: Release[];
|
|
}
|
|
|
|
function Table({ columns, data }: TableProps) {
|
|
const tableInstance = useReactTable({
|
|
columns,
|
|
data,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
})
|
|
|
|
if (data.length === 0) {
|
|
return (
|
|
<div
|
|
className="mt-4 mb-2 bg-white dark:bg-gray-800 border border-gray-250 dark:border-gray-775 shadow-table rounded-md overflow-auto">
|
|
<div className="flex items-center justify-center py-16">
|
|
<EmptyListState text="No recent activity"/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="inline-block min-w-full mt-4 mb-2 align-middle">
|
|
<div className="bg-white dark:bg-gray-800 border border-gray-250 dark:border-gray-775 shadow-table rounded-md overflow-auto">
|
|
<table className="min-w-full rounded-md divide-y divide-gray-200 dark:divide-gray-750">
|
|
<thead className="bg-gray-100 dark:bg-gray-850">
|
|
{tableInstance.getHeaderGroups().map((headerGroup) => (
|
|
<tr key={headerGroup.id}>
|
|
{headerGroup.headers.map((header) => (
|
|
<th
|
|
key={header.id}
|
|
scope="col"
|
|
className="first:pl-5 first:rounded-tl-md last:rounded-tr-md pl-3 pr-3 py-3 text-xs font-medium tracking-wider text-left uppercase group text-gray-600 dark:text-gray-400"
|
|
colSpan={header.colSpan}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
{header.isPlaceholder
|
|
? null
|
|
: flexRender(
|
|
header.column.columnDef.header,
|
|
header.getContext()
|
|
)}
|
|
</div>
|
|
</th>
|
|
)
|
|
)}
|
|
</tr>
|
|
))}
|
|
</thead>
|
|
|
|
<tbody className="divide-y divide-gray-150 dark:divide-gray-750">
|
|
{tableInstance.getRowModel().rows.map((row) => (
|
|
<tr key={row.id}>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<td
|
|
key={cell.id}
|
|
className="first:pl-5 pl-3 pr-3 whitespace-nowrap"
|
|
role="cell"
|
|
>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const ActivityTable = () => {
|
|
const columns = React.useMemo<ColumnDef<Release>[]>(() => [
|
|
{
|
|
header: "Age",
|
|
accessorKey: "timestamp",
|
|
cell: DataTable.AgeCell
|
|
},
|
|
{
|
|
header: "Release",
|
|
accessorKey: "name",
|
|
cell: DataTable.TitleCell
|
|
},
|
|
{
|
|
header: "Actions",
|
|
accessorKey: "action_status",
|
|
cell: DataTable.ReleaseStatusCell
|
|
},
|
|
{
|
|
header: "Indexer",
|
|
accessorKey: "indexer.identifier",
|
|
cell: IndexerCell,
|
|
}
|
|
], []);
|
|
|
|
const { isLoading, data } = useSuspenseQuery(ReleasesLatestQueryOptions());
|
|
|
|
const [modifiedData, setModifiedData] = useState<Release[]>([]);
|
|
const [settings, setSettings] = SettingsContext.use();
|
|
|
|
useEffect(() => {
|
|
if (settings.incognitoMode && data?.data) {
|
|
const randomIsoNames = RandomLinuxIsos(data.data.length);
|
|
const randomTorrentSiteNames = RandomIsoTracker(data.data.length);
|
|
const newData: Release[] = data.data.map((item, index) => {
|
|
const siteName = randomTorrentSiteNames[index % randomTorrentSiteNames.length];
|
|
return {
|
|
...item,
|
|
name: randomIsoNames[index],
|
|
indexer: {
|
|
id: 0,
|
|
name: siteName,
|
|
identifier: siteName,
|
|
identifier_external: siteName,
|
|
},
|
|
};
|
|
});
|
|
setModifiedData(newData);
|
|
} else {
|
|
setModifiedData([]);
|
|
}
|
|
}, [settings.incognitoMode, data?.data]);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex flex-col mt-12">
|
|
<h3 className="text-2xl font-medium leading-6 text-gray-900 dark:text-gray-200">
|
|
Recent activity
|
|
</h3>
|
|
<div className="animate-pulse text-black dark:text-white">
|
|
<EmptyListState text="Loading..."/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const toggleReleaseNames = () => {
|
|
setSettings(prev => ({ ...prev, incognitoMode: !prev.incognitoMode }));
|
|
};
|
|
|
|
const displayData = settings.incognitoMode ? modifiedData : (data?.data ?? []);
|
|
|
|
return (
|
|
<div className="flex flex-col mt-12 relative">
|
|
<h3 className="text-2xl font-medium leading-6 text-black dark:text-white">
|
|
Recent activity
|
|
</h3>
|
|
|
|
<Table columns={columns} data={displayData}/>
|
|
|
|
<button
|
|
onClick={toggleReleaseNames}
|
|
className="p-2 absolute -bottom-8 right-0 bg-gray-750 text-white rounded-full opacity-10 hover:opacity-100 transition-opacity duration-300"
|
|
aria-label="Toggle view"
|
|
title="Go incognito"
|
|
>
|
|
{settings.incognitoMode ? (
|
|
<EyeIcon className="h-4 w-4"/>
|
|
) : (
|
|
<EyeSlashIcon className="h-4 w-4"/>
|
|
)}
|
|
</button>
|
|
</div>
|
|
);
|
|
};
|