reorganize
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import { Chat } from "../../Model/chatModel";
|
||||
import { closeChatView } from "../../Model/model";
|
||||
import { translation } from "../../translations";
|
||||
|
||||
export function ChatOptionModal(chat: Chat, isPresented: React.State<boolean>) {
|
||||
function closeModal() {
|
||||
isPresented.value = false;
|
||||
}
|
||||
|
||||
function deleteChat() {
|
||||
chat.deleteSelf();
|
||||
closeModal();
|
||||
closeChatView();
|
||||
}
|
||||
|
||||
const shouldShowKey = new React.State(false);
|
||||
const inputType = React.createProxyState([shouldShowKey], () =>
|
||||
shouldShowKey.value == true ? "text" : "password"
|
||||
);
|
||||
|
||||
const secondaryChannelConverter: React.StateItemConverter<string> = (
|
||||
secondaryChannel
|
||||
) => {
|
||||
function remove() {
|
||||
chat.removeSecondaryChannel(secondaryChannel);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="tile width-input padding-0">
|
||||
<div class="flex-row justify-apart align-center">
|
||||
<b class="padding-h">{secondaryChannel}</b>
|
||||
<button
|
||||
class="danger"
|
||||
aria-label={translation.removeSecondaryChannel}
|
||||
on:click={remove}
|
||||
>
|
||||
<span class="icon">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="modal" toggle:open={isPresented}>
|
||||
<div>
|
||||
<main>
|
||||
<h2>{translation.configureChatTitle}</h2>
|
||||
|
||||
<div>
|
||||
<label class="tile">
|
||||
<span class="icon">forum</span>
|
||||
<div>
|
||||
<span>{translation.primaryChannel}</span>
|
||||
<input
|
||||
bind:value={chat.primaryChannelInput}
|
||||
placeholder={translation.primaryChannelPlaceholder}
|
||||
on:enter={chat.setChannel}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
<div class="flex-row width-input">
|
||||
<button
|
||||
aria-label={translation.undoChanges}
|
||||
class="flex justify-center"
|
||||
on:click={chat.undoChannelChange}
|
||||
toggle:disabled={chat.cannotUndoChannel}
|
||||
>
|
||||
<span class="icon">undo</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label={translation.set}
|
||||
class="flex justify-center primary"
|
||||
on:click={chat.setChannel}
|
||||
toggle:disabled={chat.cannotSetChannel}
|
||||
>
|
||||
<span class="icon">check</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr></hr>
|
||||
|
||||
<div class="flex-row margin-bottom width-input">
|
||||
<input
|
||||
aria-label={translation.secondaryChannel}
|
||||
placeholder={translation.secondaryChannelPlaceholder}
|
||||
bind:value={chat.newSecondaryChannelName}
|
||||
on:enter={chat.addSecondaryChannel}
|
||||
></input>
|
||||
<button
|
||||
class="primary"
|
||||
toggle:disabled={chat.cannotAddSecondaryChannel}
|
||||
aria-label={translation.addSecondaryChannel}
|
||||
on:click={chat.addSecondaryChannel}
|
||||
>
|
||||
<span class="icon">add</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-column gap"
|
||||
children:prepend={[
|
||||
chat.secondaryChannels,
|
||||
secondaryChannelConverter,
|
||||
]}
|
||||
></div>
|
||||
|
||||
<hr></hr>
|
||||
|
||||
<div>
|
||||
<label class="tile">
|
||||
<span class="icon">key</span>
|
||||
<div>
|
||||
<span>{translation.encryptionKey}</span>
|
||||
<input
|
||||
placeholder={translation.encryptionKeyPlaceholder}
|
||||
bind:value={chat.encryptionKey}
|
||||
set:type={inputType}
|
||||
></input>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="inline margin-0">
|
||||
<input type="checkbox" bind:checked={shouldShowKey}></input>
|
||||
{translation.showKey}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<hr></hr>
|
||||
|
||||
<div class="flex-column gap">
|
||||
<button
|
||||
class="danger"
|
||||
on:click={chat.clearMessages}
|
||||
toggle:disabled={chat.cannotClearMessages}
|
||||
>
|
||||
{translation.clearChatMessages}
|
||||
<span class="icon">delete_sweep</span>
|
||||
</button>
|
||||
|
||||
<button class="danger" on:click={deleteChat}>
|
||||
{translation.removeChat}
|
||||
<span class="icon">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
<button on:click={closeModal}>
|
||||
{translation.close}
|
||||
<span class="icon">close</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import { Chat, Item } from "../../Model/chatModel";
|
||||
|
||||
import { translation } from "../../translations";
|
||||
|
||||
export function ChatToolView(chat: Chat) {
|
||||
function createItem() {
|
||||
chat.createItem({
|
||||
id: React.UUID(),
|
||||
title: "new item",
|
||||
});
|
||||
}
|
||||
|
||||
const itemConverter: React.StateItemConverter<Item> = (item) => {
|
||||
return <span>{item.title}</span>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="chat-tool-view">
|
||||
<button on:click={createItem}>+</button>
|
||||
<hr></hr>
|
||||
<div children:append={[chat.items, itemConverter]}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import { Chat } from "../../Model/chatModel";
|
||||
import { translation } from "../../translations";
|
||||
|
||||
export function MessageComposer(chat: Chat) {
|
||||
return (
|
||||
<div class="flex-row width-100">
|
||||
{" "}
|
||||
<input
|
||||
class="width-100 flex-1"
|
||||
style="max-width: unset"
|
||||
placeholder={translation.composerPlaceholder}
|
||||
bind:value={chat.composingMessage}
|
||||
on:enter={chat.sendMessageFromComposer}
|
||||
></input>
|
||||
<button
|
||||
class="primary"
|
||||
on:click={chat.sendMessageFromComposer}
|
||||
toggle:disabled={chat.cannotSendMessage}
|
||||
>
|
||||
<span class="icon">send</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import * as React from "bloatless-react";
|
||||
|
||||
import { Chat, ChatMessage } from "../../Model/chatModel";
|
||||
|
||||
import { isShowingChatTools } from "../../Model/model";
|
||||
import { translation } from "../../translations";
|
||||
|
||||
export function ThreadView(chat: Chat) {
|
||||
const messageConverter: React.StateItemConverter<ChatMessage> = (message) => {
|
||||
function resendMessage() {
|
||||
chat.resendMessage(message);
|
||||
}
|
||||
|
||||
function copyMessage() {
|
||||
navigator.clipboard.writeText(message.body);
|
||||
}
|
||||
|
||||
function decrypt() {
|
||||
chat.decryptReceivedMessage(message);
|
||||
}
|
||||
|
||||
function remove() {
|
||||
chat.deleteMessage(message);
|
||||
}
|
||||
|
||||
const messageBody = React.createProxyState(
|
||||
[chat.messages],
|
||||
() => message.body
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="tile width-100 flex-no padding-0">
|
||||
<div class="flex-column">
|
||||
<div class="flex-row justify-apart align-center secondary">
|
||||
<span class="padding-h ellipsis">
|
||||
<b class="info">{message.sender}</b>
|
||||
</span>
|
||||
<span class="flex-row">
|
||||
<button
|
||||
aria-label={translation.resendMessage}
|
||||
on:click={resendMessage}
|
||||
toggle:disabled={chat.cannotResendMessage}
|
||||
>
|
||||
<span class="icon">replay</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label={translation.copyMessage}
|
||||
on:click={copyMessage}
|
||||
>
|
||||
<span class="icon">content_copy</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label={translation.decryptMessage}
|
||||
on:click={decrypt}
|
||||
>
|
||||
<span class="icon">key</span>
|
||||
</button>
|
||||
<button aria-label={translation.deleteMessage} on:click={remove}>
|
||||
<span class="icon">delete</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex-column padding-h padding-bottom">
|
||||
<b class="break-word" subscribe:innerText={messageBody}></b>
|
||||
<span class="secondary">
|
||||
<b>{new Date(message.isoDate).toLocaleString()}</b>
|
||||
<br></br>
|
||||
{message.channel}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const outboxMessageConverter: React.StateItemConverter<ChatMessage> = (
|
||||
message
|
||||
) => {
|
||||
function remove() {
|
||||
chat.deleteOutboxMessage(message);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="tile width-100 flex-no padding-0">
|
||||
<div class="flex-column">
|
||||
<div class="flex-row justify-apart align-center">
|
||||
<b class="error padding-h">{translation.messageInOutbox}</b>
|
||||
<span class="flex-row secondary">
|
||||
<button aria-label={translation.deleteMessage} on:click={remove}>
|
||||
<span class="icon">delete</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex-column padding-h padding-bottom">
|
||||
<b class="break-word">{message.body}</b>
|
||||
<span class="secondary">
|
||||
<b>{new Date(message.isoDate).toLocaleString()}</b>
|
||||
<br></br>
|
||||
{message.channel}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const messageList = (
|
||||
<div
|
||||
class="flex-column gap"
|
||||
children:append={[chat.messages, messageConverter]}
|
||||
></div>
|
||||
);
|
||||
const outboxList = (
|
||||
<div
|
||||
class="flex-column gap"
|
||||
children:append={[chat.outbox, outboxMessageConverter]}
|
||||
></div>
|
||||
);
|
||||
const listWrapper = (
|
||||
<div class="thread-view flex-column gap">
|
||||
{messageList}
|
||||
{outboxList}
|
||||
</div>
|
||||
);
|
||||
|
||||
function scrollToBottom() {
|
||||
listWrapper.scrollTop = listWrapper.scrollHeight;
|
||||
}
|
||||
|
||||
function scrollToBottomIfAppropriate() {
|
||||
const scrollFromBottom =
|
||||
listWrapper.scrollHeight -
|
||||
(listWrapper.scrollTop + listWrapper.offsetHeight);
|
||||
if (scrollFromBottom > 400) return;
|
||||
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
chat.messages.handleAddition(scrollToBottomIfAppropriate);
|
||||
chat.outbox.handleAddition(scrollToBottomIfAppropriate);
|
||||
isShowingChatTools.subscribe(() => scrollToBottom());
|
||||
setTimeout(() => scrollToBottom(), 50);
|
||||
|
||||
return listWrapper;
|
||||
}
|
||||
Reference in New Issue
Block a user