@goodandready/dsh-subscriptions 0.2.0 → 0.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/lib/client.js +102 -29
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -35,15 +35,76 @@ window.__ModuleLoader__.load({
|
|
|
35
35
|
document.head.appendChild(tag)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
// Строки карточки живут в реестре локалей: так их переводит отдельный
|
|
39
|
+
// пакет, не трогая код этого плагина. Английский — язык по умолчанию,
|
|
40
|
+
// на него же приходится откат, если перевода нет.
|
|
41
|
+
const NS = 'dsh-subscriptions'
|
|
42
|
+
const en = {
|
|
43
|
+
'notConnected': 'Not connected',
|
|
44
|
+
'verifyAccount': 'Verify account',
|
|
45
|
+
'coolingDown': 'Cooling down',
|
|
46
|
+
'usageFull': 'Usage 100%',
|
|
47
|
+
'connected': 'Connected',
|
|
48
|
+
'title': 'Subscriptions',
|
|
49
|
+
'intro': 'Log in with a consumer subscription. Tokens stay on the host credentials store. The browser never reads them back. After Connect, if the provider lands on localhost or a vendor page, paste the redirected URL or the code here.',
|
|
50
|
+
'useOrigin': 'Use this Web UI origin as OAuth redirect_uri',
|
|
51
|
+
'useOriginHint': 'Leave off unless you registered your own OAuth client for this origin. Vendor CLI clients usually require their published redirect URI plus the paste step.',
|
|
52
|
+
'reconnect': 'Reconnect',
|
|
53
|
+
'connect': 'Connect',
|
|
54
|
+
'disconnect': 'Disconnect',
|
|
55
|
+
'removeSlot': 'Remove slot',
|
|
56
|
+
'pastePlaceholder': 'Paste redirected URL or code',
|
|
57
|
+
'submitCode': 'Submit code',
|
|
58
|
+
'verifyPrefix': 'Google requires one-time account verification. ',
|
|
59
|
+
'verifyLink': 'Open verification link',
|
|
60
|
+
'verifySuffix': ' then reconnect.',
|
|
61
|
+
'addAccount': '+ Add account',
|
|
62
|
+
'save': 'Save',
|
|
63
|
+
'saved': 'Saved',
|
|
64
|
+
'loading': 'Loading\u2026',
|
|
65
|
+
'accountLabel': 'Account',
|
|
66
|
+
'plan': 'Plan',
|
|
67
|
+
'storedAs': 'Stored as',
|
|
68
|
+
}
|
|
69
|
+
const ru = {
|
|
70
|
+
'notConnected': 'Не подключено',
|
|
71
|
+
'verifyAccount': 'Требуется проверка',
|
|
72
|
+
'coolingDown': 'Пауза после лимита',
|
|
73
|
+
'usageFull': 'Лимит исчерпан',
|
|
74
|
+
'connected': 'Подключено',
|
|
75
|
+
'title': 'Подписки',
|
|
76
|
+
'intro': 'Вход по обычной пользовательской подписке. Токены остаются в хранилище учётных данных харнесса, браузер их обратно не читает. Если после «Подключить» провайдер увёл на localhost или на свою страницу, вставьте сюда адрес перехода или код.',
|
|
77
|
+
'useOrigin': 'Использовать адрес этого веб-интерфейса как redirect_uri',
|
|
78
|
+
'useOriginHint': 'Включайте, только если зарегистрировали собственного клиента OAuth на этот адрес. Штатным консольным клиентам провайдеров нужен их опубликованный адрес возврата и вставка кода вручную.',
|
|
79
|
+
'reconnect': 'Переподключить',
|
|
80
|
+
'connect': 'Подключить',
|
|
81
|
+
'disconnect': 'Отключить',
|
|
82
|
+
'removeSlot': 'Убрать место',
|
|
83
|
+
'pastePlaceholder': 'Адрес перехода или код',
|
|
84
|
+
'submitCode': 'Отправить код',
|
|
85
|
+
'verifyPrefix': 'Google требует однократной проверки аккаунта. ',
|
|
86
|
+
'verifyLink': 'Открыть страницу проверки',
|
|
87
|
+
'verifySuffix': ' затем подключитесь заново.',
|
|
88
|
+
'addAccount': '+ Добавить аккаунт',
|
|
89
|
+
'save': 'Сохранить',
|
|
90
|
+
'saved': 'Сохранено',
|
|
91
|
+
'loading': 'Загрузка…',
|
|
92
|
+
'accountLabel': 'Аккаунт',
|
|
93
|
+
'plan': 'Тариф',
|
|
94
|
+
'storedAs': 'Хранится как',
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function badgeFor(account, t) {
|
|
98
|
+
if (!account || !account.configured) return t('notConnected')
|
|
99
|
+
if (account.validationUrl) return t('verifyAccount')
|
|
100
|
+
if (account.cooldownUntil && account.cooldownUntil > Date.now()) return t('coolingDown')
|
|
101
|
+
if (account.usagePercent != null && account.usagePercent >= 100) return t('usageFull')
|
|
102
|
+
return t('connected')
|
|
44
103
|
}
|
|
45
104
|
|
|
46
|
-
function SubsSection() {
|
|
105
|
+
function SubsSection(props) {
|
|
106
|
+
// Переводчик приходит от слота, потому что в его записи указан locale.
|
|
107
|
+
const t = (props && props.t) || ((key) => key)
|
|
47
108
|
const [draft, setDraft] = React.useState(null)
|
|
48
109
|
const [accounts, setAccounts] = React.useState([])
|
|
49
110
|
const [providers, setProviders] = React.useState([])
|
|
@@ -67,7 +128,7 @@ window.__ModuleLoader__.load({
|
|
|
67
128
|
return () => { alive = false }
|
|
68
129
|
}, [])
|
|
69
130
|
|
|
70
|
-
if (!draft) return React.createElement('div', { className: 'dsub-wrap' }, '
|
|
131
|
+
if (!draft) return React.createElement('div', { className: 'dsub-wrap' }, t('loading'))
|
|
71
132
|
|
|
72
133
|
const slots = Array.isArray(draft.slots) ? draft.slots : []
|
|
73
134
|
const setSlots = (next) => setDraft((d) => Object.assign({}, d, { slots: next }))
|
|
@@ -137,19 +198,19 @@ window.__ModuleLoader__.load({
|
|
|
137
198
|
|
|
138
199
|
return React.createElement('div', { className: 'dsub-wrap' },
|
|
139
200
|
React.createElement('div', { className: 'dsub-block' },
|
|
140
|
-
React.createElement('div', { className: 'dsub-h' }, '
|
|
201
|
+
React.createElement('div', { className: 'dsub-h' }, t('title')),
|
|
141
202
|
React.createElement('div', { className: 'dsub-sub' },
|
|
142
|
-
'
|
|
203
|
+
t('intro')),
|
|
143
204
|
React.createElement('label', { className: 'dsub-row' },
|
|
144
205
|
React.createElement('input', {
|
|
145
206
|
type: 'checkbox',
|
|
146
207
|
checked: !!draft.useWebCallback,
|
|
147
208
|
onChange: (e) => setDraft((d) => Object.assign({}, d, { useWebCallback: e.target.checked })),
|
|
148
209
|
}),
|
|
149
|
-
React.createElement('span', null, '
|
|
210
|
+
React.createElement('span', null, t('useOrigin')),
|
|
150
211
|
),
|
|
151
212
|
React.createElement('div', { className: 'dsub-sub' },
|
|
152
|
-
'
|
|
213
|
+
t('useOriginHint')),
|
|
153
214
|
),
|
|
154
215
|
names.map((prov) => {
|
|
155
216
|
const rows = slots
|
|
@@ -166,25 +227,25 @@ window.__ModuleLoader__.load({
|
|
|
166
227
|
React.createElement('input', {
|
|
167
228
|
className: 'dsub-grow',
|
|
168
229
|
value: row.slot.label || '',
|
|
169
|
-
placeholder: account.label || ('
|
|
230
|
+
placeholder: account.label || (t('accountLabel') + ' ' + row.slot.index),
|
|
170
231
|
onChange: (e) => {
|
|
171
232
|
const next = slots.slice()
|
|
172
233
|
next[row.i] = Object.assign({}, next[row.i], { label: e.target.value })
|
|
173
234
|
setSlots(next)
|
|
174
235
|
},
|
|
175
236
|
}),
|
|
176
|
-
React.createElement('span', { className: 'dsub-badge' + (account.validationUrl ? ' dsub-badge-warn' : (on ? ' dsub-badge-on' : '')) }, badgeFor(account)),
|
|
237
|
+
React.createElement('span', { className: 'dsub-badge' + (account.validationUrl ? ' dsub-badge-warn' : (on ? ' dsub-badge-on' : '')) }, badgeFor(account, t)),
|
|
177
238
|
React.createElement('button', {
|
|
178
239
|
type: 'button', className: 'dsub-mini',
|
|
179
240
|
onClick: () => connect(row.slot.provider, row.slot.index).catch((e) => setErr(String(e.message || e))),
|
|
180
|
-
}, on ? '
|
|
241
|
+
}, on ? t('reconnect') : t('connect')),
|
|
181
242
|
React.createElement('button', {
|
|
182
243
|
type: 'button', className: 'dsub-mini',
|
|
183
244
|
disabled: !on,
|
|
184
245
|
onClick: () => logout(row.slot.provider, row.slot.index).catch((e) => setErr(String(e.message || e))),
|
|
185
|
-
}, '
|
|
246
|
+
}, t('disconnect')),
|
|
186
247
|
React.createElement('button', {
|
|
187
|
-
type: 'button', className: 'dsub-mini', title: '
|
|
248
|
+
type: 'button', className: 'dsub-mini', title: t('removeSlot'),
|
|
188
249
|
onClick: () => {
|
|
189
250
|
const slot = row.slot
|
|
190
251
|
Promise.resolve(on ? logout(slot.provider, slot.index) : null)
|
|
@@ -197,49 +258,61 @@ window.__ModuleLoader__.load({
|
|
|
197
258
|
React.createElement('input', {
|
|
198
259
|
className: 'dsub-grow',
|
|
199
260
|
value: paste[key] || '',
|
|
200
|
-
placeholder: '
|
|
261
|
+
placeholder: t('pastePlaceholder'),
|
|
201
262
|
onChange: (e) => setPaste((p) => Object.assign({}, p, { [key]: e.target.value })),
|
|
202
263
|
}),
|
|
203
264
|
React.createElement('button', {
|
|
204
265
|
type: 'button', className: 'dsub-mini',
|
|
205
266
|
onClick: () => complete(row.slot.provider, row.slot.index).catch((e) => setErr(String(e.message || e))),
|
|
206
|
-
}, '
|
|
267
|
+
}, t('submitCode')),
|
|
207
268
|
),
|
|
208
269
|
account.accountNotice ? React.createElement('div', { className: 'dsub-verify' }, account.accountNotice) : null,
|
|
209
270
|
account.validationUrl ? React.createElement('div', { className: 'dsub-verify' },
|
|
210
|
-
'
|
|
211
|
-
React.createElement('a', { href: account.validationUrl, target: '_blank', rel: 'noopener noreferrer' }, '
|
|
212
|
-
'
|
|
271
|
+
t('verifyPrefix'),
|
|
272
|
+
React.createElement('a', { href: account.validationUrl, target: '_blank', rel: 'noopener noreferrer' }, t('verifyLink')),
|
|
273
|
+
t('verifySuffix'),
|
|
213
274
|
) : null,
|
|
214
|
-
account.paidTierName ? React.createElement('span', { className: 'dsub-sub' }, '
|
|
215
|
-
account.ref ? React.createElement('span', { className: 'dsub-sub' }, '
|
|
275
|
+
account.paidTierName ? React.createElement('span', { className: 'dsub-sub' }, t('plan') + ': ' + account.paidTierName) : null,
|
|
276
|
+
account.ref ? React.createElement('span', { className: 'dsub-sub' }, t('storedAs') + ' ' + account.ref) : null,
|
|
216
277
|
)
|
|
217
278
|
}),
|
|
218
279
|
React.createElement('button', {
|
|
219
280
|
type: 'button', className: 'dsub-mini',
|
|
220
281
|
onClick: () => addSlot(prov.id),
|
|
221
|
-
}, '
|
|
282
|
+
}, t('addAccount')),
|
|
222
283
|
)
|
|
223
284
|
}),
|
|
224
285
|
React.createElement('div', { className: 'dsub-row' },
|
|
225
286
|
React.createElement('button', {
|
|
226
287
|
type: 'button', className: 'dsub-save',
|
|
227
288
|
onClick: () => save().catch((e) => setErr(String(e.message || e))),
|
|
228
|
-
}, '
|
|
229
|
-
saved ? React.createElement('span', { className: 'dsub-ok' }, '
|
|
289
|
+
}, t('save')),
|
|
290
|
+
saved ? React.createElement('span', { className: 'dsub-ok' }, t('saved')) : null,
|
|
230
291
|
err ? React.createElement('span', { className: 'dsub-bad' }, err) : null,
|
|
231
292
|
),
|
|
232
293
|
)
|
|
233
294
|
}
|
|
234
295
|
|
|
235
296
|
function registerSettings(ctx) {
|
|
297
|
+
ctx.effect(() => ctx.locale.register(NS, { en, ru }), 'dsh-subscriptions: словари')
|
|
298
|
+
// Подпись раздела рисует боковой список, а не наш компонент: props.t
|
|
299
|
+
// туда не доходит, поэтому берём переводчик, привязанный к namespace.
|
|
300
|
+
const t = ctx.locale.bind(NS)
|
|
236
301
|
ctx.slots.inject('settings.section', () => ctx.slots.register(
|
|
237
|
-
{
|
|
302
|
+
{
|
|
303
|
+
name: 'settings.section',
|
|
304
|
+
id: '@goodandready/dsh-subscriptions',
|
|
305
|
+
order: 28,
|
|
306
|
+
// locale в записи слота — то, из-за чего компонент получает props.t.
|
|
307
|
+
locale: NS,
|
|
308
|
+
label: () => t('title'),
|
|
309
|
+
inject: () => ({ ctx: ctx }),
|
|
310
|
+
},
|
|
238
311
|
SubsSection,
|
|
239
312
|
))
|
|
240
313
|
}
|
|
241
314
|
|
|
242
|
-
exports.inject = ['slots']
|
|
315
|
+
exports.inject = ['slots', 'locale']
|
|
243
316
|
exports.apply = function apply(ctx) {
|
|
244
317
|
registerSettings(ctx)
|
|
245
318
|
}
|
package/package.json
CHANGED