@arcblock/ux 2.13.13 → 2.13.14
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.
- package/lib/Address/responsive-did-address.js +3 -1
- package/lib/DIDConnect/app-icon.d.ts +8 -0
- package/lib/DIDConnect/app-icon.js +31 -0
- package/lib/DIDConnect/app-info-item.d.ts +7 -0
- package/lib/DIDConnect/app-info-item.js +73 -0
- package/lib/DIDConnect/did-connect-footer.d.ts +4 -0
- package/lib/DIDConnect/did-connect-footer.js +54 -0
- package/lib/DIDConnect/did-connect-logo.d.ts +1 -0
- package/lib/DIDConnect/did-connect-logo.js +11 -0
- package/lib/DIDConnect/index.d.ts +7 -0
- package/lib/DIDConnect/index.js +7 -0
- package/lib/DIDConnect/powered-by.d.ts +3 -0
- package/lib/DIDConnect/powered-by.js +46 -0
- package/lib/DIDConnect/with-container.d.ts +11 -0
- package/lib/DIDConnect/with-container.js +273 -0
- package/lib/DIDConnect/with-ux-theme.d.ts +1 -0
- package/lib/DIDConnect/with-ux-theme.js +23 -0
- package/lib/Dialog/confirm.d.ts +6 -1
- package/lib/Dialog/confirm.js +7 -3
- package/lib/Dialog/use-confirm.js +6 -0
- package/lib/Locale/util.d.ts +3 -3
- package/lib/Locale/util.js +6 -1
- package/lib/LoginButton/index.d.ts +12 -0
- package/lib/LoginButton/index.js +74 -0
- package/lib/SessionUser/components/un-login.js +42 -31
- package/lib/SharedBridge/index.d.ts +16 -0
- package/lib/SharedBridge/index.js +109 -0
- package/lib/SharedBridge/need-storage-access-api-dialog.d.ts +7 -0
- package/lib/SharedBridge/need-storage-access-api-dialog.js +212 -0
- package/lib/Theme/index.d.ts +2 -2
- package/lib/Theme/index.js +1 -1
- package/lib/Util/iframe.d.ts +5 -0
- package/lib/Util/iframe.js +24 -0
- package/lib/Util/index.d.ts +10 -1
- package/lib/Util/index.js +67 -4
- package/package.json +7 -6
- package/src/Address/responsive-did-address.tsx +11 -1
- package/src/DIDConnect/app-icon.tsx +36 -0
- package/src/DIDConnect/app-info-item.tsx +82 -0
- package/src/DIDConnect/did-connect-footer.tsx +51 -0
- package/src/DIDConnect/did-connect-logo.tsx +8 -0
- package/src/DIDConnect/index.ts +7 -0
- package/src/DIDConnect/powered-by.tsx +48 -0
- package/src/DIDConnect/with-container.tsx +307 -0
- package/src/DIDConnect/with-ux-theme.tsx +22 -0
- package/src/Dialog/confirm.jsx +31 -23
- package/src/Dialog/use-confirm.jsx +6 -0
- package/src/Locale/util.ts +7 -2
- package/src/LoginButton/index.tsx +73 -0
- package/src/SessionUser/components/un-login.tsx +34 -27
- package/src/SharedBridge/index.tsx +123 -0
- package/src/SharedBridge/need-storage-access-api-dialog.tsx +171 -0
- package/src/Theme/index.ts +2 -2
- package/src/Util/iframe.ts +19 -0
- package/src/Util/index.ts +77 -4
@@ -0,0 +1,307 @@
|
|
1
|
+
import { forwardRef, memo, useRef, useState } from 'react';
|
2
|
+
import { Dialog, DialogContent, useMediaQuery, Box, Backdrop, SwipeableDrawer, Drawer } from '@mui/material';
|
3
|
+
import { useDebounce, useMemoizedFn } from 'ahooks';
|
4
|
+
import { useBrowser } from '@arcblock/react-hooks';
|
5
|
+
import colorConvert from 'color-convert';
|
6
|
+
|
7
|
+
import { useTheme } from '../Theme';
|
8
|
+
import { mergeSx } from '../Util/style';
|
9
|
+
import { hexToRgba } from '../Util';
|
10
|
+
import { Locale } from '../type';
|
11
|
+
|
12
|
+
const BackdropWrap = memo(
|
13
|
+
forwardRef((backdropProps, ref) => {
|
14
|
+
return (
|
15
|
+
<Backdrop
|
16
|
+
open
|
17
|
+
ref={ref}
|
18
|
+
style={{
|
19
|
+
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
20
|
+
backdropFilter: 'blur(3px)',
|
21
|
+
touchAction: 'none',
|
22
|
+
}}
|
23
|
+
{...backdropProps}
|
24
|
+
key="background"
|
25
|
+
/>
|
26
|
+
);
|
27
|
+
})
|
28
|
+
);
|
29
|
+
|
30
|
+
export default function withContainer(Component: React.ComponentType<any>) {
|
31
|
+
function WithContainerComponent({
|
32
|
+
popup = false,
|
33
|
+
open = false,
|
34
|
+
hideCloseButton = false,
|
35
|
+
...rest
|
36
|
+
}: {
|
37
|
+
// 是否弹出显示, true 表示在 Dialog 中渲染, 并可以通过 open/onClose 控制 dialog 的显示/隐藏, false 表示直接渲染原内容
|
38
|
+
popup?: boolean;
|
39
|
+
open?: boolean;
|
40
|
+
hideCloseButton?: boolean;
|
41
|
+
onClose: () => void;
|
42
|
+
blocklet?: any;
|
43
|
+
origin?: string;
|
44
|
+
host?: string;
|
45
|
+
locale?: Locale;
|
46
|
+
}) {
|
47
|
+
const [color, setColor] = useState('#fff');
|
48
|
+
|
49
|
+
const drawerDragger = useRef(null);
|
50
|
+
const browser = useBrowser();
|
51
|
+
// 屏宽小于 sm 且在 mobile 设备中全屏显示 dialog (PC 端屏宽小于 sm 的情况正常弹窗, 不全屏显示)
|
52
|
+
const matchSm = useMediaQuery('(max-width:640px)');
|
53
|
+
let openVariant = 'page';
|
54
|
+
if (popup) {
|
55
|
+
openVariant = 'dialog';
|
56
|
+
if (matchSm && browser.mobile.any) {
|
57
|
+
openVariant = 'drawer';
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
const theme = useTheme();
|
62
|
+
|
63
|
+
// 兼容 did-react 版本中存在的 responsive prop, responsive=true 则让 dialog 始终保持 open 状态, 否则遵循外部传入的 open prop
|
64
|
+
const isOpen = open;
|
65
|
+
|
66
|
+
const leavingScreenDelay = theme?.transitions?.duration?.leavingScreen || 500; // 默认值是 195
|
67
|
+
const debouncedShow = useDebounce(isOpen, {
|
68
|
+
wait: leavingScreenDelay,
|
69
|
+
});
|
70
|
+
|
71
|
+
const removeMagicToken = useMemoizedFn(() => {
|
72
|
+
const searchParams = new URLSearchParams(window.location.search);
|
73
|
+
if (searchParams.get('magicToken')) {
|
74
|
+
searchParams.delete('magicToken');
|
75
|
+
}
|
76
|
+
window.history.replaceState({}, '', `${window.location.pathname}?${searchParams.toString()}`);
|
77
|
+
});
|
78
|
+
|
79
|
+
// eslint-disable-next-line no-unused-vars
|
80
|
+
const handleOnClose = (e: React.MouseEvent<HTMLElement>, reason: string) => {
|
81
|
+
if (['backdropClick', 'escapeKeyDown'].includes(reason)) return;
|
82
|
+
removeMagicToken();
|
83
|
+
rest?.onClose();
|
84
|
+
};
|
85
|
+
|
86
|
+
const showModal = debouncedShow || isOpen;
|
87
|
+
|
88
|
+
const DrawerComponent = hideCloseButton ? Drawer : SwipeableDrawer;
|
89
|
+
|
90
|
+
const hslColor = colorConvert.hex.hsl(color);
|
91
|
+
|
92
|
+
const [h, s, l] = hslColor;
|
93
|
+
const percentageList = [0, 30, 60, 30, 0, 30, 60, 30];
|
94
|
+
const maxPercentage = Math.max(...percentageList);
|
95
|
+
const minPercentage = Math.min(...percentageList);
|
96
|
+
let useAlpha = false;
|
97
|
+
if ((l * (100 + maxPercentage)) / 100 > 100 || (l * (100 + minPercentage)) / 100 < 0) {
|
98
|
+
// 超出范围,使用 alpha 通道变化
|
99
|
+
useAlpha = true;
|
100
|
+
}
|
101
|
+
const colorList = percentageList.map((percentageItem) => {
|
102
|
+
let finalL = (l * (100 + percentageItem)) / 100;
|
103
|
+
let finalAlpha = 0.6;
|
104
|
+
if (useAlpha) {
|
105
|
+
finalAlpha = (0.5 * (100 + percentageItem)) / 100;
|
106
|
+
} else {
|
107
|
+
finalL = (l * (100 + percentageItem)) / 100;
|
108
|
+
}
|
109
|
+
return `hsla(${h}, ${s}%, ${finalL}%, ${finalAlpha})`;
|
110
|
+
});
|
111
|
+
const background = `linear-gradient(45deg, ${colorList.join(', ')})`;
|
112
|
+
const colorListGlow = percentageList.map((percentageItem) => {
|
113
|
+
let finalL = (l * (100 + percentageItem)) / 100;
|
114
|
+
let finalAlpha = 0.2;
|
115
|
+
if (useAlpha) {
|
116
|
+
finalAlpha = (0.3 * (100 + percentageItem)) / 100;
|
117
|
+
} else {
|
118
|
+
finalL = (l * (100 + percentageItem)) / 100;
|
119
|
+
}
|
120
|
+
return `hsla(${h}, ${s}%, ${finalL}%, ${finalAlpha})`;
|
121
|
+
});
|
122
|
+
|
123
|
+
const backgroundGlow = `linear-gradient(45deg, ${colorListGlow.join(', ')})`;
|
124
|
+
|
125
|
+
const glowStyle = {
|
126
|
+
overflow: 'visible',
|
127
|
+
'&::before, &::after': {
|
128
|
+
content: '""',
|
129
|
+
position: 'absolute',
|
130
|
+
top: '-3px',
|
131
|
+
right: '-3px',
|
132
|
+
bottom: '-3px',
|
133
|
+
left: '-3px',
|
134
|
+
background,
|
135
|
+
backgroundSize: '300% 300%',
|
136
|
+
backgroundRepeat: 'no-repeat',
|
137
|
+
animation: 'glowRotate 10s linear infinite',
|
138
|
+
borderRadius: '14px !important',
|
139
|
+
zIndex: 0,
|
140
|
+
},
|
141
|
+
'&::after': {
|
142
|
+
background: backgroundGlow,
|
143
|
+
filter: 'blur(15px)',
|
144
|
+
},
|
145
|
+
|
146
|
+
'@keyframes glowRotate': {
|
147
|
+
'0%': {
|
148
|
+
backgroundPosition: '0 0',
|
149
|
+
},
|
150
|
+
'50%': {
|
151
|
+
backgroundPosition: '100% 0',
|
152
|
+
},
|
153
|
+
'100%': {
|
154
|
+
backgroundPosition: '0 0',
|
155
|
+
},
|
156
|
+
},
|
157
|
+
};
|
158
|
+
|
159
|
+
const wrapOnClose = useMemoizedFn(() => {
|
160
|
+
removeMagicToken();
|
161
|
+
rest?.onClose();
|
162
|
+
});
|
163
|
+
|
164
|
+
if (openVariant === 'page') {
|
165
|
+
return (
|
166
|
+
<Box
|
167
|
+
className="did-connect__container-page"
|
168
|
+
sx={mergeSx(glowStyle, {
|
169
|
+
borderRadius: 1,
|
170
|
+
position: 'relative',
|
171
|
+
zIndex: 1,
|
172
|
+
})}>
|
173
|
+
<Box
|
174
|
+
sx={{
|
175
|
+
border: `1px solid ${hexToRgba(color, 0.1)}`,
|
176
|
+
m: '-1px',
|
177
|
+
position: 'relative',
|
178
|
+
borderRadius: '12px',
|
179
|
+
zIndex: 2,
|
180
|
+
overflow: 'hidden',
|
181
|
+
}}>
|
182
|
+
<Component {...rest} onClose={wrapOnClose} setColor={setColor} hideCloseButton mode={openVariant} />
|
183
|
+
</Box>
|
184
|
+
</Box>
|
185
|
+
);
|
186
|
+
}
|
187
|
+
|
188
|
+
if (openVariant === 'drawer') {
|
189
|
+
return (
|
190
|
+
<DrawerComponent
|
191
|
+
className="did-connect__container-drawer"
|
192
|
+
disableSwipeToOpen
|
193
|
+
open={isOpen}
|
194
|
+
anchor="bottom"
|
195
|
+
drawerDragger={drawerDragger.current}
|
196
|
+
// @ts-ignore
|
197
|
+
onClose={handleOnClose}
|
198
|
+
slots={{
|
199
|
+
backdrop: BackdropWrap,
|
200
|
+
}}
|
201
|
+
PaperProps={{
|
202
|
+
sx: {
|
203
|
+
borderRadius: 3, // 保持跟 DID Wallet 一致
|
204
|
+
borderBottomLeftRadius: 0,
|
205
|
+
borderBottomRightRadius: 0,
|
206
|
+
p: '2px',
|
207
|
+
animation: 'glowBreathe 7s linear infinite',
|
208
|
+
'.did-connect__root': {
|
209
|
+
backgroundColor: 'transparent',
|
210
|
+
},
|
211
|
+
overflow: 'hidden',
|
212
|
+
'@keyframes glowBreathe': {
|
213
|
+
'0%, 100%': {
|
214
|
+
boxShadow: `
|
215
|
+
inset 0 0 7px ${hexToRgba(color, 0.3)},
|
216
|
+
inset 0 0 12px ${hexToRgba(color, 0.3)}`,
|
217
|
+
},
|
218
|
+
'50%': {
|
219
|
+
boxShadow: `
|
220
|
+
inset 0 0 18px ${hexToRgba(color, 0.7)},
|
221
|
+
inset 0 0 24px ${hexToRgba(color, 0.5)}`,
|
222
|
+
},
|
223
|
+
},
|
224
|
+
},
|
225
|
+
}}>
|
226
|
+
{hideCloseButton ? null : (
|
227
|
+
<Box
|
228
|
+
ref={drawerDragger}
|
229
|
+
sx={{
|
230
|
+
px: 1,
|
231
|
+
pt: 2,
|
232
|
+
m: 'auto',
|
233
|
+
mt: -1,
|
234
|
+
mb: -2,
|
235
|
+
zIndex: 2,
|
236
|
+
}}>
|
237
|
+
<Box
|
238
|
+
sx={{
|
239
|
+
width: '48px',
|
240
|
+
height: '4px',
|
241
|
+
borderRadius: '100vw',
|
242
|
+
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
243
|
+
}}
|
244
|
+
/>
|
245
|
+
</Box>
|
246
|
+
)}
|
247
|
+
<Box
|
248
|
+
sx={{
|
249
|
+
touchAction: 'none',
|
250
|
+
maxWidth: '100%',
|
251
|
+
width: 500,
|
252
|
+
height: 'auto',
|
253
|
+
}}>
|
254
|
+
{/* HACK: 由于 MUI 文档中描述 使用 keepMounted: false 可能会造成问题,所以采用下面的方案进行 HACK */}
|
255
|
+
{/* https://mui.com/material-ui/react-drawer/#keep-mounted */}
|
256
|
+
{showModal ? (
|
257
|
+
<Component {...rest} onClose={wrapOnClose} setColor={setColor} hideCloseButton mode={openVariant} />
|
258
|
+
) : null}
|
259
|
+
</Box>
|
260
|
+
</DrawerComponent>
|
261
|
+
);
|
262
|
+
}
|
263
|
+
|
264
|
+
return (
|
265
|
+
<Dialog
|
266
|
+
open={isOpen}
|
267
|
+
slots={{
|
268
|
+
backdrop: BackdropWrap,
|
269
|
+
}}
|
270
|
+
className="did-connect__container-dialog"
|
271
|
+
onClose={handleOnClose}
|
272
|
+
PaperProps={{
|
273
|
+
sx: {
|
274
|
+
// 避免样式被 server 中的定义覆盖
|
275
|
+
'&.MuiPaper-rounded': {
|
276
|
+
borderRadius: '12px !important',
|
277
|
+
},
|
278
|
+
position: 'relative',
|
279
|
+
...glowStyle,
|
280
|
+
},
|
281
|
+
}}>
|
282
|
+
<DialogContent
|
283
|
+
sx={{
|
284
|
+
maxWidth: 'calc(100vw - 18px)',
|
285
|
+
maxHeight: 'calc(100vh - 18px)',
|
286
|
+
p: '0px !important',
|
287
|
+
height: 'auto',
|
288
|
+
backgroundColor: 'background.default',
|
289
|
+
borderRadius: '12px !important',
|
290
|
+
zIndex: 1,
|
291
|
+
}}>
|
292
|
+
{showModal ? (
|
293
|
+
<Component
|
294
|
+
{...rest}
|
295
|
+
onClose={wrapOnClose}
|
296
|
+
setColor={setColor}
|
297
|
+
hideCloseButton={hideCloseButton}
|
298
|
+
mode={openVariant}
|
299
|
+
/>
|
300
|
+
) : null}
|
301
|
+
</DialogContent>
|
302
|
+
</Dialog>
|
303
|
+
);
|
304
|
+
}
|
305
|
+
|
306
|
+
return WithContainerComponent;
|
307
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { createTheme, useTheme } from '@mui/material';
|
2
|
+
import { DID_CONNECT_THEME_LIGHT, DID_CONNECT_THEME_DARK } from '@blocklet/theme';
|
3
|
+
import { ThemeProvider } from '../Theme';
|
4
|
+
|
5
|
+
// DID Connect 使用自己的 Theme 配置,不受 Blocklet theme 影响
|
6
|
+
const themeLight = createTheme(DID_CONNECT_THEME_LIGHT);
|
7
|
+
const themeDark = createTheme(DID_CONNECT_THEME_DARK);
|
8
|
+
|
9
|
+
export default function withUxTheme(Component: React.ComponentType<any>) {
|
10
|
+
function WithUxThemeComponent(props: any) {
|
11
|
+
const { palette } = useTheme();
|
12
|
+
const theme = palette.mode === 'dark' ? themeDark : themeLight;
|
13
|
+
|
14
|
+
return (
|
15
|
+
<ThemeProvider theme={theme}>
|
16
|
+
<Component {...props} />
|
17
|
+
</ThemeProvider>
|
18
|
+
);
|
19
|
+
}
|
20
|
+
|
21
|
+
return WithUxThemeComponent;
|
22
|
+
}
|
package/src/Dialog/confirm.jsx
CHANGED
@@ -15,6 +15,7 @@ import Dialog from './dialog';
|
|
15
15
|
* children?: React.ReactNode,
|
16
16
|
* showCancelButton?: true | false,
|
17
17
|
* showCloseButton?: true | false,
|
18
|
+
* showConfirmButton?: true | false,
|
18
19
|
* fullScreen?: false | true,
|
19
20
|
* confirmButton?: {text: React.ReactNode, props?: ButtonProps}
|
20
21
|
* cancelButton?: {text: React.ReactNode, props?: ButtonProps}
|
@@ -35,6 +36,7 @@ export default function Confirm({
|
|
35
36
|
onCancel,
|
36
37
|
showCloseButton,
|
37
38
|
showCancelButton,
|
39
|
+
showConfirmButton,
|
38
40
|
fullScreen,
|
39
41
|
confirmButton,
|
40
42
|
cancelButton,
|
@@ -81,29 +83,33 @@ export default function Confirm({
|
|
81
83
|
},
|
82
84
|
}}
|
83
85
|
actions={
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
e
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
86
|
+
showCancelButton || showConfirmButton ? (
|
87
|
+
<>
|
88
|
+
{showCancelButton && (
|
89
|
+
<Button
|
90
|
+
onClick={(e) => {
|
91
|
+
e.stopPropagation();
|
92
|
+
onCancel(e, 'closeButton');
|
93
|
+
}}
|
94
|
+
color="primary"
|
95
|
+
{...cancelButton.props}>
|
96
|
+
{cancelButton.text}
|
97
|
+
</Button>
|
98
|
+
)}
|
99
|
+
{showConfirmButton && (
|
100
|
+
<Button
|
101
|
+
onClick={(e) => {
|
102
|
+
e.stopPropagation();
|
103
|
+
onConfirm(e);
|
104
|
+
}}
|
105
|
+
color="primary"
|
106
|
+
autoFocus
|
107
|
+
{...confirmButton.props}>
|
108
|
+
{confirmButton.text}
|
109
|
+
</Button>
|
110
|
+
)}
|
111
|
+
</>
|
112
|
+
) : null
|
107
113
|
}>
|
108
114
|
{children}
|
109
115
|
</Dialog>
|
@@ -117,6 +123,7 @@ Confirm.propTypes = {
|
|
117
123
|
onCancel: PropTypes.func.isRequired,
|
118
124
|
children: PropTypes.node,
|
119
125
|
showCancelButton: PropTypes.bool,
|
126
|
+
showConfirmButton: PropTypes.bool,
|
120
127
|
showCloseButton: PropTypes.bool,
|
121
128
|
fullScreen: PropTypes.bool,
|
122
129
|
// 可以传入 {text: ..., props: ...}
|
@@ -133,6 +140,7 @@ Confirm.propTypes = {
|
|
133
140
|
|
134
141
|
Confirm.defaultProps = {
|
135
142
|
showCancelButton: true,
|
143
|
+
showConfirmButton: true,
|
136
144
|
showCloseButton: true,
|
137
145
|
fullScreen: false,
|
138
146
|
confirmButton: {
|
@@ -19,6 +19,7 @@ const ConfirmHolder = forwardRef((props, ref) => {
|
|
19
19
|
loading: false,
|
20
20
|
showCancelButton: true,
|
21
21
|
showCloseButton: true,
|
22
|
+
showConfirmButton: true,
|
22
23
|
confirmButtonText: 'Confirm',
|
23
24
|
confirmButtonProps: {},
|
24
25
|
cancelButtonText: 'Cancel',
|
@@ -43,6 +44,7 @@ const ConfirmHolder = forwardRef((props, ref) => {
|
|
43
44
|
state.onCancel = params.onCancel || noop;
|
44
45
|
state.showCloseButton = params.showCloseButton ?? true;
|
45
46
|
state.showCancelButton = params.showCancelButton ?? true;
|
47
|
+
state.showConfirmButton = params.showConfirmButton ?? true;
|
46
48
|
if (params.confirmButtonText) state.confirmButtonText = params.confirmButtonText;
|
47
49
|
if (params.confirmButtonProps) state.confirmButtonProps = params.confirmButtonProps;
|
48
50
|
if (params.cancelButtonText) state.cancelButtonText = params.cancelButtonText;
|
@@ -62,6 +64,9 @@ const ConfirmHolder = forwardRef((props, ref) => {
|
|
62
64
|
state.confirmButtonProps = {};
|
63
65
|
state.cancelButtonText = 'Cancel';
|
64
66
|
state.cancelButtonProps = {};
|
67
|
+
state.showConfirmButton = true;
|
68
|
+
state.showCancelButton = true;
|
69
|
+
state.showCloseButton = true;
|
65
70
|
});
|
66
71
|
const close = useMemoizedFn(() => {
|
67
72
|
state.show = false;
|
@@ -130,6 +135,7 @@ const ConfirmHolder = forwardRef((props, ref) => {
|
|
130
135
|
}}
|
131
136
|
showCloseButton={state.showCloseButton}
|
132
137
|
showCancelButton={state.showCancelButton}
|
138
|
+
showConfirmButton={state.showConfirmButton}
|
133
139
|
cancelButton={{
|
134
140
|
text: state.cancelButtonText,
|
135
141
|
props: {
|
package/src/Locale/util.ts
CHANGED
@@ -2,8 +2,13 @@ import get from 'lodash/get';
|
|
2
2
|
import type { Locale, Translations } from '../type';
|
3
3
|
|
4
4
|
/* eslint-disable no-prototype-builtins */
|
5
|
-
export const replace = (template: string, data: Record<string, any>) =>
|
6
|
-
|
5
|
+
export const replace = (template: string | Function, data: Record<string, any>) => {
|
6
|
+
if (typeof template === 'function') {
|
7
|
+
return template(data);
|
8
|
+
}
|
9
|
+
|
10
|
+
return template.replace(/{(\w*)}/g, (m, key) => (data.hasOwnProperty(key) ? data[key] : ''));
|
11
|
+
};
|
7
12
|
|
8
13
|
export const translate = (
|
9
14
|
translations: Translations,
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import { Box } from '@mui/material';
|
2
|
+
import { joinURL } from 'ufo';
|
3
|
+
import { useRef, useState } from 'react';
|
4
|
+
import { useMemoizedFn } from 'ahooks';
|
5
|
+
import { useBrowser } from '@arcblock/react-hooks';
|
6
|
+
|
7
|
+
import SharedBridge from '../SharedBridge';
|
8
|
+
import { setVisitorId } from '../Util';
|
9
|
+
import { getFederatedEnabled, getMaster } from '../Util/federated';
|
10
|
+
import { callIframe } from '../Util/iframe';
|
11
|
+
import { Locale } from '../type';
|
12
|
+
|
13
|
+
type LoginButtonProps = {
|
14
|
+
onClick: (options?: { openMode?: 'popup' | 'window' }) => void;
|
15
|
+
render: (options: { onClick: () => void }) => React.ReactNode;
|
16
|
+
locale?: Locale;
|
17
|
+
};
|
18
|
+
|
19
|
+
export default function LoginButton({ onClick, render, locale }: LoginButtonProps) {
|
20
|
+
const blocklet = window?.blocklet;
|
21
|
+
const federatedEnabled = getFederatedEnabled(blocklet);
|
22
|
+
const masterSite = getMaster(blocklet);
|
23
|
+
const sharedBridgeRef = useRef<HTMLIFrameElement>(null);
|
24
|
+
const [hasStorageAccess, setHasStorageAccess] = useState(false);
|
25
|
+
const browser = useBrowser();
|
26
|
+
|
27
|
+
const handleClick = useMemoizedFn(() => {
|
28
|
+
if (hasStorageAccess) {
|
29
|
+
onClick({ openMode: 'popup' });
|
30
|
+
} else {
|
31
|
+
onClick();
|
32
|
+
}
|
33
|
+
});
|
34
|
+
const handleLoad = useMemoizedFn(async () => {
|
35
|
+
const { value: visitorId } = await callIframe(sharedBridgeRef.current as HTMLIFrameElement, 'getVisitorId');
|
36
|
+
if (visitorId) {
|
37
|
+
setHasStorageAccess(true);
|
38
|
+
setVisitorId(visitorId);
|
39
|
+
}
|
40
|
+
});
|
41
|
+
const handleClickBridge = useMemoizedFn(({ value, visitorId }) => {
|
42
|
+
if (visitorId) {
|
43
|
+
setVisitorId(visitorId);
|
44
|
+
}
|
45
|
+
if (value) {
|
46
|
+
onClick({ openMode: 'popup' });
|
47
|
+
} else {
|
48
|
+
onClick();
|
49
|
+
}
|
50
|
+
});
|
51
|
+
|
52
|
+
if (browser.arcSphere || browser.wallet) {
|
53
|
+
return render({ onClick });
|
54
|
+
}
|
55
|
+
|
56
|
+
return (
|
57
|
+
<Box
|
58
|
+
sx={{
|
59
|
+
position: 'relative',
|
60
|
+
}}>
|
61
|
+
{render({ onClick: handleClick })}
|
62
|
+
{masterSite?.appUrl && federatedEnabled ? (
|
63
|
+
<SharedBridge
|
64
|
+
locale={locale}
|
65
|
+
iframeRef={sharedBridgeRef}
|
66
|
+
onLoad={handleLoad}
|
67
|
+
onClick={handleClickBridge}
|
68
|
+
src={joinURL(masterSite.appUrl, '/.well-known/service/share/shared-bridge.html')}
|
69
|
+
/>
|
70
|
+
) : null}
|
71
|
+
</Box>
|
72
|
+
);
|
73
|
+
}
|
@@ -27,6 +27,7 @@ import { translations } from '../libs/translation';
|
|
27
27
|
import Typography from '../../Typography';
|
28
28
|
import QuickLoginItem from './quick-login-item';
|
29
29
|
import { getFederatedEnabled, getMaster } from '../../Util/federated';
|
30
|
+
import LoginButton from '../../LoginButton';
|
30
31
|
|
31
32
|
export interface UnLoginProps {
|
32
33
|
session: Session;
|
@@ -43,7 +44,6 @@ export default function UnLogin({ session, onLogin = noop, size = 24, dark = fal
|
|
43
44
|
});
|
44
45
|
const searchParams = new URLSearchParams(window.location.search);
|
45
46
|
const browser = useBrowser();
|
46
|
-
|
47
47
|
const isFirstLoading = false;
|
48
48
|
const userAnchorRef = useRef(null);
|
49
49
|
const currentState = useReactive({
|
@@ -55,17 +55,18 @@ export default function UnLogin({ session, onLogin = noop, size = 24, dark = fal
|
|
55
55
|
const onTogglePopper = useMemoizedFn((value = !currentState.open) => {
|
56
56
|
currentState.open = value;
|
57
57
|
});
|
58
|
-
|
58
|
+
|
59
|
+
const _onLogin = useMemoizedFn(({ openMode } = {}) => {
|
59
60
|
if (isFirstLoading) {
|
60
61
|
return;
|
61
62
|
}
|
62
|
-
session.login(onLogin);
|
63
|
+
session.login(onLogin, { openMode });
|
63
64
|
});
|
64
65
|
|
65
66
|
const blocklet = window?.blocklet;
|
66
67
|
const federatedEnabled = getFederatedEnabled(blocklet);
|
67
|
-
const
|
68
|
-
const loginApp = federatedEnabled &&
|
68
|
+
const masterSite = getMaster(blocklet);
|
69
|
+
const loginApp = federatedEnabled && masterSite ? masterSite : blocklet;
|
69
70
|
const loginAppName = loginApp?.appName || 'DID Connect';
|
70
71
|
const loginAppLogo = joinURL(
|
71
72
|
loginApp?.appUrl || '/',
|
@@ -104,28 +105,34 @@ export default function UnLogin({ session, onLogin = noop, size = 24, dark = fal
|
|
104
105
|
|
105
106
|
return (
|
106
107
|
<>
|
107
|
-
<
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
108
|
+
<LoginButton
|
109
|
+
locale={locale}
|
110
|
+
onClick={_onLogin}
|
111
|
+
render={({ onClick }) => {
|
112
|
+
return (
|
113
|
+
<IconButton
|
114
|
+
ref={userAnchorRef}
|
115
|
+
data-cy="sessionManager-login"
|
116
|
+
className="arc-session-user-unlogin"
|
117
|
+
size="medium"
|
118
|
+
onClick={onClick}
|
119
|
+
aria-label="Login button">
|
120
|
+
{isFirstLoading ? (
|
121
|
+
<Box width={size} height={size} display="flex" justifyContent="center" alignItems="center">
|
122
|
+
<CircularProgress style={{ width: size - 4, height: size - 4, color: dark ? '#fff' : '' }} />
|
123
|
+
</Box>
|
124
|
+
) : (
|
125
|
+
<Icon
|
126
|
+
icon={UserIcon}
|
127
|
+
fontSize={size}
|
128
|
+
color={dark ? '#fff' : 'inherit'}
|
129
|
+
style={{ transform: 'scale(1.25)' }}
|
130
|
+
/>
|
131
|
+
)}
|
132
|
+
</IconButton>
|
133
|
+
);
|
134
|
+
}}
|
135
|
+
/>
|
129
136
|
<Popper
|
130
137
|
open={currentState.open}
|
131
138
|
anchorEl={userAnchorRef.current}
|