diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..bad8e8e --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +src/discord.js/** \ No newline at end of file diff --git a/.gitignore b/.gitignore index 239340b..4ab68ea 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,5 @@ moc/ **/*.js !**/*.util.js node_modules/ -!src/ui/client.js *.autosave Makefile \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index deb1d98..3e9f5f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "files.exclude": { "**/*.cache": true, "**/*.inf": true, - "**/*.js": true, + "src/js/**/*.js": true, "**/*.loc": true, "**/*.mmp": true, "**/*.pkg": true, diff --git a/src/globals.d.ts b/src/globals.d.ts index e8b361b..303ea7b 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -116,6 +116,7 @@ declare namespace Qml { } interface PageStackWindow extends Window { + initialPage: Qml.Page pageStack: PageStack } @@ -174,17 +175,15 @@ declare function openDatabaseSync( callback?: (db: Qt.Database) => void ): Database; -declare interface Window extends Qml.PageStackWindow { +declare interface Window extends Qml.Component, Qml.PageStackWindow { + client: import("./js/client/Client").Client + store: import("./js/store/DatabaseStore").DatabaseStore } const avkon: AvkonHelper; const socket: Socket; const http: HttpClient; const viewer: QmlApplicationViewer; -const global: Qml.Component & { - client: import("./js/client/Client").Client - store: import("./js/store/DatabaseStore").DatabaseStore -}; const ListView: { Beginning: number diff --git a/src/js/client/http/Http.ts b/src/js/client/http/Http.ts index cab938c..5a6795d 100644 --- a/src/js/client/http/Http.ts +++ b/src/js/client/http/Http.ts @@ -12,7 +12,7 @@ export const Http = { ) { const METHOD = method.toUpperCase(); - http.request(METHOD, path, global.store.get("settings").token, body ?? ""); + http.request(METHOD, path, window.store.get("settings").token, body ?? ""); function listener(response: string) { callback(null, JSON.parse(response)); diff --git a/src/js/client/socket/SocketManager.ts b/src/js/client/socket/SocketManager.ts index 55753e3..6a6aa0b 100644 --- a/src/js/client/socket/SocketManager.ts +++ b/src/js/client/socket/SocketManager.ts @@ -10,7 +10,7 @@ export class SocketManager { constructor(private client: Client) { } connect() { - const settings = global.store.get("settings"); + const settings = window.store.get("settings"); const [host, port] = (settings.proxyUrl || defaultSettings.proxyUrl).split(":"); socket.connectToServer(host, +port); diff --git a/src/js/ui/DMPage/DMPage.qml b/src/js/ui/DMPage/DMPage.qml index 351b15c..b539810 100644 --- a/src/js/ui/DMPage/DMPage.qml +++ b/src/js/ui/DMPage/DMPage.qml @@ -7,6 +7,8 @@ Page { property string pageName: "Direct Messages" + tools: toolbar + Component.onCompleted: Js.handleReady() SystemPalette { id: palette; colorGroup: SystemPalette.Active } diff --git a/src/js/ui/DMPage/DMPage.ts b/src/js/ui/DMPage/DMPage.ts index 73c710c..59d3884 100644 --- a/src/js/ui/DMPage/DMPage.ts +++ b/src/js/ui/DMPage/DMPage.ts @@ -14,19 +14,19 @@ declare const dmPage: Qml.Page & Qml.Component; function loadChannels() { dmListModel.clear(); - const cdnProxyUrl = global.store.get("settings").cdnProxyUrl || defaultSettings.cdnProxyUrl; - const channels = Object.keys(global.client.privateChannels) - .filter(a => global.client.privateChannels[a].lastMessageId) + const cdnProxyUrl = window.store.get("settings").cdnProxyUrl || defaultSettings.cdnProxyUrl; + const channels = Object.keys(window.client.privateChannels) + .filter(a => window.client.privateChannels[a].lastMessageId) .sort((a, b) => { - const ac = global.client.privateChannels[a]; - const bc = global.client.privateChannels[b]; + const ac = window.client.privateChannels[a]; + const bc = window.client.privateChannels[b]; return ac.lastMessageId.localeCompare(bc.lastMessageId); }).reverse(); channels.length = 50; channels.forEach(channelId => { - const channel: PrivateChannel = global.client.privateChannels[channelId]; + const channel: PrivateChannel = window.client.privateChannels[channelId]; const [recipient] = channel.recipients; const item = { id: channelId, @@ -42,7 +42,7 @@ function loadChannels() { function handleReady() { setTimeout(() => { - global.client.on("ready", loadChannels); + window.client.on("ready", loadChannels); }); } @@ -51,7 +51,7 @@ function openMessages(channelId: string) { Qt.resolvedUrl("../MessagesPage/MessagesPage.qml"), { channelId, - channelName: "@" + global.client.privateChannels[channelId].recipients[0].username, + channelName: "@" + window.client.privateChannels[channelId].recipients[0].username, } ); } \ No newline at end of file diff --git a/src/js/ui/DMPage/GuildPage.qml b/src/js/ui/DMPage/GuildPage.qml deleted file mode 100644 index 2fa233f..0000000 --- a/src/js/ui/DMPage/GuildPage.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 1.1 -import com.nokia.symbian 1.1 -import "GuildPage.js" as Js - -Page { - id: guildPage - property string guildId; - property string guildName; - property string pageName: guildName; -} \ No newline at end of file diff --git a/src/js/ui/GuildPage/GuildPage.qml b/src/js/ui/GuildPage/GuildPage.qml new file mode 100644 index 0000000..d7c2d2e --- /dev/null +++ b/src/js/ui/GuildPage/GuildPage.qml @@ -0,0 +1,31 @@ +import QtQuick 1.1 +import com.nokia.symbian 1.1 +import "GuildPage.js" as Js + +PageStackWindow { + id: window + platformSoftwareInputPanelEnabled: true + platformInverted: false + showStatusBar: true + showToolBar: true + + Component.onCompleted: Js.handleReady() + + Menu { + id: menu + content: MenuLayout { + MenuItem { + id: loginButton + text: "Sign in" + } + MenuItem { + id: settingsButton + text: "Settings" + } + MenuItem { + id: backButton + text: "Exit" + } + } + } +} \ No newline at end of file diff --git a/src/js/ui/GuildPage/GuildPage.ts b/src/js/ui/GuildPage/GuildPage.ts index e69de29..e93deb8 100644 --- a/src/js/ui/GuildPage/GuildPage.ts +++ b/src/js/ui/GuildPage/GuildPage.ts @@ -0,0 +1,3 @@ +import { Client } from "client/Client"; + +const client = new Client(); \ No newline at end of file diff --git a/src/js/ui/MessagesPage/MessagesPage.ts b/src/js/ui/MessagesPage/MessagesPage.ts index 7462515..877f200 100644 --- a/src/js/ui/MessagesPage/MessagesPage.ts +++ b/src/js/ui/MessagesPage/MessagesPage.ts @@ -22,7 +22,7 @@ function sendMessage(content: string) { } function appendMessage(msg: MessageDto) { - const cdnProxyUrl = global.store.get("settings").cdnProxyUrl || defaultSettings.cdnProxyUrl; + const cdnProxyUrl = window.store.get("settings").cdnProxyUrl || defaultSettings.cdnProxyUrl; const splitTimestamp = msg.timestamp.split("T"); const [year, month, day] = splitTimestamp[0].split("-"); const [hour, minute, second] = splitTimestamp[1].split(".")[0].split(":"); @@ -72,7 +72,7 @@ function handleMessage(msg: MessageDto) { function handleReady() { setTimeout(() => { loadMessages(); - global.client.on("message", handleMessage); + window.client.on("message", handleMessage); }); inputField.implicitHeightChanged.connect(() => { @@ -103,5 +103,5 @@ function handleMessageReady(attachments: string) { } function handleDestroyed() { - global.client.off("message", handleMessage); + window.client.off("message", handleMessage); } \ No newline at end of file diff --git a/src/js/ui/SettingsPage/SettingsPage.ts b/src/js/ui/SettingsPage/SettingsPage.ts index f0531d4..2eee895 100644 --- a/src/js/ui/SettingsPage/SettingsPage.ts +++ b/src/js/ui/SettingsPage/SettingsPage.ts @@ -8,7 +8,7 @@ declare const dialogField: Qml.TextField; declare const debugModeItem: Qml.SelectionListItem; function loadSettings() { - const settings = global.store.get("settings"); + const settings = window.store.get("settings"); debugModeItem.subTitle = settings.debug ? "Enabled" : "Disabled"; } @@ -18,10 +18,10 @@ function handleReady() { dialog.buttonClicked.connect(bi => { if (bi === 0) { - global.store.fetch("settings", settings => { + window.store.fetch("settings", settings => { // @ts-ignore settings[property] = dialogField.text; - global.store.set("settings", settings); + window.store.set("settings", settings); loadSettings(); }); } @@ -29,7 +29,7 @@ function handleReady() { tokenItem.clicked.connect(() => { dialog.titleText = "Token"; - dialogField.text = global.store.get("settings").token ?? ""; + dialogField.text = window.store.get("settings").token ?? ""; dialogField.placeholderText = ""; property = "token"; dialog.open(); @@ -37,7 +37,7 @@ function handleReady() { cdnProxyUrlItem.clicked.connect(() => { dialog.titleText = "CDN proxy URL"; - dialogField.text = global.store.get("settings").cdnProxyUrl ?? ""; + dialogField.text = window.store.get("settings").cdnProxyUrl ?? ""; dialogField.placeholderText = "hostname:port"; property = "cdnProxyUrl"; dialog.open(); @@ -45,16 +45,16 @@ function handleReady() { proxyUrlItem.clicked.connect(() => { dialog.titleText = "Gateway proxy URL"; - dialogField.text = global.store.get("settings").proxyUrl ?? ""; + dialogField.text = window.store.get("settings").proxyUrl ?? ""; dialogField.placeholderText = "hostname:port"; property = "proxyUrl"; dialog.open(); }); debugModeItem.clicked.connect(() => { - global.store.fetch("settings", settings => { + window.store.fetch("settings", settings => { settings.debug = !settings.debug; - global.store.set("settings", settings); + window.store.set("settings", settings); loadSettings(); }); }); diff --git a/src/js/ui/launcher.qml b/src/js/ui/launcher.qml index 174d6cf..0f96ff9 100644 --- a/src/js/ui/launcher.qml +++ b/src/js/ui/launcher.qml @@ -1,11 +1,96 @@ import QtQuick 1.1 +import QtMobility.feedback 1.1 +import com.nokia.symbian 1.1 +import com.nokia.extras 1.1 +import "DMPage" import "./launcher.js" as Js -Item { - id: global +PageStackWindow { + id: window + platformSoftwareInputPanelEnabled: true + platformInverted: false + showStatusBar: true + showToolBar: true Component.onCompleted: Js.handleReady() + HapticsEffect { + id: hapticsEffect; + attackTime: 250; + fadeTime: 250; + attackIntensity: 0; + fadeIntensity: 0 + } + + InfoBanner { + id: banner + } + + Menu { + id: menu + content: MenuLayout { + MenuItem { + id: loginButton + text: "Sign in" + } + MenuItem { + id: settingsButton + text: "Settings" + } + MenuItem { + id: backButton + text: "Exit" + } + } + } + + ToolBarLayout { + id: toolbar + + ToolButton { + id: minimizeButton + flat: true + iconSource: "toolbar-back" + } + + ToolButton { + id: menuButton + iconSource: "toolbar-menu" + } + } + + StatusBar { + y: -window.y + Item { + anchors { left: parent.left; leftMargin: 6; bottom: parent.bottom; top: parent.top } + width: parent.width - 186; + clip: true + Text { + id: statusBarText + anchors.verticalCenter: parent.verticalCenter + maximumLineCount: 1 + color: "white" + font.pointSize: 6 + font.bold: true + } + Rectangle { + width: 25 + anchors { top: parent.top; bottom: parent.bottom; right: parent.right } + rotation: -90 + gradient: Gradient { + GradientStop { position: 0.0; color: "#00000000" } + GradientStop { position: 1.0; color: "#ff000000" } + } + } + } + Connections { + target: pageStack + onCurrentPageChanged: statusBarText.text = pageStack.currentPage.pageName + } + } + + function module() {} + function exports() {} function require(url) { @@ -13,7 +98,7 @@ Item { } function setTimeout(callback, timeout) { - const timer = Qt.createQmlObject("import QtQuick 1.0; Timer {}", global); + const timer = Qt.createQmlObject("import QtQuick 1.0; Timer {}", window); timer.interval = timeout || 1; timer.repeat = false; @@ -33,7 +118,7 @@ Item { } function setInterval(callback, timeout) { - const timer = Qt.createQmlObject("import QtQuick 1.0; Timer {}", global); + const timer = Qt.createQmlObject("import QtQuick 1.0; Timer {}", window); timer.interval = timeout || 1; timer.repeat = true; diff --git a/src/js/ui/launcher.ts b/src/js/ui/launcher.ts index e3f84dc..ddebb88 100644 --- a/src/js/ui/launcher.ts +++ b/src/js/ui/launcher.ts @@ -1,11 +1,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -declare const exports: any; +declare let module: any; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +declare let exports: any; const timers: string[] = []; const directories = [ "", + "../", "ui/", "ui/DMPage/", "utils/", @@ -17,18 +20,54 @@ const directories = [ "structures/", "structures/dto/", "structures/dto/events/", + "../discord.js/", + "../discord.js/client/", + "../discord.js/sharding/", + "../discord.js/structures/", + "../discord.js/util/", ] as const; function require(url: string) { for (const dir of directories) { - if(Qt.include("../" + dir + url + ".js").status === 0) { + const modulePath = "../" + dir + url + ".js"; + const result = Qt.include(modulePath); + + if(result.status === 0) { + console.log(`Loaded module "${modulePath}"`); + + return exports; + } else if (result.status !== 2) { + // @ts-ignore + console.log(`Could not load module "${modulePath}", error code ${result.status}. ${result.exception}`); + } + } + + for (const dir of directories) { + const modulePath = "../" + dir + url + "/index.js"; + const result = Qt.include(modulePath); + + if(result.status === 0) { + console.log(`Loaded module "${modulePath}"`); + return exports; + } else if (result.status !== 2) { + // @ts-ignore + console.log(`Could not load module "${modulePath}", error code ${result.status}. ${result.exception}`); } } + + throw new Error(`Could not load module "${url}"`); } function handleReady() { + Object.defineProperty(module, "exports", { + get() { return exports; }, + set(v) { + exports = v; + }, + }); + const component = Qt.createComponent("main.qml"); - component.createObject(global); + component.createObject(window); } \ No newline at end of file diff --git a/src/js/ui/main.qml b/src/js/ui/main.qml index f4defe7..c4c9e9e 100644 --- a/src/js/ui/main.qml +++ b/src/js/ui/main.qml @@ -1,93 +1,6 @@ import QtQuick 1.1 -import QtMobility.feedback 1.1 -import com.nokia.symbian 1.1 -import com.nokia.extras 1.1 -import "DMPage" -import "MessagesPage" import "main.js" as Js -PageStackWindow { - id: window - platformSoftwareInputPanelEnabled: true - platformInverted: false - initialPage: DMPage { x: 0; y: 0; tools: toolbar; } - showStatusBar: true - showToolBar: true - +Item { Component.onCompleted: Js.handleReady() - - HapticsEffect { - id: hapticsEffect; - attackTime: 250; - fadeTime: 250; - attackIntensity: 0; - fadeIntensity: 0 - } - - InfoBanner { - id: banner - } - - Menu { - id: menu - content: MenuLayout { - MenuItem { - id: loginButton - text: "Sign in" - } - MenuItem { - id: settingsButton - text: "Settings" - } - MenuItem { - id: backButton - text: "Exit" - } - } - } - - ToolBarLayout { - id: toolbar - - ToolButton { - id: minimizeButton - flat: true - iconSource: "toolbar-back" - } - - ToolButton { - id: menuButton - iconSource: "toolbar-menu" - } - } - - StatusBar { - y: -window.y - Item { - anchors { left: parent.left; leftMargin: 6; bottom: parent.bottom; top: parent.top } - width: parent.width - 186; - clip: true - Text { - id: statusBarText - anchors.verticalCenter: parent.verticalCenter - maximumLineCount: 1 - color: "white" - font.pointSize: 6 - font.bold: true - } - Rectangle { - width: 25 - anchors { top: parent.top; bottom: parent.bottom; right: parent.right } - rotation: -90 - gradient: Gradient { - GradientStop { position: 0.0; color: "#00000000" } - GradientStop { position: 1.0; color: "#ff000000" } - } - } - } - Connections { - target: pageStack - onCurrentPageChanged: statusBarText.text = pageStack.currentPage.pageName - } - } } diff --git a/src/js/ui/main.ts b/src/js/ui/main.ts index b6a692d..aa46413 100644 --- a/src/js/ui/main.ts +++ b/src/js/ui/main.ts @@ -12,11 +12,11 @@ declare const symbian: Qml.Symbian; declare const hapticsEffect: Qml.HapticsEffect; function loadGlobalScope() { - Object.defineProperty(global, "client", { + Object.defineProperty(window, "client", { value: new Client(), }); - Object.defineProperty(global, "store", { + Object.defineProperty(window, "store", { value: new DatabaseStore(), }); } @@ -26,7 +26,7 @@ function handleReady() { symbian.foregroundChanged.connect(() => { console.log(symbian.foreground); - global.client.setBackground(!symbian.foreground); + window.client.setBackground(!symbian.foreground); }); backButton.clicked.connect(() => { @@ -34,10 +34,10 @@ function handleReady() { }); loginButton.clicked.connect(() => { - const settings = global.store.get("settings"); + const settings = window.store.get("settings"); if (settings.token) { - global.client.login(settings.token); + window.client.login(settings.token); } else { banner.text = "You need to provide a token in order to sign in."; banner.open(); @@ -58,19 +58,19 @@ function handleReady() { avkon.minimize(); }); - global.client.on("ready", () => { + window.client.on("ready", () => { console.log("Connected to Discord."); }); - global.client.on("debug", msg => { - if (global.store.get("settings").debug) { + window.client.on("debug", msg => { + if (window.store.get("settings").debug) { console.log("[Socket DEBUG]", msg); } }); - global.client.on("message", msg => { - if (msg.author.id !== global.client.user!.id - && (!msg.guild_id || msg.mentions.some(m => m.id === global.client.user!.id)) + window.client.on("message", msg => { + if (msg.author.id !== window.client.user!.id + && (!msg.guild_id || msg.mentions.some(m => m.id === window.client.user!.id)) ) { if (symbian.foreground) { banner.text = `${msg.author.username}
${msg.content}`; @@ -85,5 +85,11 @@ function handleReady() { } }); - global.client.ready(); -} + window.client.ready(); + + const dmPage = Qt.createComponent("./DMPage/DMPage.qml") as Qml.Page; + + dmPage.createObject(window); + + window.initialPage = dmPage; +} \ No newline at end of file