@appsupportkit/support-sdk 0.2.0 → 0.3.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/lib/commonjs/api.js +17 -2
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/components/SupportButton.js +123 -23
- package/lib/commonjs/components/SupportButton.js.map +1 -1
- package/lib/commonjs/components/icons.js +36 -0
- package/lib/commonjs/components/icons.js.map +1 -1
- package/lib/commonjs/components/primitives.js +9 -7
- package/lib/commonjs/components/primitives.js.map +1 -1
- package/lib/commonjs/components/screens/ComposeScreen.js +14 -23
- package/lib/commonjs/components/screens/ComposeScreen.js.map +1 -1
- package/lib/commonjs/components/screens/HomeScreen.js +70 -22
- package/lib/commonjs/components/screens/HomeScreen.js.map +1 -1
- package/lib/commonjs/components/screens/LanguageScreen.js +150 -0
- package/lib/commonjs/components/screens/LanguageScreen.js.map +1 -0
- package/lib/commonjs/components/screens/ThreadScreen.js +31 -19
- package/lib/commonjs/components/screens/ThreadScreen.js.map +1 -1
- package/lib/commonjs/i18n/index.js +101 -0
- package/lib/commonjs/i18n/index.js.map +1 -0
- package/lib/commonjs/i18n/languages.js +95 -0
- package/lib/commonjs/i18n/languages.js.map +1 -0
- package/lib/commonjs/i18n/strings/de.js +72 -0
- package/lib/commonjs/i18n/strings/de.js.map +1 -0
- package/lib/commonjs/i18n/strings/en.js +88 -0
- package/lib/commonjs/i18n/strings/en.js.map +1 -0
- package/lib/commonjs/i18n/strings/es.js +72 -0
- package/lib/commonjs/i18n/strings/es.js.map +1 -0
- package/lib/commonjs/i18n/strings/fr.js +72 -0
- package/lib/commonjs/i18n/strings/fr.js.map +1 -0
- package/lib/commonjs/i18n/strings/hi.js +72 -0
- package/lib/commonjs/i18n/strings/hi.js.map +1 -0
- package/lib/commonjs/i18n/strings/it.js +72 -0
- package/lib/commonjs/i18n/strings/it.js.map +1 -0
- package/lib/commonjs/i18n/strings/ja.js +72 -0
- package/lib/commonjs/i18n/strings/ja.js.map +1 -0
- package/lib/commonjs/i18n/strings/ko.js +72 -0
- package/lib/commonjs/i18n/strings/ko.js.map +1 -0
- package/lib/commonjs/i18n/strings/nl.js +72 -0
- package/lib/commonjs/i18n/strings/nl.js.map +1 -0
- package/lib/commonjs/i18n/strings/pl.js +72 -0
- package/lib/commonjs/i18n/strings/pl.js.map +1 -0
- package/lib/commonjs/i18n/strings/pt.js +72 -0
- package/lib/commonjs/i18n/strings/pt.js.map +1 -0
- package/lib/commonjs/i18n/strings/ru.js +72 -0
- package/lib/commonjs/i18n/strings/ru.js.map +1 -0
- package/lib/commonjs/i18n/strings/tr.js +72 -0
- package/lib/commonjs/i18n/strings/tr.js.map +1 -0
- package/lib/commonjs/i18n/strings/zh.js +72 -0
- package/lib/commonjs/i18n/strings/zh.js.map +1 -0
- package/lib/commonjs/index.js +31 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js +11 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useSupportThreads.js +19 -9
- package/lib/commonjs/useSupportThreads.js.map +1 -1
- package/lib/commonjs/utils.js +32 -17
- package/lib/commonjs/utils.js.map +1 -1
- package/lib/module/api.js +16 -2
- package/lib/module/api.js.map +1 -1
- package/lib/module/components/SupportButton.js +126 -26
- package/lib/module/components/SupportButton.js.map +1 -1
- package/lib/module/components/icons.js +35 -0
- package/lib/module/components/icons.js.map +1 -1
- package/lib/module/components/primitives.js +9 -7
- package/lib/module/components/primitives.js.map +1 -1
- package/lib/module/components/screens/ComposeScreen.js +15 -22
- package/lib/module/components/screens/ComposeScreen.js.map +1 -1
- package/lib/module/components/screens/HomeScreen.js +71 -23
- package/lib/module/components/screens/HomeScreen.js.map +1 -1
- package/lib/module/components/screens/LanguageScreen.js +147 -0
- package/lib/module/components/screens/LanguageScreen.js.map +1 -0
- package/lib/module/components/screens/ThreadScreen.js +31 -19
- package/lib/module/components/screens/ThreadScreen.js.map +1 -1
- package/lib/module/i18n/index.js +56 -0
- package/lib/module/i18n/index.js.map +1 -0
- package/lib/module/i18n/languages.js +88 -0
- package/lib/module/i18n/languages.js.map +1 -0
- package/lib/module/i18n/strings/de.js +68 -0
- package/lib/module/i18n/strings/de.js.map +1 -0
- package/lib/module/i18n/strings/en.js +84 -0
- package/lib/module/i18n/strings/en.js.map +1 -0
- package/lib/module/i18n/strings/es.js +68 -0
- package/lib/module/i18n/strings/es.js.map +1 -0
- package/lib/module/i18n/strings/fr.js +68 -0
- package/lib/module/i18n/strings/fr.js.map +1 -0
- package/lib/module/i18n/strings/hi.js +68 -0
- package/lib/module/i18n/strings/hi.js.map +1 -0
- package/lib/module/i18n/strings/it.js +68 -0
- package/lib/module/i18n/strings/it.js.map +1 -0
- package/lib/module/i18n/strings/ja.js +68 -0
- package/lib/module/i18n/strings/ja.js.map +1 -0
- package/lib/module/i18n/strings/ko.js +68 -0
- package/lib/module/i18n/strings/ko.js.map +1 -0
- package/lib/module/i18n/strings/nl.js +68 -0
- package/lib/module/i18n/strings/nl.js.map +1 -0
- package/lib/module/i18n/strings/pl.js +68 -0
- package/lib/module/i18n/strings/pl.js.map +1 -0
- package/lib/module/i18n/strings/pt.js +68 -0
- package/lib/module/i18n/strings/pt.js.map +1 -0
- package/lib/module/i18n/strings/ru.js +68 -0
- package/lib/module/i18n/strings/ru.js.map +1 -0
- package/lib/module/i18n/strings/tr.js +68 -0
- package/lib/module/i18n/strings/tr.js.map +1 -0
- package/lib/module/i18n/strings/zh.js +68 -0
- package/lib/module/i18n/strings/zh.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +11 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useSupportThreads.js +19 -9
- package/lib/module/useSupportThreads.js.map +1 -1
- package/lib/module/utils.js +32 -17
- package/lib/module/utils.js.map +1 -1
- package/lib/typescript/api.d.ts +9 -0
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/components/SupportButton.d.ts.map +1 -1
- package/lib/typescript/components/icons.d.ts +5 -0
- package/lib/typescript/components/icons.d.ts.map +1 -1
- package/lib/typescript/components/primitives.d.ts +7 -3
- package/lib/typescript/components/primitives.d.ts.map +1 -1
- package/lib/typescript/components/screens/ComposeScreen.d.ts +3 -1
- package/lib/typescript/components/screens/ComposeScreen.d.ts.map +1 -1
- package/lib/typescript/components/screens/HomeScreen.d.ts +6 -1
- package/lib/typescript/components/screens/HomeScreen.d.ts.map +1 -1
- package/lib/typescript/components/screens/LanguageScreen.d.ts +30 -0
- package/lib/typescript/components/screens/LanguageScreen.d.ts.map +1 -0
- package/lib/typescript/components/screens/ThreadScreen.d.ts +3 -1
- package/lib/typescript/components/screens/ThreadScreen.d.ts.map +1 -1
- package/lib/typescript/i18n/index.d.ts +15 -0
- package/lib/typescript/i18n/index.d.ts.map +1 -0
- package/lib/typescript/i18n/languages.d.ts +36 -0
- package/lib/typescript/i18n/languages.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/de.d.ts +3 -0
- package/lib/typescript/i18n/strings/de.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/en.d.ts +83 -0
- package/lib/typescript/i18n/strings/en.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/es.d.ts +3 -0
- package/lib/typescript/i18n/strings/es.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/fr.d.ts +3 -0
- package/lib/typescript/i18n/strings/fr.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/hi.d.ts +3 -0
- package/lib/typescript/i18n/strings/hi.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/it.d.ts +3 -0
- package/lib/typescript/i18n/strings/it.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/ja.d.ts +3 -0
- package/lib/typescript/i18n/strings/ja.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/ko.d.ts +3 -0
- package/lib/typescript/i18n/strings/ko.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/nl.d.ts +3 -0
- package/lib/typescript/i18n/strings/nl.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/pl.d.ts +3 -0
- package/lib/typescript/i18n/strings/pl.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/pt.d.ts +3 -0
- package/lib/typescript/i18n/strings/pt.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/ru.d.ts +3 -0
- package/lib/typescript/i18n/strings/ru.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/tr.d.ts +3 -0
- package/lib/typescript/i18n/strings/tr.d.ts.map +1 -0
- package/lib/typescript/i18n/strings/zh.d.ts +3 -0
- package/lib/typescript/i18n/strings/zh.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +32 -2
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/useSupportThreads.d.ts +11 -1
- package/lib/typescript/useSupportThreads.d.ts.map +1 -1
- package/lib/typescript/utils.d.ts +6 -5
- package/lib/typescript/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api.ts +24 -1
- package/src/components/SupportButton.tsx +162 -33
- package/src/components/icons.tsx +36 -0
- package/src/components/primitives.tsx +14 -4
- package/src/components/screens/ComposeScreen.tsx +19 -23
- package/src/components/screens/HomeScreen.tsx +84 -25
- package/src/components/screens/LanguageScreen.tsx +172 -0
- package/src/components/screens/ThreadScreen.tsx +48 -21
- package/src/i18n/index.ts +81 -0
- package/src/i18n/languages.ts +106 -0
- package/src/i18n/strings/de.ts +78 -0
- package/src/i18n/strings/en.ts +94 -0
- package/src/i18n/strings/es.ts +77 -0
- package/src/i18n/strings/fr.ts +78 -0
- package/src/i18n/strings/hi.ts +77 -0
- package/src/i18n/strings/it.ts +77 -0
- package/src/i18n/strings/ja.ts +77 -0
- package/src/i18n/strings/ko.ts +76 -0
- package/src/i18n/strings/nl.ts +77 -0
- package/src/i18n/strings/pl.ts +77 -0
- package/src/i18n/strings/pt.ts +77 -0
- package/src/i18n/strings/ru.ts +77 -0
- package/src/i18n/strings/tr.ts +77 -0
- package/src/i18n/strings/zh.ts +75 -0
- package/src/index.tsx +8 -0
- package/src/types.ts +32 -2
- package/src/useSupportThreads.ts +30 -10
- package/src/utils.ts +22 -17
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
import type { Language } from './i18n/languages';
|
|
2
3
|
|
|
3
4
|
export type QueryType = 'feedback' | 'feature_request' | 'bug_report';
|
|
4
5
|
export type QueryStatus = 'open' | 'resolved';
|
|
@@ -71,23 +72,45 @@ export interface SupportButtonProps {
|
|
|
71
72
|
* installed — it is an optional peer dependency.
|
|
72
73
|
*/
|
|
73
74
|
expoProjectId?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Pre-selects a language in the picker the first time a user opens support.
|
|
77
|
+
* Pass it when the host app already knows what the user reads — an in-app
|
|
78
|
+
* language setting beats the device locale, which is what the SDK falls
|
|
79
|
+
* back to. It is a default, not an override: the user still chooses, and a
|
|
80
|
+
* choice they have already made always wins.
|
|
81
|
+
*/
|
|
82
|
+
defaultLanguage?: Language;
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
export interface ThreadMessage {
|
|
77
86
|
id: string;
|
|
78
87
|
sender: MessageSender;
|
|
88
|
+
/**
|
|
89
|
+
* Already in the user's language: their own messages as they wrote them,
|
|
90
|
+
* support replies translated. The server does the choosing — see the
|
|
91
|
+
* get-threads function.
|
|
92
|
+
*/
|
|
79
93
|
body: string;
|
|
80
94
|
created_at: string;
|
|
81
|
-
|
|
95
|
+
/**
|
|
96
|
+
* A support reply whose translation hasn't landed yet. `body` currently
|
|
97
|
+
* holds the English original; it will change under the reader shortly, so
|
|
98
|
+
* the UI says so rather than letting it flicker.
|
|
99
|
+
*/
|
|
100
|
+
translation_pending: boolean;
|
|
82
101
|
}
|
|
83
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Note the absence of any admin identity: end users see one support team,
|
|
105
|
+
* not the people staffing it, so the names never leave the server. The
|
|
106
|
+
* dashboard still shows who wrote and who resolved what.
|
|
107
|
+
*/
|
|
84
108
|
export interface Thread {
|
|
85
109
|
id: string;
|
|
86
110
|
type: QueryType;
|
|
87
111
|
status: QueryStatus;
|
|
88
112
|
created_at: string;
|
|
89
113
|
resolved_at: string | null;
|
|
90
|
-
resolved_by_name: string | null;
|
|
91
114
|
app_version: string;
|
|
92
115
|
unread_count: number;
|
|
93
116
|
last_message: {
|
|
@@ -101,8 +124,15 @@ export interface Thread {
|
|
|
101
124
|
export interface ThreadsResponse {
|
|
102
125
|
threads: Thread[];
|
|
103
126
|
total_unread: number;
|
|
127
|
+
/** Null when this user has never picked one — the SDK then asks. */
|
|
128
|
+
language: string | null;
|
|
104
129
|
}
|
|
105
130
|
|
|
131
|
+
/**
|
|
132
|
+
* English labels, kept for host apps that render their own entry points.
|
|
133
|
+
* Inside the sheet the labels come from the string table instead, so they
|
|
134
|
+
* follow the user's language.
|
|
135
|
+
*/
|
|
106
136
|
export const QUERY_TYPE_LABEL: Record<QueryType, string> = {
|
|
107
137
|
feedback: 'Feedback',
|
|
108
138
|
feature_request: 'Feature request',
|
package/src/useSupportThreads.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
SupportApiError,
|
|
22
22
|
type ApiConfig,
|
|
23
23
|
} from './api';
|
|
24
|
+
import type { StringKey } from './i18n';
|
|
24
25
|
import type { Thread } from './types';
|
|
25
26
|
import {
|
|
26
27
|
latestAdminMessageAt,
|
|
@@ -50,10 +51,14 @@ const MARK_READ_BACKOFF_MS = 1_500;
|
|
|
50
51
|
* an integration mistake — the end user retrying forever won't fix a key
|
|
51
52
|
* that doesn't match the app — so the real cause is logged for whoever is
|
|
52
53
|
* running the build instead of hidden behind "couldn't load".
|
|
54
|
+
*
|
|
55
|
+
* Returns a key rather than a message: the hook has no business knowing what
|
|
56
|
+
* language the sheet is in, and resolving it here would make every callback
|
|
57
|
+
* depend on the translator.
|
|
53
58
|
*/
|
|
54
|
-
function
|
|
59
|
+
function loadErrorKey(e: unknown): StringKey {
|
|
55
60
|
if (e instanceof SupportApiError) {
|
|
56
|
-
if (e.offline) return
|
|
61
|
+
if (e.offline) return 'error.offline';
|
|
57
62
|
if (e.status === 401 || e.status === 403) {
|
|
58
63
|
if (__DEV__) {
|
|
59
64
|
console.error(
|
|
@@ -62,11 +67,11 @@ function loadErrorMessage(e: unknown): string {
|
|
|
62
67
|
'`appName` matches that app exactly.',
|
|
63
68
|
);
|
|
64
69
|
}
|
|
65
|
-
return '
|
|
70
|
+
return 'error.unavailable';
|
|
66
71
|
}
|
|
67
|
-
if (e.status === 429) return '
|
|
72
|
+
if (e.status === 429) return 'error.loadRateLimited';
|
|
68
73
|
}
|
|
69
|
-
return
|
|
74
|
+
return 'error.loadFailed';
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
interface ReadAttempt {
|
|
@@ -87,7 +92,16 @@ export interface SupportThreadsState {
|
|
|
87
92
|
/** User-pulled refresh with content already on screen. */
|
|
88
93
|
refreshing: boolean;
|
|
89
94
|
/** Set only when there is nothing cached; background failures stay quiet. */
|
|
90
|
-
|
|
95
|
+
errorKey: StringKey | null;
|
|
96
|
+
/** The language this user picked, or null if they never have. */
|
|
97
|
+
serverLanguage: string | null;
|
|
98
|
+
/**
|
|
99
|
+
* False until the first successful load. `serverLanguage === null` only
|
|
100
|
+
* means "never picked" once this is true — before that it means "don't
|
|
101
|
+
* know yet", and showing the picker on a guess would nag a user who
|
|
102
|
+
* answered on another device.
|
|
103
|
+
*/
|
|
104
|
+
languageLoaded: boolean;
|
|
91
105
|
/** User-initiated refresh (pull-to-refresh, retry button). */
|
|
92
106
|
refresh: () => void;
|
|
93
107
|
/** Awaited by send/submit handlers so the list reflects the write. */
|
|
@@ -114,7 +128,9 @@ export function useSupportThreads({
|
|
|
114
128
|
const [totalUnread, setTotalUnread] = useState(0);
|
|
115
129
|
const [loading, setLoading] = useState(false);
|
|
116
130
|
const [refreshing, setRefreshing] = useState(false);
|
|
117
|
-
const [
|
|
131
|
+
const [errorKey, setErrorKey] = useState<StringKey | null>(null);
|
|
132
|
+
const [serverLanguage, setServerLanguage] = useState<string | null>(null);
|
|
133
|
+
const [languageLoaded, setLanguageLoaded] = useState(false);
|
|
118
134
|
|
|
119
135
|
const mounted = useRef(true);
|
|
120
136
|
/** Last server payload, before local read reconciliation. */
|
|
@@ -256,7 +272,9 @@ export function useSupportThreads({
|
|
|
256
272
|
|
|
257
273
|
rawThreads.current = data.threads;
|
|
258
274
|
watermarks.current = pruneWatermarks(watermarks.current, data.threads);
|
|
259
|
-
|
|
275
|
+
setErrorKey(null);
|
|
276
|
+
setServerLanguage(data.language);
|
|
277
|
+
setLanguageLoaded(true);
|
|
260
278
|
publish();
|
|
261
279
|
|
|
262
280
|
// Replies that arrived while the user is sitting in the thread are
|
|
@@ -267,7 +285,7 @@ export function useSupportThreads({
|
|
|
267
285
|
} catch (e) {
|
|
268
286
|
if (!mounted.current) return;
|
|
269
287
|
// A failed background poll must not blow away cached threads.
|
|
270
|
-
if (rawThreads.current === null)
|
|
288
|
+
if (rawThreads.current === null) setErrorKey(loadErrorKey(e));
|
|
271
289
|
} finally {
|
|
272
290
|
if (mounted.current) {
|
|
273
291
|
if (spinner) setRefreshing(false);
|
|
@@ -365,7 +383,9 @@ export function useSupportThreads({
|
|
|
365
383
|
totalUnread,
|
|
366
384
|
loading,
|
|
367
385
|
refreshing,
|
|
368
|
-
|
|
386
|
+
errorKey,
|
|
387
|
+
serverLanguage,
|
|
388
|
+
languageLoaded,
|
|
369
389
|
refresh,
|
|
370
390
|
reload,
|
|
371
391
|
};
|
package/src/utils.ts
CHANGED
|
@@ -1,32 +1,37 @@
|
|
|
1
|
+
import type { Translator } from './i18n';
|
|
2
|
+
|
|
3
|
+
// Dates and times follow the language the user picked, not the device's:
|
|
4
|
+
// a sheet that says "Traduction…" above "2 days ago" reads as broken.
|
|
5
|
+
|
|
1
6
|
/** "2h ago" relative time — mirrors the web dashboard's formatting. */
|
|
2
|
-
export function relativeTime(iso: string): string {
|
|
7
|
+
export function relativeTime(iso: string, strings: Translator): string {
|
|
3
8
|
const then = new Date(iso).getTime();
|
|
4
9
|
const seconds = Math.max(0, Math.floor((Date.now() - then) / 1000));
|
|
5
|
-
if (seconds < 60) return '
|
|
10
|
+
if (seconds < 60) return strings.t('time.justNow');
|
|
6
11
|
const minutes = Math.floor(seconds / 60);
|
|
7
|
-
if (minutes < 60) return
|
|
12
|
+
if (minutes < 60) return strings.t('time.minutes', { count: minutes });
|
|
8
13
|
const hours = Math.floor(minutes / 60);
|
|
9
|
-
if (hours < 24) return
|
|
14
|
+
if (hours < 24) return strings.t('time.hours', { count: hours });
|
|
10
15
|
const days = Math.floor(hours / 24);
|
|
11
|
-
if (days < 7) return
|
|
16
|
+
if (days < 7) return strings.t('time.days', { count: days });
|
|
12
17
|
const weeks = Math.floor(days / 7);
|
|
13
|
-
if (weeks < 5) return
|
|
18
|
+
if (weeks < 5) return strings.t('time.weeks', { count: weeks });
|
|
14
19
|
const months = Math.floor(days / 30);
|
|
15
|
-
if (months < 12) return
|
|
16
|
-
return
|
|
20
|
+
if (months < 12) return strings.t('time.months', { count: months });
|
|
21
|
+
return strings.t('time.years', { count: Math.floor(days / 365) });
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
export function formatDate(iso: string): string {
|
|
20
|
-
return new Date(iso).toLocaleDateString(
|
|
24
|
+
export function formatDate(iso: string, strings: Translator): string {
|
|
25
|
+
return new Date(iso).toLocaleDateString(strings.locale, {
|
|
21
26
|
year: 'numeric',
|
|
22
27
|
month: 'short',
|
|
23
28
|
day: 'numeric',
|
|
24
29
|
});
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
/** "14:32" in the
|
|
28
|
-
export function formatTime(iso: string): string {
|
|
29
|
-
return new Date(iso).toLocaleTimeString(
|
|
32
|
+
/** "14:32" in the user's locale — shown under a group of messages. */
|
|
33
|
+
export function formatTime(iso: string, strings: Translator): string {
|
|
34
|
+
return new Date(iso).toLocaleTimeString(strings.locale, {
|
|
30
35
|
hour: 'numeric',
|
|
31
36
|
minute: '2-digit',
|
|
32
37
|
});
|
|
@@ -43,13 +48,13 @@ export function isSameDay(a: string, b: string): boolean {
|
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
/** Date separator label: Today / Yesterday / "Mon, 5 Jan". */
|
|
46
|
-
export function dayLabel(iso: string): string {
|
|
51
|
+
export function dayLabel(iso: string, strings: Translator): string {
|
|
47
52
|
const now = new Date();
|
|
48
|
-
if (isSameDay(iso, now.toISOString())) return '
|
|
53
|
+
if (isSameDay(iso, now.toISOString())) return strings.t('time.today');
|
|
49
54
|
const yesterday = new Date(now.getTime() - 86_400_000);
|
|
50
|
-
if (isSameDay(iso, yesterday.toISOString())) return '
|
|
55
|
+
if (isSameDay(iso, yesterday.toISOString())) return strings.t('time.yesterday');
|
|
51
56
|
const date = new Date(iso);
|
|
52
|
-
return date.toLocaleDateString(
|
|
57
|
+
return date.toLocaleDateString(strings.locale, {
|
|
53
58
|
weekday: 'short',
|
|
54
59
|
month: 'short',
|
|
55
60
|
day: 'numeric',
|