@joinskips/brand 9.0.0-develop.148 → 9.0.0-develop.149
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/chat/chat/SkipsChat.d.ts +36 -0
- package/chat/chat/SkipsChat.js +98 -0
- package/chat/chat/client.d.ts +48 -0
- package/chat/chat/client.js +173 -0
- package/chat/chat/connection.d.ts +4 -0
- package/chat/chat/connection.js +6 -0
- package/chat/chat/generated/skips-app/index.d.ts +1 -0
- package/chat/chat/generated/skips-app/index.js +2 -0
- package/chat/chat/generated/skips-app/types.gen.d.ts +733 -0
- package/chat/chat/generated/skips-app/types.gen.js +2 -0
- package/chat/chat/index.d.ts +3 -0
- package/chat/chat/index.js +2 -0
- package/chat/chat/platform.d.ts +4 -0
- package/chat/chat/platform.js +46 -0
- package/chat/chat/session.d.ts +19 -0
- package/chat/chat/session.js +95 -0
- package/chat/chat/useSkipsChat.d.ts +78 -0
- package/chat/chat/useSkipsChat.js +498 -0
- package/chat/chat/verify.d.ts +35 -0
- package/chat/chat/verify.js +84 -0
- package/chat/helpers/index.d.ts +1 -1
- package/chat/helpers/index.js +1 -1
- package/chat/helpers/message.d.ts +1 -1
- package/chat/helpers/thread.d.ts +12 -1
- package/chat/helpers/thread.js +16 -1
- package/chat/index.d.ts +1 -0
- package/chat/index.js +1 -0
- package/chat/ui/Conversation.d.ts +29 -3
- package/chat/ui/Conversation.js +37 -12
- package/chat/ui/index.d.ts +0 -1
- package/chat/ui/index.js +0 -1
- package/chat/ui/parts.d.ts +1 -1
- package/package.json +9 -1
- package/chat/ui/CheckIn.d.ts +0 -25
- package/chat/ui/CheckIn.js +0 -24
package/chat/helpers/thread.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// One conversation as the chat shows it: messages in time order, each sent, pending or failed, with no duplicates
|
|
2
2
|
// when a page and the socket bring the same message, or when the socket echoes what the member just sent.
|
|
3
|
-
import { isSkip } from "./message.js";
|
|
3
|
+
import { isSkip, } from "./message.js";
|
|
4
4
|
export const EMPTY_THREAD = { messages: [] };
|
|
5
5
|
// Pending lines get negative ids, which never collide with the Skips App's.
|
|
6
6
|
let nextPendingId = -1;
|
|
@@ -39,6 +39,21 @@ export function withFailed(thread, pending, failed = true) {
|
|
|
39
39
|
export function withoutMessage(thread, id) {
|
|
40
40
|
return { messages: thread.messages.filter((m) => m.id !== id) };
|
|
41
41
|
}
|
|
42
|
+
/** The ids of the thread's messages the Skips App holds, oldest first: pending and failed lines have no place in its pages. */
|
|
43
|
+
export const storedIds = (thread) => thread.messages.filter((message) => message.delivery === 'sent').map((message) => message.id);
|
|
44
|
+
/**
|
|
45
|
+
* The question from Skip whose buttons can still be pressed: unanswered, with nothing written after it. A line from the
|
|
46
|
+
* care team after it leaves it open, since what asked it still waits for the press.
|
|
47
|
+
*/
|
|
48
|
+
export function openQuestion(messages) {
|
|
49
|
+
for (const message of [...messages].reverse()) {
|
|
50
|
+
if (message.mine)
|
|
51
|
+
return undefined;
|
|
52
|
+
if (message.choice)
|
|
53
|
+
return message.choice.chosen === null ? { message, items: message.choice.items } : undefined;
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
42
57
|
// An answer is never taken back, so a copy of a question without one keeps the answer the chat has.
|
|
43
58
|
function keptChoice(known, incoming) {
|
|
44
59
|
if (incoming === undefined)
|
package/chat/index.d.ts
CHANGED
package/chat/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
1
2
|
import { type ImageSourcePropType } from 'react-native';
|
|
2
3
|
import type { Composer } from '../hooks/useComposer.ts';
|
|
3
|
-
import {
|
|
4
|
+
import type { ChoiceItem } from '../helpers/message.ts';
|
|
5
|
+
import { type Thread, type ThreadMessage } from '../helpers/thread.ts';
|
|
4
6
|
import { type ChatIcon, type ChatWords } from './parts.ts';
|
|
7
|
+
import { type WelcomeWords } from './Welcome.tsx';
|
|
5
8
|
export type LoadState = 'loading' | 'ready' | 'failed';
|
|
6
9
|
export interface ConversationProps {
|
|
7
10
|
thread: Thread;
|
|
@@ -13,13 +16,36 @@ export interface ConversationProps {
|
|
|
13
16
|
onRetry: (id: number) => void;
|
|
14
17
|
onLoadOlder: () => void;
|
|
15
18
|
onReload: () => void;
|
|
16
|
-
keyboardOffset?: number;
|
|
17
|
-
bottomInset?: number;
|
|
19
|
+
keyboardOffset?: number | undefined;
|
|
20
|
+
bottomInset?: number | undefined;
|
|
18
21
|
words: ChatWords;
|
|
19
22
|
Icon: ChatIcon;
|
|
20
23
|
skipMark: ImageSourcePropType;
|
|
24
|
+
label?: string;
|
|
25
|
+
lead?: ReactNode;
|
|
26
|
+
trail?: ReactNode;
|
|
27
|
+
hidden?: ReadonlySet<number>;
|
|
28
|
+
choices?: ConversationChoices;
|
|
29
|
+
composerClosed?: boolean;
|
|
30
|
+
holding?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface ConversationChoices {
|
|
33
|
+
sending: string | null;
|
|
34
|
+
failed: number | null;
|
|
35
|
+
holding?: boolean;
|
|
36
|
+
words: WelcomeWords;
|
|
37
|
+
onChoose: (question: ThreadMessage, answer: ChoiceItem) => void;
|
|
21
38
|
}
|
|
22
39
|
export declare const PHOTO_RETRIES = 5;
|
|
23
40
|
export declare const PHOTO_RETRY_MS = 1500;
|
|
24
41
|
/** One conversation: its messages oldest at the top, the care team's typing, and the box to write in. */
|
|
25
42
|
export declare function Conversation(props: ConversationProps): import("react").JSX.Element;
|
|
43
|
+
export interface UnsentProps {
|
|
44
|
+
message: ThreadMessage;
|
|
45
|
+
words: ChatWords;
|
|
46
|
+
onRetry: (id: number) => void;
|
|
47
|
+
Icon: ChatIcon;
|
|
48
|
+
holding: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** The member's line while it is on its way, or after it failed, with its retry. */
|
|
51
|
+
export declare function Unsent({ message, words, onRetry, Icon, holding, }: UnsentProps): import("react").JSX.Element;
|
package/chat/ui/Conversation.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { ActivityIndicator, FlatList, Image, KeyboardAvoidingView, Linking, Platform, Pressable, Text, TextInput, View, } from 'react-native';
|
|
4
|
-
import { groupThread } from "../helpers/thread.js";
|
|
4
|
+
import { groupThread, openQuestion, } from "../helpers/thread.js";
|
|
5
5
|
import { Bubble } from "./Bubble.js";
|
|
6
6
|
import { useColor } from "./parts.js";
|
|
7
|
+
import { Choices } from "./Welcome.js";
|
|
7
8
|
// An image from require needs its size in style: the web draws it at its own size otherwise.
|
|
8
9
|
const AVATAR_SIZE = 32;
|
|
9
10
|
export const PHOTO_RETRIES = 5;
|
|
@@ -18,40 +19,64 @@ export function Conversation(props) {
|
|
|
18
19
|
const { thread, load, feedDown, typists, words, onReload, onLoadOlder } = props;
|
|
19
20
|
const secondary = useColor('ink-secondary');
|
|
20
21
|
// The list is inverted so it rests at the newest message and stays put while the member reads older ones.
|
|
21
|
-
const
|
|
22
|
-
|
|
22
|
+
const shown = props.hidden ? thread.messages.filter((message) => !props.hidden?.has(message.id)) : thread.messages;
|
|
23
|
+
const items = groupThread(shown, props.skipUserId).reverse();
|
|
24
|
+
const question = props.choices ? openQuestion(shown) : undefined;
|
|
25
|
+
return (_jsxs(KeyboardAvoidingView, { behavior: "padding", keyboardVerticalOffset: props.keyboardOffset ?? 0, className: "flex-1", children: [feedDown ? (_jsx(Line, { role: "status", className: "bg-muted px-gutter-phone py-2 text-center text-sm text-ink-secondary", children: words.feedDown })) : null, load === 'loading' ? (_jsx(ActivityIndicator, { accessibilityLabel: words.loading, className: "flex-1", color: secondary })) : load === 'failed' ? (_jsxs(View, { role: "alert", className: "flex-1 items-center justify-center gap-3 px-gutter-phone", children: [_jsx(Line, { className: "text-center text-base text-ink-secondary", children: words.loadFailed }), _jsx(Pressable, { role: "button", onPress: onReload, className: "h-control items-center justify-center rounded-pill border border-line-control px-6 active:opacity-80", children: _jsx(Line, { className: "text-md font-semibold text-ink", children: words.retry }) })] })) : (_jsx(FlatList, { inverted: true, data: items, keyExtractor: itemKey, onEndReached: onLoadOlder, contentContainerClassName: "gap-4 px-gutter-phone py-4 md:mx-auto md:w-full md:max-w-reading", role: "log", "aria-label": props.label,
|
|
26
|
+
// The conversation scrolls, so the keyboard has to be able to reach it.
|
|
27
|
+
tabIndex: 0, ListHeaderComponent: _jsxs(_Fragment, { children: [props.trail, typists.length ? _jsx(Line, { className: "text-sm text-ink-secondary", children: words.typingLine(typists) }) : null] }), ListFooterComponent: _jsx(_Fragment, { children: props.lead }), renderItem: ({ item }) => _jsx(ThreadRow, { item: item, question: question, ...props }) })), _jsx(ComposerBox, { ...props })] }));
|
|
23
28
|
}
|
|
24
29
|
function itemKey(item) {
|
|
25
30
|
if (item.kind === 'run')
|
|
26
31
|
return `r${String(item.messages[0]?.id)}`;
|
|
27
32
|
return `${item.kind === 'notice' ? 'n' : 'u'}${String(item.message.id)}`;
|
|
28
33
|
}
|
|
29
|
-
|
|
34
|
+
// The web sends on Enter, as joinskips.com's chat does, and Shift+Enter starts a new line. A phone's return key
|
|
35
|
+
// starts a new line.
|
|
36
|
+
function sendOnEnter(canSend, submit) {
|
|
37
|
+
return (event) => {
|
|
38
|
+
const { nativeEvent } = event;
|
|
39
|
+
if (nativeEvent.key !== 'Enter' || ('shiftKey' in nativeEvent && nativeEvent.shiftKey === true))
|
|
40
|
+
return;
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
if (canSend)
|
|
43
|
+
submit();
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function ComposerBox({ composer, bottomInset, words, Icon, composerClosed = false, holding = false }) {
|
|
30
47
|
const secondary = useColor('ink-secondary');
|
|
31
48
|
const placeholder = useColor('ink-placeholder');
|
|
32
49
|
const actionInk = useColor('action-ink');
|
|
33
|
-
const { text, picked, refused
|
|
34
|
-
|
|
50
|
+
const { text, picked, refused } = composer;
|
|
51
|
+
const canSend = composer.canSend && !composerClosed && !holding;
|
|
52
|
+
return (_jsxs(View, { style: { paddingBottom: Math.max(bottomInset ?? 0, MIN_BOTTOM_SPACE) }, className: "gap-2 px-gutter-phone pt-2 md:mx-auto md:w-full md:max-w-reading", children: [refused ? (_jsx(Line, { role: "alert", className: "text-sm text-error-ink", children: words.attachmentRefused[refused] })) : null, picked ? (_jsxs(View, { className: "flex-row items-center gap-3 self-start rounded-control border border-line-subtle bg-surface p-2 pr-3", children: [isPhoto(picked) ? (_jsx(Image, { source: { uri: picked.uri }, resizeMode: "cover", className: "h-10 w-10 rounded-control bg-muted" })) : (_jsx(View, { className: "h-10 w-10 items-center justify-center", children: _jsx(Icon, { name: "file", color: secondary, size: 20 }) })), _jsxs(View, { className: "max-w-56", children: [_jsx(Line, { className: "text-sm text-ink", numberOfLines: 1, children: picked.name }), _jsx(Line, { className: "text-xs text-ink-secondary", children: `${isPhoto(picked) ? words.photo : words.pdf} · ${words.fileSize(picked.size)}` })] }), _jsx(Pressable, { role: "button", accessibilityLabel: `${words.remove} ${picked.name}`, hitSlop: 8, onPress: composer.remove, children: _jsx(Icon, { name: "close", color: secondary, size: 18 }) })] })) : null, _jsxs(View, { className: "flex-row items-end gap-1 rounded-[24px] border border-line-subtle bg-surface p-1.5 shadow-card", children: [composer.attach && !composerClosed ? (_jsx(Pressable, { role: "button", accessibilityLabel: words.attach, onPress: composer.attach, className: "h-[42px] w-[42px] items-center justify-center rounded-pill active:bg-muted", children: _jsx(Icon, { name: "attach", color: secondary, size: 18 }) })) : null, _jsx(TextInput, { value: text, editable: !composerClosed, onChangeText: composer.onChangeText, placeholder: words.composerPlaceholder, placeholderTextColor: placeholder, accessibilityLabel: words.composerPlaceholder, multiline: true, ...Platform.select({ web: { rows: 1, onKeyPress: sendOnEnter(canSend, composer.submit) }, default: {} }), className: "max-h-40 flex-1 self-center px-1 py-2 font-ui text-md leading-6 text-ink" }), _jsx(Pressable, { role: "button", accessibilityLabel: words.send, disabled: !canSend, onPress: composer.submit, className: `h-[42px] w-[42px] items-center justify-center rounded-pill bg-action active:bg-action-pressed ${canSend ? '' : 'opacity-40'}`, children: _jsx(View, { style: { transform: [{ rotate: '-90deg' }] }, children: _jsx(Icon, { name: "arrow-right", color: actionInk, size: 18 }) }) })] }), words.emergencyNote ? (_jsx(Line, { className: "text-center text-xs text-ink-secondary", children: words.emergencyNote })) : null] }));
|
|
35
53
|
}
|
|
36
54
|
function ThreadRow({ item, words, onRetry, ...props }) {
|
|
37
55
|
if (item.kind === 'notice') {
|
|
38
56
|
return (_jsx(Line, { className: "self-center rounded-control border border-line-subtle px-3 py-2 text-sm text-ink-secondary", children: item.message.text }));
|
|
39
57
|
}
|
|
40
|
-
if (item.kind === 'unsent')
|
|
41
|
-
return _jsx(Unsent, { message: item.message, words: words, onRetry: onRetry, Icon: props.Icon });
|
|
58
|
+
if (item.kind === 'unsent') {
|
|
59
|
+
return (_jsx(Unsent, { message: item.message, words: words, onRetry: onRetry, Icon: props.Icon, holding: props.holding ?? false }));
|
|
60
|
+
}
|
|
42
61
|
const { sender, messages } = item;
|
|
43
62
|
const mine = sender === 'mine';
|
|
44
63
|
const name = mine ? words.you : sender === 'skip' ? words.skip : sender.name;
|
|
45
64
|
// The run is in time order, so its time is its last line's.
|
|
46
65
|
const sentAt = Math.max(...messages.map((m) => m.sentAt));
|
|
47
|
-
return (_jsxs(View, { className: `max-w-[82%] gap-1 ${mine ? 'items-end self-end' : 'items-start self-start'}`, children: [messages.map((message, index) => (_jsxs(View, { className: "flex-row items-end gap-2", children: [mine ? null : index === messages.length - 1 ? (_jsx(SenderAvatar, { sender: sender, name: name, skipMark: props.skipMark })) : (_jsx(View, { className: "w-8" })), _jsxs(View, { className: `shrink gap-2 ${mine ? 'items-end' : 'items-start'}`, children: [message.attachments.map((attachment) => (_jsx(Attachment, { attachment: attachment, words: words, Icon: props.Icon }, attachment.url))), message.text ? _jsx(Bubble, { mine: mine, text: message.text }) : null] })] }, message.id))), _jsx(Line, { className: `px-1 text-xs text-ink-secondary ${mine ? '' : 'ml-10'}`, children: `${name} · ${words.messageTime(sentAt)}` })] }));
|
|
66
|
+
return (_jsxs(View, { className: `max-w-[82%] gap-1 ${mine ? 'items-end self-end' : 'items-start self-start'}`, children: [messages.map((message, index) => (_jsxs(View, { className: "flex-row items-end gap-2", children: [mine ? null : index === messages.length - 1 ? (_jsx(SenderAvatar, { sender: sender, name: name, skipMark: props.skipMark })) : (_jsx(View, { className: "w-8" })), _jsxs(View, { className: `shrink gap-2 ${mine ? 'items-end' : 'items-start'}`, children: [message.attachments.map((attachment) => (_jsx(Attachment, { attachment: attachment, words: words, Icon: props.Icon }, attachment.url))), message.text ? _jsx(Bubble, { mine: mine, text: message.text }) : null, props.choices && props.question?.message.id === message.id ? (_jsx(ChoiceRow, { question: props.question, choices: props.choices })) : null] })] }, message.id))), _jsx(Line, { className: `px-1 text-xs text-ink-secondary ${mine ? '' : 'ml-10'}`, children: `${name} · ${words.messageTime(sentAt)}` })] }));
|
|
67
|
+
}
|
|
68
|
+
function ChoiceRow({ question, choices }) {
|
|
69
|
+
return (_jsx(Choices, { items: question.items, question: question.message.text, sending: choices.sending, holding: choices.holding ?? false, failed: choices.failed === question.message.id, words: choices.words, onChoose: (answer) => {
|
|
70
|
+
choices.onChoose(question.message, answer);
|
|
71
|
+
} }));
|
|
48
72
|
}
|
|
49
73
|
// A failed line is a button to send it again, and its note is announced.
|
|
50
74
|
const BUTTON_ROLE = 'button';
|
|
51
75
|
const ALERT_ROLE = 'alert';
|
|
52
|
-
|
|
76
|
+
/** The member's line while it is on its way, or after it failed, with its retry. */
|
|
77
|
+
export function Unsent({ message, words, onRetry, Icon, holding, }) {
|
|
53
78
|
const failed = message.delivery === 'failed';
|
|
54
|
-
return (_jsxs(Pressable, { ...(failed ? { role: BUTTON_ROLE } : {}), disabled: !failed, onPress: () => {
|
|
79
|
+
return (_jsxs(Pressable, { ...(failed ? { role: BUTTON_ROLE } : {}), disabled: !failed || holding, onPress: () => {
|
|
55
80
|
onRetry(message.id);
|
|
56
81
|
}, className: `max-w-[82%] items-end gap-1 self-end ${failed ? '' : 'opacity-70'}`, children: [message.file ? _jsx(PickedPreview, { picked: message.file, words: words, Icon: Icon }) : null, message.text ? _jsx(Bubble, { mine: true, text: message.text }) : null, _jsx(Line, { ...(failed ? { role: ALERT_ROLE } : {}), className: `px-1 text-xs ${failed ? 'text-error-ink' : 'text-ink-secondary'}`, children: `${words.you} · ${failed ? words.notSent : words.sending}` })] }));
|
|
57
82
|
}
|
package/chat/ui/index.d.ts
CHANGED
|
@@ -2,4 +2,3 @@ export { Conversation, type ConversationProps, type LoadState } from './Conversa
|
|
|
2
2
|
export { Bubble, type BubbleProps } from './Bubble.tsx';
|
|
3
3
|
export type { ChatIcon, ChatIconName, ChatWords } from './parts.ts';
|
|
4
4
|
export { Choices, CodeStep, EmailStep, NameStep, Pill, SkipTyping, WelcomeChoice, type PillProps, type WelcomeWords, } from './Welcome.tsx';
|
|
5
|
-
export { MedicationCheck, STOPPED_REASON_MAX_LENGTH, type CheckInWords, type MedicationAnswer, type MedicationCheckProps, } from './CheckIn.tsx';
|
package/chat/ui/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { Conversation } from "./Conversation.js";
|
|
2
2
|
export { Bubble } from "./Bubble.js";
|
|
3
3
|
export { Choices, CodeStep, EmailStep, NameStep, Pill, SkipTyping, WelcomeChoice, } from "./Welcome.js";
|
|
4
|
-
export { MedicationCheck, STOPPED_REASON_MAX_LENGTH, } from "./CheckIn.js";
|
package/chat/ui/parts.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
2
|
/** The icons the chat draws, from the brand's interface icons, which each app already draws. */
|
|
3
|
-
export type ChatIconName = 'attach' | 'arrow-right' | 'file' | 'close'
|
|
3
|
+
export type ChatIconName = 'attach' | 'arrow-right' | 'file' | 'close';
|
|
4
4
|
export interface ChatIconProps {
|
|
5
5
|
name: ChatIconName;
|
|
6
6
|
color: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joinskips/brand",
|
|
3
|
-
"version": "9.0.0-develop.
|
|
3
|
+
"version": "9.0.0-develop.149",
|
|
4
4
|
"description": "Skips design tokens, Onest, logos, favicons, interface icons, member pictures and app icons.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://brand.joinskips.com",
|
|
@@ -25,6 +25,14 @@
|
|
|
25
25
|
"types": "./chat/helpers/index.d.ts",
|
|
26
26
|
"default": "./chat/helpers/index.js"
|
|
27
27
|
},
|
|
28
|
+
"./chat/chat": {
|
|
29
|
+
"types": "./chat/chat/index.d.ts",
|
|
30
|
+
"default": "./chat/chat/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./chat/connection": {
|
|
33
|
+
"types": "./chat/chat/connection.d.ts",
|
|
34
|
+
"default": "./chat/chat/connection.js"
|
|
35
|
+
},
|
|
28
36
|
"./ui": {
|
|
29
37
|
"types": "./ui/index.d.ts",
|
|
30
38
|
"default": "./ui/index.js"
|
package/chat/ui/CheckIn.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { type ChatIcon } from './parts.ts';
|
|
2
|
-
/** What a person says about one of their current medications. */
|
|
3
|
-
export type MedicationAnswer = 'taking' | 'refill' | 'reorder' | 'stopped';
|
|
4
|
-
/** Every word the medication check-in shows, which the app passes in. */
|
|
5
|
-
export interface CheckInWords {
|
|
6
|
-
taking: string;
|
|
7
|
-
refill: string;
|
|
8
|
-
reorder: string;
|
|
9
|
-
stopped: string;
|
|
10
|
-
whyStopped: string;
|
|
11
|
-
}
|
|
12
|
-
export declare const STOPPED_REASON_MAX_LENGTH = 500;
|
|
13
|
-
export interface MedicationCheckProps {
|
|
14
|
-
name: string;
|
|
15
|
-
directions: string;
|
|
16
|
-
refillable: boolean;
|
|
17
|
-
answer: MedicationAnswer | null;
|
|
18
|
-
reason: string;
|
|
19
|
-
words: CheckInWords;
|
|
20
|
-
Icon: ChatIcon;
|
|
21
|
-
onAnswer: (answer: MedicationAnswer) => void;
|
|
22
|
-
onReason: (reason: string) => void;
|
|
23
|
-
}
|
|
24
|
-
/** One current medication in the check-in: still taking, a refill or re-order, or stopped and why. */
|
|
25
|
-
export declare function MedicationCheck({ name, directions, refillable, answer, reason, words, Icon, onAnswer, onReason, }: MedicationCheckProps): import("react").JSX.Element;
|
package/chat/ui/CheckIn.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Pressable, Text, TextInput, View } from 'react-native';
|
|
3
|
-
import { useColor } from "./parts.js";
|
|
4
|
-
export const STOPPED_REASON_MAX_LENGTH = 500;
|
|
5
|
-
const ICONS = {
|
|
6
|
-
taking: 'thumbs-up',
|
|
7
|
-
refill: 'repeat',
|
|
8
|
-
reorder: 'copy-plus',
|
|
9
|
-
stopped: 'close',
|
|
10
|
-
};
|
|
11
|
-
const ICON_SIZE = 24;
|
|
12
|
-
/** One current medication in the check-in: still taking, a refill or re-order, or stopped and why. */
|
|
13
|
-
export function MedicationCheck({ name, directions, refillable, answer, reason, words, Icon, onAnswer, onReason, }) {
|
|
14
|
-
const ink = useColor('ink');
|
|
15
|
-
const accentInk = useColor('accent-ink');
|
|
16
|
-
const placeholder = useColor('ink-placeholder');
|
|
17
|
-
const answers = ['taking', refillable ? 'refill' : 'reorder', 'stopped'];
|
|
18
|
-
return (_jsxs(View, { className: "w-80 max-w-full gap-3.5 rounded-[20px] border border-line-subtle bg-surface p-4", children: [_jsxs(View, { className: "gap-0.5", children: [_jsx(Text, { className: "font-ui text-md font-semibold text-ink", children: name }), _jsx(Text, { className: "font-ui text-sm text-ink-secondary", children: directions })] }), _jsx(View, { role: "radiogroup", accessibilityLabel: name, className: "flex-row", children: answers.map((each) => {
|
|
19
|
-
const picked = answer === each;
|
|
20
|
-
return (_jsxs(Pressable, { role: "radio", "aria-checked": picked, accessibilityLabel: words[each], onPress: () => {
|
|
21
|
-
onAnswer(each);
|
|
22
|
-
}, className: "flex-1 items-center gap-1.5", children: [_jsx(View, { className: `h-[52px] w-[52px] items-center justify-center rounded-pill border ${picked ? 'border-accent bg-accent' : 'border-line-control bg-surface'}`, children: _jsx(Icon, { name: ICONS[each], color: picked ? accentInk : ink, size: ICON_SIZE }) }), _jsx(Text, { className: "text-center font-ui text-xs font-semibold text-ink", children: words[each] })] }, each));
|
|
23
|
-
}) }), answer === 'stopped' ? (_jsx(TextInput, { value: reason, onChangeText: onReason, accessibilityLabel: words.whyStopped, placeholder: words.whyStopped, placeholderTextColor: placeholder, multiline: true, maxLength: STOPPED_REASON_MAX_LENGTH, className: "min-h-20 rounded-control border border-line-control bg-surface px-3 py-2 font-ui text-md text-ink" })) : null] }));
|
|
24
|
-
}
|