saasufy-components 8.1.0 → 8.2.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/README.md +1 -0
- package/app-router.js +20 -2
- package/oauth-handler.js +7 -4
- package/oauth-link.js +16 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,6 +231,7 @@ If you want to make sure that some data (e.g. a config file) has been fully load
|
|
|
231
231
|
- `default-page`: The path/route of the default page to send the user to in case no routes are matched. Commonly used to point to a `/page-not-found` page. For the home page, it's typically recommended to create a page template with `route-path=""` instead.
|
|
232
232
|
- `debounce-delay`: The number of milliseconds to wait before changing the route. This can help to avoid multiple renders if the route changes rapidly (e.g. when doing hard redirects). Defaults to 100ms.
|
|
233
233
|
- `target-page`: A convenience attribute which can be used to programmatically change the `location.hash` in the address bar.
|
|
234
|
+
- `watch-origin-route`: If this attribute exists, state changes of an origin route (before redirects) will trigger a re-render.
|
|
234
235
|
- `force-render-paths`: An optional list of comma-separated paths to force a page render/re-render, regardless of whether or not the page has changed.
|
|
235
236
|
- `greedy-refresh`: If this attribute exists, the component will refresh itself whenever an attribute is set, even if that attribute's value did not change.
|
|
236
237
|
|
package/app-router.js
CHANGED
|
@@ -15,6 +15,7 @@ export class AppRouter extends SocketConsumer {
|
|
|
15
15
|
this.debounce = debouncer();
|
|
16
16
|
this.debounceDelay = DEFAULT_DEBOUNCE_DELAY;
|
|
17
17
|
this.lastPageState = null;
|
|
18
|
+
this.lastOriginPageState = null;
|
|
18
19
|
|
|
19
20
|
this.hashStartRegex = /^#/;
|
|
20
21
|
|
|
@@ -166,13 +167,16 @@ export class AppRouter extends SocketConsumer {
|
|
|
166
167
|
let { pageTemplate, route, regExp, params } = this.getMatchingPage(pagePath, 'path', true);
|
|
167
168
|
if (forceRenderPaths.includes(pagePath)) {
|
|
168
169
|
this.lastPageState = null;
|
|
170
|
+
this.lastOriginPageState = null;
|
|
169
171
|
}
|
|
172
|
+
let originRoute = route;
|
|
170
173
|
|
|
171
174
|
if (!pageTemplate) {
|
|
172
175
|
let defaultPage = this.getAttribute('default-page');
|
|
173
176
|
if (!defaultPage) {
|
|
174
177
|
routerViewport.innerHTML = '';
|
|
175
178
|
this.lastPageState = null;
|
|
179
|
+
this.lastOriginPageState = null;
|
|
176
180
|
return;
|
|
177
181
|
}
|
|
178
182
|
routeArgs = this.computeRouteArgs(pagePath, regExp, params);
|
|
@@ -185,8 +189,9 @@ export class AppRouter extends SocketConsumer {
|
|
|
185
189
|
};
|
|
186
190
|
|
|
187
191
|
let maxRedirects = Number(this.getAttribute('max-redirects') || DEFAULT_MAX_REDIRECTS);
|
|
188
|
-
|
|
192
|
+
let hasOriginPageChanged = false;
|
|
189
193
|
let redirectCount;
|
|
194
|
+
|
|
190
195
|
for (redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
|
|
191
196
|
let authRedirect = this.socket && this.socket.authState === 'authenticated' ?
|
|
192
197
|
pageTemplate.getAttribute('auth-redirect') : null;
|
|
@@ -212,6 +217,7 @@ export class AppRouter extends SocketConsumer {
|
|
|
212
217
|
routeArgs = this.computeRouteArgs(pagePath, regExp, params);
|
|
213
218
|
pagePath = this.substituteRouteAgs(authRedirect, routeArgs);
|
|
214
219
|
if (hardRedirect) {
|
|
220
|
+
this.lastOriginPageState = JSON.stringify({ route: originRoute, routeArgs });
|
|
215
221
|
this.replaceLocationHash(pagePath);
|
|
216
222
|
return;
|
|
217
223
|
}
|
|
@@ -224,6 +230,7 @@ export class AppRouter extends SocketConsumer {
|
|
|
224
230
|
routeArgs = this.computeRouteArgs(pagePath, regExp, params);
|
|
225
231
|
pagePath = this.substituteRouteAgs(noAuthRedirect, routeArgs);
|
|
226
232
|
if (hardRedirect) {
|
|
233
|
+
this.lastOriginPageState = JSON.stringify({ route: originRoute, routeArgs });
|
|
227
234
|
this.replaceLocationHash(pagePath);
|
|
228
235
|
return;
|
|
229
236
|
}
|
|
@@ -236,6 +243,7 @@ export class AppRouter extends SocketConsumer {
|
|
|
236
243
|
routeArgs = this.computeRouteArgs(pagePath, regExp, params);
|
|
237
244
|
pagePath = this.substituteRouteAgs(redirect, routeArgs);
|
|
238
245
|
if (hardRedirect) {
|
|
246
|
+
this.lastOriginPageState = JSON.stringify({ route: originRoute, routeArgs });
|
|
239
247
|
this.replaceLocationHash(pagePath);
|
|
240
248
|
return;
|
|
241
249
|
}
|
|
@@ -245,6 +253,13 @@ export class AppRouter extends SocketConsumer {
|
|
|
245
253
|
regExp = result.regExp;
|
|
246
254
|
params = result.params;
|
|
247
255
|
}
|
|
256
|
+
if (redirectCount === 0) {
|
|
257
|
+
let lastOriginPageState = JSON.stringify({ route: originRoute, routeArgs });
|
|
258
|
+
if (lastOriginPageState !== this.lastOriginPageState) {
|
|
259
|
+
hasOriginPageChanged = true;
|
|
260
|
+
}
|
|
261
|
+
this.lastOriginPageState = lastOriginPageState;
|
|
262
|
+
}
|
|
248
263
|
}
|
|
249
264
|
|
|
250
265
|
if (redirectCount > maxRedirects) {
|
|
@@ -264,7 +279,10 @@ export class AppRouter extends SocketConsumer {
|
|
|
264
279
|
routeArgs
|
|
265
280
|
});
|
|
266
281
|
|
|
267
|
-
|
|
282
|
+
let watchOriginRoute = this.hasAttribute('watch-origin-route');
|
|
283
|
+
let originRouteChanged = watchOriginRoute && hasOriginPageChanged;
|
|
284
|
+
|
|
285
|
+
if (pageState === this.lastPageState && !originRouteChanged && !force) return;
|
|
268
286
|
|
|
269
287
|
this.lastPageState = pageState;
|
|
270
288
|
|
package/oauth-handler.js
CHANGED
|
@@ -43,10 +43,13 @@ class OAuthHandler extends SocketConsumer {
|
|
|
43
43
|
return;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
let
|
|
46
|
+
let relevantStates = expectedOAuthState
|
|
47
|
+
.split(',')
|
|
48
|
+
.filter(expectedState => expectedState && expectedState.split('-')[0] === provider);
|
|
49
|
+
|
|
47
50
|
// Do not process. The state may have been set for a different provider
|
|
48
51
|
// which is handled on the same page.
|
|
49
|
-
if (
|
|
52
|
+
if (!relevantStates.length) return;
|
|
50
53
|
|
|
51
54
|
let codeParamName = this.getAttribute('code-param-name') || 'code';
|
|
52
55
|
let stateParamName = this.getAttribute('state-param-name') || 'state';
|
|
@@ -62,8 +65,8 @@ class OAuthHandler extends SocketConsumer {
|
|
|
62
65
|
let socket = this.getSocket();
|
|
63
66
|
|
|
64
67
|
history.replaceState({}, document.title, location.pathname);
|
|
65
|
-
|
|
66
|
-
if (
|
|
68
|
+
|
|
69
|
+
if (relevantStates.some(expectedState => expectedState === state)) {
|
|
67
70
|
try {
|
|
68
71
|
await socket.invoke('log-in-oauth', { provider, data: { code, ...extraData } });
|
|
69
72
|
} catch (error) {
|
package/oauth-link.js
CHANGED
|
@@ -75,8 +75,23 @@ class OAuthLink extends HTMLElement {
|
|
|
75
75
|
let useLocalStorage = this.hasAttribute('use-local-storage');
|
|
76
76
|
let stateSize = Number(this.getAttribute('state-size') || 20);
|
|
77
77
|
let storageKey = this.getAttribute('state-storage-key') || 'oauth.state';
|
|
78
|
+
|
|
79
|
+
let storage = useLocalStorage ? localStorage : sessionStorage;
|
|
80
|
+
let existingState = storage.getItem(storageKey);
|
|
81
|
+
let stateList;
|
|
82
|
+
if (existingState) {
|
|
83
|
+
stateList = existingState.split(',').filter(Boolean);
|
|
84
|
+
let maxStateCount = Number(this.getAttribute('max-state-count') ?? 20);
|
|
85
|
+
if (stateList.length >= maxStateCount) {
|
|
86
|
+
stateList.pop();
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
stateList = []
|
|
90
|
+
}
|
|
78
91
|
this.state = `${provider}-${generateRandomHexString(stateSize)}`;
|
|
79
|
-
|
|
92
|
+
stateList.unshift(this.state);
|
|
93
|
+
|
|
94
|
+
storage.setItem(storageKey, stateList.join(','));
|
|
80
95
|
|
|
81
96
|
this.shadowRoot.innerHTML = `
|
|
82
97
|
<slot name="item"></slot>
|