forked from OctoWoW/OctoLauncher
Initial commit
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { useState } from 'react';
|
||||
import { Plus, RefreshCw, Search } from 'lucide-react';
|
||||
|
||||
import { type AddonData, type AddonsStatus } from '~main/types';
|
||||
import { api } from '~renderer/utils/api';
|
||||
import TextButton from '~renderer/components/styled/TextButton';
|
||||
import useScrollHint from '~renderer/utils/useScrollHint';
|
||||
|
||||
import DialogButton from '../styled/DialogButton';
|
||||
import IconSpinner from '../styled/IconSpinner';
|
||||
|
||||
import AddonList from './addons/AddonList';
|
||||
import { type Dependencies } from './addons/AddonListItem';
|
||||
import CustomAddonDialog from './addons/CustomAddonDialog';
|
||||
|
||||
const localeFilter = (l: AddonData[], filter: string) => {
|
||||
const seen = new Set<string>();
|
||||
const deduped = l.filter(a => {
|
||||
if (seen.has(a.folder)) return false;
|
||||
seen.add(a.folder);
|
||||
return true;
|
||||
});
|
||||
return deduped
|
||||
.filter(
|
||||
a =>
|
||||
a.folder.toLocaleLowerCase().indexOf(filter.toLocaleLowerCase()) !== -1
|
||||
)
|
||||
.sort((a, b) => a.folder.localeCompare(b.folder));
|
||||
};
|
||||
|
||||
const AddonsTab = () => {
|
||||
const [data, setData] = useState<AddonsStatus>({
|
||||
state: 'verifying',
|
||||
addons: {},
|
||||
available: []
|
||||
});
|
||||
api.addons.observe.useSubscription(undefined, { onData: setData });
|
||||
|
||||
const isUpdateAvailable = Object.values(data.addons).some(
|
||||
a => a.status === 'outOfDate' || a.status === 'downloading'
|
||||
);
|
||||
const dependencies: Dependencies = Object.fromEntries([
|
||||
...data.available.map(a => [a.folder, 'available']),
|
||||
...Object.values(data.addons).map(a => [
|
||||
a.folder,
|
||||
a.progress ?? (a.status === 'upToDate' ? 'installed' : 'available')
|
||||
])
|
||||
]);
|
||||
|
||||
const [filter, setFilter] = useState('');
|
||||
|
||||
const verify = api.addons.verify.useMutation();
|
||||
const update = api.addons.update.useMutation();
|
||||
|
||||
const scrollRef = useScrollHint<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<div className="tw-surface relative flex min-h-0 flex-grow flex-col gap-3">
|
||||
<div
|
||||
ref={scrollRef}
|
||||
className="relative -m-4 -mb-3 flex flex-grow flex-col gap-3 overflow-y-auto overflow-x-hidden p-4 pb-3"
|
||||
>
|
||||
<AddonList
|
||||
title="Installed"
|
||||
addons={localeFilter(Object.values(data.addons), filter)}
|
||||
dependencies={dependencies}
|
||||
/>
|
||||
<AddonList
|
||||
title="Available"
|
||||
addons={localeFilter(
|
||||
data.available.filter(a => !(a.folder in data.addons)),
|
||||
filter
|
||||
)}
|
||||
dependencies={dependencies}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="-mb-4 -mt-3 grid grid-cols-[1fr_1fr_1fr] items-center justify-between gap-2 py-2">
|
||||
<TextButton
|
||||
onClick={() => verify.mutateAsync()}
|
||||
className="-ml-2 text-blueGray"
|
||||
icon={RefreshCw}
|
||||
size={18}
|
||||
loading={data.state !== 'done'}
|
||||
>
|
||||
Check for updates
|
||||
</TextButton>
|
||||
<DialogButton
|
||||
clickAway
|
||||
dialog={close => <CustomAddonDialog close={close} />}
|
||||
>
|
||||
{open => (
|
||||
<TextButton
|
||||
icon={Plus}
|
||||
size={18}
|
||||
onClick={open}
|
||||
className="s1 text-pink"
|
||||
>
|
||||
Add custom git addon
|
||||
</TextButton>
|
||||
)}
|
||||
</DialogButton>
|
||||
{data.state === 'verifying' ? (
|
||||
<IconSpinner size={18} className="justify-self-end" />
|
||||
) : isUpdateAvailable ? (
|
||||
<TextButton
|
||||
onClick={() => update.mutateAsync({})}
|
||||
className="justify-self-end text-warmGreen"
|
||||
>
|
||||
Update all
|
||||
</TextButton>
|
||||
) : (
|
||||
<p className="s1 justify-self-end text-blueGray">
|
||||
Everything is up to date.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute right-3 top-3">
|
||||
<div className="flex items-center gap-1 border-b border-blueGray bg-darkGray/70 p-1 hocus:border-orange">
|
||||
<input
|
||||
className="cursor-text bg-inherit"
|
||||
value={filter}
|
||||
onChange={e => setFilter(e.target.value)}
|
||||
/>
|
||||
<Search size={18} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default AddonsTab;
|
||||
Reference in New Issue
Block a user