Skip to content

Commit ca5d46a

Browse files
authored
Merge pull request #863 from jsonwebtoken/pending-signature-ui-state
Add loading/pending state for decoding jwt token
2 parents 1470b37 + efed95d commit ca5d46a

File tree

6 files changed

+101
-9
lines changed

6 files changed

+101
-9
lines changed

src/features/common/components/card-tabs/card-tabs.component.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,34 @@ import {
2121
TabPanel,
2222
Tabs,
2323
} from "react-aria-components";
24+
import { Spinner } from "../spinner/spinner.component";
2425

2526
type CardTabsProps = {
2627
resizeId: string;
2728
languageCode: string;
2829
title: string | null;
2930
cards: CardComponentProps[];
3031
activeTabId: string;
32+
isLoading?: boolean;
3133
handleActiveCardChange: (tabId: string) => void;
3234
};
3335

3436
const CardTabs: React.FC<CardTabsProps> = ({
3537
resizeId,
3638
title,
3739
cards,
40+
isLoading,
3841
activeTabId,
3942
handleActiveCardChange,
4043
}) => {
4144
const tabsId = useId();
4245

4346
const outputModalState$ = useDebuggerStore(
44-
(state) => state.outputModalState$,
47+
(state) => state.outputModalState$
4548
);
4649
const outputModalId$ = useDebuggerStore((state) => state.outputModalId$);
4750
const closeOutputModal$ = useDebuggerStore(
48-
(state) => state.closeOutputModal$,
51+
(state) => state.closeOutputModal$
4952
);
5053

5154
const activeCard = cards.filter((card) => card.id === activeTabId)[0];
@@ -166,9 +169,12 @@ const CardTabs: React.FC<CardTabsProps> = ({
166169
className={styles.cardTabs__container}
167170
>
168171
{title && (
169-
<h4 id={tabsId} className={styles.cardTabs__title}>
170-
{title}
171-
</h4>
172+
<div className={styles.cardTabs__title__container}>
173+
<h4 id={tabsId} className={styles.cardTabs__title}>
174+
{title}
175+
</h4>
176+
{isLoading && <Spinner />}
177+
</div>
172178
)}
173179
<Tabs
174180
selectedKey={activeCard.id}
@@ -269,6 +275,7 @@ type CardTabsWithTabPersistenceComponentProps = {
269275
languageCode: string;
270276
title: string | null;
271277
cards: CardComponentProps[];
278+
isLoading?: boolean;
272279
handleTabChange: (key: string) => void;
273280
};
274281

@@ -281,6 +288,7 @@ export const CardTabsWithTabPersistenceComponentProps: React.FC<
281288
languageCode,
282289
title,
283290
cards,
291+
isLoading,
284292
handleTabChange,
285293
}) => {
286294
const [activeTabId, setActiveTabId] = useState<string>(initialTabId);
@@ -309,6 +317,7 @@ export const CardTabsWithTabPersistenceComponentProps: React.FC<
309317
languageCode={languageCode}
310318
title={title}
311319
cards={cards}
320+
isLoading={isLoading}
312321
activeTabId={activeTabId}
313322
handleActiveCardChange={handleActiveCardChange}
314323
/>

src/features/common/components/card-tabs/card-tabs.module.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
line-height: 1.375rem;
2525
font-weight: 500;
2626
letter-spacing: 0.24px;
27+
margin-right: 8px;
2728
}
2829

2930
.cardTabs {
@@ -49,6 +50,11 @@ $cardTabs__tabList__height: 2.5rem;
4950
flex-shrink: 0;
5051
}
5152

53+
.cardTabs__title__container {
54+
display: flex;
55+
56+
}
57+
5258
.cardTab__title {
5359
position: relative;
5460
display: flex;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
import { ProgressBar } from "react-aria-components";
3+
import styles from "./spinner.module.scss";
4+
5+
export const Spinner = () => {
6+
return (
7+
<ProgressBar aria-label="Loading…" isIndeterminate>
8+
{() => (
9+
<div className={styles.circular__spinner}>
10+
<svg className={styles.spinner__svg} viewBox="0 0 20 20">
11+
<circle
12+
className={styles.spinner__circle}
13+
cx="10"
14+
cy="10"
15+
r="8"
16+
fill="none"
17+
strokeWidth="4"
18+
/>
19+
</svg>
20+
</div>
21+
)}
22+
</ProgressBar>
23+
);
24+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
.circular__spinner {
2+
width: 16px;
3+
height: 16px;
4+
display: inline-block;
5+
}
6+
7+
.spinner__svg {
8+
animation: rotate 1s linear infinite;
9+
width: 100%;
10+
height: 100%;
11+
}
12+
13+
.spinner__circle {
14+
stroke: var(--color_fg_default);
15+
stroke-dasharray: 90 150;
16+
stroke-dashoffset: 0;
17+
stroke-linecap: round;
18+
animation: dash 1.5s ease-in-out infinite;
19+
}
20+
21+
@keyframes rotate {
22+
100% {
23+
transform: rotate(360deg);
24+
}
25+
}
26+
27+
@keyframes dash {
28+
0% {
29+
stroke-dasharray: 1, 200;
30+
stroke-dashoffset: 0;
31+
}
32+
50% {
33+
stroke-dasharray: 100, 200;
34+
stroke-dashoffset: -15;
35+
}
36+
100% {
37+
stroke-dasharray: 1, 200;
38+
stroke-dashoffset: -126;
39+
}
40+
}

src/features/decoder/components/decoded-header-output.component.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const DecodedHeaderOutputComponent: React.FC<
3838
);
3939

4040
const decodedHeader$ = useDecoderStore((state) => state.decodedHeader);
41+
const isLoading = useDecoderStore((state) => state.isLoading)
4142

4243
const cards: CardComponentProps[] = [
4344
{
@@ -105,6 +106,7 @@ export const DecodedHeaderOutputComponent: React.FC<
105106
initialTabId={decodedHeaderInitialTabId}
106107
languageCode={languageCode}
107108
title={dictionary.title}
109+
isLoading={isLoading}
108110
cards={cards}
109111
handleTabChange={setDecodedHeaderTabValue$}
110112
/>

src/features/decoder/services/decoder.store.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export type DecoderStoreState = {
4848
asymmetricPublicKeyFormat: AsymmetricKeyFormatValues;
4949
decodedHeader: string;
5050
decodedPayload: string;
51+
isLoading: boolean;
5152
signatureStatus: JwtSignatureStatusValues;
5253
controlledSymmetricSecretKey: {
5354
id: number;
@@ -70,11 +71,11 @@ type DecoderStoreActions = {
7071
handleJwtChange: (newToken: string) => void;
7172
handleSymmetricSecretKeyChange: (newSymmetricSecretKey: string) => void;
7273
handleSymmetricSecretKeyEncodingChange: (
73-
newSymmetricSecretKey: EncodingValues,
74+
newSymmetricSecretKey: EncodingValues
7475
) => void;
7576
handleAsymmetricPublicKeyChange: (newAsymmetricPublicKey: string) => void;
7677
handleAsymmetricPublicKeyFormatChange: (
77-
newFormat: AsymmetricKeyFormatValues,
78+
newFormat: AsymmetricKeyFormatValues
7879
) => void;
7980
resetControlledSymmetricSecretKey: () => void;
8081
resetControlledAsymmetricPublicKey: () => void;
@@ -92,6 +93,7 @@ export const initialState: DecoderStoreState = {
9293
asymmetricPublicKeyFormat: AsymmetricKeyFormatValues.PEM,
9394
decodedHeader: DEFAULT_DECODED_HEADER,
9495
decodedPayload: DEFAULT_DECODED_PAYLOAD,
96+
isLoading: false,
9597
signatureStatus: JwtSignatureStatusValues.VALID,
9698
signatureWarnings: null,
9799
decodingErrors: null,
@@ -120,6 +122,12 @@ export const useDecoderStore = create<DecoderStore>()(
120122
asymmetricPublicKeyFormat,
121123
} = get();
122124

125+
set({
126+
isLoading: true,
127+
decodedHeader: "",
128+
decodedPayload: "",
129+
});
130+
123131
const update = await TokenDecoderService.handleJwtChange({
124132
alg,
125133
symmetricSecretKey,
@@ -129,7 +137,10 @@ export const useDecoderStore = create<DecoderStore>()(
129137
newToken,
130138
});
131139

132-
set(update);
140+
set({
141+
...update,
142+
isLoading: false,
143+
});
133144
},
134145
handleSymmetricSecretKeyChange: async (newSymmetricSecretKey) => {
135146
const { jwt, symmetricSecretKeyEncoding } = get();
@@ -208,5 +219,5 @@ export const useDecoderStore = create<DecoderStore>()(
208219

209220
set(update);
210221
},
211-
})),
222+
}))
212223
);

0 commit comments

Comments
 (0)