Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArmoredChat v3 (DomainChat) #1261

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/defaultScripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var DEFAULT_SCRIPTS_SEPARATE = [
"communityScripts/notificationCore/notificationCore.js",
"simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js",
{"stable": "system/more/app-more.js", "beta": "https://more.overte.org/more/app-more.js"},
"communityScripts/armored-chat/armored_chat.js",
"system/domainChat/domainChat.js",
//"system/chat.js"
];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Armored Chat
# Domain Chat

1. What is Armored Chat
1. What is Domain Chat
2. User manual
- Installation
- Settings
- Usability tips
3. Development

## What is Armored Chat
## What is Domain Chat

Armored Chat is a chat application strictly made to communicate between players in the same domain. It is made using QML and to be as light weight as reasonably possible.
Domain Chat is a chat application strictly made to communicate between players in the same domain. It is made using QML and to be as light weight as reasonably possible.

### Dependencies

Expand All @@ -21,7 +21,7 @@ For notifications, AC uses [notificationCore.js](https://github.com/overte-org/o

### Installation

Armored Chat is preinstalled courtesy of [defaultScripts.js](https://github.com/overte-org/overte/blob/8661e8a858663b48e8485c2cd7120dc3e2d7b87e/scripts/defaultScripts.js).
Domain Chat is preinstalled courtesy of [defaultScripts.js](https://github.com/overte-org/overte/blob/8661e8a858663b48e8485c2cd7120dc3e2d7b87e/scripts/defaultScripts.js).

If AC is not preinstalled, or for some other reason it can not be automatically installed, you can install it manually by following [these instructions](https://github.com/overte-org/overte/blob/8661e8a858663b48e8485c2cd7120dc3e2d7b87e/scripts/defaultScripts.js) to open your script management application, and loading the script url:

Expand All @@ -33,7 +33,7 @@ https://raw.githubusercontent.com/overte-org/overte/master/scripts/communityScri

### Settings

Armored Chat comes with basic settings for managing itself.
Domain Chat comes with basic settings for managing itself.

#### External window

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// armored_chat.js
// domainChat.js
//
// Created by Armored Dragon, 2024.
// Copyright 2024 Overte e.V.
Expand All @@ -10,12 +10,19 @@
(() => {
("use strict");

Script.include([
"./formatting.js"
])

var appIsVisible = false;
var settings = {
external_window: false,
maximum_messages: 200,
join_notification: true
join_notification: true,
switchToInternalOnHeadsetUsed: true,
enableEmbedding: false // Prevents information leakage, default false
};
let temporaryChangeModeToVirtual = false;

// Global vars
var tablet;
Expand All @@ -28,15 +35,11 @@
var palData = AvatarManager.getPalData().data;

Controller.keyPressEvent.connect(keyPressEvent);
Messages.subscribe("Chat"); // Floofchat
Messages.subscribe("chat");
Messages.messageReceived.connect(receivedMessage);
AvatarManager.avatarAddedEvent.connect((sessionId) => {
_avatarAction("connected", sessionId);
});
AvatarManager.avatarRemovedEvent.connect((sessionId) => {
_avatarAction("left", sessionId);
});
AvatarManager.avatarAddedEvent.connect((sessionId) => { _avatarAction("connected", sessionId); });
AvatarManager.avatarRemovedEvent.connect((sessionId) => { _avatarAction("left", sessionId); });
HMD.displayModeChanged.connect(_onHMDDisplayModeChanged);

startup();

Expand All @@ -61,7 +64,7 @@
appButton.clicked.connect(toggleMainChatWindow);

quickMessage = new OverlayWindow({
source: Script.resolvePath("./armored_chat_quick_message.qml"),
source: Script.resolvePath("./domainChatQuick.qml"),
});

_openWindow();
Expand All @@ -78,7 +81,7 @@
}
function _openWindow() {
chatOverlayWindow = new Desktop.createWindow(
Script.resolvePath("./armored_chat.qml"),
Script.resolvePath("./domainChat.qml"),
{
title: "Chat",
size: { x: 550, y: 400 },
Expand All @@ -92,64 +95,37 @@
chatOverlayWindow.fromQml.connect(fromQML);
quickMessage.fromQml.connect(fromQML);
}
function receivedMessage(channel, message) {
async function receivedMessage(channel, message) {
// Is the message a chat message?
channel = channel.toLowerCase();
if (channel !== "chat") return;
message = JSON.parse(message);

// Get the message data
const currentTimestamp = _getTimestamp();
const timeArray = _formatTimestamp(currentTimestamp);

if (!message.channel) message.channel = "domain"; // We don't know where to put this message. Assume it is a domain wide message.
if (message.forApp) return; // Floofchat

// Floofchat compatibility hook
message = floofChatCompatibilityConversion(message);
message.channel = message.channel.toLowerCase();

// Check the channel. If the channel is not one we have, do nothing.
if (!channels.includes(message.channel)) return;

// If message is local, and if player is too far away from location, do nothing.
if (message.channel == "local" && isTooFar(message.position)) return;

if ((message = formatting.toJSON(message)) == null) return; // Make sure we are working with a JSON object we expect, otherwise kill
message = formatting.addTimeAndDateStringToPacket(message);

if (!message.channel) message.channel = "domain"; // We don't know where to put this message. Assume it is a domain wide message.
message.channel = message.channel.toLowerCase(); // Only recognize channel names as lower case.

if (!channels.includes(message.channel)) return; // Check the channel. If the channel is not one we have, do nothing.
if (message.channel == "local" && isTooFar(message.position)) return; // If message is local, and if player is too far away from location, do nothing.

let formattedMessagePacket = { ...message };
formattedMessagePacket.message = await formatting.parseMessage(message.message, settings.enableEmbedding)

_emitEvent({ type: "show_message", ...formattedMessagePacket }); // Update qml view of to new message.
_notificationCoreMessage(message.displayName, message.message) // Show a new message on screen.

// Create a new variable based on the message that will be saved.
let trimmedPacket = formatting.trimPacketToSave(message);
messageHistory.push(trimmedPacket);

// Format the timestamp
message.timeString = timeArray[0];
message.dateString = timeArray[1];

// Update qml view of to new message
_emitEvent({ type: "show_message", ...message });

// Show new message on screen
Messages.sendLocalMessage(
"Floof-Notif",
JSON.stringify({
sender: message.displayName,
text: message.message,
})
);

// Save message to history
let savedMessage = message;

// Remove unnecessary data.
delete savedMessage.position;
delete savedMessage.timeString;
delete savedMessage.dateString;
delete savedMessage.action;

savedMessage.timestamp = currentTimestamp;

messageHistory.push(savedMessage);
while (messageHistory.length > settings.maximum_messages) {
messageHistory.shift();
}
Settings.setValue("ArmoredChat-Messages", messageHistory);

// Check to see if the message is close enough to the user
function isTooFar(messagePosition) {
// Check to see if the message is close enough to the user
return Vec3.distance(MyAvatar.position, messagePosition) > maxLocalDistance;
}
}
Expand All @@ -160,15 +136,16 @@
break;
case "setting_change":
// Set the setting value, and save the config
settings[event.setting] = event.value; // Update local settings
_saveSettings(); // Save local settings
settings[event.setting] = event.value; // Update local settings
_saveSettings(); // Save local settings

// Extra actions to preform.
switch (event.setting) {
case "external_window":
chatOverlayWindow.presentationMode = event.value
? Desktop.PresentationMode.NATIVE
: Desktop.PresentationMode.VIRTUAL;
_changePresentationMode(event.value);
break;
case "switchToInternalOnHeadsetUsed":
_onHMDDisplayModeChanged(HMD.active);
break;
}

Expand Down Expand Up @@ -202,6 +179,22 @@
});
}
}
function _onHMDDisplayModeChanged(isHMDActive){
// If the user enabled automatic switching to internal when they put on a headset...
if (!settings.switchToInternalOnHeadsetUsed) return;

if (isHMDActive) temporaryChangeModeToVirtual = true;
else temporaryChangeModeToVirtual = false;

_changePresentationMode(settings.external_window);
}
function _changePresentationMode(changeToExternal){
if (temporaryChangeModeToVirtual) changeToExternal = false;

chatOverlayWindow.presentationMode = changeToExternal
? Desktop.PresentationMode.NATIVE
: Desktop.PresentationMode.VIRTUAL;
}
function _sendMessage(message, channel) {
if (message.length == 0) return;

Expand All @@ -215,11 +208,9 @@
action: "send_chat_message",
})
);

floofChatCompatibilitySendMessage(message, channel);
}
function _avatarAction(type, sessionId) {
Script.setTimeout(() => {
Script.setTimeout(async () => {
if (type == "connected") {
palData = AvatarManager.getPalData().data;
}
Expand All @@ -236,65 +227,47 @@
}

// Format the packet
let message = {};
const timeArray = _formatTimestamp(_getTimestamp());
message.timeString = timeArray[0];
message.dateString = timeArray[1];
let message = addTimeAndDateStringToPacket({});
message.message = `${displayName} ${type}`;

// Show new message on screen
if (settings.join_notification){
Messages.sendLocalMessage(
"Floof-Notif",
JSON.stringify({
sender: displayName,
text: type,
})
);
_notificationCoreMessage(displayName, type)
}

_emitEvent({ type: "notification", ...message });
// Format notification message
let formattedMessagePacket = {...message};
formattedMessagePacket.message = await formatting.parseMessage(message.message);

_emitEvent({ type: "notification", ...formattedMessagePacket });
}, 1500);
}
function _loadSettings() {
async function _loadSettings() {
settings = Settings.getValue("ArmoredChat-Config", settings);

if (messageHistory) {
// Load message history
messageHistory.forEach((message) => {
const timeArray = _formatTimestamp(_getTimestamp());
message.timeString = timeArray[0];
message.dateString = timeArray[1];
_emitEvent({ type: "show_message", ...message });
});
for (message of messageHistory) {
messagePacket = { ...message }; // Create new variable
messagePacket = formatting.addTimeAndDateStringToPacket(messagePacket); // Add timestamp
messagePacket.message = await formatting.parseMessage(messagePacket.message, settings.enableEmbedding); // Parse the message for the UI

_emitEvent({ type: "show_message", ...messagePacket }); // Send message to UI
}
}

// Send current settings to the app
_emitEvent({ type: "initial_settings", settings: settings });
_emitEvent({ type: "initial_settings", settings: settings }); // Send current settings to the app
}
function _saveSettings() {
console.log("Saving config");
Settings.setValue("ArmoredChat-Config", settings);
}
function _getTimestamp(){
return Date.now();
}
function _formatTimestamp(timestamp){
let timeArray = [];

timeArray.push(new Date().toLocaleTimeString(undefined, {
hour12: false,
}));

timeArray.push(new Date(timestamp).toLocaleDateString(undefined, {
year: "numeric",
month: "long",
day: "numeric",
}));

return timeArray;
function _notificationCoreMessage(displayName, message){
Messages.sendLocalMessage(
"Floof-Notif",
JSON.stringify({ sender: displayName, text: message })
);
}

/**
* Emit a packet to the HTML front end. Easy communication!
* @param {Object} packet - The Object packet to emit to the HTML
Expand All @@ -304,33 +277,6 @@
chatOverlayWindow.sendToQml(packet);
}

//
// Floofchat compatibility functions
// Added to ease the transition between Floofchat to ArmoredChat
// These functions can be safely removed at a much later date.
function floofChatCompatibilityConversion(message) {
if (message.type === "TransmitChatMessage" && !message.forApp) {
return {
position: message.position,
message: message.message,
displayName: message.displayName,
channel: message.channel.toLowerCase(),
};
}
return message;
}

function floofChatCompatibilitySendMessage(message, channel) {
Messages.sendMessage(
"Chat",
JSON.stringify({
position: MyAvatar.position,
message: message,
displayName: MyAvatar.sessionDisplayName,
channel: channel.charAt(0).toUpperCase() + channel.slice(1),
type: "TransmitChatMessage",
forApp: "Floof",
})
);
}

})();
Loading