@scaleflex/widget-core 0.0.1
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/CHANGELOG.md +7324 -0
- package/LICENSE +21 -0
- package/README.md +1089 -0
- package/dist/style.css +190 -0
- package/dist/style.min.css +1 -0
- package/lib/Client.js +390 -0
- package/lib/Plugin.js +298 -0
- package/lib/Providers.js +112 -0
- package/lib/SassKeyRenewer.js +332 -0
- package/lib/_common.scss +240 -0
- package/lib/_utils.scss +38 -0
- package/lib/_variables.scss +64 -0
- package/lib/createStore.js +45 -0
- package/lib/defaultLocale.js +66 -0
- package/lib/hooks/index.js +7 -0
- package/lib/hooks/useContextMenu.js +42 -0
- package/lib/hooks/useContextMenuData.js +17 -0
- package/lib/hooks/useCore.js +10 -0
- package/lib/hooks/useModal.js +72 -0
- package/lib/hooks/useModalData.js +7 -0
- package/lib/hooks/usePlugin.js +20 -0
- package/lib/hooks/usePluginsType.js +18 -0
- package/lib/index.js +1918 -0
- package/lib/loggers.js +45 -0
- package/lib/slices/common.slice.js +90 -0
- package/lib/slices/index.js +12 -0
- package/lib/slices/info.slice.js +52 -0
- package/lib/slices/uploads.slice.js +294 -0
- package/lib/slices/user.slice.js +256 -0
- package/lib/style.scss +3 -0
- package/lib/supportsUploadProgress.js +42 -0
- package/lib/utils/calculateTotalEta.js +21 -0
- package/package.json +38 -0
- package/types/index.d.ts +301 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
6
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
+
import { createSlice, isAnyOf } from '@reduxjs/toolkit';
|
|
8
|
+
import checkPermissions from '@scaleflex/widget-utils/lib/checkPermissions';
|
|
9
|
+
import createThunk from '@scaleflex/widget-utils/lib/createThunk';
|
|
10
|
+
import { AUTH_STATE, PERMISSIONS, PLUGINS_IDS } from '@scaleflex/widget-utils/lib/constants';
|
|
11
|
+
import { slicePropName as coreCommonSliceName } from './common.slice';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The order of the code in slice file is:
|
|
15
|
+
* 1. initialState (*No* export needed)
|
|
16
|
+
* 2. automated thunks functions -- uses createThunk API-- (export needed)
|
|
17
|
+
* 3. createSlice (*No* export needed)
|
|
18
|
+
* 4. actions exports
|
|
19
|
+
* 5. selectors (export needed)
|
|
20
|
+
* 6. default reducer export
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
var slicePropName = 'user';
|
|
24
|
+
var sliceName = "".concat(PLUGINS_IDS.CORE, "/").concat(slicePropName);
|
|
25
|
+
var sassKeyExpireDuration = 1170000; // 19.5 minutes
|
|
26
|
+
|
|
27
|
+
var initialState = {
|
|
28
|
+
sassKey: '',
|
|
29
|
+
authState: AUTH_STATE.ANONYMOUS,
|
|
30
|
+
permissions: {},
|
|
31
|
+
// userCheckedPermissions
|
|
32
|
+
securityData: {},
|
|
33
|
+
info: {} // possible to be empty, most probably won't be empty in-case of login mode.
|
|
34
|
+
};
|
|
35
|
+
export var generateUserKey = createThunk(async function (_, thunkApi) {
|
|
36
|
+
var _userAuthInfo$key_val, _userAuthInfo$key_val2, _filerobot$getPlugin$, _filerobot$getPlugin;
|
|
37
|
+
var _thunkApi$extra = thunkApi.extra,
|
|
38
|
+
filerobot = _thunkApi$extra.filerobot,
|
|
39
|
+
apiClient = _thunkApi$extra.apiClient;
|
|
40
|
+
var url = "".concat(filerobot.opts.apiEndpointWithContainer, "/key/").concat(filerobot.opts.securityTemplateId);
|
|
41
|
+
|
|
42
|
+
// Retrieve sassKey from server through security template id
|
|
43
|
+
var userAuthInfo = await apiClient.get(url, {
|
|
44
|
+
signal: thunkApi.signal,
|
|
45
|
+
noDefaultHeaders: true
|
|
46
|
+
});
|
|
47
|
+
var userAuthState = await thunkApi.dispatch(fetchUserAuthState({
|
|
48
|
+
userSassKey: userAuthInfo.key,
|
|
49
|
+
skipDispatch: true
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
// Re-generate a new sassKey before the current one expires.
|
|
53
|
+
setTimeout(function () {
|
|
54
|
+
thunkApi.dispatch(generateUserKey());
|
|
55
|
+
}, (_userAuthInfo$key_val = (_userAuthInfo$key_val2 = userAuthInfo.key_validity) === null || _userAuthInfo$key_val2 === void 0 ? void 0 : _userAuthInfo$key_val2.expire_duration) !== null && _userAuthInfo$key_val !== void 0 ? _userAuthInfo$key_val : sassKeyExpireDuration);
|
|
56
|
+
filerobot.emit('sass-key-obtained', userAuthInfo.key);
|
|
57
|
+
var _ref = (_filerobot$getPlugin$ = (_filerobot$getPlugin = filerobot.getPlugin(PLUGINS_IDS.EXPLORER)) === null || _filerobot$getPlugin === void 0 ? void 0 : _filerobot$getPlugin.opts) !== null && _filerobot$getPlugin$ !== void 0 ? _filerobot$getPlugin$ : {},
|
|
58
|
+
useAssetsPicker = _ref.useAssetsPicker;
|
|
59
|
+
|
|
60
|
+
// Removed permissions from here, as we will always check the permissions in the userAuthState request.
|
|
61
|
+
return _objectSpread(_objectSpread({}, userAuthState), {}, {
|
|
62
|
+
useAssetsPicker: useAssetsPicker,
|
|
63
|
+
sassKey: userAuthInfo.key,
|
|
64
|
+
securityData: userAuthInfo
|
|
65
|
+
});
|
|
66
|
+
}, {
|
|
67
|
+
actionType: "".concat(sliceName, "/generateUserKey")
|
|
68
|
+
});
|
|
69
|
+
export var saveUserSassKey = createThunk(function (userSassKey, thunkApi) {
|
|
70
|
+
thunkApi.extra.filerobot.emit('sass-key-obtained', userSassKey);
|
|
71
|
+
var userSecurityData = selectUserSecurityData(thunkApi.getState());
|
|
72
|
+
return thunkApi.dispatch(userUpdated({
|
|
73
|
+
sassKey: userSassKey,
|
|
74
|
+
securityData: _objectSpread(_objectSpread({}, userSecurityData), {}, {
|
|
75
|
+
key: userSassKey
|
|
76
|
+
})
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
export var fetchUserAuthState = createThunk(async function () {
|
|
80
|
+
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
81
|
+
userSassKey = _ref2.userSassKey,
|
|
82
|
+
skipDispatch = _ref2.skipDispatch;
|
|
83
|
+
var thunkApi = arguments.length > 1 ? arguments[1] : undefined;
|
|
84
|
+
// We will have skipDispatch: true, when we call the fetchUserAuthState from the generateUserKey thunk,
|
|
85
|
+
// as we'll wait for the generateUserKey thunk & fetchUserAuthState thunk to finish then update the user state once,
|
|
86
|
+
// otherwise the user state should have skipDispatch: false, to update the user state after finishing its request.
|
|
87
|
+
if (skipDispatch) {
|
|
88
|
+
thunkApi.skipDispatch();
|
|
89
|
+
}
|
|
90
|
+
if (!userSassKey) {
|
|
91
|
+
thunkApi.skipDispatch();
|
|
92
|
+
return thunkApi.rejectWithValue('No sass key provided.');
|
|
93
|
+
}
|
|
94
|
+
var _thunkApi$extra2 = thunkApi.extra,
|
|
95
|
+
filerobot = _thunkApi$extra2.filerobot,
|
|
96
|
+
apiClient = _thunkApi$extra2.apiClient;
|
|
97
|
+
var _await$apiClient$get = await apiClient.get("".concat(filerobot.opts.apiEndpointWithContainer, "/v4/key/info?key=").concat(userSassKey), {
|
|
98
|
+
abortSignal: thunkApi.signal,
|
|
99
|
+
noDefaultHeaders: true
|
|
100
|
+
}),
|
|
101
|
+
isValidate = _await$apiClient$get.is_validate,
|
|
102
|
+
isUserInfoIncluded = _await$apiClient$get.is_user_info_included,
|
|
103
|
+
permissions = _await$apiClient$get.permissions;
|
|
104
|
+
var userAuthState = AUTH_STATE.ANONYMOUS;
|
|
105
|
+
if (isValidate) {
|
|
106
|
+
userAuthState = isUserInfoIncluded ? AUTH_STATE.AUTH : AUTH_STATE.UNAUTH;
|
|
107
|
+
}
|
|
108
|
+
// If no skipDispatch then emit the event, as this means no request is triggered for generating the sass key and sass key is provided from opts.
|
|
109
|
+
if (!skipDispatch) {
|
|
110
|
+
filerobot.emit('sass-key-obtained', userSassKey);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
sassKey: userSassKey,
|
|
114
|
+
authState: userAuthState,
|
|
115
|
+
permissions: checkPermissions(Array.isArray(permissions) && permissions.length > 0 ? permissions : filerobot.opts.userPermissions || permissions || [], Object.values(PERMISSIONS), true)
|
|
116
|
+
};
|
|
117
|
+
}, {
|
|
118
|
+
actionType: "".concat(sliceName, "/fetchUserAuthState"),
|
|
119
|
+
dismissGlobalAbort: true
|
|
120
|
+
});
|
|
121
|
+
export var userLogout = createThunk(function (_, thunkApi) {
|
|
122
|
+
var filerobot = thunkApi.extra.filerobot;
|
|
123
|
+
filerobot.emit('sass-key-obtained', undefined);
|
|
124
|
+
thunkApi.dispatch(userReset());
|
|
125
|
+
});
|
|
126
|
+
export var loginUserByCredentials = createThunk(async function (_ref3, thunkApi) {
|
|
127
|
+
var email = _ref3.email,
|
|
128
|
+
password = _ref3.password;
|
|
129
|
+
thunkApi.skipPendingDispatch();
|
|
130
|
+
var _thunkApi$extra3 = thunkApi.extra,
|
|
131
|
+
filerobot = _thunkApi$extra3.filerobot,
|
|
132
|
+
apiClient = _thunkApi$extra3.apiClient;
|
|
133
|
+
var token = filerobot.opts.container;
|
|
134
|
+
var response = await apiClient.postAdmin("login?token=".concat(token), {
|
|
135
|
+
data: {
|
|
136
|
+
login: {
|
|
137
|
+
email: email,
|
|
138
|
+
password: password
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
abortSignal: thunkApi.signal
|
|
142
|
+
});
|
|
143
|
+
var _ref4 = response || {},
|
|
144
|
+
userUuid = _ref4.user_uuid,
|
|
145
|
+
userEmail = _ref4.user_email,
|
|
146
|
+
sessionUuid = _ref4.session_uuid,
|
|
147
|
+
userPhotoUri = _ref4.user_photo_uri,
|
|
148
|
+
keychain = _ref4.keychain,
|
|
149
|
+
userName = _ref4.user_name;
|
|
150
|
+
// TODO: keyDuration is not supported from BE but added to be considered from their side once they add.
|
|
151
|
+
var _ref5 = (keychain === null || keychain === void 0 ? void 0 : keychain.tokens) || {},
|
|
152
|
+
userSassKey = _ref5.airstore_key,
|
|
153
|
+
permissions = _ref5.permissions;
|
|
154
|
+
if (userSassKey) {
|
|
155
|
+
filerobot.emit('sass-key-obtained', userSassKey);
|
|
156
|
+
if (filerobot.opts.sassKeyRenewerEnabled) {
|
|
157
|
+
// Send renew sass key request each hour
|
|
158
|
+
filerobot.sassKeyRenewer.subscribe();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Expire the sass key if the duration is not 0, then the login page will show again.
|
|
162
|
+
// if (keyDuration !== 0) { // DEPRECATED! Not necessary according to new SassKeyRenewer logic
|
|
163
|
+
// setTimeout(() => {
|
|
164
|
+
// thunkApi.dispatch(userLogout())
|
|
165
|
+
// }, keyDuration)
|
|
166
|
+
// }
|
|
167
|
+
} else {
|
|
168
|
+
return thunkApi.rejectWithValue(new Error('No sass key generated, please try again later.'));
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
sassKey: userSassKey,
|
|
172
|
+
authState: AUTH_STATE.AUTH,
|
|
173
|
+
permissions: checkPermissions(Array.isArray(permissions) && permissions.length > 0 ? permissions : filerobot.opts.userPermissions || permissions || [], Object.values(PERMISSIONS), true),
|
|
174
|
+
info: {
|
|
175
|
+
uuid: userUuid,
|
|
176
|
+
email: userEmail,
|
|
177
|
+
name: userName,
|
|
178
|
+
photo_uri: userPhotoUri,
|
|
179
|
+
sessionUuid: sessionUuid
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}, {
|
|
183
|
+
actionType: "".concat(sliceName, "/loginUserByCredentials")
|
|
184
|
+
});
|
|
185
|
+
var userSlice = createSlice({
|
|
186
|
+
name: sliceName,
|
|
187
|
+
initialState: initialState,
|
|
188
|
+
reducers: {
|
|
189
|
+
userUpdated: function userUpdated(state, action) {
|
|
190
|
+
var newUserData = action.payload;
|
|
191
|
+
return _objectSpread(_objectSpread({}, state), newUserData);
|
|
192
|
+
},
|
|
193
|
+
userReset: function userReset(state) {
|
|
194
|
+
return _objectSpread(_objectSpread({}, state), initialState);
|
|
195
|
+
},
|
|
196
|
+
userInfoUpdated: function userInfoUpdated(state, action) {
|
|
197
|
+
return _objectSpread(_objectSpread({}, state), {}, {
|
|
198
|
+
info: _objectSpread(_objectSpread({}, state.info || {}), action.payload || {})
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
extraReducers: function extraReducers(builder) {
|
|
203
|
+
builder.addCase(generateUserKey.fulfilled, function (state, action) {
|
|
204
|
+
return _objectSpread(_objectSpread({}, state), action.payload);
|
|
205
|
+
}).addMatcher(isAnyOf(fetchUserAuthState.fulfilled, loginUserByCredentials.fulfilled), function (state, action) {
|
|
206
|
+
return _objectSpread(_objectSpread({}, state), action.payload);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
var _userSlice$actions = userSlice.actions,
|
|
211
|
+
userUpdated = _userSlice$actions.userUpdated,
|
|
212
|
+
userInfoUpdated = _userSlice$actions.userInfoUpdated,
|
|
213
|
+
userReset = _userSlice$actions.userReset;
|
|
214
|
+
export { userUpdated, userInfoUpdated, userReset };
|
|
215
|
+
export var selectUser = function selectUser(state) {
|
|
216
|
+
return state[PLUGINS_IDS.CORE][slicePropName];
|
|
217
|
+
};
|
|
218
|
+
export var selectUserPermissions = function selectUserPermissions(state) {
|
|
219
|
+
return selectUser(state).permissions;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// `isDev` is used for enabling any feature requires user auth on dev. environment (in-case it is needed for some checking/improvements) and anyways it won't work from BE side as it requires the user auth.
|
|
223
|
+
export var selectIsUserAuth = function selectIsUserAuth(state) {
|
|
224
|
+
var _state$PLUGINS_IDS$CO, _state$PLUGINS_IDS$CO2;
|
|
225
|
+
return ((_state$PLUGINS_IDS$CO = state[PLUGINS_IDS.CORE]) === null || _state$PLUGINS_IDS$CO === void 0 ? void 0 : (_state$PLUGINS_IDS$CO2 = _state$PLUGINS_IDS$CO[coreCommonSliceName]) === null || _state$PLUGINS_IDS$CO2 === void 0 ? void 0 : _state$PLUGINS_IDS$CO2.isDevEnv) || selectUser(state).authState === AUTH_STATE.AUTH;
|
|
226
|
+
};
|
|
227
|
+
export var selectUserSecurityData = function selectUserSecurityData(state) {
|
|
228
|
+
return selectUser(state).securityData;
|
|
229
|
+
};
|
|
230
|
+
export var selectUserSassKey = function selectUserSassKey(state) {
|
|
231
|
+
return selectUser(state).sassKey;
|
|
232
|
+
};
|
|
233
|
+
export var selectUserInfo = function selectUserInfo(state) {
|
|
234
|
+
return selectUser(state).info;
|
|
235
|
+
};
|
|
236
|
+
export var selectUserSessionUuid = function selectUserSessionUuid(state) {
|
|
237
|
+
return selectUserInfo(state).sessionUuid;
|
|
238
|
+
};
|
|
239
|
+
export var selectIsUserPermittedTo = function selectIsUserPermittedTo(state, permissionToCheck) {
|
|
240
|
+
var userMustAuth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
241
|
+
var userPermissions = selectUserPermissions(state);
|
|
242
|
+
if (userMustAuth) {
|
|
243
|
+
var isUserAuth = selectIsUserAuth(state);
|
|
244
|
+
if (!isUserAuth) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return Boolean(userPermissions[permissionToCheck]);
|
|
249
|
+
};
|
|
250
|
+
export var selectIsUserPermittedToShare = function selectIsUserPermittedToShare(state) {
|
|
251
|
+
return selectIsUserPermittedTo(state, PERMISSIONS.OBJECTS_SHARE_MANAGE, true);
|
|
252
|
+
};
|
|
253
|
+
export var selectIsUserPermittedToDisplayFile = function selectIsUserPermittedToDisplayFile(state) {
|
|
254
|
+
return selectIsUserPermittedTo(state, PERMISSIONS.DISPLAY);
|
|
255
|
+
};
|
|
256
|
+
export default userSlice.reducer;
|
package/lib/style.scss
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
4
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
5
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
+
// Edge 15.x does not fire 'progress' events on uploads.
|
|
8
|
+
// See https://github.com/transloadit/filerobot/issues/945
|
|
9
|
+
// And https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12224510/
|
|
10
|
+
export default function supportsUploadProgress(userAgent) {
|
|
11
|
+
// Allow passing in userAgent for tests
|
|
12
|
+
if (userAgent == null) {
|
|
13
|
+
userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : null;
|
|
14
|
+
}
|
|
15
|
+
// Assume it works because basically everything supports progress events.
|
|
16
|
+
if (!userAgent) return true;
|
|
17
|
+
var m = /Edge\/(\d+\.\d+)/.exec(userAgent);
|
|
18
|
+
if (!m) return true;
|
|
19
|
+
var edgeVersion = m[1];
|
|
20
|
+
var _edgeVersion$split = edgeVersion.split('.'),
|
|
21
|
+
_edgeVersion$split2 = _slicedToArray(_edgeVersion$split, 2),
|
|
22
|
+
major = _edgeVersion$split2[0],
|
|
23
|
+
minor = _edgeVersion$split2[1];
|
|
24
|
+
major = parseInt(major, 10);
|
|
25
|
+
minor = parseInt(minor, 10);
|
|
26
|
+
|
|
27
|
+
// Worked before:
|
|
28
|
+
// Edge 40.15063.0.0
|
|
29
|
+
// Microsoft EdgeHTML 15.15063
|
|
30
|
+
if (major < 15 || major === 15 && minor < 15063) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Fixed in:
|
|
35
|
+
// Microsoft EdgeHTML 18.18218
|
|
36
|
+
if (major > 18 || major === 18 && minor >= 18218) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// other versions don't work.
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import getBytesRemaining from '@scaleflex/widget-utils/lib/getBytesRemaining';
|
|
2
|
+
import getSpeed from '@scaleflex/widget-utils/lib/getSpeed';
|
|
3
|
+
var getTotalSpeed = function getTotalSpeed(files) {
|
|
4
|
+
return files.reduce(function (total, file) {
|
|
5
|
+
return total + getSpeed(file.progress);
|
|
6
|
+
}, 0);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// Eta (ETA) = Estimated Time of Arrival to finish operations in seconds
|
|
10
|
+
var calculateTotalEta = function calculateTotalEta(files) {
|
|
11
|
+
var totalSpeed = getTotalSpeed(files);
|
|
12
|
+
if (totalSpeed === 0) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
var totalBytesRemaining = getBytesRemaining(files);
|
|
16
|
+
if (totalBytesRemaining && totalSpeed) {
|
|
17
|
+
// ETA = remainingDistance/Speed
|
|
18
|
+
return Math.round(totalBytesRemaining / totalSpeed * 10) / 10;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
export default calculateTotalEta;
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scaleflex/widget-core",
|
|
3
|
+
"description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, Box, One Drive, S3 and more.",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"style": "dist/style.min.css",
|
|
8
|
+
"types": "types/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"/dist",
|
|
11
|
+
"/lib",
|
|
12
|
+
"/types"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@reduxjs/toolkit": "^1.9.5",
|
|
19
|
+
"@scaleflex/icons": "^3.0.0-beta.11",
|
|
20
|
+
"@scaleflex/ui": "^3.0.0-beta.11",
|
|
21
|
+
"@scaleflex/widget-utils": "^0.0.1",
|
|
22
|
+
"@transloadit/prettier-bytes": "0.1.0",
|
|
23
|
+
"cuid": "^3.0.0",
|
|
24
|
+
"lodash.throttle": "^4.1.1",
|
|
25
|
+
"mime-match": "^1.0.2",
|
|
26
|
+
"namespace-emitter": "^2.0.1",
|
|
27
|
+
"react-redux": "^8.1.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"react": "^19.0.0",
|
|
31
|
+
"react-dom": "^19.0.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">=19.0.0",
|
|
35
|
+
"react-dom": ">=19.0.0"
|
|
36
|
+
},
|
|
37
|
+
"gitHead": "64ea82e745b7deda36d6794863350e6671e9010d"
|
|
38
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
const FilerobotUtils = require("@scaleflex/widget-utils");
|
|
2
|
+
|
|
3
|
+
declare module Filerobot {
|
|
4
|
+
// Utility types
|
|
5
|
+
type OmitKey<T, Key> = Pick<T, Exclude<keyof T, Key>>;
|
|
6
|
+
|
|
7
|
+
// These are defined in @scaleflex/widget-utils instead of core so it can be used there without creating import cycles
|
|
8
|
+
export type FilerobotFile<
|
|
9
|
+
TMeta extends IndexedObject<any> = {},
|
|
10
|
+
TBody extends IndexedObject<any> = {}
|
|
11
|
+
> = FilerobotUtils.FilerobotFile<TMeta, TBody>;
|
|
12
|
+
export type Store = FilerobotUtils.Store;
|
|
13
|
+
export type InternalMetadata = FilerobotUtils.InternalMetadata;
|
|
14
|
+
|
|
15
|
+
interface IndexedObject<T> {
|
|
16
|
+
[key: string]: T;
|
|
17
|
+
[key: number]: T;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface UploadedFilerobotFile<TMeta, TBody>
|
|
21
|
+
extends FilerobotFile<TMeta, TBody> {
|
|
22
|
+
uploadURL: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface FailedFilerobotFile<TMeta, TBody>
|
|
26
|
+
extends FilerobotFile<TMeta, TBody> {
|
|
27
|
+
error: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Replace the `meta` property type with one that allows omitting internal metadata; addFile() will add that
|
|
31
|
+
type FilerobotFileWithoutMeta<TMeta, TBody> = OmitKey<
|
|
32
|
+
FilerobotFile<TMeta, TBody>,
|
|
33
|
+
"meta"
|
|
34
|
+
>;
|
|
35
|
+
interface AddFileOptions<
|
|
36
|
+
TMeta = IndexedObject<any>,
|
|
37
|
+
TBody = IndexedObject<any>
|
|
38
|
+
> extends Partial<FilerobotFileWithoutMeta<TMeta, TBody>> {
|
|
39
|
+
// `.data` is the only required property here.
|
|
40
|
+
data: Blob | File;
|
|
41
|
+
meta?: Partial<InternalMetadata> & TMeta;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface PluginOptions {
|
|
45
|
+
id?: string;
|
|
46
|
+
}
|
|
47
|
+
interface DefaultPluginOptions extends PluginOptions {
|
|
48
|
+
[prop: string]: any;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type PluginTarget = string | Element | typeof Plugin;
|
|
52
|
+
|
|
53
|
+
class Plugin<TOptions extends PluginOptions = DefaultPluginOptions> {
|
|
54
|
+
id: string;
|
|
55
|
+
filerobot: Filerobot;
|
|
56
|
+
type: string;
|
|
57
|
+
constructor(filerobot: Filerobot, opts?: TOptions);
|
|
58
|
+
setOptions(update: Partial<TOptions>): void;
|
|
59
|
+
getPluginState(): object;
|
|
60
|
+
setPluginState(update: IndexedObject<any>): object;
|
|
61
|
+
update(state?: object): void;
|
|
62
|
+
mount(target: PluginTarget, plugin: typeof Plugin): void;
|
|
63
|
+
render(state: object): void;
|
|
64
|
+
addTarget<TPlugin extends Plugin>(plugin: TPlugin): void;
|
|
65
|
+
unmount(): void;
|
|
66
|
+
install(): void;
|
|
67
|
+
uninstall(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type LocaleStrings<TNames extends string> = {
|
|
71
|
+
[K in TNames]?: string | { [n: number]: string };
|
|
72
|
+
};
|
|
73
|
+
interface Locale<TNames extends string = string> {
|
|
74
|
+
strings: LocaleStrings<TNames>;
|
|
75
|
+
pluralize?: (n: number) => number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface Restrictions {
|
|
79
|
+
maxFileSize?: number | null;
|
|
80
|
+
maxNumberOfFiles?: number | null;
|
|
81
|
+
minNumberOfFiles?: number | null;
|
|
82
|
+
allowedFileTypes?: string[] | null;
|
|
83
|
+
maxItemsSizeForCompression: number | null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface FilerobotOptions<TMeta extends IndexedObject<any> = {}> {
|
|
87
|
+
container: string;
|
|
88
|
+
securityTemplateId?: string;
|
|
89
|
+
sassKey?: string;
|
|
90
|
+
dev?: boolean;
|
|
91
|
+
userInfo?: {
|
|
92
|
+
email: string;
|
|
93
|
+
name: string;
|
|
94
|
+
photo_uri: string;
|
|
95
|
+
uuid: string;
|
|
96
|
+
};
|
|
97
|
+
autoProceed?: boolean;
|
|
98
|
+
allowMultipleUploads?: boolean;
|
|
99
|
+
debug?: boolean;
|
|
100
|
+
restrictions?: Restrictions;
|
|
101
|
+
meta?: TMeta;
|
|
102
|
+
onBeforeFileAdded?: (
|
|
103
|
+
currentFile: FilerobotFile<TMeta>,
|
|
104
|
+
files: { [key: string]: FilerobotFile<TMeta> }
|
|
105
|
+
) => FilerobotFile<TMeta> | boolean | undefined;
|
|
106
|
+
onBeforeUpload?: (files: {
|
|
107
|
+
[key: string]: FilerobotFile<TMeta>;
|
|
108
|
+
}) => { [key: string]: FilerobotFile<TMeta> } | boolean;
|
|
109
|
+
locale?: Locale;
|
|
110
|
+
store?: Store;
|
|
111
|
+
userPermissions?: string[];
|
|
112
|
+
userFoldersScope?: string[];
|
|
113
|
+
apiEndpoint?: string;
|
|
114
|
+
skipApiEndpointToken?: boolean;
|
|
115
|
+
adminApiEndpoint?: string;
|
|
116
|
+
shareApiEndpoint?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface UploadResult<
|
|
120
|
+
TMeta extends IndexedObject<any> = {},
|
|
121
|
+
TBody extends IndexedObject<any> = {}
|
|
122
|
+
> {
|
|
123
|
+
successful: UploadedFilerobotFile<TMeta, TBody>[];
|
|
124
|
+
failed: FailedFilerobotFile<TMeta, TBody>[];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface State<
|
|
128
|
+
TMeta extends IndexedObject<any> = {},
|
|
129
|
+
TBody extends IndexedObject<any> = {}
|
|
130
|
+
> extends IndexedObject<any> {
|
|
131
|
+
capabilities?: { resumableUploads?: boolean };
|
|
132
|
+
currentUploads: {};
|
|
133
|
+
error?: string;
|
|
134
|
+
files: {
|
|
135
|
+
[key: string]:
|
|
136
|
+
| UploadedFilerobotFile<TMeta, TBody>
|
|
137
|
+
| FailedFilerobotFile<TMeta, TBody>;
|
|
138
|
+
};
|
|
139
|
+
info?: {
|
|
140
|
+
isHidden: boolean;
|
|
141
|
+
type: string;
|
|
142
|
+
message: string;
|
|
143
|
+
details: string;
|
|
144
|
+
};
|
|
145
|
+
plugins?: IndexedObject<any>;
|
|
146
|
+
totalProgress: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
type LogLevel = "info" | "warning" | "error";
|
|
150
|
+
|
|
151
|
+
/** Enable the old, untyped `filerobot.use()` signature. */
|
|
152
|
+
type LooseTypes = "loose";
|
|
153
|
+
/** Disable the old, untyped `filerobot.use()` signature. */
|
|
154
|
+
type StrictTypes = "strict";
|
|
155
|
+
type TypeChecking = LooseTypes | StrictTypes;
|
|
156
|
+
|
|
157
|
+
// This hack accepts _any_ string for `Event`, but also tricks VSCode and friends into providing autocompletions
|
|
158
|
+
// for the names listed. https://github.com/microsoft/TypeScript/issues/29729#issuecomment-505826972
|
|
159
|
+
type LiteralUnion<T extends U, U = string> = T | (U & {});
|
|
160
|
+
type Event = LiteralUnion<
|
|
161
|
+
| "file-added"
|
|
162
|
+
| "file-removed"
|
|
163
|
+
| "upload"
|
|
164
|
+
| "upload-progress"
|
|
165
|
+
| "upload-success"
|
|
166
|
+
| "complete"
|
|
167
|
+
| "error"
|
|
168
|
+
| "upload-error"
|
|
169
|
+
| "upload-retry"
|
|
170
|
+
| "info-visible"
|
|
171
|
+
| "info-hidden"
|
|
172
|
+
| "cancel-uploads"
|
|
173
|
+
| "restriction-failed"
|
|
174
|
+
| "reset-progress"
|
|
175
|
+
>;
|
|
176
|
+
|
|
177
|
+
type UploadHandler = (fileIDs: string[]) => Promise<void>;
|
|
178
|
+
|
|
179
|
+
class Filerobot<TUseStrictTypes extends TypeChecking = TypeChecking> {
|
|
180
|
+
constructor(opts?: FilerobotOptions);
|
|
181
|
+
on<TMeta extends IndexedObject<any> = {}>(
|
|
182
|
+
event: "upload-success",
|
|
183
|
+
callback: (
|
|
184
|
+
file: FilerobotFile<TMeta>,
|
|
185
|
+
body: any,
|
|
186
|
+
uploadURL: string
|
|
187
|
+
) => void
|
|
188
|
+
): this;
|
|
189
|
+
on<TMeta extends IndexedObject<any> = {}>(
|
|
190
|
+
event: "complete",
|
|
191
|
+
callback: (result: UploadResult<TMeta>) => void
|
|
192
|
+
): this;
|
|
193
|
+
on(event: Event, callback: (...args: any[]) => void): this;
|
|
194
|
+
off(event: Event, callback: (...args: any[]) => void): this;
|
|
195
|
+
/**
|
|
196
|
+
* For use by plugins only.
|
|
197
|
+
*/
|
|
198
|
+
emit(event: Event, ...args: any[]): void;
|
|
199
|
+
updateAll(state: object): void;
|
|
200
|
+
setOptions(update: Partial<FilerobotOptions>): void;
|
|
201
|
+
setState(patch: object): void;
|
|
202
|
+
getState<TMeta extends IndexedObject<any> = {}>(): State<TMeta>;
|
|
203
|
+
readonly state: State;
|
|
204
|
+
addPreProcessor(fn: UploadHandler): void;
|
|
205
|
+
removePreProcessor(fn: UploadHandler): void;
|
|
206
|
+
addPostProcessor(fn: UploadHandler): void;
|
|
207
|
+
removePostProcessor(fn: UploadHandler): void;
|
|
208
|
+
addFilerobot(fn: UploadHandler): void;
|
|
209
|
+
removeFilerobot(fn: UploadHandler): void;
|
|
210
|
+
setMeta<TMeta extends IndexedObject<any> = {}>(data: TMeta): void;
|
|
211
|
+
setFileMeta<TMeta extends IndexedObject<any> = {}>(
|
|
212
|
+
fileID: string,
|
|
213
|
+
data: TMeta
|
|
214
|
+
): void;
|
|
215
|
+
getFile<
|
|
216
|
+
TMeta extends IndexedObject<any> = {},
|
|
217
|
+
TBody extends IndexedObject<any> = {}
|
|
218
|
+
>(fileID: string): FilerobotFile<TMeta, TBody>;
|
|
219
|
+
getFiles<
|
|
220
|
+
TMeta extends IndexedObject<any> = {},
|
|
221
|
+
TBody extends IndexedObject<any> = {}
|
|
222
|
+
>(): Array<FilerobotFile<TMeta, TBody>>;
|
|
223
|
+
addFile<TMeta extends IndexedObject<any> = {}>(
|
|
224
|
+
file: AddFileOptions<TMeta>
|
|
225
|
+
): void;
|
|
226
|
+
removeFile(fileID: string): void;
|
|
227
|
+
pauseResume(fileID: string): void;
|
|
228
|
+
cancelUploads(): void;
|
|
229
|
+
retryUpload<TMeta extends IndexedObject<any> = {}>(
|
|
230
|
+
fileID: string
|
|
231
|
+
): Promise<UploadResult<TMeta>>;
|
|
232
|
+
pauseAll(): void;
|
|
233
|
+
resumeAll(): void;
|
|
234
|
+
retryAll<TMeta extends IndexedObject<any> = {}>(): Promise<
|
|
235
|
+
UploadResult<TMeta>
|
|
236
|
+
>;
|
|
237
|
+
cancelAll(): void;
|
|
238
|
+
getId(): string;
|
|
239
|
+
/**
|
|
240
|
+
* Add a plugin to this Filerobot instance.
|
|
241
|
+
*/
|
|
242
|
+
use<TOptions, TInstance extends Plugin<TOptions>>(
|
|
243
|
+
pluginClass: new (filerobot: this, opts: TOptions) => TInstance,
|
|
244
|
+
opts?: TOptions
|
|
245
|
+
): this;
|
|
246
|
+
/**
|
|
247
|
+
* Fallback `.use()` overload with unchecked plugin options.
|
|
248
|
+
*
|
|
249
|
+
* This does not validate that the options you pass in are correct.
|
|
250
|
+
* We recommend disabling this overload by using the `Filerobot<Filerobot.StrictTypes>` type, instead of the plain `Filerobot` type, to enforce strict typechecking.
|
|
251
|
+
* This overload will be removed in Filerobot 2.0.
|
|
252
|
+
*/
|
|
253
|
+
use(
|
|
254
|
+
pluginClass: TUseStrictTypes extends StrictTypes
|
|
255
|
+
? never
|
|
256
|
+
: new (filerobot: this, opts: any) => Plugin<any>,
|
|
257
|
+
opts?: object
|
|
258
|
+
): this;
|
|
259
|
+
getPlugin(name: string): Plugin;
|
|
260
|
+
iteratePlugins(callback: (plugin: Plugin) => void): void;
|
|
261
|
+
removePlugin(instance: Plugin): void;
|
|
262
|
+
close(): void;
|
|
263
|
+
info(
|
|
264
|
+
message: string | { message: string; details: string },
|
|
265
|
+
type?: LogLevel,
|
|
266
|
+
duration?: number
|
|
267
|
+
): void;
|
|
268
|
+
hideInfo(): void;
|
|
269
|
+
log(msg: string, type?: LogLevel): void;
|
|
270
|
+
/**
|
|
271
|
+
* Obsolete: do not use. This method does nothing and will be removed in a future release.
|
|
272
|
+
*/
|
|
273
|
+
run(): this;
|
|
274
|
+
restore<TMeta extends IndexedObject<any> = {}>(
|
|
275
|
+
uploadID: string
|
|
276
|
+
): Promise<UploadResult<TMeta>>;
|
|
277
|
+
addResultData(uploadID: string, data: object): void;
|
|
278
|
+
upload<TMeta extends IndexedObject<any> = {}>(): Promise<
|
|
279
|
+
UploadResult<TMeta>
|
|
280
|
+
>;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Create a filerobot instance.
|
|
286
|
+
*
|
|
287
|
+
* By default, Filerobot's `.use(Plugin, options)` method uses loose type checking.
|
|
288
|
+
* In Filerobot 2.0, the `.use()` method will get a stricter type signature. You can enable strict type checking of plugin classes and their options today by using:
|
|
289
|
+
* ```ts
|
|
290
|
+
* const filerobot = Filerobot<Filerobot.StrictTypes>()
|
|
291
|
+
* ```
|
|
292
|
+
* Make sure to also declare any variables and class properties with the `StrictTypes` parameter:
|
|
293
|
+
* ```ts
|
|
294
|
+
* private filerobot: Filerobot<Filerobot.StrictTypes>;
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
declare function Filerobot<
|
|
298
|
+
TUseStrictTypes extends Filerobot.TypeChecking = Filerobot.TypeChecking
|
|
299
|
+
>(opts?: Filerobot.FilerobotOptions): Filerobot.Filerobot<TUseStrictTypes>;
|
|
300
|
+
|
|
301
|
+
export = Filerobot;
|