@wtfalch/desktop 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +257 -2
- package/package.json +38 -4
- package/src/access.mjs +90 -0
- package/src/check-origin.mjs +42 -0
- package/src/company.mjs +51 -0
- package/src/config.mjs +96 -0
- package/src/deep-link.mjs +69 -0
- package/src/index.mjs +15 -0
- package/src/lifecycle.mjs +20 -0
- package/src/login.mjs +215 -0
- package/src/menu.mjs +28 -0
- package/src/resolve.mjs +105 -0
- package/src/security.mjs +96 -0
- package/src/shell.mjs +471 -0
- package/src/strategies/cookie-handoff.mjs +79 -0
- package/src/updater.mjs +60 -0
- package/src/welcome-dir.mjs +5 -0
- package/src/window-state.mjs +39 -0
- package/welcome/fonts/OFL-bricolage-grotesque.txt +93 -0
- package/welcome/fonts/OFL-figtree.txt +93 -0
- package/welcome/fonts/bricolage-grotesque-latin-wght-normal.woff2 +0 -0
- package/welcome/fonts/figtree-latin-wght-normal.woff2 +0 -0
- package/welcome/sticker.svg +1 -0
- package/welcome/welcome-preload.cjs +10 -0
- package/welcome/welcome.css +354 -0
- package/welcome/welcome.html +56 -0
- package/welcome/welcome.js +176 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// The first-launch screen. Four phases: ask (type something), checking, found
|
|
2
|
+
// ("Sign in to <name>") and waiting (the browser has opened). Main answers
|
|
3
|
+
// resolveEmail/resolveAddress(input, start) with { name, host, code } for an app that
|
|
4
|
+
// exists, or { notFound: true }; start=true also opens the browser. Cancel is
|
|
5
|
+
// the #cancel-* fragment, which main watches for. Main reloads this page with a
|
|
6
|
+
// notice in the fragment (#failed, #ended, #offline:<name>); #retry-* asks main
|
|
7
|
+
// to try the company again. For a fixed origin (config.fixed) there is no email
|
|
8
|
+
// screen: the page starts in "found" with the app's own name, #signin-* asks main
|
|
9
|
+
// to start (or restart) the sign-in, and main reloads the page as #waiting:<code>
|
|
10
|
+
// once the browser has opened. Server text is only ever textContent.
|
|
11
|
+
const config = window.shellWelcome.config;
|
|
12
|
+
const fixed = config.fixed === true;
|
|
13
|
+
document.title = config.appName;
|
|
14
|
+
for (const mark of document.querySelectorAll('.wm')) mark.firstChild.textContent = config.appName;
|
|
15
|
+
const $ = (id) => document.getElementById(id);
|
|
16
|
+
const els = {
|
|
17
|
+
title: $('title'),
|
|
18
|
+
sub: $('sub'),
|
|
19
|
+
form: $('form'),
|
|
20
|
+
label: $('label'),
|
|
21
|
+
field: $('field'),
|
|
22
|
+
error: $('error'),
|
|
23
|
+
pair: $('pair'),
|
|
24
|
+
code: $('code'),
|
|
25
|
+
go: $('go'),
|
|
26
|
+
goText: $('go-text'),
|
|
27
|
+
back: $('back'),
|
|
28
|
+
cancel: $('cancel'),
|
|
29
|
+
};
|
|
30
|
+
const modes = {
|
|
31
|
+
email: {
|
|
32
|
+
label: 'Email',
|
|
33
|
+
placeholder: 'you@company.com',
|
|
34
|
+
type: 'email',
|
|
35
|
+
call: (...args) => window.shellWelcome.resolveEmail(...args),
|
|
36
|
+
},
|
|
37
|
+
address: {
|
|
38
|
+
label: `Your company's ${config.appName} address`,
|
|
39
|
+
placeholder: `${config.hostPrefix}.company.com`,
|
|
40
|
+
type: 'text',
|
|
41
|
+
call: (...args) => window.shellWelcome.resolveAddress(...args),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
let mode = 'email';
|
|
45
|
+
const idle = fixed ? 'found' : 'ask';
|
|
46
|
+
let phase = idle;
|
|
47
|
+
let name = fixed ? config.appName : '';
|
|
48
|
+
let host = fixed ? config.host : '';
|
|
49
|
+
let code = '';
|
|
50
|
+
const notice = location.hash.slice(1);
|
|
51
|
+
if (notice.startsWith('waiting:')) {
|
|
52
|
+
phase = 'waiting';
|
|
53
|
+
code = notice.slice(8);
|
|
54
|
+
}
|
|
55
|
+
let error =
|
|
56
|
+
notice === 'failed'
|
|
57
|
+
? 'Sign-in did not finish. Try again.'
|
|
58
|
+
: notice === 'ended'
|
|
59
|
+
? 'Your session ended. Sign in again.'
|
|
60
|
+
: '';
|
|
61
|
+
if (notice.startsWith('offline:')) {
|
|
62
|
+
try {
|
|
63
|
+
name = decodeURIComponent(notice.slice(8));
|
|
64
|
+
} catch {
|
|
65
|
+
name = 'your company';
|
|
66
|
+
}
|
|
67
|
+
phase = 'offline';
|
|
68
|
+
error = `Could not reach ${name}. Check your connection.`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function render() {
|
|
72
|
+
const asking = phase === 'ask' || phase === 'checking';
|
|
73
|
+
const showsError = phase === 'ask' || phase === 'offline' || (fixed && phase === 'found');
|
|
74
|
+
const m = modes[mode];
|
|
75
|
+
els.title.textContent =
|
|
76
|
+
phase === 'found'
|
|
77
|
+
? `Sign in to ${name}`
|
|
78
|
+
: phase === 'waiting'
|
|
79
|
+
? 'Finish signing in in your browser'
|
|
80
|
+
: 'Sign in';
|
|
81
|
+
els.sub.textContent =
|
|
82
|
+
phase === 'checking'
|
|
83
|
+
? 'Looking for your company…'
|
|
84
|
+
: phase === 'found'
|
|
85
|
+
? host
|
|
86
|
+
: phase === 'waiting'
|
|
87
|
+
? host
|
|
88
|
+
: phase === 'offline'
|
|
89
|
+
? ''
|
|
90
|
+
: 'Use the address your account was set up with.';
|
|
91
|
+
els.label.textContent = m.label;
|
|
92
|
+
els.field.type = m.type;
|
|
93
|
+
els.field.placeholder = m.placeholder;
|
|
94
|
+
els.field.autocomplete = mode === 'email' ? 'email' : 'url';
|
|
95
|
+
els.field.hidden = els.label.hidden = !asking;
|
|
96
|
+
els.field.readOnly = phase === 'checking';
|
|
97
|
+
els.error.hidden = !error || !showsError;
|
|
98
|
+
els.error.textContent = error;
|
|
99
|
+
els.go.hidden = phase === 'waiting';
|
|
100
|
+
els.go.disabled = phase === 'checking';
|
|
101
|
+
els.goText.textContent =
|
|
102
|
+
phase === 'found' ? 'Continue in browser' : phase === 'offline' ? 'Try again' : 'Continue';
|
|
103
|
+
els.pair.hidden = phase !== 'waiting' || !code;
|
|
104
|
+
els.code.textContent = code;
|
|
105
|
+
els.back.hidden = phase !== 'found' || fixed;
|
|
106
|
+
els.cancel.hidden = phase !== 'waiting';
|
|
107
|
+
if (phase === 'ask') els.field.focus();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
els.form.addEventListener('submit', async (event) => {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
if (phase === 'offline') {
|
|
113
|
+
location.hash = `#retry-${Date.now()}`;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const call = modes[mode].call;
|
|
117
|
+
const value = els.field.value.trim();
|
|
118
|
+
if (fixed && phase === 'found') {
|
|
119
|
+
error = '';
|
|
120
|
+
location.hash = `#signin-${Date.now()}`;
|
|
121
|
+
phase = 'waiting';
|
|
122
|
+
render();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (phase === 'found') {
|
|
126
|
+
phase = 'waiting';
|
|
127
|
+
render();
|
|
128
|
+
try {
|
|
129
|
+
code = (await call(value, true)).code ?? '';
|
|
130
|
+
render();
|
|
131
|
+
} catch {
|
|
132
|
+
phase = 'ask';
|
|
133
|
+
error = 'Sign-in could not start. Try again.';
|
|
134
|
+
render();
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (phase !== 'ask' || !value) return;
|
|
139
|
+
phase = 'checking';
|
|
140
|
+
error = '';
|
|
141
|
+
render();
|
|
142
|
+
try {
|
|
143
|
+
const result = await call(value, false);
|
|
144
|
+
if (result.name) {
|
|
145
|
+
name = result.name;
|
|
146
|
+
host = result.host;
|
|
147
|
+
phase = 'found';
|
|
148
|
+
} else {
|
|
149
|
+
phase = 'ask';
|
|
150
|
+
error =
|
|
151
|
+
mode === 'email'
|
|
152
|
+
? `We could not find ${config.appName} for ${value.split('@').pop()}.`
|
|
153
|
+
: `We could not find ${config.appName} at that address.`;
|
|
154
|
+
mode = 'address';
|
|
155
|
+
els.field.value = '';
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
phase = 'ask';
|
|
159
|
+
error = 'That did not work. Check what you typed.';
|
|
160
|
+
}
|
|
161
|
+
render();
|
|
162
|
+
});
|
|
163
|
+
els.back.addEventListener('click', () => {
|
|
164
|
+
phase = 'ask';
|
|
165
|
+
mode = 'email';
|
|
166
|
+
error = '';
|
|
167
|
+
els.field.value = '';
|
|
168
|
+
render();
|
|
169
|
+
});
|
|
170
|
+
els.cancel.addEventListener('click', () => {
|
|
171
|
+
location.hash = `#cancel-${Date.now()}`;
|
|
172
|
+
phase = idle;
|
|
173
|
+
error = '';
|
|
174
|
+
render();
|
|
175
|
+
});
|
|
176
|
+
render();
|