@youasked/react 0.5.0 → 0.7.0
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 +3 -1
- package/dist/YouAsked.d.ts +8 -1
- package/dist/client.d.ts +2 -0
- package/dist/context.d.ts +4 -1
- package/dist/icons.d.ts +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +503 -356
- package/dist/nudge.d.ts +31 -0
- package/dist/styles.d.ts +1 -1
- package/dist/theme.d.ts +8 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/YouAsked.tsx
|
|
4
|
-
import { useCallback as
|
|
4
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useMemo, useRef as useRef4, useState as useState4, useSyncExternalStore } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
6
|
|
|
7
7
|
// src/client.ts
|
|
@@ -120,7 +120,7 @@ var DAY = 24 * 60 * MIN;
|
|
|
120
120
|
var uploads = /* @__PURE__ */ new Map();
|
|
121
121
|
var wait = (ms = 380) => new Promise((r) => setTimeout(r, ms + Math.random() * ms * 0.5));
|
|
122
122
|
var WORDS = (s) => new Set(s.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 2));
|
|
123
|
-
function createDemoClient(appName = "
|
|
123
|
+
function createDemoClient(appName = "this app") {
|
|
124
124
|
const t0 = Date.now();
|
|
125
125
|
let seq = 100;
|
|
126
126
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -147,31 +147,31 @@ function createDemoClient(appName = "Acme") {
|
|
|
147
147
|
});
|
|
148
148
|
const team = (id, body, ago2) => ({ id, body, team: true, author: null, mine: false, createdAt: t0 - ago2 });
|
|
149
149
|
const store = [
|
|
150
|
-
seed(12, "
|
|
150
|
+
seed(12, "Dark mode", "Easier on the eyes at night, and it'd match the rest of my phone.", 14, "planned", {
|
|
151
151
|
comments_: [
|
|
152
|
-
{ id: "c1", body: "Yes
|
|
153
|
-
team("c2", "On the list for December. Would
|
|
152
|
+
{ id: "c1", body: "Yes please. I use it every night.", team: false, author: "Raj", mine: false, createdAt: t0 - 3 * DAY },
|
|
153
|
+
team("c2", "On the list for December. Would following your phone's setting be enough to start?", 2 * DAY)
|
|
154
154
|
]
|
|
155
155
|
}),
|
|
156
|
-
seed(13, "
|
|
157
|
-
seed(7, "
|
|
158
|
-
shipNote: "
|
|
156
|
+
seed(13, "Export to CSV", "So I can open everything in a spreadsheet.", 11, "in_progress"),
|
|
157
|
+
seed(7, "Keyboard shortcuts", "", 8, "shipped", {
|
|
158
|
+
shipNote: "Press ? anywhere to see them all.",
|
|
159
159
|
shippedAt: t0 - 2 * DAY,
|
|
160
160
|
creditName: "Sam"
|
|
161
161
|
}),
|
|
162
|
-
seed(14, "
|
|
163
|
-
seed(15, "
|
|
164
|
-
seed(16, "Widget for the home screen", "Just today's
|
|
165
|
-
seed(9, "
|
|
166
|
-
shipNote: "
|
|
162
|
+
seed(14, "Offline mode", "I use it on the train and keep losing signal.", 6, "open"),
|
|
163
|
+
seed(15, "Share a link with a friend", "", 4, "open"),
|
|
164
|
+
seed(16, "Widget for the home screen", "Just today's number would be enough.", 3, "open"),
|
|
165
|
+
seed(9, "Sign in with Apple", "", 5, "shipped", {
|
|
166
|
+
shipNote: "You can now sign in with your Apple ID.",
|
|
167
167
|
shippedAt: t0 - 4 * 60 * MIN,
|
|
168
168
|
mine: true,
|
|
169
169
|
voted: true
|
|
170
170
|
}),
|
|
171
|
-
seed(17, "
|
|
171
|
+
seed(17, "A reminder the day before", "A nudge the day before something is due would do.", 2, "open", {
|
|
172
172
|
mine: true,
|
|
173
173
|
voted: true,
|
|
174
|
-
comments_: [team("c3", "Good one. Would a
|
|
174
|
+
comments_: [team("c3", "Good one. Would a push notification work, or would you rather get an email?", 3 * 60 * MIN)]
|
|
175
175
|
})
|
|
176
176
|
];
|
|
177
177
|
const find = (id) => store.find((r) => r.id === id);
|
|
@@ -344,11 +344,120 @@ var IconSpark = () => /* @__PURE__ */ jsxs("svg", { ...base, children: [
|
|
|
344
344
|
/* @__PURE__ */ jsx("path", { d: "m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9Z" }),
|
|
345
345
|
/* @__PURE__ */ jsx("path", { d: "M19 16v4M17 18h4" })
|
|
346
346
|
] });
|
|
347
|
-
var IconIdea = () => /* @__PURE__ */ jsxs("svg", {
|
|
348
|
-
/* @__PURE__ */ jsx("path", { d: "
|
|
349
|
-
/* @__PURE__ */ jsx("path", { d: "
|
|
347
|
+
var IconIdea = ({ ink = "var(--_bg)" }) => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
348
|
+
/* @__PURE__ */ jsx("path", { d: "M6 2.25h12a4.5 4.5 0 0 1 4.5 4.5V15a4.5 4.5 0 0 1-4.5 4.5h-6.75L6 23.25V19.5A4.5 4.5 0 0 1 1.5 15V6.75A4.5 4.5 0 0 1 6 2.25Z", fill: "currentColor" }),
|
|
349
|
+
/* @__PURE__ */ jsx("path", { d: "M7.9 13.9 12 9.8l4.1 4.1", fill: "none", stroke: ink, strokeWidth: 2.6, strokeLinecap: "round", strokeLinejoin: "round" })
|
|
350
350
|
] });
|
|
351
351
|
|
|
352
|
+
// src/nudge.tsx
|
|
353
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
354
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
355
|
+
var NUDGE_DEFAULTS = { everyDays: 7, activeSeconds: 30, skipVisits: 1, message: null, position: "bottom" };
|
|
356
|
+
var DAY2 = 24 * 60 * 60 * 1e3;
|
|
357
|
+
var VISIT_GAP = 30 * 60 * 1e3;
|
|
358
|
+
function resolveNudge(server, prop) {
|
|
359
|
+
if (prop === false) return null;
|
|
360
|
+
if (typeof prop === "object") return { ...NUDGE_DEFAULTS, ...server, ...prop };
|
|
361
|
+
if (prop === true) return server ?? NUDGE_DEFAULTS;
|
|
362
|
+
return server ?? null;
|
|
363
|
+
}
|
|
364
|
+
function load(key) {
|
|
365
|
+
const empty = { visits: 0, lastVisitAt: 0, lastShownAt: 0, lastEngagedAt: 0, dismissals: 0 };
|
|
366
|
+
if (!key) return empty;
|
|
367
|
+
try {
|
|
368
|
+
return { ...empty, ...JSON.parse(localStorage.getItem(key) ?? "{}") };
|
|
369
|
+
} catch {
|
|
370
|
+
return empty;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function save(key, m) {
|
|
374
|
+
if (!key) return;
|
|
375
|
+
try {
|
|
376
|
+
localStorage.setItem(key, JSON.stringify(m));
|
|
377
|
+
} catch {
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function useNudge(settings, storageKey, blocked) {
|
|
381
|
+
const memory = useRef(null);
|
|
382
|
+
const [show, setShow] = useState(false);
|
|
383
|
+
useEffect(() => {
|
|
384
|
+
if (!settings || memory.current) return;
|
|
385
|
+
const m = load(storageKey);
|
|
386
|
+
const now = Date.now();
|
|
387
|
+
if (now - m.lastVisitAt > VISIT_GAP) m.visits += 1;
|
|
388
|
+
m.lastVisitAt = now;
|
|
389
|
+
memory.current = m;
|
|
390
|
+
save(storageKey, m);
|
|
391
|
+
}, [settings, storageKey]);
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
if (!settings || show) return;
|
|
394
|
+
let active = 0;
|
|
395
|
+
const timer = window.setInterval(() => {
|
|
396
|
+
if (document.visibilityState !== "visible" || blocked) return;
|
|
397
|
+
active += 1;
|
|
398
|
+
if (active < settings.activeSeconds) return;
|
|
399
|
+
window.clearInterval(timer);
|
|
400
|
+
const m = memory.current;
|
|
401
|
+
if (!m) return;
|
|
402
|
+
const now = Date.now();
|
|
403
|
+
const wait2 = settings.everyDays * DAY2 * 2 ** Math.min(m.dismissals, 3);
|
|
404
|
+
const due = !storageKey || m.visits > settings.skipVisits && now - m.lastShownAt >= wait2 && now - m.lastEngagedAt >= wait2;
|
|
405
|
+
if (!due) return;
|
|
406
|
+
m.lastShownAt = now;
|
|
407
|
+
save(storageKey, m);
|
|
408
|
+
setShow(true);
|
|
409
|
+
}, 1e3);
|
|
410
|
+
return () => window.clearInterval(timer);
|
|
411
|
+
}, [settings, storageKey, blocked, show]);
|
|
412
|
+
const engaged = useCallback(() => {
|
|
413
|
+
const m = memory.current;
|
|
414
|
+
if (m) {
|
|
415
|
+
m.lastEngagedAt = Date.now();
|
|
416
|
+
m.dismissals = 0;
|
|
417
|
+
save(storageKey, m);
|
|
418
|
+
}
|
|
419
|
+
setShow(false);
|
|
420
|
+
}, [storageKey]);
|
|
421
|
+
const dismiss = useCallback(() => {
|
|
422
|
+
const m = memory.current;
|
|
423
|
+
if (m) {
|
|
424
|
+
m.dismissals += 1;
|
|
425
|
+
save(storageKey, m);
|
|
426
|
+
}
|
|
427
|
+
setShow(false);
|
|
428
|
+
}, [storageKey]);
|
|
429
|
+
return { show, engaged, dismiss };
|
|
430
|
+
}
|
|
431
|
+
function NudgeToast({
|
|
432
|
+
settings,
|
|
433
|
+
appName,
|
|
434
|
+
position,
|
|
435
|
+
trigger,
|
|
436
|
+
onOpen,
|
|
437
|
+
onDismiss
|
|
438
|
+
}) {
|
|
439
|
+
return /* @__PURE__ */ jsxs2("div", { className: "ya-toast ya-nudge", role: "status", "data-pos": position, "data-trigger": trigger, "data-place": settings.position, children: [
|
|
440
|
+
/* @__PURE__ */ jsxs2("div", { className: "ya-toast-top", children: [
|
|
441
|
+
/* @__PURE__ */ jsxs2("p", { className: "ya-toast-kicker", children: [
|
|
442
|
+
"Help shape ",
|
|
443
|
+
appName
|
|
444
|
+
] }),
|
|
445
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: "ya-icon-btn", onClick: onDismiss, "aria-label": "Dismiss", children: /* @__PURE__ */ jsx2(IconClose, {}) })
|
|
446
|
+
] }),
|
|
447
|
+
/* @__PURE__ */ jsxs2("div", { className: "ya-nudge-body", children: [
|
|
448
|
+
/* @__PURE__ */ jsx2("span", { className: "ya-card-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(IconIdea, {}) }),
|
|
449
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
450
|
+
/* @__PURE__ */ jsx2("p", { className: "ya-toast-title", children: settings.message ?? `Got an idea for ${appName}?` }),
|
|
451
|
+
/* @__PURE__ */ jsx2("p", { className: "ya-toast-note", children: "Ask for a feature or vote on what others want." })
|
|
452
|
+
] })
|
|
453
|
+
] }),
|
|
454
|
+
/* @__PURE__ */ jsxs2("div", { className: "ya-toast-actions", children: [
|
|
455
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: "ya-btn ya-btn-sm", onClick: onOpen, children: "Share an idea" }),
|
|
456
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: "ya-btn ya-btn-sm ya-btn-ghost", onClick: onDismiss, children: "Not now" })
|
|
457
|
+
] })
|
|
458
|
+
] });
|
|
459
|
+
}
|
|
460
|
+
|
|
352
461
|
// src/styles.ts
|
|
353
462
|
var css = `
|
|
354
463
|
.ya-scope{--_bg:var(--ya-bg);--_fg:var(--ya-fg);--_accent:var(--ya-accent);--_accent-fg:var(--ya-accent-fg);
|
|
@@ -462,7 +571,7 @@ box-shadow:0 0 0 1px var(--_border),0 24px 70px rgba(0,0,0,.22);padding-bottom:0
|
|
|
462
571
|
.ya-typing span{width:7px;height:7px;border-radius:50%;background:var(--_muted);animation:ya-dot 1s infinite ease-in-out}
|
|
463
572
|
.ya-typing span:nth-child(2){animation-delay:.15s}
|
|
464
573
|
.ya-typing span:nth-child(3){animation-delay:.3s}
|
|
465
|
-
.ya-chip{align-self:center;margin-top:6px;font-size:13.5px;font-weight:600;color:var(--_accent);padding:8px 14px;border-radius:999px;background:var(--_tint)}
|
|
574
|
+
.ya-chip{align-self:center;margin-top:6px;font-size:13.5px;font-weight:600;color:var(--_accent);padding:8px 14px;border-radius:var(--ya-button-radius,999px);background:var(--_tint)}
|
|
466
575
|
.ya-section{margin-top:12px}
|
|
467
576
|
.ya-section-head{display:flex;align-items:center;justify-content:space-between;margin:0 0 8px}
|
|
468
577
|
.ya-section-head h3{font-size:13px;font-weight:650;color:var(--_muted)}
|
|
@@ -478,7 +587,7 @@ box-shadow:0 0 0 1px var(--_border),0 24px 70px rgba(0,0,0,.22);padding-bottom:0
|
|
|
478
587
|
.ya-tray{padding:10px;margin-bottom:8px;border-radius:calc(var(--_radius) + 2px);background:var(--_tint);animation:ya-tray .22s cubic-bezier(.2,.9,.3,1)}
|
|
479
588
|
.ya-tray-h{font-size:13px;font-weight:650;margin:0 2px 8px}
|
|
480
589
|
.ya-tray .ya-row{background:var(--_bg)}
|
|
481
|
-
.ya-composer{display:flex;align-items:flex-end;gap:8px;padding:5px 5px 5px 16px;border-radius:24px;background:var(--_surface);border:1.5px solid transparent;transition:border-color .15s}
|
|
590
|
+
.ya-composer{display:flex;align-items:flex-end;gap:8px;padding:5px 5px 5px 16px;border-radius:var(--ya-button-radius,24px);background:var(--_surface);border:1.5px solid transparent;transition:border-color .15s}
|
|
482
591
|
.ya-composer:focus-within{border-color:color-mix(in srgb,var(--_accent) 55%,transparent)}
|
|
483
592
|
.ya-composer-bar{flex:none;margin:8px 12px 12px}
|
|
484
593
|
.ya-composer-input{flex:1;min-width:0;border:0;outline:none;background:none;resize:none;font:inherit;font-size:16px;line-height:1.4;color:var(--_fg);padding:7px 0;margin:0;min-height:calc(1.4em + 14px);max-height:132px;overflow-y:auto}
|
|
@@ -495,7 +604,7 @@ box-shadow:0 0 0 1px var(--_border),0 24px 70px rgba(0,0,0,.22);padding-bottom:0
|
|
|
495
604
|
.ya-shot-x:hover{color:var(--_fg);background:var(--_border)}
|
|
496
605
|
.ya-shot-x svg{width:13px;height:13px}
|
|
497
606
|
.ya-tools{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:8px;min-height:32px}
|
|
498
|
-
.ya-tool{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px 0 9px;border-radius:999px;background:var(--_tint);color:var(--_accent);font-size:13.5px;font-weight:650;transition:transform .12s,opacity .15s;flex:none}
|
|
607
|
+
.ya-tool{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px 0 9px;border-radius:var(--ya-button-radius,999px);background:var(--_tint);color:var(--_accent);font-size:13.5px;font-weight:650;transition:transform .12s,opacity .15s;flex:none}
|
|
499
608
|
.ya-tool svg{width:16px;height:16px}
|
|
500
609
|
.ya-tool:disabled{opacity:.4}
|
|
501
610
|
.ya-tool:active{transform:scale(.96)}
|
|
@@ -525,10 +634,10 @@ box-shadow:0 0 0 1px var(--_border),0 24px 70px rgba(0,0,0,.22);padding-bottom:0
|
|
|
525
634
|
.ya-tab{flex:1;padding:7px 0;border-radius:calc(var(--_radius) - 5px);font-size:14px;font-weight:600;color:var(--_muted);text-align:center;transition:background .15s,color .15s}
|
|
526
635
|
.ya-tab[aria-selected=true]{background:var(--_bg);color:var(--_fg);box-shadow:0 1px 3px rgba(0,0,0,.1)}
|
|
527
636
|
.ya-foot{padding:12px 16px 14px;border-top:1px solid var(--_border);display:flex;flex-direction:column;gap:9px;flex:none;background:var(--_bg)}
|
|
528
|
-
.ya-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:48px;padding:0 18px;border-radius:var(--_radius);background:var(--_accent);color:var(--_accent-fg);font-weight:650;font-size:15px;width:100%;transition:opacity .15s,transform .12s;white-space:nowrap}
|
|
637
|
+
.ya-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:48px;padding:0 18px;border-radius:var(--ya-button-radius,var(--_radius));background:var(--_accent);color:var(--_accent-fg);font-weight:650;font-size:15px;width:100%;transition:opacity .15s,transform .12s;white-space:nowrap}
|
|
529
638
|
.ya-btn svg{width:18px;height:18px}
|
|
530
639
|
.ya-btn:disabled{opacity:.4}
|
|
531
|
-
.ya-btn-sm{height:38px;width:auto;padding:0 14px;font-size:14px;border-radius:calc(var(--_radius) - 3px)}
|
|
640
|
+
.ya-btn-sm{height:38px;width:auto;padding:0 14px;font-size:14px;border-radius:var(--ya-button-radius,calc(var(--_radius) - 3px))}
|
|
532
641
|
.ya-btn-ghost{background:var(--_surface);color:var(--_fg)}
|
|
533
642
|
.ya-callout{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;border-radius:var(--_radius);font-size:14px;margin-top:14px;line-height:1.4;background:color-mix(in srgb,var(--_green) 12%,var(--_bg))}
|
|
534
643
|
.ya-callout svg{width:18px;height:18px;margin-top:1px;color:var(--_green)}
|
|
@@ -536,7 +645,7 @@ box-shadow:0 0 0 1px var(--_border),0 24px 70px rgba(0,0,0,.22);padding-bottom:0
|
|
|
536
645
|
.ya-detail-title{font-size:21px;font-weight:700;letter-spacing:-.015em;margin:10px 0 8px;overflow-wrap:anywhere;line-height:1.28}
|
|
537
646
|
.ya-text{white-space:pre-wrap;overflow-wrap:anywhere;font-size:15px;color:var(--_fg)}
|
|
538
647
|
.ya-text-muted{color:var(--_muted)}
|
|
539
|
-
.ya-bigvote{margin-top:16px;display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px 0 12px;border-radius:999px;border:1px solid var(--_border);font-weight:650;font-size:14.5px;transition:transform .12s,background .15s,color .15s;font-variant-numeric:tabular-nums}
|
|
648
|
+
.ya-bigvote{margin-top:16px;display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px 0 12px;border-radius:var(--ya-button-radius,999px);border:1px solid var(--_border);font-weight:650;font-size:14.5px;transition:transform .12s,background .15s,color .15s;font-variant-numeric:tabular-nums}
|
|
540
649
|
.ya-bigvote svg{width:17px;height:17px}
|
|
541
650
|
.ya-bigvote[aria-pressed=true]{background:var(--_accent);border-color:var(--_accent);color:var(--_accent-fg)}
|
|
542
651
|
.ya-attachments{display:flex;flex-direction:column;gap:8px;margin-top:12px}
|
|
@@ -579,12 +688,16 @@ box-shadow:0 0 0 1px var(--_border),0 24px 70px rgba(0,0,0,.22);padding-bottom:0
|
|
|
579
688
|
.ya-toast .ya-live{margin-top:8px}
|
|
580
689
|
.ya-live svg{width:15px;height:15px}
|
|
581
690
|
.ya-toast-note{font-size:13.5px;color:var(--_muted);margin-top:6px;overflow-wrap:anywhere}
|
|
691
|
+
.ya-toast[data-place=top]{top:calc(16px + env(safe-area-inset-top));bottom:auto}
|
|
692
|
+
.ya-nudge-body{display:flex;gap:12px;align-items:flex-start;margin-top:6px}
|
|
693
|
+
.ya-nudge-body .ya-card-icon{flex:none;margin-top:2px}
|
|
694
|
+
.ya-nudge-body .ya-toast-title{margin-top:0}
|
|
582
695
|
.ya-toast-actions{display:flex;gap:8px;margin-top:12px}
|
|
583
696
|
.ya-toast-quote{color:var(--_fg);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
|
|
584
697
|
.ya-toast .ya-icon-btn{width:30px;height:30px;margin:-6px -6px 0 0;background:none}
|
|
585
698
|
|
|
586
699
|
/* ---------- inline entry points */
|
|
587
|
-
.ya-askbar{display:flex;align-items:center;gap:12px;width:100%;padding:8px 8px 8px 10px;border-radius:999px;background:var(--_bg);border:1px solid var(--_border);color:var(--_muted);font-size:15.5px;text-align:left;box-shadow:0 1px 2px rgba(0,0,0,.04);transition:border-color .15s,transform .12s}
|
|
700
|
+
.ya-askbar{display:flex;align-items:center;gap:12px;width:100%;padding:8px 8px 8px 10px;border-radius:var(--ya-button-radius,999px);background:var(--_bg);border:1px solid var(--_border);color:var(--_muted);font-size:15.5px;text-align:left;box-shadow:0 1px 2px rgba(0,0,0,.04);transition:border-color .15s,transform .12s}
|
|
588
701
|
.ya-askbar:hover{border-color:color-mix(in srgb,var(--_accent) 50%,var(--_border))}
|
|
589
702
|
.ya-askbar .ya-avatar{border-radius:30%}
|
|
590
703
|
.ya-askbar-glyph{width:30px;height:30px;border-radius:50%;display:grid;place-items:center;background:var(--_tint);color:var(--_accent);flex:none}
|
|
@@ -606,7 +719,7 @@ box-shadow:0 0 0 1px var(--_border),0 24px 70px rgba(0,0,0,.22);padding-bottom:0
|
|
|
606
719
|
.ya-card-actions{display:flex;gap:8px}
|
|
607
720
|
.ya-card-actions .ya-btn{flex:1}
|
|
608
721
|
|
|
609
|
-
.ya-pillbtn{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px 0 13px;border-radius:999px;background:var(--_tint);color:var(--_accent);font-weight:650;font-size:14.5px;transition:transform .12s}
|
|
722
|
+
.ya-pillbtn{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px 0 13px;border-radius:var(--ya-button-radius,999px);background:var(--_tint);color:var(--_accent);font-weight:650;font-size:14.5px;transition:transform .12s}
|
|
610
723
|
.ya-pillbtn svg{width:18px;height:18px}
|
|
611
724
|
.ya-pillbtn .ya-badge{margin-right:-6px}
|
|
612
725
|
.ya-iconbtn{position:relative;width:40px;height:40px;border-radius:50%;display:grid;place-items:center;background:var(--_surface);color:var(--_fg);transition:transform .12s}
|
|
@@ -732,12 +845,13 @@ function defined(obj) {
|
|
|
732
845
|
function resolveTokens(appearance = {}) {
|
|
733
846
|
const mode = appearance.mode === "dark" || appearance.mode !== "light" && hostIsDark() ? "dark" : "light";
|
|
734
847
|
const host = appearance.inherit === false ? {} : readHost();
|
|
735
|
-
const { mode: _m, inherit: _i, radius, ...colours } = appearance;
|
|
848
|
+
const { mode: _m, inherit: _i, radius, buttonRadius, ...colours } = appearance;
|
|
736
849
|
const merged = {
|
|
737
850
|
...DEFAULTS[mode],
|
|
738
851
|
...defined(host),
|
|
739
852
|
...defined(colours),
|
|
740
853
|
...radius !== void 0 ? { radius: typeof radius === "number" ? `${radius}px` : radius } : {},
|
|
854
|
+
...buttonRadius !== void 0 ? { buttonRadius: buttonRadius === "pill" ? "999px" : typeof buttonRadius === "number" ? `${buttonRadius}px` : buttonRadius } : {},
|
|
741
855
|
mode
|
|
742
856
|
};
|
|
743
857
|
if (!merged.accentForeground) {
|
|
@@ -756,6 +870,7 @@ function tokenStyle(t) {
|
|
|
756
870
|
"--ya-fg": t.foreground,
|
|
757
871
|
"--ya-radius": t.radius
|
|
758
872
|
};
|
|
873
|
+
if (t.buttonRadius) style["--ya-button-radius"] = t.buttonRadius;
|
|
759
874
|
if (t.fontFamily && t.fontFamily !== "inherit") style["--ya-font"] = t.fontFamily;
|
|
760
875
|
if (t.muted) style["--ya-muted"] = t.muted;
|
|
761
876
|
if (t.surface) style["--ya-surface"] = t.surface;
|
|
@@ -764,13 +879,13 @@ function tokenStyle(t) {
|
|
|
764
879
|
}
|
|
765
880
|
|
|
766
881
|
// src/shared.tsx
|
|
767
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
768
|
-
import { jsx as
|
|
882
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
883
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
769
884
|
function useVote(client, initial) {
|
|
770
|
-
const [state, setState] =
|
|
771
|
-
const busy =
|
|
772
|
-
|
|
773
|
-
const toggle =
|
|
885
|
+
const [state, setState] = useState2({ voted: initial.voted, votes: initial.votes });
|
|
886
|
+
const busy = useRef2(false);
|
|
887
|
+
useEffect2(() => setState({ voted: initial.voted, votes: initial.votes }), [initial.voted, initial.votes]);
|
|
888
|
+
const toggle = useCallback2(async () => {
|
|
774
889
|
if (busy.current) return;
|
|
775
890
|
busy.current = true;
|
|
776
891
|
const before = state;
|
|
@@ -787,7 +902,7 @@ function useVote(client, initial) {
|
|
|
787
902
|
}
|
|
788
903
|
function VoteButton({ client, request, compact }) {
|
|
789
904
|
const { voted, votes, toggle } = useVote(client, request);
|
|
790
|
-
return /* @__PURE__ */
|
|
905
|
+
return /* @__PURE__ */ jsxs3(
|
|
791
906
|
"button",
|
|
792
907
|
{
|
|
793
908
|
type: "button",
|
|
@@ -799,7 +914,7 @@ function VoteButton({ client, request, compact }) {
|
|
|
799
914
|
toggle();
|
|
800
915
|
},
|
|
801
916
|
children: [
|
|
802
|
-
/* @__PURE__ */
|
|
917
|
+
/* @__PURE__ */ jsx3(IconUp, {}),
|
|
803
918
|
votes
|
|
804
919
|
]
|
|
805
920
|
}
|
|
@@ -811,9 +926,9 @@ function BigVote({
|
|
|
811
926
|
onChange
|
|
812
927
|
}) {
|
|
813
928
|
const { voted, votes, toggle } = useVote(client, request);
|
|
814
|
-
|
|
815
|
-
return /* @__PURE__ */
|
|
816
|
-
/* @__PURE__ */
|
|
929
|
+
useEffect2(() => onChange?.(voted), [voted, onChange]);
|
|
930
|
+
return /* @__PURE__ */ jsxs3("button", { type: "button", className: "ya-bigvote", "aria-pressed": voted, onClick: toggle, children: [
|
|
931
|
+
/* @__PURE__ */ jsx3(IconUp, {}),
|
|
817
932
|
voted ? "Voted" : "Upvote",
|
|
818
933
|
" \xB7 ",
|
|
819
934
|
votes
|
|
@@ -828,8 +943,8 @@ var STATUS_LABEL = {
|
|
|
828
943
|
pending: "Awaiting approval"
|
|
829
944
|
};
|
|
830
945
|
function StatusPill({ status }) {
|
|
831
|
-
return /* @__PURE__ */
|
|
832
|
-
status === "shipped" && /* @__PURE__ */
|
|
946
|
+
return /* @__PURE__ */ jsxs3("span", { className: "ya-pill", "data-s": status, children: [
|
|
947
|
+
status === "shipped" && /* @__PURE__ */ jsx3(IconCheck, {}),
|
|
833
948
|
STATUS_LABEL[status]
|
|
834
949
|
] });
|
|
835
950
|
}
|
|
@@ -839,32 +954,32 @@ function RequestRow({
|
|
|
839
954
|
onOpen,
|
|
840
955
|
compact
|
|
841
956
|
}) {
|
|
842
|
-
return /* @__PURE__ */
|
|
843
|
-
/* @__PURE__ */
|
|
844
|
-
/* @__PURE__ */
|
|
845
|
-
/* @__PURE__ */
|
|
846
|
-
(request.status !== "open" || request.comments > 0 || request.mine) && /* @__PURE__ */
|
|
847
|
-
request.status !== "open" && /* @__PURE__ */
|
|
848
|
-
request.comments > 0 && /* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
957
|
+
return /* @__PURE__ */ jsxs3("li", { className: compact ? "ya-row ya-row-sm" : "ya-row", children: [
|
|
958
|
+
/* @__PURE__ */ jsx3(VoteButton, { client, request, compact }),
|
|
959
|
+
/* @__PURE__ */ jsxs3("button", { type: "button", className: "ya-row-main", onClick: onOpen, children: [
|
|
960
|
+
/* @__PURE__ */ jsx3("span", { className: "ya-row-title", children: request.title }),
|
|
961
|
+
(request.status !== "open" || request.comments > 0 || request.mine) && /* @__PURE__ */ jsxs3("span", { className: "ya-meta", children: [
|
|
962
|
+
request.status !== "open" && /* @__PURE__ */ jsx3(StatusPill, { status: request.status }),
|
|
963
|
+
request.comments > 0 && /* @__PURE__ */ jsxs3("span", { children: [
|
|
964
|
+
/* @__PURE__ */ jsx3(IconChat, {}),
|
|
850
965
|
request.comments
|
|
851
966
|
] }),
|
|
852
|
-
request.mine && /* @__PURE__ */
|
|
967
|
+
request.mine && /* @__PURE__ */ jsx3("span", { children: "You asked" })
|
|
853
968
|
] })
|
|
854
969
|
] })
|
|
855
970
|
] });
|
|
856
971
|
}
|
|
857
972
|
function Skeleton({ rows = 3 }) {
|
|
858
|
-
return /* @__PURE__ */
|
|
973
|
+
return /* @__PURE__ */ jsx3("div", { className: "ya-list", "aria-label": "Loading", role: "status", children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx3("div", { className: "ya-skel" }, i)) });
|
|
859
974
|
}
|
|
860
|
-
function useLoad(
|
|
861
|
-
const [data, setData] =
|
|
862
|
-
const [error, setError] =
|
|
863
|
-
|
|
975
|
+
function useLoad(load2, deps) {
|
|
976
|
+
const [data, setData] = useState2(null);
|
|
977
|
+
const [error, setError] = useState2(null);
|
|
978
|
+
useEffect2(() => {
|
|
864
979
|
let alive = true;
|
|
865
980
|
setData(null);
|
|
866
981
|
setError(null);
|
|
867
|
-
|
|
982
|
+
load2().then((d) => alive && setData(d)).catch((e) => alive && setError(e instanceof YouAskedError ? e.code : "network"));
|
|
868
983
|
return () => {
|
|
869
984
|
alive = false;
|
|
870
985
|
};
|
|
@@ -904,9 +1019,9 @@ function ago(t) {
|
|
|
904
1019
|
return new Date(t).toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
905
1020
|
}
|
|
906
1021
|
function AppAvatar({ icon, size = 36 }) {
|
|
907
|
-
const [failed, setFailed] =
|
|
1022
|
+
const [failed, setFailed] = useState2(false);
|
|
908
1023
|
if (!icon || failed) return null;
|
|
909
|
-
return /* @__PURE__ */
|
|
1024
|
+
return /* @__PURE__ */ jsx3(
|
|
910
1025
|
"img",
|
|
911
1026
|
{
|
|
912
1027
|
className: "ya-avatar",
|
|
@@ -927,31 +1042,31 @@ function Header({
|
|
|
927
1042
|
onClose,
|
|
928
1043
|
right
|
|
929
1044
|
}) {
|
|
930
|
-
return /* @__PURE__ */
|
|
931
|
-
onBack && /* @__PURE__ */
|
|
1045
|
+
return /* @__PURE__ */ jsxs3("div", { className: onBack ? "ya-head ya-head-back" : "ya-head", children: [
|
|
1046
|
+
onBack && /* @__PURE__ */ jsx3("button", { type: "button", className: "ya-icon-btn", onClick: onBack, "aria-label": "Back", children: /* @__PURE__ */ jsx3(IconBack, {}) }),
|
|
932
1047
|
leading,
|
|
933
|
-
/* @__PURE__ */
|
|
934
|
-
/* @__PURE__ */
|
|
935
|
-
subtitle && /* @__PURE__ */
|
|
1048
|
+
/* @__PURE__ */ jsxs3("div", { className: "ya-head-text", children: [
|
|
1049
|
+
/* @__PURE__ */ jsx3("h2", { className: "ya-h", children: title }),
|
|
1050
|
+
subtitle && /* @__PURE__ */ jsx3("p", { className: "ya-sub", children: subtitle })
|
|
936
1051
|
] }),
|
|
937
1052
|
right,
|
|
938
|
-
onClose && /* @__PURE__ */
|
|
1053
|
+
onClose && /* @__PURE__ */ jsx3("button", { type: "button", className: "ya-icon-btn", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx3(IconClose, {}) })
|
|
939
1054
|
] });
|
|
940
1055
|
}
|
|
941
1056
|
|
|
942
1057
|
// src/views.tsx
|
|
943
|
-
import { useEffect as
|
|
944
|
-
import { Fragment, jsx as
|
|
1058
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
1059
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
945
1060
|
function Navigator({ route, setRoute, onClose }) {
|
|
946
1061
|
switch (route.view) {
|
|
947
1062
|
case "home":
|
|
948
|
-
return /* @__PURE__ */
|
|
1063
|
+
return /* @__PURE__ */ jsx4(Home, { route, setRoute, onClose }, `${route.draft ?? ""}${route.focus ? "f" : ""}${route.topic ?? ""}`);
|
|
949
1064
|
case "board":
|
|
950
|
-
return /* @__PURE__ */
|
|
1065
|
+
return /* @__PURE__ */ jsx4(Board, { initialTab: route.tab, setRoute, onClose });
|
|
951
1066
|
case "detail":
|
|
952
|
-
return /* @__PURE__ */
|
|
1067
|
+
return /* @__PURE__ */ jsx4(Detail, { id: route.id, from: route.from, setRoute, onClose }, route.id);
|
|
953
1068
|
case "mine":
|
|
954
|
-
return /* @__PURE__ */
|
|
1069
|
+
return /* @__PURE__ */ jsx4(Mine, { setRoute, onClose });
|
|
955
1070
|
}
|
|
956
1071
|
}
|
|
957
1072
|
function splitMessage(text) {
|
|
@@ -963,23 +1078,23 @@ function splitMessage(text) {
|
|
|
963
1078
|
return { title, body: t };
|
|
964
1079
|
}
|
|
965
1080
|
function ReplyBubble({ notice, appName, onOpen }) {
|
|
966
|
-
return /* @__PURE__ */
|
|
967
|
-
/* @__PURE__ */
|
|
1081
|
+
return /* @__PURE__ */ jsxs4("div", { className: "ya-bubble ya-bubble-in", children: [
|
|
1082
|
+
/* @__PURE__ */ jsxs4("span", { className: "ya-bubble-kicker", children: [
|
|
968
1083
|
appName,
|
|
969
1084
|
" replied about ",
|
|
970
1085
|
notice.title
|
|
971
1086
|
] }),
|
|
972
|
-
/* @__PURE__ */
|
|
973
|
-
/* @__PURE__ */
|
|
1087
|
+
/* @__PURE__ */ jsx4("span", { children: notice.reply }),
|
|
1088
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "ya-bubble-link", onClick: onOpen, children: "Reply" })
|
|
974
1089
|
] });
|
|
975
1090
|
}
|
|
976
1091
|
var MAX_SHOTS = 3;
|
|
977
1092
|
function Confetti() {
|
|
978
1093
|
const colors = ["var(--_accent)", "var(--_green)", "var(--_amber)", "#ff7eb3"];
|
|
979
|
-
return /* @__PURE__ */
|
|
1094
|
+
return /* @__PURE__ */ jsx4("span", { className: "ya-confetti", "aria-hidden": "true", children: Array.from({ length: 14 }, (_, i) => {
|
|
980
1095
|
const angle = Math.PI * (0.15 + i / 13 * 0.7) % Math.PI;
|
|
981
1096
|
const dist = 38 + i % 4 * 14;
|
|
982
|
-
return /* @__PURE__ */
|
|
1097
|
+
return /* @__PURE__ */ jsx4(
|
|
983
1098
|
"i",
|
|
984
1099
|
{
|
|
985
1100
|
style: {
|
|
@@ -998,33 +1113,33 @@ function Home({ route, setRoute, onClose }) {
|
|
|
998
1113
|
const { client, config, appName, appIcon, greeting, markSeen, whatsNew, dismissWhatsNew, pointAtIt } = useInternal();
|
|
999
1114
|
const top = useLoad(() => client.list("top", "active"), [client]);
|
|
1000
1115
|
const shipped = useLoad(() => client.list("new", "shipped"), [client]);
|
|
1001
|
-
const [text, setText] =
|
|
1002
|
-
const [thread, setThread] =
|
|
1003
|
-
const [similar, setSimilar] =
|
|
1004
|
-
const [notices] =
|
|
1005
|
-
const [news] =
|
|
1006
|
-
const [shots, setShots] =
|
|
1007
|
-
const [shotError, setShotError] =
|
|
1008
|
-
const [credit, setCredit] =
|
|
1009
|
-
const urls =
|
|
1010
|
-
const inputRef =
|
|
1011
|
-
const bodyRef =
|
|
1116
|
+
const [text, setText] = useState3(route.draft ?? "");
|
|
1117
|
+
const [thread, setThread] = useState3([]);
|
|
1118
|
+
const [similar, setSimilar] = useState3([]);
|
|
1119
|
+
const [notices] = useState3(() => config?.unseen ?? []);
|
|
1120
|
+
const [news] = useState3(() => whatsNew);
|
|
1121
|
+
const [shots, setShots] = useState3([]);
|
|
1122
|
+
const [shotError, setShotError] = useState3(null);
|
|
1123
|
+
const [credit, setCredit] = useState3(false);
|
|
1124
|
+
const urls = useRef3([]);
|
|
1125
|
+
const inputRef = useRef3(null);
|
|
1126
|
+
const bodyRef = useRef3(null);
|
|
1012
1127
|
const sending = thread.some((t) => t.kind === "typing");
|
|
1013
|
-
|
|
1128
|
+
useEffect3(() => {
|
|
1014
1129
|
if (notices.length) markSeen(notices.map((n) => n.id));
|
|
1015
1130
|
}, [notices, markSeen]);
|
|
1016
|
-
|
|
1131
|
+
useEffect3(() => {
|
|
1017
1132
|
if (news) dismissWhatsNew();
|
|
1018
1133
|
}, [news, dismissWhatsNew]);
|
|
1019
1134
|
const arrived = config?.unseen.filter((n) => n.kind === "reply" && !notices.some((o) => o.id === n.id)) ?? [];
|
|
1020
1135
|
const arrivedKey = arrived.map((n) => n.id).join(",");
|
|
1021
|
-
|
|
1136
|
+
useEffect3(() => {
|
|
1022
1137
|
if (!arrived.length) return;
|
|
1023
1138
|
setThread((th) => [...th, ...arrived.map((notice) => ({ kind: "reply", notice }))]);
|
|
1024
1139
|
markSeen(arrived.map((n) => n.id));
|
|
1025
1140
|
}, [arrivedKey]);
|
|
1026
|
-
|
|
1027
|
-
|
|
1141
|
+
useEffect3(() => () => urls.current.forEach((u) => URL.revokeObjectURL(u)), []);
|
|
1142
|
+
useEffect3(() => {
|
|
1028
1143
|
if (!route.focus) return;
|
|
1029
1144
|
const t = setTimeout(() => {
|
|
1030
1145
|
const el = inputRef.current;
|
|
@@ -1034,16 +1149,16 @@ function Home({ route, setRoute, onClose }) {
|
|
|
1034
1149
|
}, 60);
|
|
1035
1150
|
return () => clearTimeout(t);
|
|
1036
1151
|
}, [route.focus]);
|
|
1037
|
-
|
|
1152
|
+
useEffect3(() => {
|
|
1038
1153
|
const el = inputRef.current;
|
|
1039
1154
|
if (!el) return;
|
|
1040
1155
|
el.style.height = "";
|
|
1041
1156
|
if (text.includes("\n") || el.scrollHeight > el.clientHeight + 2) el.style.height = `${Math.min(el.scrollHeight, 132)}px`;
|
|
1042
1157
|
}, [text]);
|
|
1043
|
-
|
|
1158
|
+
useEffect3(() => {
|
|
1044
1159
|
if (thread.length) bodyRef.current?.scrollTo({ top: bodyRef.current.scrollHeight, behavior: "smooth" });
|
|
1045
1160
|
}, [thread]);
|
|
1046
|
-
|
|
1161
|
+
useEffect3(() => {
|
|
1047
1162
|
const q = text.trim();
|
|
1048
1163
|
if (q.length < 4) {
|
|
1049
1164
|
setSimilar([]);
|
|
@@ -1104,106 +1219,106 @@ function Home({ route, setRoute, onClose }) {
|
|
|
1104
1219
|
}
|
|
1105
1220
|
};
|
|
1106
1221
|
const chatting = thread.length > 0;
|
|
1107
|
-
const openRequest = (id) => setRoute({ view: "detail", id });
|
|
1222
|
+
const openRequest = (id) => setRoute({ view: "detail", id, from: "home" });
|
|
1108
1223
|
const asking = route.topic && route.placeholder ? route.topic : null;
|
|
1109
1224
|
const placeholder = route.placeholder ?? "Ask for a feature\u2026";
|
|
1110
1225
|
const yours = news?.items.find((i) => i.mine);
|
|
1111
1226
|
const listed = new Set(news?.items.map((i) => i.id));
|
|
1112
1227
|
const shownNotices = notices.filter((n) => !(n.kind === "shipped" && listed.has(n.requestId)));
|
|
1113
|
-
return /* @__PURE__ */
|
|
1114
|
-
/* @__PURE__ */
|
|
1228
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1229
|
+
/* @__PURE__ */ jsx4(
|
|
1115
1230
|
Header,
|
|
1116
1231
|
{
|
|
1117
|
-
leading: /* @__PURE__ */
|
|
1232
|
+
leading: /* @__PURE__ */ jsx4(AppAvatar, { icon: appIcon }),
|
|
1118
1233
|
title: appName,
|
|
1119
1234
|
subtitle: "Ideas, votes and what's new",
|
|
1120
1235
|
onClose
|
|
1121
1236
|
}
|
|
1122
1237
|
),
|
|
1123
|
-
/* @__PURE__ */
|
|
1124
|
-
/* @__PURE__ */
|
|
1125
|
-
news && /* @__PURE__ */
|
|
1126
|
-
yours && /* @__PURE__ */
|
|
1127
|
-
/* @__PURE__ */
|
|
1128
|
-
/* @__PURE__ */
|
|
1129
|
-
/* @__PURE__ */
|
|
1130
|
-
/* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1132
|
-
i.mine && /* @__PURE__ */
|
|
1238
|
+
/* @__PURE__ */ jsxs4("div", { className: "ya-body ya-chat", ref: bodyRef, children: [
|
|
1239
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-bubble ya-bubble-in", children: asking ? `Let's talk about ${asking}. What's missing, or what bugs you?` : greeting ?? `Hey! What would make ${appName} better for you?` }),
|
|
1240
|
+
news && /* @__PURE__ */ jsxs4("div", { className: yours ? "ya-bubble ya-bubble-in ya-bubble-win ya-bubble-news" : "ya-bubble ya-bubble-in ya-bubble-news", children: [
|
|
1241
|
+
yours && /* @__PURE__ */ jsx4(Confetti, {}),
|
|
1242
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-bubble-kicker", children: whatsNewHeading(news.total) }),
|
|
1243
|
+
/* @__PURE__ */ jsx4("ul", { className: "ya-news", children: news.items.map((i) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs4("button", { type: "button", onClick: () => openRequest(i.id), children: [
|
|
1244
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-shipped-dot", children: /* @__PURE__ */ jsx4(IconCheck, {}) }),
|
|
1245
|
+
/* @__PURE__ */ jsxs4("span", { className: "ya-news-text", children: [
|
|
1246
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-news-title", children: i.title }),
|
|
1247
|
+
i.mine && /* @__PURE__ */ jsx4("span", { className: "ya-news-you", children: "You asked for this" })
|
|
1133
1248
|
] }),
|
|
1134
|
-
/* @__PURE__ */
|
|
1249
|
+
/* @__PURE__ */ jsx4(IconChevron, {})
|
|
1135
1250
|
] }) }, i.id)) }),
|
|
1136
|
-
yours && /* @__PURE__ */
|
|
1137
|
-
news.total > news.items.length && /* @__PURE__ */
|
|
1251
|
+
yours && /* @__PURE__ */ jsx4("span", { children: "That one was yours. Thanks for asking." }),
|
|
1252
|
+
news.total > news.items.length && /* @__PURE__ */ jsxs4("button", { type: "button", className: "ya-bubble-link", onClick: () => setRoute({ view: "board", tab: "shipped" }), children: [
|
|
1138
1253
|
"See all ",
|
|
1139
1254
|
news.total
|
|
1140
1255
|
] })
|
|
1141
1256
|
] }),
|
|
1142
1257
|
shownNotices.map(
|
|
1143
|
-
(n) => n.kind === "reply" ? /* @__PURE__ */
|
|
1144
|
-
/* @__PURE__ */
|
|
1145
|
-
/* @__PURE__ */
|
|
1258
|
+
(n) => n.kind === "reply" ? /* @__PURE__ */ jsx4(ReplyBubble, { notice: n, appName, onOpen: () => openRequest(n.requestId) }, n.id) : /* @__PURE__ */ jsxs4("div", { className: "ya-bubble ya-bubble-in ya-bubble-win", children: [
|
|
1259
|
+
/* @__PURE__ */ jsxs4("span", { className: "ya-live", children: [
|
|
1260
|
+
/* @__PURE__ */ jsx4(IconCheck, {}),
|
|
1146
1261
|
"It's live"
|
|
1147
1262
|
] }),
|
|
1148
|
-
/* @__PURE__ */
|
|
1263
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
1149
1264
|
"You asked for ",
|
|
1150
|
-
/* @__PURE__ */
|
|
1265
|
+
/* @__PURE__ */ jsx4("b", { children: n.title }),
|
|
1151
1266
|
". ",
|
|
1152
1267
|
n.shipNote ?? "Thanks for asking."
|
|
1153
1268
|
] }),
|
|
1154
|
-
/* @__PURE__ */
|
|
1269
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "ya-bubble-link", onClick: () => openRequest(n.requestId), children: "See it" })
|
|
1155
1270
|
] }, n.id)
|
|
1156
1271
|
),
|
|
1157
|
-
!chatting && /* @__PURE__ */
|
|
1158
|
-
/* @__PURE__ */
|
|
1159
|
-
shipped.data && shipped.data.length > 0 && /* @__PURE__ */
|
|
1160
|
-
/* @__PURE__ */
|
|
1161
|
-
/* @__PURE__ */
|
|
1162
|
-
/* @__PURE__ */
|
|
1272
|
+
!chatting && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1273
|
+
/* @__PURE__ */ jsx4(Section, { title: "Popular right now", action: "See all", onAction: () => setRoute({ view: "board" }), children: top.error ? /* @__PURE__ */ jsx4("p", { className: "ya-note", children: errorText(top.error) }) : !top.data ? /* @__PURE__ */ jsx4(Skeleton, { rows: 2 }) : top.data.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "ya-note", children: "No requests yet. Yours could be the first." }) : /* @__PURE__ */ jsx4("ul", { className: "ya-list", children: top.data.slice(0, 3).map((r) => /* @__PURE__ */ jsx4(RequestRow, { client, request: r, compact: true, onOpen: () => openRequest(r.id) }, r.id)) }) }),
|
|
1274
|
+
shipped.data && shipped.data.length > 0 && /* @__PURE__ */ jsx4(Section, { title: "Recently shipped", action: "All", onAction: () => setRoute({ view: "board", tab: "shipped" }), children: /* @__PURE__ */ jsx4("ul", { className: "ya-shipped", children: shipped.data.slice(0, 3).map((r) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs4("button", { type: "button", onClick: () => openRequest(r.id), children: [
|
|
1275
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-shipped-dot", children: /* @__PURE__ */ jsx4(IconCheck, {}) }),
|
|
1276
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-shipped-title", children: r.title }),
|
|
1277
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-shipped-when", children: r.shippedAt ? ago(r.shippedAt) : "" })
|
|
1163
1278
|
] }) }, r.id)) }) })
|
|
1164
1279
|
] }),
|
|
1165
1280
|
thread.map(
|
|
1166
|
-
(t, i) => t.kind === "out" ? /* @__PURE__ */
|
|
1167
|
-
t.images?.map((src) => /* @__PURE__ */
|
|
1281
|
+
(t, i) => t.kind === "out" ? /* @__PURE__ */ jsxs4("div", { className: "ya-bubble ya-bubble-out", children: [
|
|
1282
|
+
t.images?.map((src) => /* @__PURE__ */ jsx4("img", { className: "ya-bubble-img", src, alt: "Your screenshot" }, src)),
|
|
1168
1283
|
t.text
|
|
1169
|
-
] }, i) : t.kind === "typing" ? /* @__PURE__ */
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
] }, i) : t.kind === "reply" ? /* @__PURE__ */
|
|
1174
|
-
/* @__PURE__ */
|
|
1284
|
+
] }, i) : t.kind === "typing" ? /* @__PURE__ */ jsxs4("div", { className: "ya-bubble ya-bubble-in ya-typing", "aria-label": "Posting", children: [
|
|
1285
|
+
/* @__PURE__ */ jsx4("span", {}),
|
|
1286
|
+
/* @__PURE__ */ jsx4("span", {}),
|
|
1287
|
+
/* @__PURE__ */ jsx4("span", {})
|
|
1288
|
+
] }, i) : t.kind === "reply" ? /* @__PURE__ */ jsx4(ReplyBubble, { notice: t.notice, appName, onOpen: () => openRequest(t.notice.requestId) }, i) : t.kind === "posted" ? /* @__PURE__ */ jsxs4("div", { className: "ya-bubble ya-bubble-in", children: [
|
|
1289
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
1175
1290
|
"Got it. ",
|
|
1176
|
-
/* @__PURE__ */
|
|
1291
|
+
/* @__PURE__ */ jsx4("b", { children: t.request.title }),
|
|
1177
1292
|
" is on the board, and we'll tell you right here when it ships."
|
|
1178
1293
|
] }),
|
|
1179
|
-
/* @__PURE__ */
|
|
1180
|
-
] }, i) : /* @__PURE__ */
|
|
1294
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "ya-bubble-link", onClick: () => openRequest(t.request.id), children: "View request" })
|
|
1295
|
+
] }, i) : /* @__PURE__ */ jsx4("div", { className: "ya-bubble ya-bubble-in ya-bubble-error", role: "alert", children: t.text }, i)
|
|
1181
1296
|
),
|
|
1182
|
-
chatting && !sending && /* @__PURE__ */
|
|
1297
|
+
chatting && !sending && /* @__PURE__ */ jsx4("button", { type: "button", className: "ya-chip", onClick: () => setRoute({ view: "board" }), children: "See what others asked for" })
|
|
1183
1298
|
] }),
|
|
1184
|
-
/* @__PURE__ */
|
|
1185
|
-
similar.length > 0 && /* @__PURE__ */
|
|
1186
|
-
/* @__PURE__ */
|
|
1187
|
-
/* @__PURE__ */
|
|
1299
|
+
/* @__PURE__ */ jsxs4("div", { className: "ya-composer-wrap", children: [
|
|
1300
|
+
similar.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "ya-tray", "aria-live": "polite", children: [
|
|
1301
|
+
/* @__PURE__ */ jsx4("p", { className: "ya-tray-h", children: "Sounds like one of these. Vote instead?" }),
|
|
1302
|
+
/* @__PURE__ */ jsx4("ul", { className: "ya-list", children: similar.map((r) => /* @__PURE__ */ jsx4(RequestRow, { client, request: r, compact: true, onOpen: () => openRequest(r.id) }, r.id)) })
|
|
1188
1303
|
] }),
|
|
1189
|
-
shots.length > 0 && /* @__PURE__ */
|
|
1190
|
-
/* @__PURE__ */
|
|
1191
|
-
/* @__PURE__ */
|
|
1304
|
+
shots.length > 0 && /* @__PURE__ */ jsx4("ul", { className: "ya-shots", "aria-label": "Screenshots", children: shots.map((s, i) => /* @__PURE__ */ jsxs4("li", { className: "ya-shot", children: [
|
|
1305
|
+
/* @__PURE__ */ jsx4("img", { src: s.url, alt: "" }),
|
|
1306
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
1192
1307
|
"Screenshot",
|
|
1193
1308
|
shots.length > 1 ? ` ${i + 1}` : ""
|
|
1194
1309
|
] }),
|
|
1195
|
-
/* @__PURE__ */
|
|
1310
|
+
/* @__PURE__ */ jsx4(
|
|
1196
1311
|
"button",
|
|
1197
1312
|
{
|
|
1198
1313
|
type: "button",
|
|
1199
1314
|
className: "ya-shot-x",
|
|
1200
1315
|
"aria-label": `Remove screenshot${shots.length > 1 ? ` ${i + 1}` : ""}`,
|
|
1201
1316
|
onClick: () => setShots((all) => all.filter((x) => x !== s)),
|
|
1202
|
-
children: /* @__PURE__ */
|
|
1317
|
+
children: /* @__PURE__ */ jsx4(IconClose, {})
|
|
1203
1318
|
}
|
|
1204
1319
|
)
|
|
1205
1320
|
] }, s.url)) }),
|
|
1206
|
-
/* @__PURE__ */
|
|
1321
|
+
/* @__PURE__ */ jsxs4(
|
|
1207
1322
|
"form",
|
|
1208
1323
|
{
|
|
1209
1324
|
className: "ya-composer",
|
|
@@ -1212,7 +1327,7 @@ function Home({ route, setRoute, onClose }) {
|
|
|
1212
1327
|
send();
|
|
1213
1328
|
},
|
|
1214
1329
|
children: [
|
|
1215
|
-
/* @__PURE__ */
|
|
1330
|
+
/* @__PURE__ */ jsx4(
|
|
1216
1331
|
"textarea",
|
|
1217
1332
|
{
|
|
1218
1333
|
ref: inputRef,
|
|
@@ -1231,12 +1346,12 @@ function Home({ route, setRoute, onClose }) {
|
|
|
1231
1346
|
}
|
|
1232
1347
|
}
|
|
1233
1348
|
),
|
|
1234
|
-
/* @__PURE__ */
|
|
1349
|
+
/* @__PURE__ */ jsx4("button", { type: "submit", className: "ya-send", disabled: text.trim().length < 3 || sending, "aria-label": "Send", children: /* @__PURE__ */ jsx4(IconSend, {}) })
|
|
1235
1350
|
]
|
|
1236
1351
|
}
|
|
1237
1352
|
),
|
|
1238
|
-
/* @__PURE__ */
|
|
1239
|
-
/* @__PURE__ */
|
|
1353
|
+
/* @__PURE__ */ jsxs4("div", { className: "ya-tools", children: [
|
|
1354
|
+
/* @__PURE__ */ jsxs4(
|
|
1240
1355
|
"button",
|
|
1241
1356
|
{
|
|
1242
1357
|
type: "button",
|
|
@@ -1246,18 +1361,18 @@ function Home({ route, setRoute, onClose }) {
|
|
|
1246
1361
|
onFocus: () => void import("./capture-FYZGL54F.js"),
|
|
1247
1362
|
disabled: sending || shots.length >= MAX_SHOTS,
|
|
1248
1363
|
children: [
|
|
1249
|
-
/* @__PURE__ */
|
|
1364
|
+
/* @__PURE__ */ jsx4(IconFrame, {}),
|
|
1250
1365
|
"Show us"
|
|
1251
1366
|
]
|
|
1252
1367
|
}
|
|
1253
1368
|
),
|
|
1254
|
-
config?.canCredit && text.trim().length > 0 && /* @__PURE__ */
|
|
1369
|
+
config?.canCredit && text.trim().length > 0 && /* @__PURE__ */ jsxs4("button", { type: "button", className: "ya-switch-row", role: "switch", "aria-checked": credit, onClick: () => setCredit((c) => !c), children: [
|
|
1255
1370
|
"Credit me when it ships",
|
|
1256
|
-
/* @__PURE__ */
|
|
1371
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-switch", "aria-hidden": "true" })
|
|
1257
1372
|
] })
|
|
1258
1373
|
] }),
|
|
1259
|
-
shotError && /* @__PURE__ */
|
|
1260
|
-
config?.watermark && /* @__PURE__ */
|
|
1374
|
+
shotError && /* @__PURE__ */ jsx4("p", { className: "ya-note", role: "alert", children: shotError }),
|
|
1375
|
+
config?.watermark && /* @__PURE__ */ jsx4("p", { className: "ya-mark", children: "Feedback by YouAsked" })
|
|
1261
1376
|
] })
|
|
1262
1377
|
] });
|
|
1263
1378
|
}
|
|
@@ -1267,12 +1382,12 @@ function Section({
|
|
|
1267
1382
|
onAction,
|
|
1268
1383
|
children
|
|
1269
1384
|
}) {
|
|
1270
|
-
return /* @__PURE__ */
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
/* @__PURE__ */
|
|
1273
|
-
action && /* @__PURE__ */
|
|
1385
|
+
return /* @__PURE__ */ jsxs4("section", { className: "ya-section", children: [
|
|
1386
|
+
/* @__PURE__ */ jsxs4("div", { className: "ya-section-head", children: [
|
|
1387
|
+
/* @__PURE__ */ jsx4("h3", { children: title }),
|
|
1388
|
+
action && /* @__PURE__ */ jsxs4("button", { type: "button", className: "ya-link", onClick: onAction, children: [
|
|
1274
1389
|
action,
|
|
1275
|
-
/* @__PURE__ */
|
|
1390
|
+
/* @__PURE__ */ jsx4(IconChevron, {})
|
|
1276
1391
|
] })
|
|
1277
1392
|
] }),
|
|
1278
1393
|
children
|
|
@@ -1289,36 +1404,36 @@ function Board({
|
|
|
1289
1404
|
onClose
|
|
1290
1405
|
}) {
|
|
1291
1406
|
const { client, title } = useInternal();
|
|
1292
|
-
const [tab, setTab] =
|
|
1407
|
+
const [tab, setTab] = useState3(TABS.find((t) => t.key === initialTab) ?? TABS[0]);
|
|
1293
1408
|
const { data, error } = useLoad(() => client.list(tab.sort, tab.status), [client, tab.key]);
|
|
1294
|
-
return /* @__PURE__ */
|
|
1295
|
-
/* @__PURE__ */
|
|
1409
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1410
|
+
/* @__PURE__ */ jsx4(
|
|
1296
1411
|
Header,
|
|
1297
1412
|
{
|
|
1298
1413
|
title,
|
|
1299
1414
|
onBack: () => setRoute({ view: "home" }),
|
|
1300
1415
|
onClose,
|
|
1301
|
-
right: /* @__PURE__ */
|
|
1416
|
+
right: /* @__PURE__ */ jsx4("button", { type: "button", className: "ya-link", onClick: () => setRoute({ view: "mine" }), children: "Yours" })
|
|
1302
1417
|
}
|
|
1303
1418
|
),
|
|
1304
|
-
/* @__PURE__ */
|
|
1305
|
-
/* @__PURE__ */
|
|
1306
|
-
/* @__PURE__ */
|
|
1419
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-tabs", role: "tablist", "aria-label": "Sort requests", children: TABS.map((t) => /* @__PURE__ */ jsx4("button", { type: "button", role: "tab", className: "ya-tab", "aria-selected": tab.key === t.key, onClick: () => setTab(t), children: t.label }, t.key)) }),
|
|
1420
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-body", children: error ? /* @__PURE__ */ jsx4("p", { className: "ya-empty", children: errorText(error) }) : !data ? /* @__PURE__ */ jsx4(Skeleton, {}) : data.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "ya-empty", children: [
|
|
1421
|
+
/* @__PURE__ */ jsx4("b", { children: tab.key === "shipped" ? "Nothing shipped yet" : "No requests yet" }),
|
|
1307
1422
|
tab.key === "shipped" ? "Requests show up here once they're live." : "Tell us what you'd like next. Others can vote on it."
|
|
1308
|
-
] }) : /* @__PURE__ */
|
|
1309
|
-
/* @__PURE__ */
|
|
1310
|
-
/* @__PURE__ */
|
|
1423
|
+
] }) : /* @__PURE__ */ jsx4("ul", { className: "ya-list", children: data.map((r) => /* @__PURE__ */ jsx4(RequestRow, { client, request: r, onOpen: () => setRoute({ view: "detail", id: r.id, from: "board" }) }, r.id)) }) }),
|
|
1424
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-foot", children: /* @__PURE__ */ jsxs4("button", { type: "button", className: "ya-btn", onClick: () => setRoute({ view: "home", focus: true }), children: [
|
|
1425
|
+
/* @__PURE__ */ jsx4(IconPlus, {}),
|
|
1311
1426
|
"Request a feature"
|
|
1312
1427
|
] }) })
|
|
1313
1428
|
] });
|
|
1314
1429
|
}
|
|
1315
|
-
function Detail({ id, setRoute, onClose }) {
|
|
1430
|
+
function Detail({ id, from, setRoute, onClose }) {
|
|
1316
1431
|
const { client, config, markSeen, appName, appIcon } = useInternal();
|
|
1317
1432
|
const { data, error, setData } = useLoad(() => client.get(id), [client, id]);
|
|
1318
1433
|
const unseenHere = config?.unseen.filter((n) => n.requestId === id).map((n) => n.id).join(",") ?? "";
|
|
1319
|
-
const loaded =
|
|
1434
|
+
const loaded = useRef3(false);
|
|
1320
1435
|
loaded.current = Boolean(data);
|
|
1321
|
-
|
|
1436
|
+
useEffect3(() => {
|
|
1322
1437
|
if (!unseenHere) return;
|
|
1323
1438
|
markSeen(unseenHere.split(","));
|
|
1324
1439
|
if (loaded.current) {
|
|
@@ -1326,15 +1441,15 @@ function Detail({ id, setRoute, onClose }) {
|
|
|
1326
1441
|
});
|
|
1327
1442
|
}
|
|
1328
1443
|
}, [unseenHere, markSeen]);
|
|
1329
|
-
const [reply, setReply] =
|
|
1330
|
-
const [sending, setSending] =
|
|
1331
|
-
const [replyError, setReplyError] =
|
|
1332
|
-
const [voted, setVoted] =
|
|
1333
|
-
const [early, setEarly] =
|
|
1334
|
-
|
|
1335
|
-
const [confirmDelete, setConfirmDelete] =
|
|
1336
|
-
const [deleting, setDeleting] =
|
|
1337
|
-
const [deleteError, setDeleteError] =
|
|
1444
|
+
const [reply, setReply] = useState3("");
|
|
1445
|
+
const [sending, setSending] = useState3(false);
|
|
1446
|
+
const [replyError, setReplyError] = useState3(null);
|
|
1447
|
+
const [voted, setVoted] = useState3(null);
|
|
1448
|
+
const [early, setEarly] = useState3(false);
|
|
1449
|
+
useEffect3(() => setEarly(Boolean(data?.earlyAccess)), [data?.earlyAccess]);
|
|
1450
|
+
const [confirmDelete, setConfirmDelete] = useState3(false);
|
|
1451
|
+
const [deleting, setDeleting] = useState3(false);
|
|
1452
|
+
const [deleteError, setDeleteError] = useState3(null);
|
|
1338
1453
|
const remove = async () => {
|
|
1339
1454
|
if (!data?.request || deleting) return;
|
|
1340
1455
|
setDeleting(true);
|
|
@@ -1357,7 +1472,7 @@ function Detail({ id, setRoute, onClose }) {
|
|
|
1357
1472
|
setEarly(before);
|
|
1358
1473
|
}
|
|
1359
1474
|
};
|
|
1360
|
-
|
|
1475
|
+
useEffect3(() => {
|
|
1361
1476
|
if (data?.redirect) setRoute({ view: "detail", id: data.redirect });
|
|
1362
1477
|
}, [data?.redirect, setRoute]);
|
|
1363
1478
|
const request = data?.request;
|
|
@@ -1377,58 +1492,58 @@ function Detail({ id, setRoute, onClose }) {
|
|
|
1377
1492
|
setSending(false);
|
|
1378
1493
|
}
|
|
1379
1494
|
};
|
|
1380
|
-
return /* @__PURE__ */
|
|
1381
|
-
/* @__PURE__ */
|
|
1382
|
-
/* @__PURE__ */
|
|
1383
|
-
/* @__PURE__ */
|
|
1384
|
-
request.status !== "open" && /* @__PURE__ */
|
|
1385
|
-
/* @__PURE__ */
|
|
1495
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1496
|
+
/* @__PURE__ */ jsx4(Header, { title: "", onBack: () => setRoute({ view: from ?? "board" }), onClose }),
|
|
1497
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-body", children: error ? /* @__PURE__ */ jsx4("p", { className: "ya-empty", children: error === "not_found" ? "This request was removed." : errorText(error) }) : !request ? /* @__PURE__ */ jsx4(Skeleton, {}) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1498
|
+
/* @__PURE__ */ jsxs4("div", { className: "ya-detail-meta", children: [
|
|
1499
|
+
request.status !== "open" && /* @__PURE__ */ jsx4(StatusPill, { status: request.status }),
|
|
1500
|
+
/* @__PURE__ */ jsxs4("span", { className: "ya-ref", children: [
|
|
1386
1501
|
"YA-",
|
|
1387
1502
|
request.number
|
|
1388
1503
|
] })
|
|
1389
1504
|
] }),
|
|
1390
|
-
/* @__PURE__ */
|
|
1391
|
-
request.body && request.body !== request.title && /* @__PURE__ */
|
|
1392
|
-
data?.attachments && data.attachments.length > 0 && /* @__PURE__ */
|
|
1393
|
-
/* @__PURE__ */
|
|
1394
|
-
(request.status === "planned" || request.status === "in_progress") && (voted ?? request.voted) && /* @__PURE__ */
|
|
1395
|
-
/* @__PURE__ */
|
|
1396
|
-
/* @__PURE__ */
|
|
1397
|
-
/* @__PURE__ */
|
|
1398
|
-
/* @__PURE__ */
|
|
1505
|
+
/* @__PURE__ */ jsx4("h3", { className: "ya-detail-title", children: request.title }),
|
|
1506
|
+
request.body && request.body !== request.title && /* @__PURE__ */ jsx4("p", { className: "ya-text", children: request.body }),
|
|
1507
|
+
data?.attachments && data.attachments.length > 0 && /* @__PURE__ */ jsx4("div", { className: "ya-attachments", children: data.attachments.map((a) => /* @__PURE__ */ jsx4("a", { href: a.url, target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx4("img", { src: a.url, alt: "Screenshot attached to this request", loading: "lazy" }) }, a.id)) }),
|
|
1508
|
+
/* @__PURE__ */ jsx4(BigVote, { client, request, onChange: setVoted }),
|
|
1509
|
+
(request.status === "planned" || request.status === "in_progress") && (voted ?? request.voted) && /* @__PURE__ */ jsxs4("button", { type: "button", className: "ya-early", role: "switch", "aria-checked": early, onClick: toggleEarly, children: [
|
|
1510
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-card-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(IconSpark, {}) }),
|
|
1511
|
+
/* @__PURE__ */ jsxs4("span", { className: "ya-menurow-text", children: [
|
|
1512
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-menurow-label", children: "Try it early" }),
|
|
1513
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-menurow-sub", children: early ? "You're on the list. We'll let you in first." : "Get access before everyone else" })
|
|
1399
1514
|
] }),
|
|
1400
|
-
/* @__PURE__ */
|
|
1515
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-switch", "aria-hidden": "true" })
|
|
1401
1516
|
] }),
|
|
1402
|
-
request.source && /* @__PURE__ */
|
|
1517
|
+
request.source && /* @__PURE__ */ jsxs4("p", { className: "ya-note", children: [
|
|
1403
1518
|
"Also asked",
|
|
1404
1519
|
" ",
|
|
1405
|
-
request.sourceUrl ? /* @__PURE__ */
|
|
1520
|
+
request.sourceUrl ? /* @__PURE__ */ jsx4("a", { href: request.sourceUrl, target: "_blank", rel: "noopener noreferrer", children: request.source === "reddit" ? "on Reddit" : "in App Store reviews" }) : request.source === "reddit" ? "on Reddit" : "in App Store reviews",
|
|
1406
1521
|
"."
|
|
1407
1522
|
] }),
|
|
1408
|
-
request.mine && request.status !== "shipped" && /* @__PURE__ */
|
|
1409
|
-
data?.canDelete && (confirmDelete ? /* @__PURE__ */
|
|
1523
|
+
request.mine && request.status !== "shipped" && /* @__PURE__ */ jsx4("p", { className: "ya-note", children: "You asked for this. We'll tell you here when it ships." }),
|
|
1524
|
+
data?.canDelete && (confirmDelete ? /* @__PURE__ */ jsxs4("p", { className: "ya-note ya-delete-row", children: [
|
|
1410
1525
|
"Delete this request? This can't be undone.",
|
|
1411
|
-
/* @__PURE__ */
|
|
1412
|
-
/* @__PURE__ */
|
|
1413
|
-
] }) : /* @__PURE__ */
|
|
1414
|
-
deleteError && /* @__PURE__ */
|
|
1415
|
-
request.status === "shipped" && /* @__PURE__ */
|
|
1416
|
-
/* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
1418
|
-
/* @__PURE__ */
|
|
1526
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "ya-link ya-link-danger", onClick: remove, disabled: deleting, children: deleting ? "Deleting\u2026" : "Delete" }),
|
|
1527
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "ya-link", onClick: () => setConfirmDelete(false), disabled: deleting, children: "Keep it" })
|
|
1528
|
+
] }) : /* @__PURE__ */ jsx4("p", { className: "ya-note ya-delete-row", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "ya-link ya-link-quiet", onClick: () => setConfirmDelete(true), children: "Delete my request" }) })),
|
|
1529
|
+
deleteError && /* @__PURE__ */ jsx4("p", { className: "ya-note", role: "alert", children: deleteError }),
|
|
1530
|
+
request.status === "shipped" && /* @__PURE__ */ jsxs4("div", { className: "ya-callout", "data-tone": "success", children: [
|
|
1531
|
+
/* @__PURE__ */ jsx4(IconCheck, {}),
|
|
1532
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
1533
|
+
/* @__PURE__ */ jsx4("b", { children: "It's live." }),
|
|
1419
1534
|
request.shipNote ? ` ${request.shipNote}` : " Thanks for asking.",
|
|
1420
|
-
request.creditName && /* @__PURE__ */
|
|
1535
|
+
request.creditName && /* @__PURE__ */ jsxs4("span", { className: "ya-credit", children: [
|
|
1421
1536
|
"Thanks to ",
|
|
1422
1537
|
request.creditName,
|
|
1423
1538
|
" for asking."
|
|
1424
1539
|
] })
|
|
1425
1540
|
] })
|
|
1426
1541
|
] }),
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
comments.length === 0 ? /* @__PURE__ */
|
|
1542
|
+
/* @__PURE__ */ jsx4("p", { className: "ya-section-h", children: "Conversation" }),
|
|
1543
|
+
comments.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "ya-text ya-text-muted", children: "No messages yet. Add details or ask a question below." }) : /* @__PURE__ */ jsx4("div", { className: "ya-thread", children: comments.map((c) => /* @__PURE__ */ jsx4(Message, { comment: c, appName, appIcon }, c.id)) })
|
|
1429
1544
|
] }) }),
|
|
1430
|
-
request && /* @__PURE__ */
|
|
1431
|
-
/* @__PURE__ */
|
|
1545
|
+
request && /* @__PURE__ */ jsxs4("form", { className: "ya-composer ya-composer-bar", onSubmit: send, children: [
|
|
1546
|
+
/* @__PURE__ */ jsx4(
|
|
1432
1547
|
"textarea",
|
|
1433
1548
|
{
|
|
1434
1549
|
className: "ya-composer-input",
|
|
@@ -1446,49 +1561,49 @@ function Detail({ id, setRoute, onClose }) {
|
|
|
1446
1561
|
}
|
|
1447
1562
|
}
|
|
1448
1563
|
),
|
|
1449
|
-
/* @__PURE__ */
|
|
1450
|
-
replyError && /* @__PURE__ */
|
|
1564
|
+
/* @__PURE__ */ jsx4("button", { type: "submit", className: "ya-send", disabled: !reply.trim() || sending, "aria-label": "Send comment", children: /* @__PURE__ */ jsx4(IconSend, {}) }),
|
|
1565
|
+
replyError && /* @__PURE__ */ jsx4("span", { className: "ya-sr", role: "alert", children: replyError })
|
|
1451
1566
|
] })
|
|
1452
1567
|
] });
|
|
1453
1568
|
}
|
|
1454
1569
|
function Message({ comment, appName, appIcon }) {
|
|
1455
|
-
const when = /* @__PURE__ */
|
|
1570
|
+
const when = /* @__PURE__ */ jsx4("time", { dateTime: new Date(comment.createdAt).toISOString(), children: ago(comment.createdAt) });
|
|
1456
1571
|
if (comment.mine) {
|
|
1457
|
-
return /* @__PURE__ */
|
|
1458
|
-
/* @__PURE__ */
|
|
1459
|
-
/* @__PURE__ */
|
|
1572
|
+
return /* @__PURE__ */ jsxs4("div", { className: "ya-msg ya-msg-out", children: [
|
|
1573
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-bubble ya-bubble-out", children: comment.body }),
|
|
1574
|
+
/* @__PURE__ */ jsx4("span", { className: "ya-msg-meta", children: when })
|
|
1460
1575
|
] });
|
|
1461
1576
|
}
|
|
1462
|
-
return /* @__PURE__ */
|
|
1463
|
-
comment.team && appIcon ? /* @__PURE__ */
|
|
1464
|
-
/* @__PURE__ */
|
|
1465
|
-
/* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
comment.team && /* @__PURE__ */
|
|
1577
|
+
return /* @__PURE__ */ jsxs4("div", { className: "ya-msg", children: [
|
|
1578
|
+
comment.team && appIcon ? /* @__PURE__ */ jsx4(AppAvatar, { icon: appIcon, size: 26 }) : null,
|
|
1579
|
+
/* @__PURE__ */ jsxs4("div", { className: "ya-msg-body", children: [
|
|
1580
|
+
/* @__PURE__ */ jsxs4("span", { className: "ya-msg-meta", children: [
|
|
1581
|
+
/* @__PURE__ */ jsx4("b", { children: comment.team ? appName : comment.author || "Someone" }),
|
|
1582
|
+
comment.team && /* @__PURE__ */ jsx4("span", { className: "ya-team", children: "Team" }),
|
|
1468
1583
|
when
|
|
1469
1584
|
] }),
|
|
1470
|
-
/* @__PURE__ */
|
|
1585
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-bubble ya-bubble-in", children: comment.body })
|
|
1471
1586
|
] })
|
|
1472
1587
|
] });
|
|
1473
1588
|
}
|
|
1474
1589
|
function Mine({ setRoute, onClose }) {
|
|
1475
1590
|
const { client } = useInternal();
|
|
1476
1591
|
const { data, error } = useLoad(() => client.mine(), [client]);
|
|
1477
|
-
return /* @__PURE__ */
|
|
1478
|
-
/* @__PURE__ */
|
|
1479
|
-
/* @__PURE__ */
|
|
1480
|
-
/* @__PURE__ */
|
|
1592
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1593
|
+
/* @__PURE__ */ jsx4(Header, { title: "Your requests", onBack: () => setRoute({ view: "board" }), onClose }),
|
|
1594
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-body", children: error ? /* @__PURE__ */ jsx4("p", { className: "ya-empty", children: errorText(error) }) : !data ? /* @__PURE__ */ jsx4(Skeleton, {}) : data.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "ya-empty", children: [
|
|
1595
|
+
/* @__PURE__ */ jsx4("b", { children: "Nothing yet" }),
|
|
1481
1596
|
"Requests you post or vote on show up here, with their status."
|
|
1482
|
-
] }) : /* @__PURE__ */
|
|
1483
|
-
/* @__PURE__ */
|
|
1484
|
-
/* @__PURE__ */
|
|
1597
|
+
] }) : /* @__PURE__ */ jsx4("ul", { className: "ya-list", children: data.map((r) => /* @__PURE__ */ jsx4(RequestRow, { client, request: r, onOpen: () => setRoute({ view: "detail", id: r.id, from: "mine" }) }, r.id)) }) }),
|
|
1598
|
+
/* @__PURE__ */ jsx4("div", { className: "ya-foot", children: /* @__PURE__ */ jsxs4("button", { type: "button", className: "ya-btn", onClick: () => setRoute({ view: "home", focus: true }), children: [
|
|
1599
|
+
/* @__PURE__ */ jsx4(IconPlus, {}),
|
|
1485
1600
|
"Request a feature"
|
|
1486
1601
|
] }) })
|
|
1487
1602
|
] });
|
|
1488
1603
|
}
|
|
1489
1604
|
|
|
1490
1605
|
// src/YouAsked.tsx
|
|
1491
|
-
import { Fragment as Fragment2, jsx as
|
|
1606
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1492
1607
|
var warnedTestMode = false;
|
|
1493
1608
|
var DEFAULT_API_URL = "https://api.youasked.dev";
|
|
1494
1609
|
function lastWhatsNew(key) {
|
|
@@ -1505,6 +1620,7 @@ function YouAsked({
|
|
|
1505
1620
|
appKey = "",
|
|
1506
1621
|
demo = false,
|
|
1507
1622
|
cards = true,
|
|
1623
|
+
nudge: nudgeProp,
|
|
1508
1624
|
user,
|
|
1509
1625
|
signature,
|
|
1510
1626
|
ready = true,
|
|
@@ -1528,26 +1644,36 @@ function YouAsked({
|
|
|
1528
1644
|
[demo, appName, appKey, apiUrl, userId, userEmail, userName, signature]
|
|
1529
1645
|
);
|
|
1530
1646
|
const whatsNewKey = demo ? null : `youasked:whats-new:${appKey}:${userId ?? "anon"}`;
|
|
1531
|
-
const [mounted, setMounted] =
|
|
1532
|
-
const [route, setRoute] =
|
|
1533
|
-
const [config, setConfig] =
|
|
1534
|
-
const [testMode, setTestMode] =
|
|
1535
|
-
const [whatsNew, setWhatsNew] =
|
|
1536
|
-
const [whatsNewToast, setWhatsNewToast] =
|
|
1537
|
-
const [capture, setCapture] =
|
|
1647
|
+
const [mounted, setMounted] = useState4(false);
|
|
1648
|
+
const [route, setRoute] = useState4(null);
|
|
1649
|
+
const [config, setConfig] = useState4(null);
|
|
1650
|
+
const [testMode, setTestMode] = useState4(false);
|
|
1651
|
+
const [whatsNew, setWhatsNew] = useState4(null);
|
|
1652
|
+
const [whatsNewToast, setWhatsNewToast] = useState4(false);
|
|
1653
|
+
const [capture, setCapture] = useState4(null);
|
|
1538
1654
|
const tokens = useTokens(appearance, mounted);
|
|
1539
|
-
const openerRef =
|
|
1540
|
-
const [detectedIcon, setDetectedIcon] =
|
|
1541
|
-
|
|
1655
|
+
const openerRef = useRef4(null);
|
|
1656
|
+
const [detectedIcon, setDetectedIcon] = useState4(null);
|
|
1657
|
+
useEffect4(() => {
|
|
1542
1658
|
if (appIcon === void 0) setDetectedIcon(findPageIcon());
|
|
1543
1659
|
}, [appIcon]);
|
|
1544
|
-
|
|
1660
|
+
useEffect4(() => {
|
|
1545
1661
|
injectStyles();
|
|
1546
1662
|
setMounted(true);
|
|
1547
1663
|
}, []);
|
|
1548
|
-
|
|
1664
|
+
const [refresh, setRefresh] = useState4(0);
|
|
1665
|
+
const lastLoad = useRef4(0);
|
|
1666
|
+
useEffect4(() => {
|
|
1667
|
+
const onVisible = () => {
|
|
1668
|
+
if (document.visibilityState === "visible" && Date.now() - lastLoad.current > 6e4) setRefresh((n) => n + 1);
|
|
1669
|
+
};
|
|
1670
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
1671
|
+
return () => document.removeEventListener("visibilitychange", onVisible);
|
|
1672
|
+
}, []);
|
|
1673
|
+
useEffect4(() => {
|
|
1549
1674
|
if (!ready) return;
|
|
1550
1675
|
let alive = true;
|
|
1676
|
+
lastLoad.current = Date.now();
|
|
1551
1677
|
client.config().then((c) => {
|
|
1552
1678
|
if (!alive) return;
|
|
1553
1679
|
const seenAt = lastWhatsNew(whatsNewKey);
|
|
@@ -1567,14 +1693,14 @@ function YouAsked({
|
|
|
1567
1693
|
return () => {
|
|
1568
1694
|
alive = false;
|
|
1569
1695
|
};
|
|
1570
|
-
}, [client, ready]);
|
|
1571
|
-
|
|
1696
|
+
}, [client, ready, refresh]);
|
|
1697
|
+
useEffect4(() => {
|
|
1572
1698
|
if (!("onNotice" in client)) return;
|
|
1573
1699
|
return client.onNotice(
|
|
1574
1700
|
(n) => setConfig((c) => c ? { ...c, unseen: [...c.unseen, n] } : c)
|
|
1575
1701
|
);
|
|
1576
1702
|
}, [client]);
|
|
1577
|
-
const dismissWhatsNew =
|
|
1703
|
+
const dismissWhatsNew = useCallback3(() => {
|
|
1578
1704
|
setWhatsNew(null);
|
|
1579
1705
|
setWhatsNewToast(false);
|
|
1580
1706
|
try {
|
|
@@ -1584,7 +1710,7 @@ function YouAsked({
|
|
|
1584
1710
|
client.whatsNewSeen().catch(() => {
|
|
1585
1711
|
});
|
|
1586
1712
|
}, [client, whatsNewKey]);
|
|
1587
|
-
const pointAtIt =
|
|
1713
|
+
const pointAtIt = useCallback3(
|
|
1588
1714
|
() => new Promise((resolve, reject) => {
|
|
1589
1715
|
import("./capture-FYZGL54F.js").then(
|
|
1590
1716
|
(m) => setCapture({ Layer: m.default, resolve }),
|
|
@@ -1593,20 +1719,20 @@ function YouAsked({
|
|
|
1593
1719
|
}),
|
|
1594
1720
|
[]
|
|
1595
1721
|
);
|
|
1596
|
-
const open =
|
|
1722
|
+
const open = useCallback3((options) => {
|
|
1597
1723
|
openerRef.current = document.activeElement;
|
|
1598
1724
|
setRoute(toRoute(options));
|
|
1599
1725
|
}, []);
|
|
1600
|
-
const close =
|
|
1726
|
+
const close = useCallback3(() => {
|
|
1601
1727
|
setRoute(null);
|
|
1602
1728
|
openerRef.current?.focus?.({ preventScroll: true });
|
|
1603
1729
|
}, []);
|
|
1604
|
-
|
|
1730
|
+
useEffect4(() => {
|
|
1605
1731
|
const onOpen = (e) => open(e.detail);
|
|
1606
1732
|
window.addEventListener("youasked:open", onOpen);
|
|
1607
1733
|
return () => window.removeEventListener("youasked:open", onOpen);
|
|
1608
1734
|
}, [open]);
|
|
1609
|
-
const markSeen =
|
|
1735
|
+
const markSeen = useCallback3(
|
|
1610
1736
|
(ids) => {
|
|
1611
1737
|
if (!ids.length) return;
|
|
1612
1738
|
setConfig((c) => c ? { ...c, unseen: c.unseen.filter((n) => !ids.includes(n.id)) } : c);
|
|
@@ -1645,12 +1771,22 @@ function YouAsked({
|
|
|
1645
1771
|
);
|
|
1646
1772
|
const standing = unseen.filter((n) => !coveredBy(whatsNew, n));
|
|
1647
1773
|
const notice = standing.find((n) => n.kind === "shipped") ?? standing.find((n) => n.kind === "reply");
|
|
1648
|
-
|
|
1649
|
-
|
|
1774
|
+
const nudgeSettings = useMemo(
|
|
1775
|
+
() => config && cards && !testMode ? resolveNudge(config.nudge, nudgeProp) : null,
|
|
1776
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1777
|
+
[Boolean(config), JSON.stringify(config?.nudge ?? null), cards, testMode, typeof nudgeProp === "object" ? JSON.stringify(nudgeProp) : nudgeProp]
|
|
1778
|
+
);
|
|
1779
|
+
const busy = route !== null || Boolean(notice) || whatsNewToast && Boolean(whatsNew);
|
|
1780
|
+
const nudge = useNudge(nudgeSettings, demo ? null : `youasked:nudge:${appKey}:${userId ?? "anon"}`, busy);
|
|
1781
|
+
useEffect4(() => {
|
|
1782
|
+
if (route !== null) nudge.engaged();
|
|
1783
|
+
}, [route, nudge.engaged]);
|
|
1784
|
+
if (testMode) return /* @__PURE__ */ jsx5(YouAskedContext.Provider, { value, children });
|
|
1785
|
+
return /* @__PURE__ */ jsxs5(YouAskedContext.Provider, { value, children: [
|
|
1650
1786
|
children,
|
|
1651
1787
|
mounted && createPortal(
|
|
1652
|
-
/* @__PURE__ */
|
|
1653
|
-
trigger === "floating" && !capture && /* @__PURE__ */
|
|
1788
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-scope", "data-ya-mode": tokens?.mode, "data-ya-skip": "", style: scopeStyle, children: [
|
|
1789
|
+
trigger === "floating" && !capture && /* @__PURE__ */ jsx5(
|
|
1654
1790
|
Launcher,
|
|
1655
1791
|
{
|
|
1656
1792
|
position,
|
|
@@ -1660,7 +1796,7 @@ function YouAsked({
|
|
|
1660
1796
|
onClick: () => route ? close() : open()
|
|
1661
1797
|
}
|
|
1662
1798
|
),
|
|
1663
|
-
cards && !notice && whatsNewToast && whatsNew && route === null && /* @__PURE__ */
|
|
1799
|
+
cards && !notice && whatsNewToast && whatsNew && route === null && /* @__PURE__ */ jsx5(
|
|
1664
1800
|
WhatsNewToast,
|
|
1665
1801
|
{
|
|
1666
1802
|
news: whatsNew,
|
|
@@ -1670,7 +1806,18 @@ function YouAsked({
|
|
|
1670
1806
|
onDismiss: dismissWhatsNew
|
|
1671
1807
|
}
|
|
1672
1808
|
),
|
|
1673
|
-
|
|
1809
|
+
nudgeSettings && nudge.show && !busy && /* @__PURE__ */ jsx5(
|
|
1810
|
+
NudgeToast,
|
|
1811
|
+
{
|
|
1812
|
+
settings: nudgeSettings,
|
|
1813
|
+
appName: appName ?? config?.app.name ?? "us",
|
|
1814
|
+
position,
|
|
1815
|
+
trigger,
|
|
1816
|
+
onOpen: () => open({ view: "new" }),
|
|
1817
|
+
onDismiss: nudge.dismiss
|
|
1818
|
+
}
|
|
1819
|
+
),
|
|
1820
|
+
cards && notice && route === null && /* @__PURE__ */ jsx5(
|
|
1674
1821
|
ShippedToast,
|
|
1675
1822
|
{
|
|
1676
1823
|
notice,
|
|
@@ -1683,7 +1830,7 @@ function YouAsked({
|
|
|
1683
1830
|
onDismiss: () => markSeen([notice.id])
|
|
1684
1831
|
}
|
|
1685
1832
|
),
|
|
1686
|
-
route && /* @__PURE__ */
|
|
1833
|
+
route && /* @__PURE__ */ jsx5(
|
|
1687
1834
|
Panel,
|
|
1688
1835
|
{
|
|
1689
1836
|
route,
|
|
@@ -1694,7 +1841,7 @@ function YouAsked({
|
|
|
1694
1841
|
hidden: capture !== null
|
|
1695
1842
|
}
|
|
1696
1843
|
),
|
|
1697
|
-
capture && /* @__PURE__ */
|
|
1844
|
+
capture && /* @__PURE__ */ jsx5(
|
|
1698
1845
|
capture.Layer,
|
|
1699
1846
|
{
|
|
1700
1847
|
onDone: (image) => {
|
|
@@ -1721,8 +1868,8 @@ function findPageIcon() {
|
|
|
1721
1868
|
}
|
|
1722
1869
|
function useTokens(appearance, mounted) {
|
|
1723
1870
|
const key = JSON.stringify(appearance ?? {});
|
|
1724
|
-
const [tokens, setTokens] =
|
|
1725
|
-
|
|
1871
|
+
const [tokens, setTokens] = useState4(null);
|
|
1872
|
+
useEffect4(() => {
|
|
1726
1873
|
if (!mounted) return;
|
|
1727
1874
|
const parsed = JSON.parse(key);
|
|
1728
1875
|
const update = () => setTokens(resolveTokens(parsed));
|
|
@@ -1757,20 +1904,20 @@ function Panel({
|
|
|
1757
1904
|
hasLauncher,
|
|
1758
1905
|
hidden
|
|
1759
1906
|
}) {
|
|
1760
|
-
const panelRef =
|
|
1761
|
-
const backdropRef =
|
|
1762
|
-
const [closing, setClosing] =
|
|
1907
|
+
const panelRef = useRef4(null);
|
|
1908
|
+
const backdropRef = useRef4(null);
|
|
1909
|
+
const [closing, setClosing] = useState4(false);
|
|
1763
1910
|
const isSheet = useMedia("(max-width: 639px)");
|
|
1764
1911
|
const reduceMotion = useMedia("(prefers-reduced-motion: reduce)");
|
|
1765
|
-
const hiddenRef =
|
|
1912
|
+
const hiddenRef = useRef4(hidden);
|
|
1766
1913
|
hiddenRef.current = hidden;
|
|
1767
|
-
const drag =
|
|
1768
|
-
const requestClose =
|
|
1914
|
+
const drag = useRef4(null);
|
|
1915
|
+
const requestClose = useCallback3(() => {
|
|
1769
1916
|
if (reduceMotion) return onClose();
|
|
1770
1917
|
setClosing(true);
|
|
1771
1918
|
setTimeout(onClose, 200);
|
|
1772
1919
|
}, [onClose, reduceMotion]);
|
|
1773
|
-
|
|
1920
|
+
useEffect4(() => {
|
|
1774
1921
|
if (!isSheet) return;
|
|
1775
1922
|
const html = document.documentElement;
|
|
1776
1923
|
const previous = html.style.overflow;
|
|
@@ -1779,10 +1926,10 @@ function Panel({
|
|
|
1779
1926
|
html.style.overflow = previous;
|
|
1780
1927
|
};
|
|
1781
1928
|
}, [isSheet]);
|
|
1782
|
-
|
|
1929
|
+
useEffect4(() => {
|
|
1783
1930
|
panelRef.current?.focus({ preventScroll: true });
|
|
1784
1931
|
}, []);
|
|
1785
|
-
|
|
1932
|
+
useEffect4(() => {
|
|
1786
1933
|
const onKey = (e) => {
|
|
1787
1934
|
if (hiddenRef.current) return;
|
|
1788
1935
|
if (e.key === "Escape") {
|
|
@@ -1836,7 +1983,7 @@ function Panel({
|
|
|
1836
1983
|
if (backdrop) backdrop.style.opacity = "";
|
|
1837
1984
|
}
|
|
1838
1985
|
};
|
|
1839
|
-
return /* @__PURE__ */
|
|
1986
|
+
return /* @__PURE__ */ jsxs5(
|
|
1840
1987
|
"div",
|
|
1841
1988
|
{
|
|
1842
1989
|
className: "ya-overlay",
|
|
@@ -1845,8 +1992,8 @@ function Panel({
|
|
|
1845
1992
|
"data-closing": closing || void 0,
|
|
1846
1993
|
hidden,
|
|
1847
1994
|
children: [
|
|
1848
|
-
/* @__PURE__ */
|
|
1849
|
-
/* @__PURE__ */
|
|
1995
|
+
/* @__PURE__ */ jsx5("div", { className: "ya-backdrop", ref: backdropRef, onClick: requestClose, "aria-hidden": "true" }),
|
|
1996
|
+
/* @__PURE__ */ jsxs5(
|
|
1850
1997
|
"div",
|
|
1851
1998
|
{
|
|
1852
1999
|
className: "ya-panel",
|
|
@@ -1860,8 +2007,8 @@ function Panel({
|
|
|
1860
2007
|
onPointerUp,
|
|
1861
2008
|
onPointerCancel: onPointerUp,
|
|
1862
2009
|
children: [
|
|
1863
|
-
/* @__PURE__ */
|
|
1864
|
-
/* @__PURE__ */
|
|
2010
|
+
/* @__PURE__ */ jsx5("div", { className: "ya-grab-zone", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("div", { className: "ya-grab" }) }),
|
|
2011
|
+
/* @__PURE__ */ jsx5(Navigator, { route, setRoute, onClose: requestClose })
|
|
1865
2012
|
]
|
|
1866
2013
|
}
|
|
1867
2014
|
)
|
|
@@ -1891,7 +2038,7 @@ function Launcher({
|
|
|
1891
2038
|
badge,
|
|
1892
2039
|
onClick
|
|
1893
2040
|
}) {
|
|
1894
|
-
return /* @__PURE__ */
|
|
2041
|
+
return /* @__PURE__ */ jsxs5(
|
|
1895
2042
|
"button",
|
|
1896
2043
|
{
|
|
1897
2044
|
type: "button",
|
|
@@ -1902,9 +2049,9 @@ function Launcher({
|
|
|
1902
2049
|
"aria-label": isOpen ? "Close feature requests" : label || "Feature requests",
|
|
1903
2050
|
"aria-expanded": isOpen,
|
|
1904
2051
|
children: [
|
|
1905
|
-
/* @__PURE__ */
|
|
1906
|
-
label && !isOpen && /* @__PURE__ */
|
|
1907
|
-
badge && !isOpen && /* @__PURE__ */
|
|
2052
|
+
/* @__PURE__ */ jsx5("span", { className: "ya-fab-icon", children: isOpen ? /* @__PURE__ */ jsx5(IconClose, {}) : /* @__PURE__ */ jsx5(IconIdea, { ink: "var(--_accent)" }) }),
|
|
2053
|
+
label && !isOpen && /* @__PURE__ */ jsx5("span", { children: label }),
|
|
2054
|
+
badge && !isOpen && /* @__PURE__ */ jsx5("span", { className: "ya-dot", "aria-hidden": "true" })
|
|
1908
2055
|
]
|
|
1909
2056
|
}
|
|
1910
2057
|
);
|
|
@@ -1916,18 +2063,18 @@ function ShippedToast({
|
|
|
1916
2063
|
onOpen,
|
|
1917
2064
|
onDismiss
|
|
1918
2065
|
}) {
|
|
1919
|
-
return /* @__PURE__ */
|
|
1920
|
-
/* @__PURE__ */
|
|
1921
|
-
/* @__PURE__ */
|
|
1922
|
-
/* @__PURE__ */
|
|
2066
|
+
return /* @__PURE__ */ jsx5("div", { className: "ya-toast", role: "status", "data-pos": position, "data-trigger": trigger, children: notice.kind === "reply" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2067
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-toast-top", children: [
|
|
2068
|
+
/* @__PURE__ */ jsx5("p", { className: "ya-toast-kicker", children: "New reply about" }),
|
|
2069
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-icon-btn", onClick: onDismiss, "aria-label": "Dismiss", children: /* @__PURE__ */ jsx5(IconClose, {}) })
|
|
1923
2070
|
] }),
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
1926
|
-
/* @__PURE__ */
|
|
1927
|
-
/* @__PURE__ */
|
|
1928
|
-
/* @__PURE__ */
|
|
2071
|
+
/* @__PURE__ */ jsx5("p", { className: "ya-toast-title", children: notice.title }),
|
|
2072
|
+
/* @__PURE__ */ jsx5("p", { className: "ya-toast-note ya-toast-quote", children: notice.reply }),
|
|
2073
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-toast-actions", children: [
|
|
2074
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-btn ya-btn-sm", onClick: onOpen, children: "Reply" }),
|
|
2075
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-btn ya-btn-sm ya-btn-ghost", onClick: onDismiss, children: "Later" })
|
|
1929
2076
|
] })
|
|
1930
|
-
] }) : /* @__PURE__ */
|
|
2077
|
+
] }) : /* @__PURE__ */ jsx5(ShippedBody, { notice, onOpen, onDismiss }) });
|
|
1931
2078
|
}
|
|
1932
2079
|
function WhatsNewToast({
|
|
1933
2080
|
news,
|
|
@@ -1937,44 +2084,44 @@ function WhatsNewToast({
|
|
|
1937
2084
|
onDismiss
|
|
1938
2085
|
}) {
|
|
1939
2086
|
const mine = news.items.some((i) => i.mine);
|
|
1940
|
-
return /* @__PURE__ */
|
|
1941
|
-
/* @__PURE__ */
|
|
1942
|
-
/* @__PURE__ */
|
|
1943
|
-
/* @__PURE__ */
|
|
2087
|
+
return /* @__PURE__ */ jsxs5("div", { className: "ya-toast", role: "status", "data-pos": position, "data-trigger": trigger, children: [
|
|
2088
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-toast-top", children: [
|
|
2089
|
+
/* @__PURE__ */ jsx5("p", { className: "ya-toast-kicker", children: "What's new" }),
|
|
2090
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-icon-btn", onClick: onDismiss, "aria-label": "Dismiss", children: /* @__PURE__ */ jsx5(IconClose, {}) })
|
|
1944
2091
|
] }),
|
|
1945
|
-
/* @__PURE__ */
|
|
1946
|
-
mine && /* @__PURE__ */
|
|
1947
|
-
/* @__PURE__ */
|
|
2092
|
+
/* @__PURE__ */ jsx5("p", { className: "ya-toast-title", children: whatsNewHeading(news.total) }),
|
|
2093
|
+
mine && /* @__PURE__ */ jsxs5("div", { className: "ya-live", children: [
|
|
2094
|
+
/* @__PURE__ */ jsx5(IconCheck, {}),
|
|
1948
2095
|
"Including something you asked for"
|
|
1949
2096
|
] }),
|
|
1950
|
-
/* @__PURE__ */
|
|
1951
|
-
/* @__PURE__ */
|
|
1952
|
-
/* @__PURE__ */
|
|
2097
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-toast-actions", children: [
|
|
2098
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-btn ya-btn-sm", onClick: onOpen, children: "See what's new" }),
|
|
2099
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-btn ya-btn-sm ya-btn-ghost", onClick: onDismiss, children: "Not now" })
|
|
1953
2100
|
] })
|
|
1954
2101
|
] });
|
|
1955
2102
|
}
|
|
1956
2103
|
function ShippedBody({ notice, onOpen, onDismiss }) {
|
|
1957
|
-
return /* @__PURE__ */
|
|
1958
|
-
/* @__PURE__ */
|
|
1959
|
-
/* @__PURE__ */
|
|
1960
|
-
/* @__PURE__ */
|
|
2104
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2105
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-toast-top", children: [
|
|
2106
|
+
/* @__PURE__ */ jsx5("p", { className: "ya-toast-kicker", children: "You asked for" }),
|
|
2107
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-icon-btn", onClick: onDismiss, "aria-label": "Dismiss", children: /* @__PURE__ */ jsx5(IconClose, {}) })
|
|
1961
2108
|
] }),
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
1964
|
-
/* @__PURE__ */
|
|
2109
|
+
/* @__PURE__ */ jsx5("p", { className: "ya-toast-title", children: notice.title }),
|
|
2110
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-live", children: [
|
|
2111
|
+
/* @__PURE__ */ jsx5(IconCheck, {}),
|
|
1965
2112
|
"It's live"
|
|
1966
2113
|
] }),
|
|
1967
|
-
notice.shipNote && /* @__PURE__ */
|
|
1968
|
-
/* @__PURE__ */
|
|
1969
|
-
/* @__PURE__ */
|
|
1970
|
-
/* @__PURE__ */
|
|
2114
|
+
notice.shipNote && /* @__PURE__ */ jsx5("p", { className: "ya-toast-note", children: notice.shipNote }),
|
|
2115
|
+
/* @__PURE__ */ jsxs5("div", { className: "ya-toast-actions", children: [
|
|
2116
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-btn ya-btn-sm", onClick: onOpen, children: "See it" }),
|
|
2117
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "ya-btn ya-btn-sm ya-btn-ghost", onClick: onDismiss, children: "Nice" })
|
|
1971
2118
|
] })
|
|
1972
2119
|
] });
|
|
1973
2120
|
}
|
|
1974
2121
|
|
|
1975
2122
|
// src/entries.tsx
|
|
1976
|
-
import { useState as
|
|
1977
|
-
import { jsx as
|
|
2123
|
+
import { useState as useState5 } from "react";
|
|
2124
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1978
2125
|
function useScope(className, style) {
|
|
1979
2126
|
const { scope } = useInternal();
|
|
1980
2127
|
return {
|
|
@@ -1985,11 +2132,11 @@ function useScope(className, style) {
|
|
|
1985
2132
|
}
|
|
1986
2133
|
function AskBar({ placeholder = "Ask for a feature\u2026", className, style }) {
|
|
1987
2134
|
const { open, appIcon, unseenCount } = useInternal();
|
|
1988
|
-
return /* @__PURE__ */
|
|
1989
|
-
appIcon ? /* @__PURE__ */
|
|
1990
|
-
/* @__PURE__ */
|
|
1991
|
-
unseenCount > 0 && /* @__PURE__ */
|
|
1992
|
-
/* @__PURE__ */
|
|
2135
|
+
return /* @__PURE__ */ jsx6("div", { ...useScope(className, style), children: /* @__PURE__ */ jsxs6("button", { type: "button", className: "ya-askbar", onClick: () => open({ view: "new" }), children: [
|
|
2136
|
+
appIcon ? /* @__PURE__ */ jsx6(AppAvatar, { icon: appIcon, size: 30 }) : /* @__PURE__ */ jsx6("span", { className: "ya-askbar-glyph", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(IconIdea, {}) }),
|
|
2137
|
+
/* @__PURE__ */ jsx6("span", { className: "ya-askbar-text", children: placeholder }),
|
|
2138
|
+
unseenCount > 0 && /* @__PURE__ */ jsx6("span", { className: "ya-badge", children: unseenCount }),
|
|
2139
|
+
/* @__PURE__ */ jsx6("span", { className: "ya-askbar-send", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(IconSend, {}) })
|
|
1993
2140
|
] }) });
|
|
1994
2141
|
}
|
|
1995
2142
|
function FeatureCard({
|
|
@@ -2002,15 +2149,15 @@ function FeatureCard({
|
|
|
2002
2149
|
const { client, open, config, markSeen } = useInternal();
|
|
2003
2150
|
const { data } = useLoad(() => client.list("top", "active"), [client]);
|
|
2004
2151
|
const notice = config?.unseen.find((n) => n.kind === "shipped");
|
|
2005
|
-
return /* @__PURE__ */
|
|
2006
|
-
/* @__PURE__ */
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2009
|
-
/* @__PURE__ */
|
|
2010
|
-
/* @__PURE__ */
|
|
2152
|
+
return /* @__PURE__ */ jsx6("div", { ...useScope(className, style), children: /* @__PURE__ */ jsxs6("section", { className: "ya-card", children: [
|
|
2153
|
+
/* @__PURE__ */ jsxs6("div", { className: "ya-card-head", children: [
|
|
2154
|
+
/* @__PURE__ */ jsx6("span", { className: "ya-card-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(IconIdea, {}) }),
|
|
2155
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2156
|
+
/* @__PURE__ */ jsx6("h3", { className: "ya-card-title", children: heading }),
|
|
2157
|
+
/* @__PURE__ */ jsx6("p", { className: "ya-card-sub", children: description })
|
|
2011
2158
|
] })
|
|
2012
2159
|
] }),
|
|
2013
|
-
notice && /* @__PURE__ */
|
|
2160
|
+
notice && /* @__PURE__ */ jsxs6(
|
|
2014
2161
|
"button",
|
|
2015
2162
|
{
|
|
2016
2163
|
type: "button",
|
|
@@ -2020,20 +2167,20 @@ function FeatureCard({
|
|
|
2020
2167
|
open({ requestId: notice.requestId });
|
|
2021
2168
|
},
|
|
2022
2169
|
children: [
|
|
2023
|
-
/* @__PURE__ */
|
|
2024
|
-
/* @__PURE__ */
|
|
2170
|
+
/* @__PURE__ */ jsx6("span", { className: "ya-shipped-dot", children: /* @__PURE__ */ jsx6(IconCheck, {}) }),
|
|
2171
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
2025
2172
|
"You asked for ",
|
|
2026
|
-
/* @__PURE__ */
|
|
2173
|
+
/* @__PURE__ */ jsx6("b", { children: notice.title }),
|
|
2027
2174
|
". It's live."
|
|
2028
2175
|
] }),
|
|
2029
|
-
/* @__PURE__ */
|
|
2176
|
+
/* @__PURE__ */ jsx6(IconChevron, {})
|
|
2030
2177
|
]
|
|
2031
2178
|
}
|
|
2032
2179
|
),
|
|
2033
|
-
!data ? /* @__PURE__ */
|
|
2034
|
-
/* @__PURE__ */
|
|
2035
|
-
/* @__PURE__ */
|
|
2036
|
-
data && data.length > limit && /* @__PURE__ */
|
|
2180
|
+
!data ? /* @__PURE__ */ jsx6(Skeleton, { rows: 2 }) : data.length > 0 ? /* @__PURE__ */ jsx6("ul", { className: "ya-list", children: data.slice(0, limit).map((r) => /* @__PURE__ */ jsx6(RequestRow, { client, request: r, compact: true, onOpen: () => open({ requestId: r.id }) }, r.id)) }) : null,
|
|
2181
|
+
/* @__PURE__ */ jsxs6("div", { className: "ya-card-actions", children: [
|
|
2182
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "ya-btn ya-btn-sm", onClick: () => open({ view: "new" }), children: "Suggest an idea" }),
|
|
2183
|
+
data && data.length > limit && /* @__PURE__ */ jsxs6("button", { type: "button", className: "ya-btn ya-btn-sm ya-btn-ghost", onClick: () => open({ view: "board" }), children: [
|
|
2037
2184
|
"See all ",
|
|
2038
2185
|
data.length
|
|
2039
2186
|
] })
|
|
@@ -2049,26 +2196,26 @@ function RequestButton({
|
|
|
2049
2196
|
}) {
|
|
2050
2197
|
const { open, unseenCount } = useInternal();
|
|
2051
2198
|
const scope = useScope(className, style);
|
|
2052
|
-
const badge = unseenCount > 0 && (variant === "icon" ? /* @__PURE__ */
|
|
2199
|
+
const badge = unseenCount > 0 && (variant === "icon" ? /* @__PURE__ */ jsx6("span", { className: "ya-dot", "aria-hidden": "true" }) : /* @__PURE__ */ jsx6("span", { className: "ya-badge", children: unseenCount }));
|
|
2053
2200
|
if (variant === "icon") {
|
|
2054
|
-
return /* @__PURE__ */
|
|
2055
|
-
/* @__PURE__ */
|
|
2201
|
+
return /* @__PURE__ */ jsx6("span", { ...scope, children: /* @__PURE__ */ jsxs6("button", { type: "button", className: "ya-iconbtn", onClick: () => open(), "aria-label": label, children: [
|
|
2202
|
+
/* @__PURE__ */ jsx6(IconIdea, {}),
|
|
2056
2203
|
badge
|
|
2057
2204
|
] }) });
|
|
2058
2205
|
}
|
|
2059
2206
|
if (variant === "row") {
|
|
2060
|
-
return /* @__PURE__ */
|
|
2061
|
-
/* @__PURE__ */
|
|
2062
|
-
/* @__PURE__ */
|
|
2063
|
-
/* @__PURE__ */
|
|
2064
|
-
sublabel && /* @__PURE__ */
|
|
2207
|
+
return /* @__PURE__ */ jsx6("div", { ...scope, children: /* @__PURE__ */ jsxs6("button", { type: "button", className: "ya-menurow", onClick: () => open(), children: [
|
|
2208
|
+
/* @__PURE__ */ jsx6("span", { className: "ya-card-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(IconIdea, {}) }),
|
|
2209
|
+
/* @__PURE__ */ jsxs6("span", { className: "ya-menurow-text", children: [
|
|
2210
|
+
/* @__PURE__ */ jsx6("span", { className: "ya-menurow-label", children: label }),
|
|
2211
|
+
sublabel && /* @__PURE__ */ jsx6("span", { className: "ya-menurow-sub", children: sublabel })
|
|
2065
2212
|
] }),
|
|
2066
2213
|
badge,
|
|
2067
|
-
/* @__PURE__ */
|
|
2214
|
+
/* @__PURE__ */ jsx6(IconChevron, {})
|
|
2068
2215
|
] }) });
|
|
2069
2216
|
}
|
|
2070
|
-
return /* @__PURE__ */
|
|
2071
|
-
/* @__PURE__ */
|
|
2217
|
+
return /* @__PURE__ */ jsx6("span", { ...scope, children: /* @__PURE__ */ jsxs6("button", { type: "button", className: "ya-pillbtn", onClick: () => open(), children: [
|
|
2218
|
+
/* @__PURE__ */ jsx6(IconIdea, {}),
|
|
2072
2219
|
label,
|
|
2073
2220
|
badge
|
|
2074
2221
|
] }) });
|
|
@@ -2080,8 +2227,8 @@ function AskAbout({
|
|
|
2080
2227
|
style
|
|
2081
2228
|
}) {
|
|
2082
2229
|
const { open } = useInternal();
|
|
2083
|
-
return /* @__PURE__ */
|
|
2084
|
-
/* @__PURE__ */
|
|
2230
|
+
return /* @__PURE__ */ jsx6("span", { ...useScope(className, style), children: /* @__PURE__ */ jsxs6("button", { type: "button", className: "ya-askabout", onClick: () => open({ view: "new", topic }), children: [
|
|
2231
|
+
/* @__PURE__ */ jsx6(IconIdea, {}),
|
|
2085
2232
|
prompt ?? `Want more from ${topic}?`
|
|
2086
2233
|
] }) });
|
|
2087
2234
|
}
|
|
@@ -2089,11 +2236,11 @@ function AskForIt({ query, className, style }) {
|
|
|
2089
2236
|
const { open } = useInternal();
|
|
2090
2237
|
const q = query.trim();
|
|
2091
2238
|
const shown = q.length > 40 ? `${q.slice(0, 39).trimEnd()}\u2026` : q;
|
|
2092
|
-
return /* @__PURE__ */
|
|
2093
|
-
/* @__PURE__ */
|
|
2094
|
-
/* @__PURE__ */
|
|
2239
|
+
return /* @__PURE__ */ jsx6("span", { ...useScope(className, style), children: /* @__PURE__ */ jsxs6("button", { type: "button", className: "ya-askforit", onClick: () => open({ view: "new", draft: q, topic: "search" }), children: [
|
|
2240
|
+
/* @__PURE__ */ jsx6("span", { children: shown ? `Can't find \u201C${shown}\u201D?` : "Can't find it?" }),
|
|
2241
|
+
/* @__PURE__ */ jsxs6("b", { children: [
|
|
2095
2242
|
"Ask for it",
|
|
2096
|
-
/* @__PURE__ */
|
|
2243
|
+
/* @__PURE__ */ jsx6(IconChevron, {})
|
|
2097
2244
|
] })
|
|
2098
2245
|
] }) });
|
|
2099
2246
|
}
|
|
@@ -2103,8 +2250,8 @@ function FeatureBoard({
|
|
|
2103
2250
|
className,
|
|
2104
2251
|
style
|
|
2105
2252
|
}) {
|
|
2106
|
-
const [route, setRoute] =
|
|
2107
|
-
return /* @__PURE__ */
|
|
2253
|
+
const [route, setRoute] = useState5(start === "board" ? { view: "board" } : { view: "home" });
|
|
2254
|
+
return /* @__PURE__ */ jsx6("div", { ...useScope(className, style), children: /* @__PURE__ */ jsx6("div", { className: "ya-embed", style: { height }, children: /* @__PURE__ */ jsx6(Navigator, { route, setRoute }) }) });
|
|
2108
2255
|
}
|
|
2109
2256
|
export {
|
|
2110
2257
|
AskAbout,
|