turbodesk-livechat-react-native 0.1.0-alpha.3 → 0.1.0-alpha.31
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/CHANGELOG.md +50 -0
- package/dist/api/conversation-api.d.ts +9 -0
- package/dist/api/conversation-api.d.ts.map +1 -1
- package/dist/api/conversation-api.js +15 -1
- package/dist/api/conversation-api.js.map +1 -1
- package/dist/hooks/use-live-chat.d.ts +1 -0
- package/dist/hooks/use-live-chat.d.ts.map +1 -1
- package/dist/hooks/use-live-chat.js +2 -0
- package/dist/hooks/use-live-chat.js.map +1 -1
- package/dist/hooks/use-send-message.d.ts +1 -0
- package/dist/hooks/use-send-message.d.ts.map +1 -1
- package/dist/hooks/use-send-message.js +20 -14
- package/dist/hooks/use-send-message.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/navigation/LiveChatPanel.d.ts +2 -1
- package/dist/navigation/LiveChatPanel.d.ts.map +1 -1
- package/dist/navigation/LiveChatPanel.js +21 -2
- package/dist/navigation/LiveChatPanel.js.map +1 -1
- package/dist/navigation/panel-router-context.d.ts +2 -1
- package/dist/navigation/panel-router-context.d.ts.map +1 -1
- package/dist/navigation/panel-router-context.js +5 -4
- package/dist/navigation/panel-router-context.js.map +1 -1
- package/dist/provider/LiveChatContext.d.ts.map +1 -1
- package/dist/provider/LiveChatContext.js +2 -0
- package/dist/provider/LiveChatContext.js.map +1 -1
- package/dist/provider/LiveChatProvider.d.ts +1 -1
- package/dist/provider/LiveChatProvider.d.ts.map +1 -1
- package/dist/provider/LiveChatProvider.js +17 -2
- package/dist/provider/LiveChatProvider.js.map +1 -1
- package/dist/provider/types.d.ts +4 -0
- package/dist/provider/types.d.ts.map +1 -1
- package/dist/ui/components/ConversationHeader.d.ts.map +1 -1
- package/dist/ui/components/ConversationHeader.js +7 -4
- package/dist/ui/components/ConversationHeader.js.map +1 -1
- package/dist/ui/components/ConversationListScreen.d.ts.map +1 -1
- package/dist/ui/components/ConversationListScreen.js +11 -2
- package/dist/ui/components/ConversationListScreen.js.map +1 -1
- package/dist/ui/components/ConversationScreen.d.ts.map +1 -1
- package/dist/ui/components/ConversationScreen.js +13 -3
- package/dist/ui/components/ConversationScreen.js.map +1 -1
- package/dist/ui/components/HomeScreen.d.ts.map +1 -1
- package/dist/ui/components/HomeScreen.js +14 -4
- package/dist/ui/components/HomeScreen.js.map +1 -1
- package/dist/ui/components/MessageComposer.d.ts +1 -0
- package/dist/ui/components/MessageComposer.d.ts.map +1 -1
- package/dist/ui/components/MessageComposer.js +89 -30
- package/dist/ui/components/MessageComposer.js.map +1 -1
- package/dist/ui/safe-area.d.ts +9 -0
- package/dist/ui/safe-area.d.ts.map +1 -0
- package/dist/ui/safe-area.js +28 -0
- package/dist/ui/safe-area.js.map +1 -0
- package/package.json +11 -3
- package/src/api/conversation-api.ts +33 -8
- package/src/hooks/use-live-chat.ts +3 -0
- package/src/hooks/use-send-message.ts +169 -159
- package/src/index.ts +3 -0
- package/src/navigation/LiveChatPanel.tsx +30 -6
- package/src/navigation/panel-router-context.tsx +5 -4
- package/src/provider/LiveChatContext.ts +2 -0
- package/src/provider/LiveChatProvider.tsx +396 -380
- package/src/provider/types.ts +63 -57
- package/src/ui/components/ConversationHeader.tsx +7 -6
- package/src/ui/components/ConversationListScreen.tsx +18 -5
- package/src/ui/components/ConversationScreen.tsx +369 -362
- package/src/ui/components/HomeScreen.tsx +21 -6
- package/src/ui/components/MessageComposer.tsx +116 -36
- package/src/ui/safe-area.ts +34 -0
|
@@ -1,159 +1,169 @@
|
|
|
1
|
-
import { useCallback, useState } from "react";
|
|
2
|
-
import { conversationApi } from "../api/conversation-api";
|
|
3
|
-
import { useLiveChatContext } from "../provider/LiveChatContext";
|
|
4
|
-
|
|
5
|
-
function pickConversationId(raw: any): string | null {
|
|
6
|
-
if (!raw) return null;
|
|
7
|
-
const a = raw?.data !== undefined ? raw.data : raw;
|
|
8
|
-
const b = a?.data !== undefined ? a.data : a;
|
|
9
|
-
const id = b?.conversationId ?? a?.conversationId;
|
|
10
|
-
return id != null && id !== "" ? String(id) : null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type AttachmentMeta = {
|
|
14
|
-
fileId?: string;
|
|
15
|
-
fileUrl: string;
|
|
16
|
-
fileName: string;
|
|
17
|
-
fileExtension?: string;
|
|
18
|
-
type: "image" | "video" | "document" | "audio";
|
|
19
|
-
caption?: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type InteractiveReplyMeta = {
|
|
23
|
-
type: "button_reply" | "list_reply";
|
|
24
|
-
id: string;
|
|
25
|
-
title: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type UseSendMessageResult = {
|
|
29
|
-
sending: boolean;
|
|
30
|
-
error: string | null;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const [
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { conversationApi } from "../api/conversation-api";
|
|
3
|
+
import { useLiveChatContext } from "../provider/LiveChatContext";
|
|
4
|
+
|
|
5
|
+
function pickConversationId(raw: any): string | null {
|
|
6
|
+
if (!raw) return null;
|
|
7
|
+
const a = raw?.data !== undefined ? raw.data : raw;
|
|
8
|
+
const b = a?.data !== undefined ? a.data : a;
|
|
9
|
+
const id = b?.conversationId ?? a?.conversationId;
|
|
10
|
+
return id != null && id !== "" ? String(id) : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type AttachmentMeta = {
|
|
14
|
+
fileId?: string;
|
|
15
|
+
fileUrl: string;
|
|
16
|
+
fileName: string;
|
|
17
|
+
fileExtension?: string;
|
|
18
|
+
type: "image" | "video" | "document" | "audio";
|
|
19
|
+
caption?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type InteractiveReplyMeta = {
|
|
23
|
+
type: "button_reply" | "list_reply";
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type UseSendMessageResult = {
|
|
29
|
+
sending: boolean;
|
|
30
|
+
error: string | null;
|
|
31
|
+
clearError: () => void;
|
|
32
|
+
sendText: (
|
|
33
|
+
text: string,
|
|
34
|
+
conversationId?: string | null
|
|
35
|
+
) => Promise<string | null>;
|
|
36
|
+
sendAttachment: (
|
|
37
|
+
attachment: AttachmentMeta,
|
|
38
|
+
conversationId?: string | null
|
|
39
|
+
) => Promise<string | null>;
|
|
40
|
+
sendInteractive: (
|
|
41
|
+
reply: InteractiveReplyMeta,
|
|
42
|
+
conversationId?: string | null
|
|
43
|
+
) => Promise<string | null>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function useSendMessage(): UseSendMessageResult {
|
|
47
|
+
const { visitorQueryParams } = useLiveChatContext();
|
|
48
|
+
const [sending, setSending] = useState(false);
|
|
49
|
+
const [error, setError] = useState<string | null>(null);
|
|
50
|
+
|
|
51
|
+
const clearError = useCallback(() => setError(null), []);
|
|
52
|
+
|
|
53
|
+
const sendText = useCallback(
|
|
54
|
+
async (text: string, conversationId?: string | null): Promise<string | null> => {
|
|
55
|
+
const trimmed = text.trim();
|
|
56
|
+
if (!trimmed) throw new Error("Message cannot be empty");
|
|
57
|
+
if (!visitorQueryParams) throw new Error("Not connected yet. Please wait and try again.");
|
|
58
|
+
|
|
59
|
+
const localMessageId =
|
|
60
|
+
typeof crypto !== "undefined" && crypto.randomUUID
|
|
61
|
+
? crypto.randomUUID()
|
|
62
|
+
: "lc-" + Date.now();
|
|
63
|
+
const body: any = {
|
|
64
|
+
localMessageId,
|
|
65
|
+
message: { type: "text", text: { body: trimmed } },
|
|
66
|
+
};
|
|
67
|
+
if (conversationId) body.conversationId = conversationId;
|
|
68
|
+
|
|
69
|
+
setSending(true);
|
|
70
|
+
setError(null);
|
|
71
|
+
try {
|
|
72
|
+
const raw = await conversationApi.sendVisitorMessage(body, {
|
|
73
|
+
params: visitorQueryParams,
|
|
74
|
+
});
|
|
75
|
+
return pickConversationId(raw);
|
|
76
|
+
} catch (e: any) {
|
|
77
|
+
const msg = e?.response?.data?.message ?? e?.message ?? "Failed to send message";
|
|
78
|
+
setError(msg);
|
|
79
|
+
throw new Error(msg);
|
|
80
|
+
} finally {
|
|
81
|
+
setSending(false);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
[visitorQueryParams]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const sendAttachment = useCallback(
|
|
88
|
+
async (
|
|
89
|
+
attachment: AttachmentMeta,
|
|
90
|
+
conversationId?: string | null
|
|
91
|
+
): Promise<string | null> => {
|
|
92
|
+
if (!visitorQueryParams) throw new Error("Not connected yet. Please wait and try again.");
|
|
93
|
+
|
|
94
|
+
const localMessageId =
|
|
95
|
+
typeof crypto !== "undefined" && crypto.randomUUID
|
|
96
|
+
? crypto.randomUUID()
|
|
97
|
+
: "lc-" + Date.now();
|
|
98
|
+
const typeObj: Record<string, any> = { link: attachment.fileUrl };
|
|
99
|
+
if (attachment.caption) typeObj.caption = attachment.caption;
|
|
100
|
+
if (attachment.type === "document") typeObj.filename = attachment.fileName;
|
|
101
|
+
|
|
102
|
+
const body: any = {
|
|
103
|
+
localMessageId,
|
|
104
|
+
fileId: attachment.fileId,
|
|
105
|
+
fileUrl: attachment.fileUrl,
|
|
106
|
+
fileName: attachment.fileName,
|
|
107
|
+
fileExtension: attachment.fileExtension,
|
|
108
|
+
message: { type: attachment.type, [attachment.type]: typeObj },
|
|
109
|
+
};
|
|
110
|
+
if (conversationId) body.conversationId = conversationId;
|
|
111
|
+
|
|
112
|
+
setSending(true);
|
|
113
|
+
setError(null);
|
|
114
|
+
try {
|
|
115
|
+
const raw = await conversationApi.sendVisitorMessage(body, {
|
|
116
|
+
params: visitorQueryParams,
|
|
117
|
+
});
|
|
118
|
+
return pickConversationId(raw);
|
|
119
|
+
} catch (e: any) {
|
|
120
|
+
const msg = e?.response?.data?.message ?? e?.message ?? "Failed to send attachment";
|
|
121
|
+
setError(msg);
|
|
122
|
+
throw new Error(msg);
|
|
123
|
+
} finally {
|
|
124
|
+
setSending(false);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
[visitorQueryParams]
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const sendInteractive = useCallback(
|
|
131
|
+
async (reply: InteractiveReplyMeta, conversationId?: string | null): Promise<string | null> => {
|
|
132
|
+
if (!visitorQueryParams) throw new Error("Not connected yet. Please wait and try again.");
|
|
133
|
+
|
|
134
|
+
const localMessageId =
|
|
135
|
+
typeof crypto !== "undefined" && crypto.randomUUID
|
|
136
|
+
? crypto.randomUUID()
|
|
137
|
+
: "lc-" + Date.now();
|
|
138
|
+
const body: any = {
|
|
139
|
+
localMessageId,
|
|
140
|
+
message: {
|
|
141
|
+
type: "interactive",
|
|
142
|
+
interactive: {
|
|
143
|
+
type: reply.type,
|
|
144
|
+
[reply.type]: { id: reply.id, title: reply.title },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
if (conversationId) body.conversationId = conversationId;
|
|
149
|
+
|
|
150
|
+
setSending(true);
|
|
151
|
+
setError(null);
|
|
152
|
+
try {
|
|
153
|
+
const raw = await conversationApi.sendVisitorMessage(body, {
|
|
154
|
+
params: visitorQueryParams,
|
|
155
|
+
});
|
|
156
|
+
return pickConversationId(raw);
|
|
157
|
+
} catch (e: any) {
|
|
158
|
+
const msg = e?.response?.data?.message ?? e?.message ?? "Failed to send reply";
|
|
159
|
+
setError(msg);
|
|
160
|
+
throw new Error(msg);
|
|
161
|
+
} finally {
|
|
162
|
+
setSending(false);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
[visitorQueryParams]
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return { sending, error, clearError, sendText, sendAttachment, sendInteractive };
|
|
169
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -75,6 +75,9 @@ export { PanelRouterProvider, usePanelRouter } from "./navigation/panel-router-c
|
|
|
75
75
|
export type { PanelRouterContextValue, WidgetPanelView } from "./navigation/panel-router-context";
|
|
76
76
|
|
|
77
77
|
// ─── Theme ─────────────────────────────────────────────────────────────────────
|
|
78
|
+
export { usePanelSafeAreaInsets } from "./ui/safe-area";
|
|
79
|
+
export type { PanelSafeAreaInsets } from "./ui/safe-area";
|
|
80
|
+
|
|
78
81
|
export {
|
|
79
82
|
defaultTheme,
|
|
80
83
|
mergeTheme,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from "react";
|
|
2
|
-
import { View, StyleSheet, BackHandler, PanResponder
|
|
1
|
+
import React, { useEffect, useRef, type ComponentType, type ReactNode } from "react";
|
|
2
|
+
import { View, StyleSheet, BackHandler, PanResponder } from "react-native";
|
|
3
3
|
import { PanelRouterProvider, usePanelRouter } from "./panel-router-context";
|
|
4
4
|
import { HomeScreen } from "../ui/components/HomeScreen";
|
|
5
5
|
import { ConversationListScreen } from "../ui/components/ConversationListScreen";
|
|
@@ -8,10 +8,25 @@ import { useLiveChatContext } from "../provider/LiveChatContext";
|
|
|
8
8
|
import { getBrandColor, useEffectiveAppearance } from "../ui/theme";
|
|
9
9
|
import { WsStatusStrip } from "../ui/components/WsStatusStrip";
|
|
10
10
|
|
|
11
|
+
let KeyboardProvider: ComponentType<{ children: ReactNode }> | null = null;
|
|
12
|
+
try {
|
|
13
|
+
KeyboardProvider = require("react-native-keyboard-controller").KeyboardProvider;
|
|
14
|
+
} catch {
|
|
15
|
+
KeyboardProvider = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
export type LiveChatPanelProps = {
|
|
12
19
|
onClose?: () => void;
|
|
20
|
+
initialConversationId?: string | null;
|
|
13
21
|
};
|
|
14
22
|
|
|
23
|
+
let PanelSafeAreaProvider: ComponentType<{ children: ReactNode }> | null = null;
|
|
24
|
+
try {
|
|
25
|
+
PanelSafeAreaProvider = require("react-native-safe-area-context").SafeAreaProvider;
|
|
26
|
+
} catch {
|
|
27
|
+
PanelSafeAreaProvider = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
15
30
|
// Mirrors web PanelShell — neutral chrome for chat screens, brand bg for home
|
|
16
31
|
function usePanelBackground(view: string): string {
|
|
17
32
|
const { widgetConfig } = useLiveChatContext();
|
|
@@ -27,7 +42,7 @@ function usePanelBackground(view: string): string {
|
|
|
27
42
|
const SWIPE_THRESHOLD = 80; // px — minimum horizontal swipe to trigger back
|
|
28
43
|
const SWIPE_VELOCITY = 0.3; // minimum velocity
|
|
29
44
|
|
|
30
|
-
function PanelContent({ onClose }:
|
|
45
|
+
function PanelContent({ onClose }: { onClose?: () => void }) {
|
|
31
46
|
const { view, conversationId, goConversation, goPreviousChats, goBack } = usePanelRouter();
|
|
32
47
|
const bg = usePanelBackground(view);
|
|
33
48
|
const canGoBack = view === "conversation" || view === "previousChats";
|
|
@@ -103,14 +118,23 @@ function PanelContent({ onClose }: LiveChatPanelProps) {
|
|
|
103
118
|
);
|
|
104
119
|
}
|
|
105
120
|
|
|
106
|
-
export function LiveChatPanel({ onClose }: LiveChatPanelProps) {
|
|
107
|
-
|
|
108
|
-
<PanelRouterProvider>
|
|
121
|
+
export function LiveChatPanel({ onClose, initialConversationId }: LiveChatPanelProps) {
|
|
122
|
+
const panel = (
|
|
123
|
+
<PanelRouterProvider initialConversationId={initialConversationId}>
|
|
109
124
|
<View style={styles.flex}>
|
|
110
125
|
<PanelContent onClose={onClose} />
|
|
111
126
|
</View>
|
|
112
127
|
</PanelRouterProvider>
|
|
113
128
|
);
|
|
129
|
+
|
|
130
|
+
const withKeyboard = KeyboardProvider ? (
|
|
131
|
+
<KeyboardProvider>{panel}</KeyboardProvider>
|
|
132
|
+
) : panel;
|
|
133
|
+
|
|
134
|
+
if (PanelSafeAreaProvider) {
|
|
135
|
+
return <PanelSafeAreaProvider>{withKeyboard}</PanelSafeAreaProvider>;
|
|
136
|
+
}
|
|
137
|
+
return withKeyboard;
|
|
114
138
|
}
|
|
115
139
|
|
|
116
140
|
const styles = StyleSheet.create({
|
|
@@ -27,11 +27,12 @@ type RouteEntry = {
|
|
|
27
27
|
|
|
28
28
|
const PanelRouterContext = createContext<PanelRouterContextValue | null>(null);
|
|
29
29
|
|
|
30
|
-
export function PanelRouterProvider({ children }: { children: ReactNode }) {
|
|
31
|
-
const
|
|
32
|
-
const [
|
|
30
|
+
export function PanelRouterProvider({ children, initialConversationId }: { children: ReactNode; initialConversationId?: string | null }) {
|
|
31
|
+
const initialView: WidgetPanelView = initialConversationId ? "conversation" : "home";
|
|
32
|
+
const [view, setView] = useState<WidgetPanelView>(initialView);
|
|
33
|
+
const [conversationId, setConversationId] = useState<string | null>(initialConversationId ?? null);
|
|
33
34
|
|
|
34
|
-
const stack = useRef<RouteEntry[]>([{ view:
|
|
35
|
+
const stack = useRef<RouteEntry[]>([{ view: initialView, conversationId: initialConversationId ?? null }]);
|
|
35
36
|
|
|
36
37
|
const navigate = useCallback((entry: RouteEntry, replace: boolean) => {
|
|
37
38
|
if (replace) {
|
|
@@ -23,6 +23,8 @@ export const LiveChatContext = createContext<LiveChatContextValue>({
|
|
|
23
23
|
isVisible: true,
|
|
24
24
|
show: () => undefined,
|
|
25
25
|
hide: () => undefined,
|
|
26
|
+
activeConversationId: null,
|
|
27
|
+
setActiveConversationId: () => undefined,
|
|
26
28
|
wsClient: null,
|
|
27
29
|
connectionState: { isConnected: false, isConnecting: false, isAwaitingRetry: false, lastError: null },
|
|
28
30
|
theme: defaultTheme,
|