add message notifications
This commit is contained in:
@@ -84,7 +84,7 @@ export default class ChatModel {
|
||||
|
||||
chatMessage.status = ChatMessageStatus.Received;
|
||||
this.addMessage(chatMessage);
|
||||
|
||||
|
||||
this.setReadStatus(true);
|
||||
};
|
||||
|
||||
@@ -271,6 +271,10 @@ export default class ChatModel {
|
||||
}
|
||||
|
||||
// utility
|
||||
static splitChannel(channelString: string): string[] {
|
||||
return channelString.split("/");
|
||||
}
|
||||
|
||||
static generateChatInfo = (primaryChannel: string): ChatInfo => {
|
||||
return {
|
||||
dataVersion: DATA_VERSION,
|
||||
|
||||
@@ -227,6 +227,7 @@ export enum StorageModelSubPath {
|
||||
Chat = "chat",
|
||||
|
||||
ConnectionModel = "connection",
|
||||
NotificationModel = "notifications",
|
||||
SettingsModel = "settings",
|
||||
}
|
||||
|
||||
@@ -253,6 +254,10 @@ export const filePaths = {
|
||||
files: (id: string) => [...filePaths.chat.chatBase(id), "files"],
|
||||
},
|
||||
|
||||
notificationModel: {
|
||||
base: [],
|
||||
},
|
||||
|
||||
settingsModel: {
|
||||
username: ["user-name"],
|
||||
firstDayOfWeek: ["first-day-of-week"],
|
||||
|
||||
+35
-4
@@ -125,15 +125,46 @@ body[showing-chat] #chat-page-wrapper {
|
||||
}
|
||||
|
||||
#chat-page > div > #ribbon > span {
|
||||
height: 100%;
|
||||
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
flex-direction: column;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
#chat-page > div > #ribbon > span.marquee {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#chat-page > div > #ribbon > span.marquee > span {
|
||||
transform: rotate(270deg);
|
||||
transform-origin: left;
|
||||
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: calc(var(--control-height) / 2);
|
||||
|
||||
height: var(--control-height);
|
||||
min-width: 100vh;
|
||||
|
||||
overflow: visible;
|
||||
text-overflow: visible;
|
||||
white-space: nowrap;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--gap-between-tiles);
|
||||
}
|
||||
|
||||
#chat-page > div > #ribbon > span.marquee > span > * {
|
||||
animation: slide-up var(--animation-duration);
|
||||
}
|
||||
|
||||
#chat-page > div > #ribbon > .navigation-buttons {
|
||||
min-height: calc(var(--control-height) * 4);
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
#chat-page > div > #ribbon button,
|
||||
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar button {
|
||||
padding: 0px;
|
||||
|
||||
+18
-1
@@ -34,6 +34,21 @@ export function ChatPage(chatViewModel: ChatViewModel) {
|
||||
},
|
||||
);
|
||||
|
||||
const marqueeContent = React.createProxyState(
|
||||
[chatViewModel.notificationViewModel.marquee],
|
||||
() => {
|
||||
const value = chatViewModel.notificationViewModel.marquee.value;
|
||||
if (value == undefined) return <span></span>;
|
||||
return (
|
||||
<span>
|
||||
<b>{value.sender}</b>
|
||||
<span class="secondary">{value.chat}: </span>
|
||||
<span>{value.body}</span>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<article
|
||||
id="chat-page"
|
||||
@@ -63,7 +78,9 @@ export function ChatPage(chatViewModel: ChatViewModel) {
|
||||
<span class="icon">signal_disconnected</span>
|
||||
</button>
|
||||
|
||||
<span>
|
||||
<span class="marquee" children:set={marqueeContent}></span>
|
||||
|
||||
<span class="navigation-buttons">
|
||||
{ChatViewToggleButton(
|
||||
translations.chatPage.pages.calendar,
|
||||
"calendar_month",
|
||||
|
||||
@@ -8,11 +8,14 @@ import { IndexManager } from "../../Model/Utility/utility";
|
||||
import SettingsViewModel from "../Global/settingsViewModel";
|
||||
import StorageModel from "../../Model/Global/storageModel";
|
||||
import ConnectionViewModel from "../Global/connectionViewModel";
|
||||
import NotificationViewModel from "../Global/notificationViewModel";
|
||||
import NotificationModel from "../../Model/Global/notificationModel";
|
||||
|
||||
export default class ChatListViewModel {
|
||||
storageModel: StorageModel;
|
||||
chatListModel: ChatListModel;
|
||||
settingsViewModel: SettingsViewModel;
|
||||
notificationViewModel: NotificationViewModel;
|
||||
connectionViewModel: ConnectionViewModel;
|
||||
|
||||
// data
|
||||
@@ -63,6 +66,7 @@ export default class ChatListViewModel {
|
||||
this.storageModel,
|
||||
chatModel,
|
||||
this.settingsViewModel,
|
||||
this.notificationViewModel,
|
||||
this.connectionViewModel,
|
||||
this,
|
||||
);
|
||||
@@ -105,6 +109,7 @@ export default class ChatListViewModel {
|
||||
this.storageModel = storageModel;
|
||||
this.chatListModel = chatListModel;
|
||||
this.settingsViewModel = settingsViewModel;
|
||||
this.notificationViewModel = new NotificationViewModel(this);
|
||||
this.connectionViewModel = connectionViewModel;
|
||||
|
||||
this.loadChats();
|
||||
|
||||
@@ -19,11 +19,13 @@ import SettingsViewModel from "../Global/settingsViewModel";
|
||||
import TaskPageViewModel from "../Pages/taskPageViewModel";
|
||||
import { translations } from "../../View/translations";
|
||||
import ConnectionViewModel from "../Global/connectionViewModel";
|
||||
import NotificationViewModel from "../Global/notificationViewModel";
|
||||
|
||||
export default class ChatViewModel {
|
||||
chatModel: ChatModel;
|
||||
storageModel: StorageModel;
|
||||
settingsViewModel: SettingsViewModel;
|
||||
notificationViewModel: NotificationViewModel;
|
||||
connectionViewModel: ConnectionViewModel;
|
||||
chatListViewModel: ChatListViewModel;
|
||||
|
||||
@@ -53,7 +55,7 @@ export default class ChatViewModel {
|
||||
this.chatListViewModel.closeChat();
|
||||
};
|
||||
|
||||
closeSubPages = (): void => { };
|
||||
closeSubPages = (): void => {};
|
||||
|
||||
setColor = (color: Color): void => {
|
||||
this.setDisplayedColor(color);
|
||||
@@ -104,12 +106,12 @@ export default class ChatViewModel {
|
||||
this.chatModel.setReadStatus(false);
|
||||
}
|
||||
this.hasUnreadMessages.value = this.chatModel.info.hasUnreadMessages;
|
||||
}
|
||||
};
|
||||
|
||||
setReadStatus = (hasUnreadMessages: boolean): void => {
|
||||
this.chatModel.setReadStatus(hasUnreadMessages);
|
||||
this.hasUnreadMessages.value = hasUnreadMessages;
|
||||
}
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(
|
||||
@@ -117,6 +119,7 @@ export default class ChatViewModel {
|
||||
storageModel: StorageModel,
|
||||
chatModel: ChatModel,
|
||||
settingsViewModel: SettingsViewModel,
|
||||
notificationViewModel: NotificationViewModel,
|
||||
connectionViewModel: ConnectionViewModel,
|
||||
chatListViewModel: ChatListViewModel,
|
||||
) {
|
||||
@@ -125,6 +128,7 @@ export default class ChatViewModel {
|
||||
this.chatModel = chatModel;
|
||||
this.settingsViewModel = settingsViewModel;
|
||||
this.connectionViewModel = connectionViewModel;
|
||||
this.notificationViewModel = notificationViewModel;
|
||||
this.chatListViewModel = chatListViewModel;
|
||||
|
||||
// page viewModels
|
||||
@@ -155,6 +159,8 @@ export default class ChatViewModel {
|
||||
(chatMessage: ChatMessage) => {
|
||||
this.messagePageViewModel.showChatMessage(chatMessage);
|
||||
this.updateReadStatus();
|
||||
|
||||
this.notificationViewModel.showNotification(chatMessage);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import * as React from "bloatless-react";
|
||||
import ChatModel, { ChatMessage } from "../../Model/Chat/chatModel";
|
||||
import ChatListViewModel from "../Chat/chatListViewModel";
|
||||
|
||||
export default class NotificationViewModel {
|
||||
chatListViewModel: ChatListViewModel;
|
||||
|
||||
// data
|
||||
seenMessageIds = new Set<string>();
|
||||
messagesInMarquee: Notification[] = [];
|
||||
marquee = new React.State<Notification | undefined>(undefined);
|
||||
currentIndex = 0;
|
||||
interval: number | undefined = undefined;
|
||||
|
||||
// main
|
||||
showNotification = (message: ChatMessage): void => {
|
||||
const notification = NotificationViewModel.createNotification(message);
|
||||
if (this.seenMessageIds.has(message.id)) return;
|
||||
|
||||
const currentChat = this.chatListViewModel.selectedChat.value.chatModel.info.primaryChannel;
|
||||
if (notification.chat == currentChat) return;
|
||||
|
||||
this.messagesInMarquee.push(notification);
|
||||
this.startLoop();
|
||||
};
|
||||
|
||||
// loop
|
||||
loop = () => {
|
||||
if (this.messagesInMarquee.length == 0) {
|
||||
this.marquee.value = undefined;
|
||||
return this.stopLoop();
|
||||
}
|
||||
|
||||
const notification = this.messagesInMarquee.shift();
|
||||
this.seenMessageIds.delete(notification.messageId);
|
||||
this.marquee.value = notification;
|
||||
};
|
||||
|
||||
startLoop = () => {
|
||||
if (this.interval != undefined) return;
|
||||
|
||||
this.loop();
|
||||
this.interval = setInterval(() => {
|
||||
this.loop();
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
stopLoop = () => {
|
||||
clearInterval(this.interval);
|
||||
this.interval = undefined;
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(chatListViewModel: ChatListViewModel) {
|
||||
this.chatListViewModel = chatListViewModel;
|
||||
}
|
||||
|
||||
// util
|
||||
static createNotification(message: ChatMessage): Notification {
|
||||
return {
|
||||
messageId: message.id,
|
||||
chat: ChatModel.splitChannel(message.channel)[0],
|
||||
sender: message.sender,
|
||||
body: message.body,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
messageId: string;
|
||||
chat: string;
|
||||
sender: string;
|
||||
body: string;
|
||||
}
|
||||
@@ -212,7 +212,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
|
||||
this.showTask(taskFileContent);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
// initiate
|
||||
this.showToday();
|
||||
}
|
||||
|
||||
+1
-1
@@ -85,4 +85,4 @@ document
|
||||
ConnectionModal(connectionViewModel),
|
||||
DataTransferModalWrapper(connectionViewModel, fileTransferViewModel),
|
||||
StorageModal(storageViewModel),
|
||||
);
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user