mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 06:54:38 +08:00
Mark unread private messages in inbox
This commit is contained in:
parent
1f8fb7acf4
commit
2fca031362
7 changed files with 29 additions and 6 deletions
|
|
@ -114,6 +114,7 @@ Command output pages use `j/k`, `Ctrl+D/U`, and `g/G` for paging. `:inbox`
|
||||||
shows incoming and sent private messages newest-first; press `r` to refresh it
|
shows incoming and sent private messages newest-first; press `r` to refresh it
|
||||||
manually, and it refreshes when a new private message arrives while the inbox
|
manually, and it refreshes when a new private message arrives while the inbox
|
||||||
is open. `:reply text` and `:r text` send to the latest private-message peer.
|
is open. `:reply text` and `:r text` send to the latest private-message peer.
|
||||||
|
Unread incoming private messages are marked with `*` until `:inbox` renders.
|
||||||
Private messages are per-session only and are not written to `messages.log`.
|
Private messages are per-session only and are not written to `messages.log`.
|
||||||
|
|
||||||
**Special messages (INSERT mode)**
|
**Special messages (INSERT mode)**
|
||||||
|
|
|
||||||
|
|
@ -167,8 +167,9 @@ persisted to `messages.log` and are not included in exec `tail`, exec `dump`,
|
||||||
|
|
||||||
Each participant keeps a bounded in-memory `:inbox` for the current session.
|
Each participant keeps a bounded in-memory `:inbox` for the current session.
|
||||||
Recipients see incoming private messages; senders see local sent-message
|
Recipients see incoming private messages; senders see local sent-message
|
||||||
copies. `:inbox` displays newest messages first, can be refreshed with `r`,
|
copies. Unread incoming messages are marked with `*` until `:inbox` renders.
|
||||||
and refreshes automatically while open when a new private message arrives.
|
`:inbox` displays newest messages first, can be refreshed with `r`, and
|
||||||
|
refreshes automatically while open when a new private message arrives.
|
||||||
|
|
||||||
### `help`
|
### `help`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ The product path should stay short:
|
||||||
reconnect.
|
reconnect.
|
||||||
- `:inbox` is live enough for normal chat use: it can be refreshed with `r`
|
- `:inbox` is live enough for normal chat use: it can be refreshed with `r`
|
||||||
and refreshes automatically when a new private message arrives while the
|
and refreshes automatically when a new private message arrives while the
|
||||||
inbox is open.
|
inbox is open. Incoming unread messages are marked with `*` until the inbox
|
||||||
|
renders them.
|
||||||
- `:reply` / `:r` keeps the private-message path keyboard-short: it answers
|
- `:reply` / `:r` keeps the private-message path keyboard-short: it answers
|
||||||
the latest private-message peer in the current session without retyping a
|
the latest private-message peer in the current session without retyping a
|
||||||
username.
|
username.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ typedef struct {
|
||||||
char to[MAX_USERNAME_LEN];
|
char to[MAX_USERNAME_LEN];
|
||||||
char content[MAX_MESSAGE_LEN];
|
char content[MAX_MESSAGE_LEN];
|
||||||
bool outgoing;
|
bool outgoing;
|
||||||
|
bool unread;
|
||||||
} whisper_t;
|
} whisper_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ static void client_append_whisper(client_t *owner, const char *from,
|
||||||
snprintf(owner->whisper_inbox[slot].content,
|
snprintf(owner->whisper_inbox[slot].content,
|
||||||
sizeof(owner->whisper_inbox[slot].content), "%s", content);
|
sizeof(owner->whisper_inbox[slot].content), "%s", content);
|
||||||
owner->whisper_inbox[slot].outgoing = outgoing;
|
owner->whisper_inbox[slot].outgoing = outgoing;
|
||||||
|
owner->whisper_inbox[slot].unread = count_unread;
|
||||||
snprintf(owner->last_whisper_peer, sizeof(owner->last_whisper_peer), "%s",
|
snprintf(owner->last_whisper_peer, sizeof(owner->last_whisper_peer), "%s",
|
||||||
outgoing ? to : from);
|
outgoing ? to : from);
|
||||||
if (count_unread) {
|
if (count_unread) {
|
||||||
|
|
@ -142,6 +143,9 @@ static void append_inbox_output(client_t *client, char *output,
|
||||||
snap_count = client->whisper_inbox_count;
|
snap_count = client->whisper_inbox_count;
|
||||||
memcpy(snapshot, client->whisper_inbox,
|
memcpy(snapshot, client->whisper_inbox,
|
||||||
snap_count * sizeof(whisper_t));
|
snap_count * sizeof(whisper_t));
|
||||||
|
for (int i = 0; i < snap_count; i++) {
|
||||||
|
client->whisper_inbox[i].unread = false;
|
||||||
|
}
|
||||||
client->unread_whispers = 0;
|
client->unread_whispers = 0;
|
||||||
pthread_mutex_unlock(&client->whisper_lock);
|
pthread_mutex_unlock(&client->whisper_lock);
|
||||||
|
|
||||||
|
|
@ -157,6 +161,7 @@ static void append_inbox_output(client_t *client, char *output,
|
||||||
for (int i = snap_count - 1; i >= 0; i--) {
|
for (int i = snap_count - 1; i >= 0; i--) {
|
||||||
char ts[20];
|
char ts[20];
|
||||||
char peer[MAX_USERNAME_LEN + 16];
|
char peer[MAX_USERNAME_LEN + 16];
|
||||||
|
const char *marker = snapshot[i].unread ? "\033[1;35m*\033[0m" : " ";
|
||||||
struct tm tmi;
|
struct tm tmi;
|
||||||
localtime_r(&snapshot[i].timestamp, &tmi);
|
localtime_r(&snapshot[i].timestamp, &tmi);
|
||||||
strftime(ts, sizeof(ts), "%m-%d %H:%M", &tmi);
|
strftime(ts, sizeof(ts), "%m-%d %H:%M", &tmi);
|
||||||
|
|
@ -169,8 +174,8 @@ static void append_inbox_output(client_t *client, char *output,
|
||||||
snprintf(peer, sizeof(peer), "%s", snapshot[i].from);
|
snprintf(peer, sizeof(peer), "%s", snapshot[i].from);
|
||||||
}
|
}
|
||||||
buffer_appendf(output, buf_size, pos,
|
buffer_appendf(output, buf_size, pos,
|
||||||
" \033[90m%s\033[0m \033[35m%s\033[0m: %s\n",
|
" %s \033[90m%s\033[0m \033[35m%s\033[0m: %s\n",
|
||||||
ts, peer, snapshot[i].content);
|
marker, ts, peer, snapshot[i].content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -273,6 +273,18 @@ else
|
||||||
fi
|
fi
|
||||||
BOB_PID=""
|
BOB_PID=""
|
||||||
|
|
||||||
|
if grep -q '.*alice.*private lifecycle second' "$STATE_DIR/bob.log" &&
|
||||||
|
grep -q '\*.*alice.*private lifecycle second' "$STATE_DIR/bob.log" &&
|
||||||
|
grep -q '\*.*bob.*private lifecycle reply' "$STATE_DIR/alice.log"; then
|
||||||
|
echo "✓ unread private messages are visibly marked in inbox"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
echo "✗ inbox unread marker missing"
|
||||||
|
sed -n '1,220p' "$STATE_DIR/bob.log"
|
||||||
|
sed -n '1,260p' "$STATE_DIR/alice.log"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
TAIL_OUTPUT=$(ssh $SSH_EXEC_OPTS localhost "tail -n 10" 2>/dev/null || true)
|
TAIL_OUTPUT=$(ssh $SSH_EXEC_OPTS localhost "tail -n 10" 2>/dev/null || true)
|
||||||
printf '%s\n' "$TAIL_OUTPUT" | grep -q 'hello lifecycle alpha' &&
|
printf '%s\n' "$TAIL_OUTPUT" | grep -q 'hello lifecycle alpha' &&
|
||||||
printf '%s\n' "$TAIL_OUTPUT" | grep -q 'alice2 ships lifecycle'
|
printf '%s\n' "$TAIL_OUTPUT" | grep -q 'alice2 ships lifecycle'
|
||||||
|
|
|
||||||
4
tnt.1
4
tnt.1
|
|
@ -253,7 +253,9 @@ The
|
||||||
.B :inbox
|
.B :inbox
|
||||||
page shows incoming messages and local sent-message copies for the current
|
page shows incoming messages and local sent-message copies for the current
|
||||||
session. It refreshes automatically when a new private message arrives while
|
session. It refreshes automatically when a new private message arrives while
|
||||||
it is open. Use
|
it is open. Incoming unread messages are marked with
|
||||||
|
.B *
|
||||||
|
until the inbox renders them. Use
|
||||||
.B :reply
|
.B :reply
|
||||||
or
|
or
|
||||||
.B :r
|
.B :r
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue