groupchat 0.0.8 → 0.0.10
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/dist/index.js +102 -42
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -349,15 +349,7 @@ function Header({
|
|
|
349
349
|
width: "100%",
|
|
350
350
|
flexShrink: 0,
|
|
351
351
|
children: [
|
|
352
|
-
/* @__PURE__ */ jsx3(Box3, { children: title
|
|
353
|
-
/* @__PURE__ */ jsxs3(Text3, { color: "cyan", bold: true, children: [
|
|
354
|
-
"$",
|
|
355
|
-
" "
|
|
356
|
-
] }),
|
|
357
|
-
/* @__PURE__ */ jsx3(Text3, { color: "blue", bold: true, children: "groupchat" }),
|
|
358
|
-
/* @__PURE__ */ jsx3(Text3, { color: "gray", children: " --session " }),
|
|
359
|
-
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: username || "..." })
|
|
360
|
-
] }) }),
|
|
352
|
+
/* @__PURE__ */ jsx3(Box3, { children: title }),
|
|
361
353
|
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
362
354
|
showStatus && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
363
355
|
/* @__PURE__ */ jsxs3(Text3, { color: statusColor, children: [
|
|
@@ -599,7 +591,9 @@ function Menu({
|
|
|
599
591
|
publicChannels,
|
|
600
592
|
privateChannels,
|
|
601
593
|
unreadCounts,
|
|
602
|
-
aggregatedPresence
|
|
594
|
+
aggregatedPresence,
|
|
595
|
+
isLoadingChannels = false,
|
|
596
|
+
totalUnreadCount = 0
|
|
603
597
|
}) {
|
|
604
598
|
const { stdout } = useStdout2();
|
|
605
599
|
const { navigate } = useNavigation();
|
|
@@ -632,8 +626,9 @@ function Menu({
|
|
|
632
626
|
}, [menuItems, currentChannel]);
|
|
633
627
|
useEffect2(() => {
|
|
634
628
|
if (!stdout) return;
|
|
635
|
-
|
|
636
|
-
|
|
629
|
+
const unreadSuffix = totalUnreadCount > 0 ? ` (${totalUnreadCount})` : "";
|
|
630
|
+
stdout.write(`\x1B]0;Menu${unreadSuffix}\x07`);
|
|
631
|
+
}, [stdout, totalUnreadCount]);
|
|
637
632
|
useInput3((input, key) => {
|
|
638
633
|
if (key.escape) {
|
|
639
634
|
navigate("chat");
|
|
@@ -721,7 +716,7 @@ function Menu({
|
|
|
721
716
|
}
|
|
722
717
|
)
|
|
723
718
|
] }),
|
|
724
|
-
allChannels.length === 0 && /* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsx7(Text5, { color: "gray", children: "No channels available" }) })
|
|
719
|
+
allChannels.length === 0 && /* @__PURE__ */ jsx7(Box6, { children: isLoadingChannels ? /* @__PURE__ */ jsx7(Text5, { color: "cyan", children: "Loading channels..." }) : /* @__PURE__ */ jsx7(Text5, { color: "gray", children: "No channels available" }) })
|
|
725
720
|
] }),
|
|
726
721
|
/* @__PURE__ */ jsx7(Box6, { paddingRight: 2, paddingTop: 2, children: /* @__PURE__ */ jsx7(AtAGlance, { presenceState: aggregatedPresence }) })
|
|
727
722
|
] }),
|
|
@@ -1747,7 +1742,8 @@ function ChatView({
|
|
|
1747
1742
|
onTypingStop,
|
|
1748
1743
|
onCommandSend,
|
|
1749
1744
|
error,
|
|
1750
|
-
token
|
|
1745
|
+
token,
|
|
1746
|
+
totalUnreadCount = 0
|
|
1751
1747
|
}) {
|
|
1752
1748
|
const { stdout } = useStdout3();
|
|
1753
1749
|
const { tooltip, isInputDisabled, handleInputChange, handleSubmit } = useCommandInput({
|
|
@@ -1766,8 +1762,9 @@ function ChatView({
|
|
|
1766
1762
|
useEffect5(() => {
|
|
1767
1763
|
if (!stdout) return;
|
|
1768
1764
|
const prefix = connectionStatus === "connected" ? "\u2022 " : "";
|
|
1769
|
-
|
|
1770
|
-
|
|
1765
|
+
const unreadSuffix = totalUnreadCount > 0 ? ` (${totalUnreadCount})` : "";
|
|
1766
|
+
stdout.write(`\x1B]0;${prefix}#${displayName}${unreadSuffix}\x07`);
|
|
1767
|
+
}, [stdout, connectionStatus, displayName, totalUnreadCount]);
|
|
1771
1768
|
return /* @__PURE__ */ jsxs13(Layout, { width: terminalSize.columns, height: terminalSize.rows, topPadding, children: [
|
|
1772
1769
|
/* @__PURE__ */ jsx14(Layout.Header, { children: /* @__PURE__ */ jsx14(
|
|
1773
1770
|
Header,
|
|
@@ -1866,7 +1863,8 @@ function CreateChannelScreen({
|
|
|
1866
1863
|
connectionStatus,
|
|
1867
1864
|
onLogout,
|
|
1868
1865
|
onCreateChannel,
|
|
1869
|
-
topPadding = 0
|
|
1866
|
+
topPadding = 0,
|
|
1867
|
+
totalUnreadCount = 0
|
|
1870
1868
|
}) {
|
|
1871
1869
|
const { stdout } = useStdout4();
|
|
1872
1870
|
const { navigate } = useNavigation();
|
|
@@ -1877,8 +1875,9 @@ function CreateChannelScreen({
|
|
|
1877
1875
|
const [error, setError] = useState7(null);
|
|
1878
1876
|
useEffect6(() => {
|
|
1879
1877
|
if (!stdout) return;
|
|
1880
|
-
|
|
1881
|
-
|
|
1878
|
+
const unreadSuffix = totalUnreadCount > 0 ? ` (${totalUnreadCount})` : "";
|
|
1879
|
+
stdout.write(`\x1B]0;Create Channel${unreadSuffix}\x07`);
|
|
1880
|
+
}, [stdout, totalUnreadCount]);
|
|
1882
1881
|
useInput5((input, key) => {
|
|
1883
1882
|
if (key.escape) {
|
|
1884
1883
|
navigate("menu");
|
|
@@ -2189,6 +2188,7 @@ var init_channel_manager = __esm({
|
|
|
2189
2188
|
state.realtimeMessages.shift();
|
|
2190
2189
|
}
|
|
2191
2190
|
}
|
|
2191
|
+
this.callbacks.onNonActiveChannelMessage?.(channelSlug, message);
|
|
2192
2192
|
}
|
|
2193
2193
|
});
|
|
2194
2194
|
channel.on("presence_state", (payload) => {
|
|
@@ -2611,18 +2611,20 @@ var init_channel_manager = __esm({
|
|
|
2611
2611
|
|
|
2612
2612
|
// src/hooks/use-multi-channel-chat.ts
|
|
2613
2613
|
import { useState as useState8, useCallback as useCallback3, useRef as useRef3, useEffect as useEffect7 } from "react";
|
|
2614
|
-
function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
2615
|
-
const [
|
|
2614
|
+
function useMultiChannelChat(token, currentChannel, onChannelListChanged, incrementUnreadCount) {
|
|
2615
|
+
const [messageCache, setMessageCache] = useState8({});
|
|
2616
|
+
const [subscriberCache, setSubscriberCache] = useState8({});
|
|
2616
2617
|
const [connectionStatus, setConnectionStatus] = useState8("disconnected");
|
|
2617
2618
|
const [username, setUsername] = useState8(null);
|
|
2618
2619
|
const [error, setError] = useState8(null);
|
|
2619
2620
|
const [typingUsers, setTypingUsers] = useState8([]);
|
|
2620
2621
|
const [presenceState, setPresenceState] = useState8({});
|
|
2621
|
-
const [subscribers, setSubscribers] = useState8([]);
|
|
2622
2622
|
const [channelsReady, setChannelsReady] = useState8(false);
|
|
2623
2623
|
const managerRef = useRef3(null);
|
|
2624
2624
|
const prevChannelRef = useRef3(null);
|
|
2625
2625
|
const isLoadingHistory = useRef3(false);
|
|
2626
|
+
const messages = messageCache[currentChannel] || [];
|
|
2627
|
+
const subscribers = subscriberCache[currentChannel] || [];
|
|
2626
2628
|
useEffect7(() => {
|
|
2627
2629
|
if (!token) {
|
|
2628
2630
|
if (managerRef.current) {
|
|
@@ -2641,7 +2643,13 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2641
2643
|
token,
|
|
2642
2644
|
{
|
|
2643
2645
|
onMessage: (channelSlug, message) => {
|
|
2644
|
-
|
|
2646
|
+
setMessageCache((prev) => ({
|
|
2647
|
+
...prev,
|
|
2648
|
+
[channelSlug]: [...prev[channelSlug] || [], message]
|
|
2649
|
+
}));
|
|
2650
|
+
},
|
|
2651
|
+
onNonActiveChannelMessage: (channelSlug, _message) => {
|
|
2652
|
+
incrementUnreadCount?.(channelSlug);
|
|
2645
2653
|
},
|
|
2646
2654
|
onPresenceState: (channelSlug, state) => {
|
|
2647
2655
|
setPresenceState(state);
|
|
@@ -2705,10 +2713,14 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2705
2713
|
}
|
|
2706
2714
|
},
|
|
2707
2715
|
onUserInvitedToChannel: (channelSlug, invitedUsername, invitedUserId, invitedBy) => {
|
|
2708
|
-
|
|
2709
|
-
const
|
|
2716
|
+
setSubscriberCache((prev) => {
|
|
2717
|
+
const currentSubs = prev[channelSlug] || [];
|
|
2718
|
+
const exists = currentSubs.some((s) => s.user_id === invitedUserId);
|
|
2710
2719
|
if (!exists) {
|
|
2711
|
-
return
|
|
2720
|
+
return {
|
|
2721
|
+
...prev,
|
|
2722
|
+
[channelSlug]: [...currentSubs, { username: invitedUsername, user_id: invitedUserId, role: "member" }]
|
|
2723
|
+
};
|
|
2712
2724
|
}
|
|
2713
2725
|
return prev;
|
|
2714
2726
|
});
|
|
@@ -2717,7 +2729,13 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2717
2729
|
setError(`You were removed from ${channelSlug} by ${removedBy}`);
|
|
2718
2730
|
},
|
|
2719
2731
|
onUserRemovedFromChannel: (channelSlug, removedUsername, removedBy) => {
|
|
2720
|
-
|
|
2732
|
+
setSubscriberCache((prev) => {
|
|
2733
|
+
const currentSubs = prev[channelSlug] || [];
|
|
2734
|
+
return {
|
|
2735
|
+
...prev,
|
|
2736
|
+
[channelSlug]: currentSubs.filter((s) => s.username !== removedUsername)
|
|
2737
|
+
};
|
|
2738
|
+
});
|
|
2721
2739
|
}
|
|
2722
2740
|
}
|
|
2723
2741
|
);
|
|
@@ -2768,9 +2786,15 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2768
2786
|
const history = await manager.fetchHistory(currentChannel);
|
|
2769
2787
|
if (currentChannel.startsWith("private_room:")) {
|
|
2770
2788
|
const subs = await manager.fetchSubscribers(currentChannel);
|
|
2771
|
-
|
|
2789
|
+
setSubscriberCache((prev) => ({
|
|
2790
|
+
...prev,
|
|
2791
|
+
[currentChannel]: subs
|
|
2792
|
+
}));
|
|
2772
2793
|
} else {
|
|
2773
|
-
|
|
2794
|
+
setSubscriberCache((prev) => ({
|
|
2795
|
+
...prev,
|
|
2796
|
+
[currentChannel]: []
|
|
2797
|
+
}));
|
|
2774
2798
|
}
|
|
2775
2799
|
const realtimeMessages = manager.getRealtimeMessages(currentChannel);
|
|
2776
2800
|
const merged = [...history, ...realtimeMessages];
|
|
@@ -2781,7 +2805,10 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2781
2805
|
return true;
|
|
2782
2806
|
});
|
|
2783
2807
|
deduplicated.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
2784
|
-
|
|
2808
|
+
setMessageCache((prev) => ({
|
|
2809
|
+
...prev,
|
|
2810
|
+
[currentChannel]: deduplicated
|
|
2811
|
+
}));
|
|
2785
2812
|
manager.clearRealtimeMessages(currentChannel);
|
|
2786
2813
|
const presence = manager.getPresence(currentChannel);
|
|
2787
2814
|
setPresenceState(presence);
|
|
@@ -2947,7 +2974,7 @@ var init_use_agent_detection = __esm({
|
|
|
2947
2974
|
});
|
|
2948
2975
|
|
|
2949
2976
|
// src/hooks/use-channels.ts
|
|
2950
|
-
import { useState as useState9, useCallback as useCallback5, useEffect as useEffect9 } from "react";
|
|
2977
|
+
import { useState as useState9, useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo6 } from "react";
|
|
2951
2978
|
function useChannels(token) {
|
|
2952
2979
|
const [publicChannels, setPublicChannels] = useState9([]);
|
|
2953
2980
|
const [privateChannels, setPrivateChannels] = useState9([]);
|
|
@@ -2983,6 +3010,21 @@ function useChannels(token) {
|
|
|
2983
3010
|
console.error("Failed to refetch unread counts:", err);
|
|
2984
3011
|
}
|
|
2985
3012
|
}, [token]);
|
|
3013
|
+
const incrementUnreadCount = useCallback5((channelSlug) => {
|
|
3014
|
+
setUnreadCounts((prev) => ({
|
|
3015
|
+
...prev,
|
|
3016
|
+
[channelSlug]: (prev[channelSlug] || 0) + 1
|
|
3017
|
+
}));
|
|
3018
|
+
}, []);
|
|
3019
|
+
const clearUnreadCount = useCallback5((channelSlug) => {
|
|
3020
|
+
setUnreadCounts((prev) => ({
|
|
3021
|
+
...prev,
|
|
3022
|
+
[channelSlug]: 0
|
|
3023
|
+
}));
|
|
3024
|
+
}, []);
|
|
3025
|
+
const totalUnreadCount = useMemo6(() => {
|
|
3026
|
+
return Object.values(unreadCounts).reduce((sum, count) => sum + count, 0);
|
|
3027
|
+
}, [unreadCounts]);
|
|
2986
3028
|
useEffect9(() => {
|
|
2987
3029
|
if (token) {
|
|
2988
3030
|
fetchData();
|
|
@@ -2995,7 +3037,10 @@ function useChannels(token) {
|
|
|
2995
3037
|
loading,
|
|
2996
3038
|
error,
|
|
2997
3039
|
refetch: fetchData,
|
|
2998
|
-
refetchUnreadCounts
|
|
3040
|
+
refetchUnreadCounts,
|
|
3041
|
+
incrementUnreadCount,
|
|
3042
|
+
clearUnreadCount,
|
|
3043
|
+
totalUnreadCount
|
|
2999
3044
|
};
|
|
3000
3045
|
}
|
|
3001
3046
|
var init_use_channels = __esm({
|
|
@@ -3068,7 +3113,7 @@ function AppContent() {
|
|
|
3068
3113
|
}
|
|
3069
3114
|
checkAuth();
|
|
3070
3115
|
}, []);
|
|
3071
|
-
const { publicChannels, privateChannels, unreadCounts, refetchUnreadCounts, refetch: refetchChannels } = useChannels(token);
|
|
3116
|
+
const { publicChannels, privateChannels, unreadCounts, loading: isLoadingChannels, refetchUnreadCounts, refetch: refetchChannels, incrementUnreadCount, clearUnreadCount, totalUnreadCount } = useChannels(token);
|
|
3072
3117
|
const {
|
|
3073
3118
|
messages,
|
|
3074
3119
|
connectionStatus,
|
|
@@ -3083,7 +3128,7 @@ function AppContent() {
|
|
|
3083
3128
|
connect,
|
|
3084
3129
|
disconnect,
|
|
3085
3130
|
channelManager
|
|
3086
|
-
} = useMultiChannelChat(token, currentChannel, refetchChannels);
|
|
3131
|
+
} = useMultiChannelChat(token, currentChannel, refetchChannels, incrementUnreadCount);
|
|
3087
3132
|
const { users } = usePresence(presenceState, subscribers, currentChannel);
|
|
3088
3133
|
useAgentDetection(channelManager, connectionStatus === "connected");
|
|
3089
3134
|
const sendCommand = useCallback6(
|
|
@@ -3127,10 +3172,11 @@ function AppContent() {
|
|
|
3127
3172
|
}
|
|
3128
3173
|
if (currentChannel) {
|
|
3129
3174
|
markChannelAsRead(currentChannel, true);
|
|
3175
|
+
clearUnreadCount(currentChannel);
|
|
3130
3176
|
}
|
|
3131
3177
|
prevChannelForMarkAsReadRef.current = currentChannel;
|
|
3132
3178
|
}
|
|
3133
|
-
}, [currentChannel, channelManager, refetchUnreadCounts]);
|
|
3179
|
+
}, [currentChannel, channelManager, refetchUnreadCounts, clearUnreadCount]);
|
|
3134
3180
|
useEffect10(() => {
|
|
3135
3181
|
return () => {
|
|
3136
3182
|
if (currentChannel && channelManager) {
|
|
@@ -3143,6 +3189,10 @@ function AppContent() {
|
|
|
3143
3189
|
refetchUnreadCounts();
|
|
3144
3190
|
}
|
|
3145
3191
|
}, [route, refetchUnreadCounts]);
|
|
3192
|
+
useEffect10(() => {
|
|
3193
|
+
setScrollOffset(0);
|
|
3194
|
+
setIsScrollDetached(false);
|
|
3195
|
+
}, [currentChannel]);
|
|
3146
3196
|
const handleLogin = useCallback6(async () => {
|
|
3147
3197
|
setAuthState("authenticating");
|
|
3148
3198
|
setAuthStatus("Starting login...");
|
|
@@ -3271,7 +3321,9 @@ function AppContent() {
|
|
|
3271
3321
|
publicChannels,
|
|
3272
3322
|
privateChannels,
|
|
3273
3323
|
unreadCounts,
|
|
3274
|
-
aggregatedPresence
|
|
3324
|
+
aggregatedPresence,
|
|
3325
|
+
isLoadingChannels,
|
|
3326
|
+
totalUnreadCount
|
|
3275
3327
|
}
|
|
3276
3328
|
)
|
|
3277
3329
|
}
|
|
@@ -3294,7 +3346,8 @@ function AppContent() {
|
|
|
3294
3346
|
connectionStatus,
|
|
3295
3347
|
onLogout: handleLogout,
|
|
3296
3348
|
onCreateChannel: handleCreateChannel,
|
|
3297
|
-
topPadding
|
|
3349
|
+
topPadding,
|
|
3350
|
+
totalUnreadCount
|
|
3298
3351
|
}
|
|
3299
3352
|
)
|
|
3300
3353
|
}
|
|
@@ -3325,7 +3378,8 @@ function AppContent() {
|
|
|
3325
3378
|
onTypingStop: stopTyping,
|
|
3326
3379
|
onCommandSend: sendCommand,
|
|
3327
3380
|
error,
|
|
3328
|
-
token
|
|
3381
|
+
token,
|
|
3382
|
+
totalUnreadCount
|
|
3329
3383
|
}
|
|
3330
3384
|
);
|
|
3331
3385
|
}
|
|
@@ -3419,7 +3473,7 @@ function getUpdateCommand() {
|
|
|
3419
3473
|
// src/components/UpdatePrompt.tsx
|
|
3420
3474
|
import { useState } from "react";
|
|
3421
3475
|
import { Box, Text, useInput, useApp } from "ink";
|
|
3422
|
-
import { execSync } from "child_process";
|
|
3476
|
+
import { execSync, spawn } from "child_process";
|
|
3423
3477
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3424
3478
|
var options = [
|
|
3425
3479
|
{ label: "Update now", value: "update" },
|
|
@@ -3454,8 +3508,14 @@ function UpdatePrompt({ updateInfo, onComplete }) {
|
|
|
3454
3508
|
const command = getUpdateCommand();
|
|
3455
3509
|
try {
|
|
3456
3510
|
execSync(command, { stdio: "inherit" });
|
|
3457
|
-
console.log("\n\nUpdate complete!
|
|
3458
|
-
|
|
3511
|
+
console.log("\n\nUpdate complete! Restarting...\n");
|
|
3512
|
+
const args = process.argv.slice(2);
|
|
3513
|
+
const child = spawn("groupchat", args, {
|
|
3514
|
+
detached: true,
|
|
3515
|
+
stdio: "inherit"
|
|
3516
|
+
});
|
|
3517
|
+
child.unref();
|
|
3518
|
+
setTimeout(() => exit(), 100);
|
|
3459
3519
|
} catch (err) {
|
|
3460
3520
|
setUpdateError(
|
|
3461
3521
|
`Update failed. Please run manually: ${command}`
|
|
@@ -3511,7 +3571,7 @@ function UpdatePrompt({ updateInfo, onComplete }) {
|
|
|
3511
3571
|
// package.json
|
|
3512
3572
|
var package_default = {
|
|
3513
3573
|
name: "groupchat",
|
|
3514
|
-
version: "0.0.
|
|
3574
|
+
version: "0.0.10",
|
|
3515
3575
|
description: "CLI chat client for Groupchat",
|
|
3516
3576
|
type: "module",
|
|
3517
3577
|
main: "./dist/index.js",
|