Skip to content

Commit e9bec5b

Browse files
committed
Use message tags data in messages and user colors
1 parent d478f97 commit e9bec5b

4 files changed

Lines changed: 67 additions & 33 deletions

File tree

src/models/message.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
export type MessageType = 'message' | 'action' | 'system';
1+
import type { Tags } from 'irc-framework';
2+
3+
export type MessageType = 'privmsg' | 'action' | 'notice' | 'system';
24

35
export interface Message {
4-
time: Date;
6+
id: string;
7+
time: number;
58
starred: boolean;
69
message: string;
710
target: string;
811
nick: string;
912
type: MessageType;
10-
tags: Record<string, string>;
13+
tags: Tags;
1114
}

src/stores/channel.ts

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const useChannelStore = defineStore('channel', () => {
4747
}
4848
const newChannel = irc.client.channel(channel);
4949
newChannel.updateUsers();
50-
getChannel(channel);
50+
changeActiveChannel(channel);
5151
}
5252
function leaveChannel(channel: string) {
5353
if (!irc.client) {
@@ -63,19 +63,17 @@ const useChannelStore = defineStore('channel', () => {
6363
const channel = getChannel(channelName);
6464
channel.hasNotification = true;
6565

66-
try {
67-
const notification = await notifications.sendNotification(
68-
`New message in ${channel.name}`,
69-
`${user.username}: ${message.message}`,
70-
`new-message-${message.target}`,
71-
);
72-
notification?.addEventListener('click', () => {
73-
// View channel if notification is clicked
66+
notifications.sendNotification(
67+
{
68+
title: `New message in ${channel.name}`,
69+
body: `${user.username}: ${message.message}`,
70+
tag: `new-message-${message.target}`,
71+
},
72+
() => {
73+
// Open channel if notification is clicked
7474
changeActiveChannel(channel.name);
75-
});
76-
} catch (err) {
77-
log.error('Notification error:', err);
78-
}
75+
},
76+
);
7977
}
8078

8179
function markAsRead(channelName: string) {
@@ -105,13 +103,16 @@ const useChannelStore = defineStore('channel', () => {
105103
}
106104

107105
client
108-
.on('action', (event) => {
109-
log.debug('Action', event);
110-
// TODO: Use typing client-tag
106+
.on('tagmsg', (event) => {
107+
log.debug('TAGMSG', event);
108+
// TODO: Use '+typing' client-tag
111109
// See https://ircv3.net/specs/client-tags/typing
112110
})
111+
.on('action', (event) => {
112+
log.debug('ACTION', event);
113+
})
113114
.on('notice', (event) => {
114-
log.debug('Notice', event);
115+
log.debug('NOTICE', event);
115116
})
116117
.on('privmsg', (event) => {
117118
log.debug('PRIVMSG', event);
@@ -123,17 +124,19 @@ const useChannelStore = defineStore('channel', () => {
123124

124125
const highlightedMessage = highlightKeywords(message);
125126
const newMessage: Message = {
126-
// TODO: Read from server time, if available
127-
time: new Date(),
127+
id: event.tags.msgid ?? crypto.randomUUID(),
128+
time: event.time ?? Date.now(),
128129
starred: false,
129130
message: highlightedMessage,
130131
target,
131132
nick,
132-
type: 'message',
133+
type: 'privmsg',
133134
tags: tags ?? {},
134135
};
135-
// TODO: Cap messages per channel (50)
136-
log.debug(newMessage);
136+
// TODO: Cap messages per channel (100)
137+
// TODO: Sort-insert messages based on `time` value
138+
// TODO: Prevent duplicate message insertions with `id` value
139+
log.debug('new message:', newMessage);
137140
channel.messages.push(newMessage);
138141

139142
// If the user has allows for notifications on channel or keywords

src/stores/notifications.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useLocalStorage, useWebNotification } from '@vueuse/core';
1+
import { useLocalStorage, useWebNotification, type WebNotificationOptions } from '@vueuse/core';
22
import log from 'loglevel';
33
import { defineStore } from 'pinia';
44
import { computed } from 'vue';
@@ -38,13 +38,22 @@ const useNotificationsStore = defineStore('notifications', () => {
3838
}
3939

4040
/** Sends device notification only if granted permission */
41-
async function sendNotification(title: string, body: string, tag: string) {
41+
async function sendNotification(
42+
{ title, body, tag }: WebNotificationOptions,
43+
onClick?: () => void,
44+
) {
4245
if (!notificationsEnabled.value) {
43-
return null;
46+
return;
4447
}
4548

46-
const notification = await show({ title, body, tag });
47-
return notification ?? null;
49+
try {
50+
const notification = await show({ title, body, tag });
51+
if (notification && onClick) {
52+
notification.addEventListener('click', onClick);
53+
}
54+
} catch (error) {
55+
log.error('Notification error', error);
56+
}
4857
}
4958

5059
return {

src/stores/user-list.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { ANONYMOUS_USER, DEFAULT_COLORS } from '#constants';
22
import type { User } from '#models';
3-
import { parseNick, parseUid } from '#utils';
3+
import { getUserProfile } from '#services';
4+
import { isValidHexColor, parseNick, parseUid } from '#utils';
45
import log from 'loglevel';
56
import { defineStore } from 'pinia';
67
import { computed, reactive, readonly, watch } from 'vue';
78
import useIrcStore from './irc';
8-
import { getUserProfile as fetchUserProfile } from '#services';
99

1010
const useUserListStore = defineStore('userList', () => {
1111
const irc = useIrcStore();
@@ -88,6 +88,16 @@ const useUserListStore = defineStore('userList', () => {
8888
user.awayReason = '';
8989
}
9090

91+
function updateUserColor(nick: string, color: string) {
92+
const username = parseNick(nick);
93+
const user = knownUsers.get(username);
94+
if (!user) {
95+
log.warn(`Tried to update color for user "${nick}", but they weren't in the user list.`);
96+
return;
97+
}
98+
user.color = color;
99+
}
100+
91101
async function loadProfile(nick: string) {
92102
const username = parseNick(nick);
93103
const user = knownUsers.get(username);
@@ -97,7 +107,7 @@ const useUserListStore = defineStore('userList', () => {
97107

98108
user.isFetchingProfile = true;
99109
try {
100-
const profile = await fetchUserProfile(user.uid);
110+
const profile = await getUserProfile(user.uid);
101111
user.profile = profile;
102112
} catch (error) {
103113
log.error(error);
@@ -138,6 +148,15 @@ const useUserListStore = defineStore('userList', () => {
138148
return;
139149
}
140150
setUserBack(event.nick);
151+
})
152+
.on('tagmsg', (event) => {
153+
// Using '+color' client-tag for updating username color
154+
// See https://ircv3.net/specs/extensions/message-tags
155+
const nick = event.nick;
156+
const color = event.tags['+color'];
157+
if (typeof color === 'string' && isValidHexColor(color)) {
158+
updateUserColor(nick, color);
159+
}
141160
});
142161
},
143162
);

0 commit comments

Comments
 (0)