Using a general client tag element for all client tags and introduced the channel conversation mode variable

canary
WolverinDEV 2020-10-04 15:52:15 +02:00
parent 4cdabc620b
commit 1996878f12
13 changed files with 89 additions and 45 deletions

View File

@ -1,4 +1,7 @@
# Changelog:
* **04.10.20**
- Fixed invalid channel tree unique id assignment for the initial server entry ([#F2986](https://forum.teaspeak.de/index.php?threads/2986))
* **27.09.20**
- Middle clicking on bookmarks now directly connects in a new tab

View File

@ -73,7 +73,7 @@ export class ChannelProperties {
//Only after request
channel_description: string = "";
channel_flag_conversation_private: boolean = true; /* TeamSpeak mode */
channel_conversation_mode: number = 0; /* 0 := Private, the default */
channel_conversation_history_length: number = -1;
}
@ -594,6 +594,10 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
this._cached_channel_description_promise = undefined;
this._cached_channel_description_promise_resolve = undefined;
this._cached_channel_description_promise_reject = undefined;
} else if(key === "channel_flag_conversation_private") {
/* "fix" for older TeaSpeak server versions (pre. 1.4.22) */
this.properties.channel_conversation_mode = value === "1" ? 0 : 1;
variables.push({ key: "channel_conversation_mode", value: this.properties.channel_conversation_mode + "" });
}
}
/* devel-block(log-channel-property-updates) */

View File

@ -1233,8 +1233,9 @@ export class MusicClientEntry extends ClientEntry {
name: tr("Change remote volume"),
callback: () => {
let max_volume = this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME).value;
if(max_volume < 0)
if(max_volume < 0) {
max_volume = 100;
}
spawnMusicBotVolumeChange(this, max_volume / 100);
}

View File

@ -1,5 +1,3 @@
.clientEntry, .channelEntry {
color: var(--server-log-tree-entry);
font-weight: 700;
cursor: pointer;
}

View File

@ -7,6 +7,7 @@ import {BBCodeRenderer} from "tc-shared/text/bbcode";
import {format_time} from "tc-shared/ui/frames/chat";
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
import {XBBCodeRenderer} from "vendor/xbbcode/react";
import {ChannelTag, ClientTag} from "tc-shared/ui/tree/EntryTags";
const cssStyle = require("./DispatcherLog.scss");
const cssStyleRenderer = require("./Renderer.scss");
@ -26,18 +27,23 @@ export function getRegisteredLogDispatchers() : TypeInfo[] {
return Object.keys(dispatchers) as any;
}
/* TODO: Enable context menu */
const ClientRenderer = (props: { client: EventClient, handlerId: string, braces?: boolean }) => (
<div className={cssStyle.clientEntry}>
{props.client.client_name}
</div>
<ClientTag
handlerId={props.handlerId}
clientName={props.client.client_name}
clientId={props.client.client_id}
clientUniqueId={props.client.client_unique_id}
className={cssStyle.clientEntry}
/>
);
/* TODO: Enable context menu */
const ChannelRenderer = (props: { channel: EventChannelData, handlerId: string, braces?: boolean }) => (
<div className={cssStyle.channelEntry}>
{props.channel.channel_name}
</div>
<ChannelTag
handlerId={props.handlerId}
channelName={props.channel.channel_name}
channelId={props.channel.channel_id}
className={cssStyle.channelEntry}
/>
);
registerDispatcher(EventType.ERROR_CUSTOM, data => <div className={cssStyleRenderer.errorMessage}>{data.message}</div>);

View File

@ -62,10 +62,11 @@ export abstract class AbstractChat<Events extends ConversationUIEvents> {
this.hasHistory = true;
index -= deleteMessageCount;
if(event.isOwnMessage)
if(event.isOwnMessage) {
this.setUnreadTimestamp(undefined);
else if(!this.unreadTimestamp)
} else if(!this.unreadTimestamp) {
this.setUnreadTimestamp(event.message.timestamp);
}
/* let all other events run before */
this.events.fire_async("notify_chat_event", {
@ -333,11 +334,12 @@ export abstract class AbstractChatManager<Events extends ConversationUIEvents> {
return;
}
if(conversation.currentMode() === "unloaded")
if(conversation.currentMode() === "unloaded") {
conversation.queryCurrentMessages();
else
} else {
conversation.reportStateToUI();
}
}
@EventHandler<ConversationUIEvents>("query_conversation_history")
protected handleQueryHistory(event: ConversationUIEvents["query_conversation_history"]) {

View File

@ -49,14 +49,17 @@ export class Conversation extends AbstractChat<ConversationUIEvents> {
cid: this.conversationId
} as any;
if(typeof criteria.begin === "number")
if(typeof criteria.begin === "number") {
requestObject.timestamp_begin = criteria.begin;
}
if(typeof criteria.end === "number")
if(typeof criteria.end === "number") {
requestObject.timestamp_end = criteria.end;
}
if(typeof criteria.limit === "number")
if(typeof criteria.limit === "number") {
requestObject.message_count = criteria.limit;
}
return this.handle.connection.serverConnection.send_command("conversationhistory", requestObject, { flagset: [ "merge" ], process_result: false }).then(() => {
resolve({ status: "success", events: this.historyQueryResponse.map(e => {
@ -198,12 +201,14 @@ export class Conversation extends AbstractChat<ConversationUIEvents> {
}
const timestamp = parseInt(info["timestamp"]);
if(isNaN(timestamp))
if(isNaN(timestamp)) {
return;
}
if(timestamp > this.lastReadMessage)
if(timestamp > this.lastReadMessage) {
this.setUnreadTimestamp(this.lastReadMessage);
}
}
public handleIncomingMessage(message: ChatMessage, isOwnMessage: boolean) {
this.registerIncomingMessage(message, isOwnMessage, "m-" + this.conversationId + "-" + message.timestamp + "-" + Math.random());
@ -277,7 +282,7 @@ export class Conversation extends AbstractChat<ConversationUIEvents> {
}
sendMessage(text: string) {
this.doSendMessage(text, this.conversationId ? 2 : 3, this.conversationId);
this.doSendMessage(text, this.conversationId ? 2 : 3, this.conversationId).then(() => {});
}
}
@ -344,7 +349,7 @@ export class ConversationManager extends AbstractChatManager<ConversationUIEvent
this.queryUnreadFlags();
}));
this.uiEvents.on("notify_destroy", this.connection.channelTree.events.on("notify_channel_updated", event => {
this.uiEvents.on("notify_destroy", this.connection.channelTree.events.on("notify_channel_updated", _event => {
/* TODO private flag! */
}));
}
@ -403,9 +408,6 @@ export class ConversationManager extends AbstractChatManager<ConversationUIEvent
}
queryUnreadFlags() {
/* FIXME: Test for old TeaSpeak servers which don't support this */
return;
const commandData = this.connection.channelTree.channels.map(e => { return { cid: e.channelId, cpw: e.cached_password() }});
this.connection.serverConnection.send_command("conversationfetch", commandData).catch(error => {
log.warn(LogCategory.CHAT, tr("Failed to query conversation indexes: %o"), error);
@ -465,7 +467,7 @@ export class ConversationManager extends AbstractChatManager<ConversationUIEvent
}
@EventHandler<ConversationUIEvents>("query_selected_chat")
private handleQuerySelectedChat(event: ConversationUIEvents["query_selected_chat"]) {
private handleQuerySelectedChat() {
this.uiEvents.fire_async("notify_selected_chat", { chatId: isNaN(this.selectedConversation_) ? "unselected" : this.selectedConversation_ + ""})
}

View File

@ -22,10 +22,14 @@ import {TimestampRenderer} from "tc-shared/ui/react-elements/TimestampRenderer";
import {BBCodeRenderer} from "tc-shared/text/bbcode";
import {getGlobalAvatarManagerFactory} from "tc-shared/file/Avatars";
import {ColloquialFormat, date_format, format_date_general, formatDayTime} from "tc-shared/utils/DateUtils";
import {ClientTag} from "tc-shared/ui/tree/EntryTags";
const cssStyle = require("./ConversationUI.scss");
const CMTextRenderer = React.memo((props: { text: string }) => <BBCodeRenderer settings={{ convertSingleUrls: true }} message={props.text} />);
const ChatMessageTextRenderer = React.memo((props: { text: string }) => {
if(typeof props.text !== "string") { debugger; }
return <BBCodeRenderer settings={{ convertSingleUrls: true }} message={props.text || ""} />;
});
const ChatEventMessageRenderer = React.memo((props: {
message: ChatMessage,
@ -57,17 +61,8 @@ const ChatEventMessageRenderer = React.memo((props: {
<div className={cssStyle.message}>
<div className={cssStyle.info}>
{deleteButton}
{/*
<a className={cssStyle.sender} dangerouslySetInnerHTML={{ __html: generate_client({
client_database_id: props.message.sender_database_id,
client_id: -1,
client_name: props.message.sender_name,
client_unique_id: props.message.sender_unique_id,
add_braces: false
})}} />
*/}
<a className={cssStyle.sender}>
<div className={"htmltag-client"}>{props.message.sender_name}</div>
<ClientTag clientName={props.message.sender_name} clientUniqueId={props.message.sender_unique_id} handlerId={props.handlerId} clientDatabaseId={props.message.sender_database_id} />
</a>
<span> </span> { /* Only for copy purposes */}
<a className={cssStyle.timestamp}>
@ -76,7 +71,7 @@ const ChatEventMessageRenderer = React.memo((props: {
<br /> { /* Only for copy purposes */ }
</div>
<div className={cssStyle.text}>
<CMTextRenderer text={props.message.message} />
<ChatMessageTextRenderer text={props.message.message} />
</div>
<br style={{ content: " ", display: "none" }} /> { /* Only for copy purposes */ }
</div>
@ -481,7 +476,7 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
case "no-permission":
contents.push(<div key={"ol-permission"} className={cssStyle.overlay}><a>
<Translatable>You don't have permissions to participate in this conversation!</Translatable><br />
>{this.state.failedPermission}</a>
{this.state.failedPermission}</a>
</div>);
break;

View File

@ -72,8 +72,9 @@ const ConversationEntryInfo = React.memo((props: { events: Registry<PrivateConve
const isTyping = Date.now() - kTypingTimeout < typingTimestamp;
useEffect(() => {
if(!isTyping)
if(!isTyping) {
return;
}
const timeout = setTimeout(() => {
setTypingTimestamp(0);

View File

@ -75,13 +75,16 @@ export function spawnBanClient(client: ConnectionHandler, entries: BanEntry | Ba
input_duration_value.firstParent(".input-boxed").removeClass("is-invalid");
}
if (max != -1)
if (max != -1) {
tooltip_duration_max.html(tr("You're allowed to ban a maximum of ") + "<b>" + max + " " + duration_data[type][max == 1 ? "1-text" : "text"] + "</b>");
else
tooltip_duration_max.html(tr("You're allowed to ban <b>permanent</b>."));
} else {
if (value && !Number.isNaN(value))
tooltip_duration_max.html(tr("You're allowed to ban <b>permanent</b>."));
}
} else {
if (value && !Number.isNaN(value)) {
input_duration_value.attr("x-saved-value", value);
}
input_duration_value.attr("placeholder", tr("for ever")).val(null);
tooltip_duration_max.html(tr("You're allowed to ban <b>permanent</b>."));
}

View File

@ -0,0 +1,6 @@
.client {
display: inline-block;
color: #D8D8D8;
font-weight: 700;
cursor: pointer;
}

View File

@ -0,0 +1,23 @@
import * as React from "react";
const cssStyle = require("./EntryTags.scss");
export const ClientTag = (props: { clientName: string, clientUniqueId: string, handlerId: string, clientId?: number, clientDatabaseId?: number, className?: string }) => {
return (
<div className={cssStyle.client + (props.className ? ` ${props.className}` : ``)}
onContextMenu={event => {
event.preventDefault();
/* TODO: Enable context menus */
}}
>
{props.clientName}
</div>
);
};
export const ChannelTag = (props: { channelName: string, channelId: number, handlerId: string, className?: string }) => {
return <div className={cssStyle.client + (props.className ? ` ${props.className}` : ``)}>{props.channelName}</div>;
};

View File

@ -20,7 +20,7 @@ export class AudioLibrary {
this.worker = new WorkerOwner(AudioLibrary.spawnNewWorker);
}
private static spawnNewWorker() {
private static spawnNewWorker() : Worker {
/*
* Attention don't use () => new Worker(...).
* This confuses the worker plugin and will not emit any modules