Skip to content

Commit a8b66d9

Browse files
authored
Show loading indicator for peer detail view as groups are loading (#343)
1 parent f74f9cf commit a8b66d9

File tree

8 files changed

+188
-117
lines changed

8 files changed

+188
-117
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Skeleton from "react-loading-skeleton";
2+
3+
export default function SkeletonPeerDetail() {
4+
return (
5+
<div className={"w-full mt-6 p-default"}>
6+
<div className={"flex flex-wrap w-full justify-between max-w-6xl "}>
7+
<Skeleton height={24} width={300} className={"rounded-md"} />
8+
</div>
9+
<div className={"flex flex-wrap w-full justify-between mt-4 max-w-6xl "}>
10+
<Skeleton height={42} width={400} className={"rounded-md"} />
11+
<div className={"flex gap-3"}>
12+
<Skeleton height={42} width={80} className={"rounded-md"} />
13+
<Skeleton height={42} width={120} className={"rounded-md"} />
14+
</div>
15+
</div>
16+
<div
17+
className={
18+
"flex flex-wrap w-full justify-between mt-6 max-w-6xl gap-10"
19+
}
20+
>
21+
<Skeleton
22+
height={400}
23+
width={"100%"}
24+
className={"rounded-md"}
25+
containerClassName={"flex-1 "}
26+
/>
27+
<Skeleton
28+
height={300}
29+
width={"100%"}
30+
className={"rounded-md opacity-30"}
31+
containerClassName={"flex-1 "}
32+
/>
33+
</div>
34+
</div>
35+
);
36+
}

src/components/ui/GroupBadge.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ export default function GroupBadge({
3131
<TextWithTooltip text={group.name} maxChars={20} />
3232
{children}
3333
{showX && (
34-
<XIcon size={12} className={"cursor-pointer group-hover:text-white"} />
34+
<XIcon
35+
size={12}
36+
className={"cursor-pointer group-hover:text-white shrink-0"}
37+
/>
3538
)}
3639
</Badge>
3740
);

src/contexts/GroupsProvider.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ const GroupContext = React.createContext(
1212
refresh: () => void;
1313
dropdownOptions: Group[];
1414
setDropdownOptions: React.Dispatch<React.SetStateAction<Group[]>>;
15+
isLoading: boolean;
1516
},
1617
);
1718

1819
export default function GroupsProvider({ children }: Props) {
19-
const { data: groups, mutate } = useFetchApi<Group[]>("/groups");
20+
const { data: groups, mutate, isLoading } = useFetchApi<Group[]>("/groups");
2021
const [dropdownOptions, setDropdownOptions] = useState<Group[]>([]);
2122

2223
const refresh = () => {
@@ -25,7 +26,13 @@ export default function GroupsProvider({ children }: Props) {
2526

2627
return (
2728
<GroupContext.Provider
28-
value={{ groups, refresh, dropdownOptions, setDropdownOptions }}
29+
value={{
30+
groups,
31+
refresh,
32+
dropdownOptions,
33+
setDropdownOptions,
34+
isLoading,
35+
}}
2936
>
3037
{children}
3138
</GroupContext.Provider>

src/contexts/PeerProvider.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { notify } from "@components/Notification";
2+
import SkeletonPeerDetail from "@components/skeletons/SkeletonPeerDetail";
23
import { useApiCall } from "@utils/api";
34
import React, { useMemo } from "react";
45
import { useSWRConfig } from "swr";
@@ -27,12 +28,13 @@ const PeerContext = React.createContext(
2728
) => Promise<Peer>;
2829
openSSHDialog: () => Promise<boolean>;
2930
deletePeer: () => void;
31+
isLoading: boolean;
3032
},
3133
);
3234

3335
export default function PeerProvider({ children, peer }: Props) {
3436
const user = usePeerUser(peer);
35-
const peerGroups = usePeerGroups(peer);
37+
const { peerGroups, isLoading } = usePeerGroups(peer);
3638
const peerRequest = useApiCall<Peer>("/peers");
3739
const { confirm } = useDialog();
3840
const { mutate } = useSWRConfig();
@@ -94,12 +96,22 @@ export default function PeerProvider({ children, peer }: Props) {
9496
});
9597
};
9698

97-
return (
99+
return !isLoading ? (
98100
<PeerContext.Provider
99-
value={{ peer, peerGroups, user, update, openSSHDialog, deletePeer }}
101+
value={{
102+
peer,
103+
peerGroups,
104+
user,
105+
update,
106+
openSSHDialog,
107+
deletePeer,
108+
isLoading,
109+
}}
100110
>
101111
{children}
102112
</PeerContext.Provider>
113+
) : (
114+
<SkeletonPeerDetail />
103115
);
104116
}
105117

@@ -108,9 +120,9 @@ export default function PeerProvider({ children, peer }: Props) {
108120
* @param peer
109121
*/
110122
export const usePeerGroups = (peer?: Peer) => {
111-
const { groups } = useGroups();
123+
const { groups, isLoading } = useGroups();
112124

113-
return useMemo(() => {
125+
const peerGroups = useMemo(() => {
114126
if (!peer) return [];
115127
const peerGroups = groups?.filter((group) => {
116128
const foundGroup = group.peers?.find((p) => {
@@ -121,6 +133,8 @@ export const usePeerGroups = (peer?: Peer) => {
121133
});
122134
return peerGroups || [];
123135
}, [groups, peer]);
136+
137+
return { peerGroups, isLoading };
124138
};
125139

126140
/**

src/modules/groups/useGroupHelper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default function useGroupHelper({ initial = [], peer }: Props) {
3030
}, [groups, initial]);
3131

3232
const [selectedGroups, setSelectedGroups] = useState<Group[]>(initialGroups);
33-
const peerGroups = usePeerGroups(peer);
33+
const { peerGroups } = usePeerGroups(peer);
3434

3535
const save = async () => {
3636
return Promise.all(getAllGroupCalls()).then((groups) => {

src/modules/peer/usePeerRoutes.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type Props = {
99
};
1010
export default function usePeerRoutes({ peer }: Props) {
1111
const { data: routes } = useFetchApi<Route[]>("/routes");
12-
const peerGroups = usePeerGroups(peer);
12+
const { peerGroups } = usePeerGroups(peer);
1313

1414
return useMemo(() => {
1515
if (!routes) return undefined;

src/modules/setup-keys/SetupKeyModal.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
"use client";
2-
31
import Button from "@components/Button";
42
import Code from "@components/Code";
53
import FancyToggleSwitch from "@components/FancyToggleSwitch";
@@ -39,11 +37,12 @@ import { SetupKey } from "@/interfaces/SetupKey";
3937
import useGroupHelper from "@/modules/groups/useGroupHelper";
4038

4139
type Props = {
42-
children: React.ReactNode;
40+
children?: React.ReactNode;
41+
open: boolean;
42+
setOpen: (open: boolean) => void;
4343
};
4444
const copyMessage = "Setup-Key was copied to your clipboard!";
45-
export default function SetupKeyModal({ children }: Props) {
46-
const [modal, setModal] = useState(false);
45+
export default function SetupKeyModal({ children, open, setOpen }: Props) {
4746
const [successModal, setSuccessModal] = useState(false);
4847
const [setupKey, setSetupKey] = useState<SetupKey>();
4948
const [, copy] = useCopyToClipboard(setupKey?.key);
@@ -55,15 +54,15 @@ export default function SetupKeyModal({ children }: Props) {
5554

5655
return (
5756
<>
58-
<Modal open={modal} onOpenChange={setModal} key={modal ? 1 : 0}>
59-
<ModalTrigger asChild>{children}</ModalTrigger>
57+
<Modal open={open} onOpenChange={setOpen} key={open ? 1 : 0}>
58+
{children && <ModalTrigger asChild>{children}</ModalTrigger>}
6059
<SetupKeyModalContent onSuccess={handleSuccess} />
6160
</Modal>
6261
<Modal
6362
open={successModal}
6463
onOpenChange={(open) => {
6564
setSuccessModal(open);
66-
setModal(open);
65+
setOpen(open);
6766
}}
6867
>
6968
<ModalContent

0 commit comments

Comments
 (0)