Using a general client tag element for all client tags and introduced the channel conversation mode variable
This commit is contained in:
parent
4cdabc620b
commit
1996878f12
13 changed files with 89 additions and 45 deletions
|
@ -1,4 +1,7 @@
|
||||||
# Changelog:
|
# 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**
|
* **27.09.20**
|
||||||
- Middle clicking on bookmarks now directly connects in a new tab
|
- Middle clicking on bookmarks now directly connects in a new tab
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ export class ChannelProperties {
|
||||||
//Only after request
|
//Only after request
|
||||||
channel_description: string = "";
|
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;
|
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 = undefined;
|
||||||
this._cached_channel_description_promise_resolve = undefined;
|
this._cached_channel_description_promise_resolve = undefined;
|
||||||
this._cached_channel_description_promise_reject = 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) */
|
/* devel-block(log-channel-property-updates) */
|
||||||
|
|
|
@ -1233,8 +1233,9 @@ export class MusicClientEntry extends ClientEntry {
|
||||||
name: tr("Change remote volume"),
|
name: tr("Change remote volume"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
let max_volume = this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME).value;
|
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;
|
max_volume = 100;
|
||||||
|
}
|
||||||
|
|
||||||
spawnMusicBotVolumeChange(this, max_volume / 100);
|
spawnMusicBotVolumeChange(this, max_volume / 100);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
.clientEntry, .channelEntry {
|
.clientEntry, .channelEntry {
|
||||||
color: var(--server-log-tree-entry);
|
color: var(--server-log-tree-entry);
|
||||||
font-weight: 700;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ import {BBCodeRenderer} from "tc-shared/text/bbcode";
|
||||||
import {format_time} from "tc-shared/ui/frames/chat";
|
import {format_time} from "tc-shared/ui/frames/chat";
|
||||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||||
import {XBBCodeRenderer} from "vendor/xbbcode/react";
|
import {XBBCodeRenderer} from "vendor/xbbcode/react";
|
||||||
|
import {ChannelTag, ClientTag} from "tc-shared/ui/tree/EntryTags";
|
||||||
|
|
||||||
const cssStyle = require("./DispatcherLog.scss");
|
const cssStyle = require("./DispatcherLog.scss");
|
||||||
const cssStyleRenderer = require("./Renderer.scss");
|
const cssStyleRenderer = require("./Renderer.scss");
|
||||||
|
@ -26,18 +27,23 @@ export function getRegisteredLogDispatchers() : TypeInfo[] {
|
||||||
return Object.keys(dispatchers) as any;
|
return Object.keys(dispatchers) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Enable context menu */
|
|
||||||
const ClientRenderer = (props: { client: EventClient, handlerId: string, braces?: boolean }) => (
|
const ClientRenderer = (props: { client: EventClient, handlerId: string, braces?: boolean }) => (
|
||||||
<div className={cssStyle.clientEntry}>
|
<ClientTag
|
||||||
{props.client.client_name}
|
handlerId={props.handlerId}
|
||||||
</div>
|
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 }) => (
|
const ChannelRenderer = (props: { channel: EventChannelData, handlerId: string, braces?: boolean }) => (
|
||||||
<div className={cssStyle.channelEntry}>
|
<ChannelTag
|
||||||
{props.channel.channel_name}
|
handlerId={props.handlerId}
|
||||||
</div>
|
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>);
|
registerDispatcher(EventType.ERROR_CUSTOM, data => <div className={cssStyleRenderer.errorMessage}>{data.message}</div>);
|
||||||
|
|
|
@ -62,10 +62,11 @@ export abstract class AbstractChat<Events extends ConversationUIEvents> {
|
||||||
this.hasHistory = true;
|
this.hasHistory = true;
|
||||||
index -= deleteMessageCount;
|
index -= deleteMessageCount;
|
||||||
|
|
||||||
if(event.isOwnMessage)
|
if(event.isOwnMessage) {
|
||||||
this.setUnreadTimestamp(undefined);
|
this.setUnreadTimestamp(undefined);
|
||||||
else if(!this.unreadTimestamp)
|
} else if(!this.unreadTimestamp) {
|
||||||
this.setUnreadTimestamp(event.message.timestamp);
|
this.setUnreadTimestamp(event.message.timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
/* let all other events run before */
|
/* let all other events run before */
|
||||||
this.events.fire_async("notify_chat_event", {
|
this.events.fire_async("notify_chat_event", {
|
||||||
|
@ -333,11 +334,12 @@ export abstract class AbstractChatManager<Events extends ConversationUIEvents> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(conversation.currentMode() === "unloaded")
|
if(conversation.currentMode() === "unloaded") {
|
||||||
conversation.queryCurrentMessages();
|
conversation.queryCurrentMessages();
|
||||||
else
|
} else {
|
||||||
conversation.reportStateToUI();
|
conversation.reportStateToUI();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler<ConversationUIEvents>("query_conversation_history")
|
@EventHandler<ConversationUIEvents>("query_conversation_history")
|
||||||
protected handleQueryHistory(event: ConversationUIEvents["query_conversation_history"]) {
|
protected handleQueryHistory(event: ConversationUIEvents["query_conversation_history"]) {
|
||||||
|
|
|
@ -49,14 +49,17 @@ export class Conversation extends AbstractChat<ConversationUIEvents> {
|
||||||
cid: this.conversationId
|
cid: this.conversationId
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
if(typeof criteria.begin === "number")
|
if(typeof criteria.begin === "number") {
|
||||||
requestObject.timestamp_begin = criteria.begin;
|
requestObject.timestamp_begin = criteria.begin;
|
||||||
|
}
|
||||||
|
|
||||||
if(typeof criteria.end === "number")
|
if(typeof criteria.end === "number") {
|
||||||
requestObject.timestamp_end = criteria.end;
|
requestObject.timestamp_end = criteria.end;
|
||||||
|
}
|
||||||
|
|
||||||
if(typeof criteria.limit === "number")
|
if(typeof criteria.limit === "number") {
|
||||||
requestObject.message_count = criteria.limit;
|
requestObject.message_count = criteria.limit;
|
||||||
|
}
|
||||||
|
|
||||||
return this.handle.connection.serverConnection.send_command("conversationhistory", requestObject, { flagset: [ "merge" ], process_result: false }).then(() => {
|
return this.handle.connection.serverConnection.send_command("conversationhistory", requestObject, { flagset: [ "merge" ], process_result: false }).then(() => {
|
||||||
resolve({ status: "success", events: this.historyQueryResponse.map(e => {
|
resolve({ status: "success", events: this.historyQueryResponse.map(e => {
|
||||||
|
@ -198,12 +201,14 @@ export class Conversation extends AbstractChat<ConversationUIEvents> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const timestamp = parseInt(info["timestamp"]);
|
const timestamp = parseInt(info["timestamp"]);
|
||||||
if(isNaN(timestamp))
|
if(isNaN(timestamp)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(timestamp > this.lastReadMessage)
|
if(timestamp > this.lastReadMessage) {
|
||||||
this.setUnreadTimestamp(this.lastReadMessage);
|
this.setUnreadTimestamp(this.lastReadMessage);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public handleIncomingMessage(message: ChatMessage, isOwnMessage: boolean) {
|
public handleIncomingMessage(message: ChatMessage, isOwnMessage: boolean) {
|
||||||
this.registerIncomingMessage(message, isOwnMessage, "m-" + this.conversationId + "-" + message.timestamp + "-" + Math.random());
|
this.registerIncomingMessage(message, isOwnMessage, "m-" + this.conversationId + "-" + message.timestamp + "-" + Math.random());
|
||||||
|
@ -277,7 +282,7 @@ export class Conversation extends AbstractChat<ConversationUIEvents> {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(text: string) {
|
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.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! */
|
/* TODO private flag! */
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -403,9 +408,6 @@ export class ConversationManager extends AbstractChatManager<ConversationUIEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
queryUnreadFlags() {
|
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() }});
|
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 => {
|
this.connection.serverConnection.send_command("conversationfetch", commandData).catch(error => {
|
||||||
log.warn(LogCategory.CHAT, tr("Failed to query conversation indexes: %o"), 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")
|
@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_ + ""})
|
this.uiEvents.fire_async("notify_selected_chat", { chatId: isNaN(this.selectedConversation_) ? "unselected" : this.selectedConversation_ + ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,14 @@ import {TimestampRenderer} from "tc-shared/ui/react-elements/TimestampRenderer";
|
||||||
import {BBCodeRenderer} from "tc-shared/text/bbcode";
|
import {BBCodeRenderer} from "tc-shared/text/bbcode";
|
||||||
import {getGlobalAvatarManagerFactory} from "tc-shared/file/Avatars";
|
import {getGlobalAvatarManagerFactory} from "tc-shared/file/Avatars";
|
||||||
import {ColloquialFormat, date_format, format_date_general, formatDayTime} from "tc-shared/utils/DateUtils";
|
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 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: {
|
const ChatEventMessageRenderer = React.memo((props: {
|
||||||
message: ChatMessage,
|
message: ChatMessage,
|
||||||
|
@ -57,17 +61,8 @@ const ChatEventMessageRenderer = React.memo((props: {
|
||||||
<div className={cssStyle.message}>
|
<div className={cssStyle.message}>
|
||||||
<div className={cssStyle.info}>
|
<div className={cssStyle.info}>
|
||||||
{deleteButton}
|
{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}>
|
<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>
|
</a>
|
||||||
<span> </span> { /* Only for copy purposes */}
|
<span> </span> { /* Only for copy purposes */}
|
||||||
<a className={cssStyle.timestamp}>
|
<a className={cssStyle.timestamp}>
|
||||||
|
@ -76,7 +71,7 @@ const ChatEventMessageRenderer = React.memo((props: {
|
||||||
<br /> { /* Only for copy purposes */ }
|
<br /> { /* Only for copy purposes */ }
|
||||||
</div>
|
</div>
|
||||||
<div className={cssStyle.text}>
|
<div className={cssStyle.text}>
|
||||||
<CMTextRenderer text={props.message.message} />
|
<ChatMessageTextRenderer text={props.message.message} />
|
||||||
</div>
|
</div>
|
||||||
<br style={{ content: " ", display: "none" }} /> { /* Only for copy purposes */ }
|
<br style={{ content: " ", display: "none" }} /> { /* Only for copy purposes */ }
|
||||||
</div>
|
</div>
|
||||||
|
@ -481,7 +476,7 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
||||||
case "no-permission":
|
case "no-permission":
|
||||||
contents.push(<div key={"ol-permission"} className={cssStyle.overlay}><a>
|
contents.push(<div key={"ol-permission"} className={cssStyle.overlay}><a>
|
||||||
<Translatable>You don't have permissions to participate in this conversation!</Translatable><br />
|
<Translatable>You don't have permissions to participate in this conversation!</Translatable><br />
|
||||||
>{this.state.failedPermission}</a>
|
{this.state.failedPermission}</a>
|
||||||
</div>);
|
</div>);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,9 @@ const ConversationEntryInfo = React.memo((props: { events: Registry<PrivateConve
|
||||||
const isTyping = Date.now() - kTypingTimeout < typingTimestamp;
|
const isTyping = Date.now() - kTypingTimeout < typingTimestamp;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!isTyping)
|
if(!isTyping) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
setTypingTimestamp(0);
|
setTypingTimestamp(0);
|
||||||
|
|
|
@ -75,13 +75,16 @@ export function spawnBanClient(client: ConnectionHandler, entries: BanEntry | Ba
|
||||||
input_duration_value.firstParent(".input-boxed").removeClass("is-invalid");
|
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>");
|
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 {
|
} 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("x-saved-value", value);
|
||||||
|
}
|
||||||
|
|
||||||
input_duration_value.attr("placeholder", tr("for ever")).val(null);
|
input_duration_value.attr("placeholder", tr("for ever")).val(null);
|
||||||
tooltip_duration_max.html(tr("You're allowed to ban <b>permanent</b>."));
|
tooltip_duration_max.html(tr("You're allowed to ban <b>permanent</b>."));
|
||||||
}
|
}
|
||||||
|
|
6
shared/js/ui/tree/EntryTags.scss
Normal file
6
shared/js/ui/tree/EntryTags.scss
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.client {
|
||||||
|
display: inline-block;
|
||||||
|
color: #D8D8D8;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
23
shared/js/ui/tree/EntryTags.tsx
Normal file
23
shared/js/ui/tree/EntryTags.tsx
Normal 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>;
|
||||||
|
};
|
|
@ -20,7 +20,7 @@ export class AudioLibrary {
|
||||||
this.worker = new WorkerOwner(AudioLibrary.spawnNewWorker);
|
this.worker = new WorkerOwner(AudioLibrary.spawnNewWorker);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static spawnNewWorker() {
|
private static spawnNewWorker() : Worker {
|
||||||
/*
|
/*
|
||||||
* Attention don't use () => new Worker(...).
|
* Attention don't use () => new Worker(...).
|
||||||
* This confuses the worker plugin and will not emit any modules
|
* This confuses the worker plugin and will not emit any modules
|
||||||
|
|
Loading…
Add table
Reference in a new issue