@abtnode/ux 1.16.21-beta-420f105a → 1.16.21-beta-93c6e42b

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.
@@ -39,6 +39,7 @@ import BlockletSource from '../blocklet-source';
39
39
  import BlockletStatus from '../status';
40
40
  import BlockletVersion from '../version';
41
41
  import Confirm from '../../confirm';
42
+ import AutoCheckUpdate from '../storage/auto-check-update';
42
43
  import { useNodeContext } from '../../contexts/node';
43
44
  import { formatPerson, formatError, sleep, BlockletAdminRoles, formatToDatetime } from '../../util';
44
45
  import Permission from '../../permission';
@@ -52,6 +53,7 @@ import AddComponent from './add';
52
53
  import AddRule from '../router/action/add-rule';
53
54
  import RuleList from '../router/rule-list';
54
55
  import Actions from '../../actions';
56
+ import { useCheckUpdateByUrl } from '../../hooks/use-check-update-by-url';
55
57
  import { jsx as _jsx } from "react/jsx-runtime";
56
58
  import { jsxs as _jsxs } from "react/jsx-runtime";
57
59
  const getEngineRows = (engine, t) => {
@@ -182,7 +184,8 @@ function ComponentItem({
182
184
  name: t('common.status'),
183
185
  value: /*#__PURE__*/_jsx(BlockletStatus, {
184
186
  status: status,
185
- source: componentInfo.source
187
+ source: componentInfo.source,
188
+ progress: blocklet.progress
186
189
  })
187
190
  }, blocklet.status === 'running' ? {
188
191
  name: t('blocklet.startedAt'),
@@ -298,7 +301,8 @@ function ComponentItem({
298
301
  marginLeft: 4
299
302
  },
300
303
  status: status,
301
- source: blocklet.source
304
+ source: blocklet.source,
305
+ progress: blocklet.progress
302
306
  }), /*#__PURE__*/_jsx(BlockletMode, {
303
307
  style: {
304
308
  marginLeft: 4
@@ -724,6 +728,7 @@ export default function BlockletComponent({
724
728
  };
725
729
  });
726
730
  };
731
+ useCheckUpdateByUrl(checkForUpdates);
727
732
  return /*#__PURE__*/_jsxs(Div, {
728
733
  component: "div",
729
734
  ...rest,
@@ -731,36 +736,44 @@ export default function BlockletComponent({
731
736
  display: "flex",
732
737
  justifyContent: "space-between",
733
738
  alignItems: "center",
734
- children: [/*#__PURE__*/_jsxs("div", {
739
+ children: [/*#__PURE__*/_jsxs(Box, {
735
740
  className: "title",
741
+ flexDirection: "row",
742
+ display: "flex",
743
+ alignItems: "center",
744
+ style: {
745
+ marginLeft: '1em'
746
+ },
736
747
  children: [t('common.components'), !needMigration && /*#__PURE__*/_jsx(Permission, {
737
748
  permission: inService ? '' : 'mutate_blocklets',
738
749
  role: inService ? BlockletAdminRoles : [],
739
- children: /*#__PURE__*/_jsx(StyledBadge, {
740
- top: 1.2,
741
- right: 0.4,
742
- color: "error",
743
- badgeContent: "",
744
- variant: "dot",
745
- invisible: !showUpdateDot,
746
- children: /*#__PURE__*/_jsxs(Button, {
747
- disabled: loading || isInProgress(blocklet.status),
748
- color: "secondary",
749
- onClick: () => {
750
- checkForUpdates();
751
- },
752
- "data-cy": "check-for-updates",
753
- children: [/*#__PURE__*/_jsx(Box, {
754
- mr: 0.5,
755
- display: "flex",
756
- children: loading ? /*#__PURE__*/_jsx(Spinner, {
757
- size: 16
758
- }) : /*#__PURE__*/_jsx(UpdateIcon, {
759
- style: {
760
- fontSize: '1em'
761
- }
762
- })
763
- }), t('blocklet.component.checkUpdateTitle')]
750
+ children: /*#__PURE__*/_jsx(AutoCheckUpdate, {
751
+ children: /*#__PURE__*/_jsx(StyledBadge, {
752
+ top: 1.2,
753
+ right: 0.4,
754
+ color: "error",
755
+ badgeContent: "",
756
+ variant: "dot",
757
+ invisible: !showUpdateDot,
758
+ children: /*#__PURE__*/_jsxs(Button, {
759
+ disabled: loading || isInProgress(blocklet.status),
760
+ color: "secondary",
761
+ onClick: () => {
762
+ checkForUpdates();
763
+ },
764
+ "data-cy": "check-for-updates",
765
+ children: [/*#__PURE__*/_jsx(Box, {
766
+ mr: 0.5,
767
+ display: "flex",
768
+ children: loading ? /*#__PURE__*/_jsx(Spinner, {
769
+ size: 16
770
+ }) : /*#__PURE__*/_jsx(UpdateIcon, {
771
+ style: {
772
+ fontSize: '1em'
773
+ }
774
+ })
775
+ }), t('blocklet.component.checkUpdateTitle')]
776
+ })
764
777
  })
765
778
  })
766
779
  })]
@@ -71,7 +71,8 @@ export default function BlockletOverview() {
71
71
  name: t('common.status'),
72
72
  value: /*#__PURE__*/_jsx(BlockletStatus, {
73
73
  status: blocklet.status,
74
- source: blocklet.source
74
+ source: blocklet.source,
75
+ progress: blocklet.progress
75
76
  })
76
77
  }, isServerless(blocklet) ? {
77
78
  name: 'NFT',
@@ -16,6 +16,7 @@ export default function BlockletStatus({
16
16
  status,
17
17
  variant,
18
18
  source,
19
+ progress,
19
20
  ...rest
20
21
  }) {
21
22
  const {
@@ -25,6 +26,7 @@ export default function BlockletStatus({
25
26
  added: 'primary',
26
27
  waiting: 'warning',
27
28
  downloading: 'warning',
29
+ extracting: 'warning',
28
30
  downloaded: 'primary',
29
31
  installing: 'warning',
30
32
  installed: 'primary',
@@ -42,6 +44,7 @@ export default function BlockletStatus({
42
44
  added: colors.grey,
43
45
  waiting: colors.yellow,
44
46
  downloading: colors.yellow,
47
+ extracting: colors.yellow,
45
48
  downloaded: colors.grey,
46
49
  installing: colors.yellow,
47
50
  installed: colors.grey,
@@ -77,15 +80,22 @@ export default function BlockletStatus({
77
80
  style: {
78
81
  marginRight: 5
79
82
  }
80
- }), statusTxt]
83
+ }), statusTxt, status === 'downloading' && progress ? /*#__PURE__*/_jsx("div", {
84
+ style: {
85
+ marginLeft: 5
86
+ },
87
+ children: `${progress} %`
88
+ }) : null]
81
89
  });
82
90
  }
83
91
  BlockletStatus.propTypes = {
84
92
  status: PropTypes.string.isRequired,
85
93
  variant: PropTypes.oneOf(['dot', 'tag']),
86
- source: PropTypes.string
94
+ source: PropTypes.string,
95
+ progress: PropTypes.number
87
96
  };
88
97
  BlockletStatus.defaultProps = {
89
98
  variant: 'tag',
90
- source: ''
99
+ source: '',
100
+ progress: 0
91
101
  };
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import { Box } from '@mui/material';
3
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
+ import Toast from '@arcblock/ux/lib/Toast';
5
+ import PropTypes from 'prop-types';
6
+ import Tooltip from '@mui/material/Tooltip';
7
+ import SwitchControl from '../component/switch-control';
8
+ import { useBlockletStorageContext } from '../../contexts/blocklet-storage';
9
+
10
+ /**
11
+ * @description
12
+ * @param {{
13
+ * style: import('react').CSSProperties
14
+ * }} {...rest}
15
+ * @return {*}
16
+ */
17
+ import { jsx as _jsx } from "react/jsx-runtime";
18
+ import { jsxs as _jsxs } from "react/jsx-runtime";
19
+ function AutoCheckUpdate({
20
+ children,
21
+ ...rest
22
+ }) {
23
+ const {
24
+ t
25
+ } = useLocaleContext();
26
+ const {
27
+ updateAutoCheckUpdate,
28
+ autoCheckUpdate
29
+ } = useBlockletStorageContext();
30
+ const configAutoBackup = async () => {
31
+ try {
32
+ const enabled = !autoCheckUpdate?.enabled;
33
+ await updateAutoCheckUpdate({
34
+ enabled
35
+ });
36
+ Toast.success(enabled ? t('storage.spaces.autoCheckUpdate.enabled') : t('storage.spaces.autoCheckUpdate.disabled'));
37
+ } catch (error) {
38
+ Toast.error(error.message);
39
+ }
40
+ };
41
+ const enabled = Boolean(autoCheckUpdate?.enabled);
42
+ return /*#__PURE__*/_jsxs(Box, {
43
+ display: "flex",
44
+ flexDirection: "row",
45
+ alignItems: "center",
46
+ children: [children, /*#__PURE__*/_jsx(Tooltip, {
47
+ title: t('storage.spaces.autoCheckUpdate.title'),
48
+ arrow: true,
49
+ placement: "right",
50
+ children: /*#__PURE__*/_jsx("div", {
51
+ children: /*#__PURE__*/_jsx(SwitchControl, {
52
+ checked: enabled,
53
+ onChange: configAutoBackup,
54
+ name: "enableWelcomePage",
55
+ style: {
56
+ ...rest.style,
57
+ zoom: 0.8
58
+ }
59
+ })
60
+ })
61
+ })]
62
+ });
63
+ }
64
+ AutoCheckUpdate.propTypes = {
65
+ children: PropTypes.any
66
+ };
67
+ AutoCheckUpdate.defaultProps = {
68
+ children: null
69
+ };
70
+ export default AutoCheckUpdate;
@@ -10,6 +10,7 @@ import { useNodeContext } from './node';
10
10
  import useBackupProgress from '../hooks/use-backup-progress';
11
11
  import useBackups from '../hooks/use-backups';
12
12
  import { useAutoBackup } from '../hooks/use-auto-backup';
13
+ import { useAutoCheckUpdate } from '../hooks/use-auto-check-update';
13
14
 
14
15
  /**
15
16
  * @typedef {import('../blocklet/storage/connected').SpaceGateway} SpaceGateway
@@ -86,6 +87,10 @@ function BlockletStorageProvider({
86
87
  const autoBackup = useAutoBackup({
87
88
  deps: [autoBackupRefresh]
88
89
  });
90
+ const [autoCheckUpdateRefresh, setAutoCheckUpdateRefresh] = useState(false);
91
+ const autoCheckUpdate = useAutoCheckUpdate({
92
+ deps: [autoCheckUpdateRefresh]
93
+ });
89
94
  const refreshSpaceGateways = () => {
90
95
  setSpaceGatewaysRefresh(p => !p);
91
96
  };
@@ -95,6 +100,9 @@ function BlockletStorageProvider({
95
100
  const refreshAutoBackup = () => {
96
101
  setAutoBackupRefresh(x => !x);
97
102
  };
103
+ const refreshAutoCheckUpdate = () => {
104
+ setAutoCheckUpdateRefresh(x => !x);
105
+ };
98
106
 
99
107
  /**
100
108
  * @description
@@ -200,6 +208,15 @@ function BlockletStorageProvider({
200
208
  });
201
209
  refreshAutoBackup();
202
210
  };
211
+ const updateAutoCheckUpdate = async value => {
212
+ await api.updateAutoCheckUpdate({
213
+ input: {
214
+ did: blocklet.meta.did,
215
+ autoCheckUpdate: value
216
+ }
217
+ });
218
+ refreshAutoCheckUpdate();
219
+ };
203
220
  return /*#__PURE__*/_jsx(Provider, {
204
221
  value: {
205
222
  spaceGateways,
@@ -211,6 +228,8 @@ function BlockletStorageProvider({
211
228
  spaceGatewaysLoading,
212
229
  spaceGatewayIsSelected,
213
230
  updateAutoBackup,
231
+ updateAutoCheckUpdate,
232
+ autoCheckUpdate,
214
233
  backupEndpoint,
215
234
  backupProgress,
216
235
  backups,
@@ -6,6 +6,18 @@ import Toast from '@arcblock/ux/lib/Toast';
6
6
  import { toSlotDomain, getBlockletUrlParams, getBlockletUrl } from '../util';
7
7
  import { fixBlocklet } from '../blocklet/util';
8
8
  import { useDomainsAccessibility } from './url-evaluation';
9
+ import parseProgress from '../util/parse-progress';
10
+ const excludeDownloadingStatus = {
11
+ waiting: 1,
12
+ installing: 1,
13
+ installed: 1,
14
+ starting: 1,
15
+ running: 1,
16
+ stopped: 1,
17
+ upgrading: 1,
18
+ restarting: 1,
19
+ deleting: 1
20
+ };
9
21
 
10
22
  // eslint-disable-next-line import/prefer-default-export
11
23
  export function useBlocklet({
@@ -104,6 +116,12 @@ export function useBlocklet({
104
116
  fixRuntime: 'merge-old',
105
117
  oldBlocklet
106
118
  });
119
+ if (oldBlocklet && excludeDownloadingStatus[data.status]) {
120
+ oldBlocklet?.children?.forEach(item => {
121
+ delete item.progress;
122
+ });
123
+ delete oldBlocklet.progress;
124
+ }
107
125
  return data;
108
126
  });
109
127
  setLoading(false);
@@ -137,6 +155,62 @@ export function useBlocklet({
137
155
  });
138
156
  }
139
157
  };
158
+ const handleProgress = data => {
159
+ if (!data || data.meta?.did !== blocklet?.meta?.did) {
160
+ return;
161
+ }
162
+ if (data.status === 'extracting') {
163
+ setBlocklet(oldBlocklet => {
164
+ if (!oldBlocklet) {
165
+ return oldBlocklet;
166
+ }
167
+ const children = oldBlocklet.children || [];
168
+ children.forEach(x => {
169
+ if (x.meta?.bundleDid === data.component?.did) {
170
+ x.status = data.status;
171
+ }
172
+ });
173
+
174
+ // 判断当前下载的 children 是否都处于 extracting
175
+ const extracting = children.every(x => {
176
+ if (excludeDownloadingStatus[x.status]) {
177
+ return true;
178
+ }
179
+ return x.status === 'extracting';
180
+ });
181
+ return {
182
+ ...oldBlocklet,
183
+ children,
184
+ status: extracting ? 'extracting' : oldBlocklet.status
185
+ };
186
+ });
187
+ }
188
+ if (data.status === 'downloading' && data.total) {
189
+ setBlocklet(oldBlocklet => {
190
+ if (!oldBlocklet) {
191
+ return oldBlocklet;
192
+ }
193
+
194
+ // keep largest files as progress
195
+ const children = oldBlocklet.children || [];
196
+ let progress = 100;
197
+ children.forEach(item => {
198
+ if (item.meta?.bundleDid === data.component?.did) {
199
+ item.progress = parseProgress(data.current, data.total);
200
+ item.status = data.status;
201
+ if (item.progress < progress) {
202
+ progress = item.progress;
203
+ }
204
+ }
205
+ });
206
+ return {
207
+ ...oldBlocklet,
208
+ children,
209
+ progress
210
+ };
211
+ });
212
+ }
213
+ };
140
214
  const refreshDomainStatus = () => {
141
215
  if (did && domainListStr) {
142
216
  client.checkDomains({
@@ -196,6 +270,7 @@ export function useBlocklet({
196
270
  useSubscription(BlockletEvents.installed, handleRefresh, [blocklet?.meta?.did, !disableSubscription]);
197
271
  useSubscription(BlockletEvents.upgraded, handleRefresh, [blocklet?.meta?.did, !disableSubscription]);
198
272
  useSubscription(BlockletEvents.updated, handleRefresh, [blocklet?.meta?.did, !disableSubscription]);
273
+ useSubscription(BlockletEvents.downloadBundleProgress, handleProgress, [blocklet?.meta?.did, !disableSubscription]);
199
274
  const data = blocklet ? merge(blocklet, {
200
275
  site: {
201
276
  domainAliases: blocklet.site.domainAliases.map(x => {
@@ -0,0 +1,33 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ import { useAsyncEffect } from 'ahooks';
3
+ import { useState } from 'react';
4
+ import { useBlockletContext } from '../contexts/blocklet';
5
+ import { useNodeContext } from '../contexts/node';
6
+ export function useAutoCheckUpdate({
7
+ deps
8
+ } = {
9
+ deps: []
10
+ }) {
11
+ /** @type {{ blocklet: import('@abtnode/client').BlockletState }} */
12
+ const {
13
+ blocklet
14
+ } = useBlockletContext();
15
+ /** @type {{api: import('@abtnode/client')}} */
16
+ const {
17
+ api
18
+ } = useNodeContext();
19
+ const [autoCheckUpdate, setAutoCheckUpdate] = useState(null);
20
+ useAsyncEffect(async () => {
21
+ if (blocklet?.meta?.did) {
22
+ const {
23
+ autoCheckUpdate: data
24
+ } = await api.getAutoCheckUpdate({
25
+ input: {
26
+ did: blocklet.meta.did
27
+ }
28
+ });
29
+ setAutoCheckUpdate(data);
30
+ }
31
+ }, [blocklet?.meta?.did].concat(deps));
32
+ return autoCheckUpdate;
33
+ }
@@ -0,0 +1,28 @@
1
+ import { useEffect } from 'react';
2
+ import Toast from '@arcblock/ux/lib/Toast';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
5
+ import useEventListen from './use-event-listen';
6
+ export function dispatchCheckUpdate() {
7
+ window.dispatchEvent(new CustomEvent('checkUpdate'));
8
+ }
9
+ export function useCheckUpdateByUrl(event) {
10
+ const navigate = useNavigate();
11
+ const {
12
+ t
13
+ } = useLocaleContext();
14
+ const handle = () => {
15
+ const params = new URLSearchParams(window.location.search);
16
+ if (params.get('checkUpdate')) {
17
+ params.delete('checkUpdate');
18
+ navigate(params.toString());
19
+ event();
20
+ Toast.success(t('notification.checkingForUpdates'));
21
+ }
22
+ };
23
+
24
+ // eslint-disable-next-line react-hooks/exhaustive-deps
25
+ useEffect(handle, []);
26
+ useEventListen('checkUpdate', handle);
27
+ }
28
+ export default useCheckUpdateByUrl;
@@ -0,0 +1,11 @@
1
+ import { useRef, useEffect } from 'react';
2
+ function useEventListen(key, callback) {
3
+ // reduce rerender
4
+ const ref = useRef(callback);
5
+ useEffect(() => {
6
+ const event = e => ref.current(e.detail);
7
+ window.addEventListener(key, event);
8
+ return () => window.removeEventListener(key, event);
9
+ }, [key]);
10
+ }
11
+ export default useEventListen;
package/es/locales/en.js CHANGED
@@ -509,6 +509,7 @@ export default {
509
509
  added: 'Added',
510
510
  waiting: 'Waiting',
511
511
  downloading: 'Downloading',
512
+ extracting: 'Extracting',
512
513
  installing: 'Installing',
513
514
  upgrading: 'Upgrading',
514
515
  installed: 'Installed',
@@ -841,6 +842,8 @@ export default {
841
842
  showRead: 'Show Read',
842
843
  markAllAsRead: 'Mark All as Read',
843
844
  markAsRead: 'Mark As Read',
845
+ proceed: 'Proceed',
846
+ checkingForUpdates: 'Checking for updates...',
844
847
  email: {
845
848
  enable: 'Enable email notification',
846
849
  from: 'Email sender',
@@ -1523,6 +1526,11 @@ export default {
1523
1526
  enabled: 'Automatic backup is on',
1524
1527
  disabled: 'Automatic backup is off'
1525
1528
  },
1529
+ autoCheckUpdate: {
1530
+ title: 'Automatically check for new version notification',
1531
+ enabled: 'Automatically check for new version is on',
1532
+ disabled: 'Automatically check for new version is off'
1533
+ },
1526
1534
  backupSuccessfully: 'Backup successfully',
1527
1535
  restore: 'Restore',
1528
1536
  restoreSuccessfully: 'Restore successfully',
package/es/locales/zh.js CHANGED
@@ -516,6 +516,7 @@ export default {
516
516
  added: '已添加',
517
517
  waiting: '等待中',
518
518
  downloading: '下载中',
519
+ extracting: '提取中',
519
520
  installing: '安装中',
520
521
  upgrading: '升级中',
521
522
  installed: '已安装',
@@ -844,6 +845,8 @@ export default {
844
845
  showRead: '显示已读',
845
846
  markAllAsRead: '全部标记为已读',
846
847
  markAsRead: '标记为已读',
848
+ proceed: '处理',
849
+ checkingForUpdates: '正在检查更新...',
847
850
  email: {
848
851
  enable: '是否开启 Email 通知',
849
852
  from: '发件人邮箱',
@@ -1528,6 +1531,11 @@ export default {
1528
1531
  enabled: '自动备份已开启',
1529
1532
  disabled: '自动备份已关闭'
1530
1533
  },
1534
+ autoCheckUpdate: {
1535
+ title: '自动检测新版本提醒',
1536
+ enabled: '自动检测新版本提醒已开启',
1537
+ disabled: '自动检测新版本提醒已关闭'
1538
+ },
1531
1539
  backupSuccessfully: '备份成功',
1532
1540
  restore: '还原',
1533
1541
  restoreSuccessfully: '还原成功',
@@ -22,6 +22,7 @@ import Permission from '../permission';
22
22
  import RelativeTime from '../relative-time';
23
23
  import { useNodeContext } from '../contexts/node';
24
24
  import { BlockletAdminRoles } from '../util';
25
+ import { dispatchCheckUpdate } from '../hooks/use-check-update-by-url';
25
26
  import { jsx as _jsx } from "react/jsx-runtime";
26
27
  import { jsxs as _jsxs } from "react/jsx-runtime";
27
28
  import { Fragment as _Fragment } from "react/jsx-runtime";
@@ -87,6 +88,12 @@ export default function NotificationsList({
87
88
  onHide(e);
88
89
  navigate(viewAllUrl);
89
90
  };
91
+ const handleCheckForUpdates = (event, action) => {
92
+ event.preventDefault();
93
+ event.stopPropagation();
94
+ navigate(action);
95
+ dispatchCheckUpdate();
96
+ };
90
97
  return /*#__PURE__*/_jsxs(Div, {
91
98
  children: [/*#__PURE__*/_jsxs(Typography, {
92
99
  component: "h3",
@@ -142,14 +149,30 @@ export default function NotificationsList({
142
149
  'text-overflow': 'ellipsis'
143
150
  },
144
151
  children: x.description
145
- }), /*#__PURE__*/_jsx(RelativeTime, {
146
- value: x.createdAt,
147
- locale: locale,
148
- style: {
149
- color: theme.palette.text.secondary,
150
- fontSize: '0.875rem',
151
- marginTop: '4px'
152
- }
152
+ }), /*#__PURE__*/_jsxs(Box, {
153
+ display: "flex",
154
+ flexDirection: "row",
155
+ justifyContent: "space-between",
156
+ alignItems: "center",
157
+ children: [/*#__PURE__*/_jsx(RelativeTime, {
158
+ value: x.createdAt,
159
+ locale: locale,
160
+ style: {
161
+ color: theme.palette.text.secondary,
162
+ fontSize: '0.875rem',
163
+ marginTop: '4px'
164
+ }
165
+ }), x.action && !x.read ? /*#__PURE__*/_jsx(Button, {
166
+ color: "primary",
167
+ variant: "outlined",
168
+ onPointerDown: event => {
169
+ event.stopPropagation();
170
+ event.preventDefault();
171
+ },
172
+ onClick: e => handleCheckForUpdates(e, x.action),
173
+ "data-cy": "check-for-updates",
174
+ children: t('notification.proceed')
175
+ }) : null]
153
176
  })]
154
177
  })
155
178
  })]
@@ -0,0 +1,10 @@
1
+ // 0~100
2
+ const parseProgress = (current, total) => {
3
+ const totalNumber = typeof total !== 'number' ? Number(total) : total;
4
+ if (Number.isNaN(totalNumber) || totalNumber <= 0) {
5
+ return 0;
6
+ }
7
+ const value = Math.floor(current / totalNumber * 100);
8
+ return Math.min(Math.max(value, 0), 100);
9
+ };
10
+ export default parseProgress;