@futo-org/backups-orchestrator-ui 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,10 @@
5
5
  LocalRepositoryDto,
6
6
  RepositoryBackendDto,
7
7
  } from "../../fetch-client";
8
- import { getBackendActions } from "../../services/backend.service";
8
+ import {
9
+ getBackendActions,
10
+ useYuccaLogin,
11
+ } from "../../services/backend.service";
9
12
  import { Badge, Icon, Text } from "@immich/ui";
10
13
  import { mdiCloudOutline, mdiHarddisk, mdiShieldCheckOutline } from "@mdi/js";
11
14
  import StackListItem from "../ui/StackListItem.svelte";
@@ -30,8 +33,12 @@
30
33
  s3: "S3 Server",
31
34
  };
32
35
 
36
+ const yuccaLogin = useYuccaLogin();
37
+
33
38
  const { LoginAgain, Reconfigure } = $derived(
34
- getBackendActions(repository, backend, repositoryBackend),
39
+ getBackendActions(repository, backend, repositoryBackend, () =>
40
+ yuccaLogin.mutate(undefined),
41
+ ),
35
42
  );
36
43
  </script>
37
44
 
@@ -3,9 +3,9 @@
3
3
  import { options } from "../../options";
4
4
  import {
5
5
  handleSetupLocalStorage,
6
- handleYuccaLogin,
7
6
  useBackendEventHandler,
8
7
  useBackends,
8
+ useYuccaLogin,
9
9
  } from "../../services/backend.service";
10
10
  import { Button, HStack } from "@immich/ui";
11
11
  import StackList from "../ui/StackList.svelte";
@@ -21,6 +21,7 @@
21
21
 
22
22
  const { advanced } = options;
23
23
  const query = useBackends();
24
+ const yuccaLogin = useYuccaLogin();
24
25
  const { onBackendCreate } = useBackendEventHandler();
25
26
 
26
27
  const repositoryBackends = $derived(
@@ -61,8 +62,11 @@
61
62
 
62
63
  {#if $advanced}
63
64
  <HStack>
64
- <Button size="small" variant="outline" onclick={() => handleYuccaLogin()}
65
- >Login with FUTO Backups</Button
65
+ <Button
66
+ size="small"
67
+ variant="outline"
68
+ loading={yuccaLogin.isPending}
69
+ onclick={() => yuccaLogin.mutate(undefined)}>Login with FUTO Backups</Button
66
70
  >
67
71
  <Button
68
72
  size="small"
@@ -13,6 +13,7 @@
13
13
  ModalFooter,
14
14
  Stack,
15
15
  Text,
16
+ toastManager,
16
17
  VStack,
17
18
  } from "@immich/ui";
18
19
  import { mdiContentCopy } from "@mdi/js";
@@ -35,6 +36,11 @@
35
36
  onClose();
36
37
  }
37
38
 
39
+ function onDeviceFlowFailure() {
40
+ toastManager.danger("Failed to log into FUTO Backups");
41
+ onClose();
42
+ }
43
+
38
44
  function onOpen() {
39
45
  window.open(verificationUri, "_blank");
40
46
  }
@@ -44,7 +50,7 @@
44
50
  }
45
51
  </script>
46
52
 
47
- <OnEvents {onBackendCreate} />
53
+ <OnEvents {onBackendCreate} {onDeviceFlowFailure} />
48
54
 
49
55
  <Modal title="Logging into FUTO Backups" icon={false} {onClose}>
50
56
  <ModalBody>
@@ -4,13 +4,14 @@
4
4
  import Suspense from "../../util/Suspense.svelte";
5
5
  import {
6
6
  handleSetupLocalStorage,
7
- handleYuccaLogin,
8
7
  useBackends,
8
+ useYuccaLogin,
9
9
  } from "../../../services/backend.service";
10
10
  import {
11
11
  Button,
12
12
  HStack,
13
13
  Icon,
14
+ LoadingSpinner,
14
15
  Modal,
15
16
  ModalBody,
16
17
  ModalFooter,
@@ -37,6 +38,7 @@
37
38
  }: Props = $props();
38
39
 
39
40
  const backends = useBackends();
41
+ const yuccaLogin = useYuccaLogin();
40
42
 
41
43
  const onFutoBackups = () => {
42
44
  const futoBackend = backends.data!.find(
@@ -46,7 +48,7 @@
46
48
  if (futoBackend) {
47
49
  onSelect(futoBackend.id);
48
50
  } else {
49
- handleYuccaLogin(onSelect);
51
+ yuccaLogin.mutate(onSelect);
50
52
  }
51
53
  };
52
54
 
@@ -63,7 +65,7 @@
63
65
 
64
66
  <Suspense query={backends}>
65
67
  <StackList>
66
- <StackListItem {disabled} onclick={onFutoBackups}>
68
+ <StackListItem disabled={disabled || yuccaLogin.isPending} onclick={onFutoBackups}>
67
69
  {#snippet icon()}
68
70
  <Icon icon={mdiShieldCheck} size="36px" />
69
71
  {/snippet}
@@ -74,10 +76,14 @@
74
76
  </Stack>
75
77
 
76
78
  {#snippet trailing()}
77
- <Icon icon={mdiChevronRight} />
79
+ {#if yuccaLogin.isPending}
80
+ <LoadingSpinner />
81
+ {:else}
82
+ <Icon icon={mdiChevronRight} />
83
+ {/if}
78
84
  {/snippet}
79
85
  </StackListItem>
80
- <StackListItem {disabled} onclick={onLocalBackups}>
86
+ <StackListItem disabled={disabled || yuccaLogin.isPending} onclick={onLocalBackups}>
81
87
  {#snippet icon()}
82
88
  <Icon icon={mdiHarddisk} size="36px" />
83
89
  {/snippet}
@@ -95,7 +101,7 @@
95
101
  {#each backends.data as backend}
96
102
  {#if backend.type === "local"}
97
103
  <StackListItem
98
- {disabled}
104
+ disabled={disabled || yuccaLogin.isPending}
99
105
  onclick={() => {
100
106
  onSelect(backend.id);
101
107
  }}
@@ -53,9 +53,9 @@
53
53
  <Heading size="tiny">Your library</Heading>
54
54
  <Text>
55
55
  {#if repository.meter}
56
- Estimated <FormatBytes bytes={repository.meter.sizeBytes} />
56
+ <FormatBytes bytes={repository.meter.sizeBytes} />
57
57
  {:else}
58
- <FormatBytes bytes={repository.metrics.sizeBytes} />
58
+ Estimated <FormatBytes bytes={repository.metrics.sizeBytes} />
59
59
  {/if} &middot;
60
60
  <span class="lowercase"
61
61
  >{cronstrue.toString(schedule.cron, {
@@ -3,12 +3,13 @@
3
3
  import StackListItem from "../../ui/StackListItem.svelte";
4
4
  import {
5
5
  handleSetupLocalStorage,
6
- handleYuccaLogin,
6
+ useYuccaLogin,
7
7
  } from "../../../services/backend.service";
8
8
  import {
9
9
  Button,
10
10
  HStack,
11
11
  Icon,
12
+ LoadingSpinner,
12
13
  Modal,
13
14
  ModalBody,
14
15
  ModalFooter,
@@ -25,8 +26,10 @@
25
26
 
26
27
  const { restore = false, onNext, onCancel }: Props = $props();
27
28
 
29
+ const yuccaLogin = useYuccaLogin();
30
+
28
31
  function onFutoBackups() {
29
- handleYuccaLogin(onNext);
32
+ yuccaLogin.mutate(onNext);
30
33
  }
31
34
 
32
35
  function onLocalBackups() {
@@ -44,7 +47,7 @@
44
47
  >
45
48
  <ModalBody>
46
49
  <StackList>
47
- <StackListItem onclick={onFutoBackups}>
50
+ <StackListItem disabled={yuccaLogin.isPending} onclick={onFutoBackups}>
48
51
  {#snippet icon()}
49
52
  <Icon icon={mdiShieldCheck} size="36px" />
50
53
  {/snippet}
@@ -55,10 +58,14 @@
55
58
  </Stack>
56
59
 
57
60
  {#snippet trailing()}
58
- <Icon icon={mdiChevronRight} />
61
+ {#if yuccaLogin.isPending}
62
+ <LoadingSpinner />
63
+ {:else}
64
+ <Icon icon={mdiChevronRight} />
65
+ {/if}
59
66
  {/snippet}
60
67
  </StackListItem>
61
- <StackListItem onclick={onLocalBackups}>
68
+ <StackListItem disabled={yuccaLogin.isPending} onclick={onLocalBackups}>
62
69
  {#snippet icon()}
63
70
  <Icon icon={mdiHarddisk} size="36px" />
64
71
  {/snippet}
@@ -10,14 +10,14 @@ export declare const useBackendEventHandler: () => {
10
10
  backend: BackendDto;
11
11
  }>): void;
12
12
  };
13
- export declare const handleYuccaLogin: (onCreate?: (backendId: string) => void) => Promise<void>;
13
+ export declare const useYuccaLogin: () => import("@tanstack/svelte-query").CreateMutationResult<void, Error, ((backendId: string) => void) | undefined, unknown>;
14
14
  export declare const handleSetupLocalStorage: (onCreate?: (backendId: string) => void) => void;
15
15
  export declare const useCreateLocalBackend: () => import("@tanstack/svelte-query").CreateMutationResult<import("../fetch-client").BackendResponseDto, Error, CreateLocalBackendRequestDto, unknown>;
16
16
  export declare const handleReconfigureRepositoryBackend: (repository: LocalRepositoryDto) => Promise<void>;
17
17
  export declare const handleRemoveRepositoryBackend: (_backend: BackendDto, _repositoryBackend: RepositoryBackendDto) => void;
18
18
  export declare const getBackendActions: (repository: LocalRepositoryDto | undefined, backend: BackendDto, repositoryBackend?: RepositoryBackendDto & {
19
19
  primary?: boolean;
20
- }) => {
20
+ }, onLogin?: () => void) => {
21
21
  LoginAgain: ActionItem;
22
22
  Reconfigure: ActionItem;
23
23
  Remove: ActionItem;
@@ -29,20 +29,18 @@ export const useBackendEventHandler = () => {
29
29
  },
30
30
  };
31
31
  };
32
- export const handleYuccaLogin = async (onCreate) => {
33
- try {
34
- const response = await oidcDeviceFlow();
35
- void modalManager.show(OAuthDeviceFlow, {
36
- ...response,
37
- onCreate,
38
- });
39
- window.open(response.verificationUri, '_blank');
40
- }
41
- catch (error) {
42
- handleError(error, 'Failed to start login');
43
- throw error;
44
- }
32
+ const startYuccaLogin = async (onCreate) => {
33
+ const response = await oidcDeviceFlow();
34
+ void modalManager.show(OAuthDeviceFlow, {
35
+ ...response,
36
+ onCreate,
37
+ });
38
+ window.open(response.verificationUri, '_blank');
45
39
  };
40
+ export const useYuccaLogin = () => createMutation(() => ({
41
+ mutationFn: (onCreate) => startYuccaLogin(onCreate),
42
+ onError: (error) => handleError(error, 'Failed to start login'),
43
+ }), () => queryClient);
46
44
  export const handleSetupLocalStorage = (onCreate) => {
47
45
  void modalManager.show(CreateLocalBackend, { onCreate });
48
46
  };
@@ -58,11 +56,11 @@ export const handleReconfigureRepositoryBackend = async (repository) => {
58
56
  export const handleRemoveRepositoryBackend = (_backend, _repositoryBackend) => {
59
57
  alert('TODO: implement me when multi-repository-backend support is added');
60
58
  };
61
- export const getBackendActions = (repository, backend, repositoryBackend) => {
59
+ export const getBackendActions = (repository, backend, repositoryBackend, onLogin) => {
62
60
  const LoginAgain = {
63
61
  icon: mdiLogin,
64
62
  title: 'Login again',
65
- onAction: () => void handleYuccaLogin(),
63
+ onAction: () => onLogin?.(),
66
64
  $if: () => backend.type === 'yucca' && !backend.isOnline,
67
65
  };
68
66
  const Reconfigure = {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@futo-org/backups-orchestrator-ui",
3
3
  "repository": "https://github.com/immich-app/yucca",
4
4
  "description": "Backups orchestrator (UI library)",
5
- "version": "0.4.0",
5
+ "version": "0.5.0",
6
6
  "license": "Source First License 1.1",
7
7
  "files": [
8
8
  "dist",
@@ -64,7 +64,7 @@
64
64
  "lodash.debounce": "^4.0.8",
65
65
  "luxon": "^3.7.2",
66
66
  "socket.io-client": "^4.8.3",
67
- "@futo-org/backups-api-client": "^0.4.0"
67
+ "@futo-org/backups-api-client": "^0.5.0"
68
68
  },
69
69
  "scripts": {
70
70
  "dev": "vite dev --port 5174",