mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
reimplement with evite extension v2
This commit is contained in:
parent
e41cf70b51
commit
2745104b7c
166
packages/app/src/extensions/api.extension.js
Normal file
166
packages/app/src/extensions/api.extension.js
Normal file
@ -0,0 +1,166 @@
|
||||
import { Extension } from "evite"
|
||||
import config from "config"
|
||||
import { Bridge } from "linebridge/dist/client"
|
||||
import { Session } from "models"
|
||||
|
||||
export default class ApiExtension extends Extension {
|
||||
constructor(app, main) {
|
||||
super(app, main)
|
||||
|
||||
this.apiBridge = this.createBridge()
|
||||
this.WSInterface = this.apiBridge.wsInterface
|
||||
this.WSInterface.request = this.WSRequest
|
||||
this.WSInterface.listen = this.handleWSListener
|
||||
this.WSSockets = this.WSInterface.sockets
|
||||
this.WSInterface.mainSocketConnected = false
|
||||
}
|
||||
|
||||
initializers = [
|
||||
async () => {
|
||||
this.WSSockets.main.on("authenticated", () => {
|
||||
console.debug("[WS] Authenticated")
|
||||
})
|
||||
this.WSSockets.main.on("authenticateFailed", (error) => {
|
||||
console.error("[WS] Authenticate Failed", error)
|
||||
})
|
||||
|
||||
this.WSSockets.main.on("connect", () => {
|
||||
window.app.eventBus.emit("websocket_connected")
|
||||
this.WSInterface.mainSocketConnected = true
|
||||
})
|
||||
|
||||
this.WSSockets.main.on("disconnect", (...context) => {
|
||||
window.app.eventBus.emit("websocket_disconnected", ...context)
|
||||
this.WSInterface.mainSocketConnected = false
|
||||
})
|
||||
|
||||
this.WSSockets.main.on("connect_error", (...context) => {
|
||||
window.app.eventBus.emit("websocket_connection_error", ...context)
|
||||
this.WSInterface.mainSocketConnected = false
|
||||
})
|
||||
|
||||
this.mainContext.setToWindowContext("api", this.apiBridge)
|
||||
this.mainContext.setToWindowContext("ws", this.WSInterface)
|
||||
|
||||
this.mainContext.setToWindowContext("request", this.apiBridge.endpoints)
|
||||
this.mainContext.setToWindowContext("WSRequest", this.WSInterface.wsEndpoints)
|
||||
}
|
||||
]
|
||||
|
||||
createBridge() {
|
||||
const getSessionContext = async () => {
|
||||
const obj = {}
|
||||
const token = await Session.token
|
||||
|
||||
if (token) {
|
||||
// append token to context
|
||||
obj.headers = {
|
||||
Authorization: `Bearer ${token ?? null}`,
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
const handleResponse = async (data) => {
|
||||
if (data.headers?.regenerated_token) {
|
||||
Session.token = data.headers.regenerated_token
|
||||
console.debug("[REGENERATION] New token generated")
|
||||
}
|
||||
|
||||
if (data instanceof Error) {
|
||||
if (data.response.status === 401) {
|
||||
window.app.eventBus.emit("invalid_session")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Bridge({
|
||||
origin: config.api.address,
|
||||
wsOrigin: config.ws.address,
|
||||
wsOptions: {
|
||||
autoConnect: false,
|
||||
},
|
||||
onRequest: getSessionContext,
|
||||
onResponse: handleResponse,
|
||||
})
|
||||
}
|
||||
|
||||
async attachWSConnection() {
|
||||
if (!this.WSInterface.sockets.main.connected) {
|
||||
await this.WSInterface.sockets.main.connect()
|
||||
}
|
||||
|
||||
let startTime = null
|
||||
let latency = null
|
||||
let latencyWarning = false
|
||||
|
||||
let pingInterval = setInterval(() => {
|
||||
if (!this.WSInterface.mainSocketConnected) {
|
||||
return clearTimeout(pingInterval)
|
||||
}
|
||||
|
||||
startTime = Date.now()
|
||||
this.WSInterface.sockets.main.emit("ping")
|
||||
}, 2000)
|
||||
|
||||
this.WSInterface.sockets.main.on("pong", () => {
|
||||
latency = Date.now() - startTime
|
||||
|
||||
if (latency > 800 && this.WSInterface.mainSocketConnected) {
|
||||
latencyWarning = true
|
||||
console.error("[WS] Latency is too high > 800ms", latency)
|
||||
window.app.eventBus.emit("websocket_latency_too_high", latency)
|
||||
} else if (latencyWarning && this.WSInterface.mainSocketConnected) {
|
||||
latencyWarning = false
|
||||
window.app.eventBus.emit("websocket_latency_normal", latency)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async attachAPIConnection() {
|
||||
await this.apiBridge.initialize()
|
||||
}
|
||||
|
||||
handleWSListener = (to, fn) => {
|
||||
if (typeof to === "undefined") {
|
||||
console.error("handleWSListener: to must be defined")
|
||||
return false
|
||||
}
|
||||
if (typeof fn !== "function") {
|
||||
console.error("handleWSListener: fn must be function")
|
||||
return false
|
||||
}
|
||||
|
||||
let ns = "main"
|
||||
let event = null
|
||||
|
||||
if (typeof to === "string") {
|
||||
event = to
|
||||
} else if (typeof to === "object") {
|
||||
ns = to.ns
|
||||
event = to.event
|
||||
}
|
||||
|
||||
return window.app.ws.sockets[ns].on(event, async (...context) => {
|
||||
return await fn(...context)
|
||||
})
|
||||
}
|
||||
|
||||
WSRequest = (socket = "main", channel, ...args) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const request = await window.app.ws.sockets[socket].emit(channel, ...args)
|
||||
|
||||
request.on("responseError", (...errors) => {
|
||||
return reject(...errors)
|
||||
})
|
||||
request.on("response", (...responses) => {
|
||||
return resolve(...responses)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
window = {
|
||||
ApiController: this
|
||||
}
|
||||
}
|
@ -1,187 +0,0 @@
|
||||
import config from "config"
|
||||
import { Bridge } from "linebridge/dist/client"
|
||||
import { Session } from "models"
|
||||
import io from "socket.io-client"
|
||||
|
||||
class WSInterface {
|
||||
constructor(params = {}) {
|
||||
this.params = params
|
||||
this.manager = new io.Manager(this.params.origin, {
|
||||
autoConnect: true,
|
||||
transports: ["websocket"],
|
||||
...this.params.managerOptions,
|
||||
})
|
||||
this.sockets = {}
|
||||
|
||||
this.register("/", "main")
|
||||
}
|
||||
|
||||
register = (socket, as) => {
|
||||
if (typeof socket !== "string") {
|
||||
console.error("socket must be string")
|
||||
return false
|
||||
}
|
||||
|
||||
socket = this.manager.socket(socket)
|
||||
return this.sockets[as ?? socket] = socket
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
key: "apiBridge",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
app.apiBridge = await app.createApiBridge()
|
||||
|
||||
app.WSInterface = app.apiBridge.wsInterface
|
||||
app.WSInterface.request = app.WSRequest
|
||||
app.WSInterface.listen = app.handleWSListener
|
||||
app.WSSockets = app.WSInterface.sockets
|
||||
app.WSInterface.mainSocketConnected = false
|
||||
|
||||
app.WSSockets.main.on("authenticated", () => {
|
||||
console.debug("[WS] Authenticated")
|
||||
})
|
||||
app.WSSockets.main.on("authenticateFailed", (error) => {
|
||||
console.error("[WS] Authenticate Failed", error)
|
||||
})
|
||||
|
||||
app.WSSockets.main.on("connect", () => {
|
||||
window.app.eventBus.emit("websocket_connected")
|
||||
app.WSInterface.mainSocketConnected = true
|
||||
})
|
||||
|
||||
app.WSSockets.main.on("disconnect", (...context) => {
|
||||
window.app.eventBus.emit("websocket_disconnected", ...context)
|
||||
app.WSInterface.mainSocketConnected = false
|
||||
})
|
||||
|
||||
app.WSSockets.main.on("connect_error", (...context) => {
|
||||
window.app.eventBus.emit("websocket_connection_error", ...context)
|
||||
app.WSInterface.mainSocketConnected = false
|
||||
})
|
||||
|
||||
window.app.api = app.apiBridge
|
||||
window.app.ws = app.WSInterface
|
||||
|
||||
window.app.request = app.apiBridge.endpoints
|
||||
window.app.wsRequest = app.apiBridge.wsEndpoints
|
||||
},
|
||||
],
|
||||
mutateContext: {
|
||||
async attachWSConnection() {
|
||||
if (!this.WSInterface.sockets.main.connected) {
|
||||
await this.WSInterface.sockets.main.connect()
|
||||
}
|
||||
|
||||
let startTime = null
|
||||
let latency = null
|
||||
let latencyWarning = false
|
||||
|
||||
let pingInterval = setInterval(() => {
|
||||
if (!this.WSInterface.mainSocketConnected) {
|
||||
return clearTimeout(pingInterval)
|
||||
}
|
||||
|
||||
startTime = Date.now()
|
||||
this.WSInterface.sockets.main.emit("ping")
|
||||
}, 2000)
|
||||
|
||||
this.WSInterface.sockets.main.on("pong", () => {
|
||||
latency = Date.now() - startTime
|
||||
|
||||
if (latency > 800 && this.WSInterface.mainSocketConnected) {
|
||||
latencyWarning = true
|
||||
console.error("[WS] Latency is too high > 800ms", latency)
|
||||
window.app.eventBus.emit("websocket_latency_too_high", latency)
|
||||
} else if (latencyWarning && this.WSInterface.mainSocketConnected) {
|
||||
latencyWarning = false
|
||||
window.app.eventBus.emit("websocket_latency_normal", latency)
|
||||
}
|
||||
})
|
||||
},
|
||||
async attachAPIConnection() {
|
||||
await this.apiBridge.initialize()
|
||||
},
|
||||
handleWSListener: (to, fn) => {
|
||||
if (typeof to === "undefined") {
|
||||
console.error("handleWSListener: to must be defined")
|
||||
return false
|
||||
}
|
||||
if (typeof fn !== "function") {
|
||||
console.error("handleWSListener: fn must be function")
|
||||
return false
|
||||
}
|
||||
|
||||
let ns = "main"
|
||||
let event = null
|
||||
|
||||
if (typeof to === "string") {
|
||||
event = to
|
||||
} else if (typeof to === "object") {
|
||||
ns = to.ns
|
||||
event = to.event
|
||||
}
|
||||
|
||||
return window.app.ws.sockets[ns].on(event, async (...context) => {
|
||||
return await fn(...context)
|
||||
})
|
||||
},
|
||||
createApiBridge: async () => {
|
||||
const getSessionContext = async () => {
|
||||
const obj = {}
|
||||
const token = await Session.token
|
||||
|
||||
if (token) {
|
||||
// append token to context
|
||||
obj.headers = {
|
||||
Authorization: `Bearer ${token ?? null}`,
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
const handleResponse = async (data) => {
|
||||
if (data.headers?.regenerated_token) {
|
||||
Session.token = data.headers.regenerated_token
|
||||
console.debug("[REGENERATION] New token generated")
|
||||
}
|
||||
|
||||
if (data instanceof Error) {
|
||||
if (data.response.status === 401) {
|
||||
window.app.eventBus.emit("invalid_session")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bridge = new Bridge({
|
||||
origin: config.api.address,
|
||||
wsOrigin: config.ws.address,
|
||||
wsOptions: {
|
||||
autoConnect: false,
|
||||
},
|
||||
onRequest: getSessionContext,
|
||||
onResponse: handleResponse,
|
||||
})
|
||||
|
||||
return bridge
|
||||
},
|
||||
WSRequest: (socket = "main", channel, ...args) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const request = await window.app.ws.sockets[socket].emit(channel, ...args)
|
||||
|
||||
request.on("responseError", (...errors) => {
|
||||
return reject(...errors)
|
||||
})
|
||||
request.on("response", (...responses) => {
|
||||
return resolve(...responses)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import { Extension } from "evite"
|
||||
import React from "react"
|
||||
import { Window } from "components"
|
||||
import { Skeleton, Tabs } from "antd"
|
||||
@ -121,15 +122,8 @@ class Debugger {
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
key: "visualDebugger",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
main.setToWindowContext("debug", new Debugger(main))
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
export default class VisualDebugger extends Extension {
|
||||
window = {
|
||||
debug: new Debugger(this.mainContext)
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import Evite from "evite"
|
||||
|
||||
import { Haptics, ImpactStyle } from "@capacitor/haptics"
|
||||
|
||||
// This is a temporal workaround to make the extension work with the new evite extension system.
|
||||
export default class HapticExtensionV2 extends Evite.Extension {
|
||||
static id = "hapticsEngine"
|
||||
|
||||
static compatible = ["mobile"]
|
||||
|
||||
static extendsWith = ["SettingsController"]
|
||||
|
||||
statement = {
|
||||
test: "macarronie",
|
||||
}
|
||||
|
||||
initialization = [
|
||||
async (app, main) => {
|
||||
console.log(this.statement.test)
|
||||
}
|
||||
]
|
||||
|
||||
debug = {
|
||||
testVibrate: () => {
|
||||
|
||||
},
|
||||
testSelectionStart: () => {
|
||||
|
||||
},
|
||||
testSelectionChanged: () => {
|
||||
|
||||
},
|
||||
testSelectionEnd: () => {
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
public = {
|
||||
vibrate: async function () {
|
||||
const enabled = this.extended.SettingsController.get("haptic_feedback")
|
||||
|
||||
if (enabled) {
|
||||
await Haptics.vibrate()
|
||||
}
|
||||
},
|
||||
selectionStart: async function () {
|
||||
const enabled = this.extended.SettingsController.get("haptic_feedback")
|
||||
|
||||
if (enabled) {
|
||||
await Haptics.selectionStart()
|
||||
}
|
||||
},
|
||||
selectionChanged: async function () {
|
||||
const enabled = this.extended.SettingsController.get("haptic_feedback")
|
||||
|
||||
if (enabled) {
|
||||
await Haptics.selectionChanged()
|
||||
}
|
||||
},
|
||||
selectionEnd: async function () {
|
||||
const enabled = this.extended.SettingsController.get("haptic_feedback")
|
||||
|
||||
if (enabled) {
|
||||
await Haptics.selectionEnd()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
73
packages/app/src/extensions/i18n.extension.js
Normal file
73
packages/app/src/extensions/i18n.extension.js
Normal file
@ -0,0 +1,73 @@
|
||||
import { Extension } from "evite"
|
||||
import config from "config"
|
||||
import i18n from "i18next"
|
||||
import { initReactI18next } from "react-i18next"
|
||||
|
||||
export const SUPPORTED_LANGUAGES = config.i18n?.languages ?? {}
|
||||
export const SUPPORTED_LOCALES = SUPPORTED_LANGUAGES.map((l) => l.locale)
|
||||
export const DEFAULT_LOCALE = config.i18n?.defaultLocale
|
||||
|
||||
export function extractLocaleFromPath(path = "") {
|
||||
const [_, maybeLocale] = path.split("/")
|
||||
return SUPPORTED_LOCALES.includes(maybeLocale) ? maybeLocale : DEFAULT_LOCALE
|
||||
}
|
||||
|
||||
const messageImports = import.meta.glob("./translations/*.json")
|
||||
|
||||
export default class I18nExtension extends Extension {
|
||||
depends = ["SettingsExtension"]
|
||||
|
||||
importLocale = async (locale) => {
|
||||
const [, importLocale] =
|
||||
Object.entries(messageImports).find(([key]) =>
|
||||
key.includes(`/${locale}.`)
|
||||
) || []
|
||||
|
||||
return importLocale && importLocale()
|
||||
}
|
||||
|
||||
loadAsyncLanguage = async function (locale) {
|
||||
locale = locale ?? DEFAULT_LOCALE
|
||||
|
||||
try {
|
||||
const result = await this.importLocale(locale)
|
||||
|
||||
if (result) {
|
||||
i18n.addResourceBundle(locale, "translation", result.default || result)
|
||||
i18n.changeLanguage(locale)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
initializers = [
|
||||
async () => {
|
||||
let locale = app.settings.get("language") ?? DEFAULT_LOCALE
|
||||
|
||||
if (!SUPPORTED_LOCALES.includes(locale)) {
|
||||
locale = DEFAULT_LOCALE
|
||||
}
|
||||
|
||||
const messages = await this.importLocale(locale)
|
||||
|
||||
i18n
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
// debug: true,
|
||||
resources: {
|
||||
[locale]: { translation: messages.default || messages },
|
||||
},
|
||||
lng: locale,
|
||||
//fallbackLng: DEFAULT_LOCALE,
|
||||
interpolation: {
|
||||
escapeValue: false, // react already safes from xss
|
||||
},
|
||||
})
|
||||
|
||||
this.mainContext.eventBus.on("changeLanguage", (locale) => {
|
||||
this.loadAsyncLanguage(locale)
|
||||
})
|
||||
},
|
||||
]
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
import config from "config"
|
||||
import i18n from "i18next"
|
||||
import { initReactI18next } from "react-i18next"
|
||||
|
||||
export const SUPPORTED_LANGUAGES = config.i18n?.languages ?? {}
|
||||
export const SUPPORTED_LOCALES = SUPPORTED_LANGUAGES.map((l) => l.locale)
|
||||
export const DEFAULT_LOCALE = config.i18n?.defaultLocale
|
||||
|
||||
export function extractLocaleFromPath(path = "") {
|
||||
const [_, maybeLocale] = path.split("/")
|
||||
return SUPPORTED_LOCALES.includes(maybeLocale) ? maybeLocale : DEFAULT_LOCALE
|
||||
}
|
||||
|
||||
const messageImports = import.meta.glob("./translations/*.json")
|
||||
|
||||
export const extension = {
|
||||
key: "i18n",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
let locale = app.settingsController.get("language") ?? DEFAULT_LOCALE
|
||||
|
||||
if (!SUPPORTED_LOCALES.includes(locale)) {
|
||||
locale = DEFAULT_LOCALE
|
||||
}
|
||||
|
||||
const messages = await app.importLocale(locale)
|
||||
|
||||
i18n
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
// debug: true,
|
||||
resources: {
|
||||
[locale]: { translation: messages.default || messages },
|
||||
},
|
||||
lng: locale,
|
||||
//fallbackLng: DEFAULT_LOCALE,
|
||||
interpolation: {
|
||||
escapeValue: false, // react already safes from xss
|
||||
},
|
||||
})
|
||||
|
||||
main.eventBus.on("changeLanguage", (locale) => {
|
||||
app.loadAsyncLanguage(locale)
|
||||
})
|
||||
},
|
||||
],
|
||||
mutateContext: {
|
||||
importLocale: async (locale) => {
|
||||
const [, importLocale] =
|
||||
Object.entries(messageImports).find(([key]) =>
|
||||
key.includes(`/${locale}.`)
|
||||
) || []
|
||||
|
||||
return importLocale && importLocale()
|
||||
},
|
||||
loadAsyncLanguage: async function (locale) {
|
||||
locale = locale ?? DEFAULT_LOCALE
|
||||
|
||||
try {
|
||||
const result = await this.importLocale(locale)
|
||||
|
||||
if (result) {
|
||||
i18n.addResourceBundle(locale, "translation", result.default || result)
|
||||
i18n.changeLanguage(locale)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default extension
|
@ -1,11 +1,2 @@
|
||||
export * as Render from "./render"
|
||||
export * as Splash from "./splash"
|
||||
export * as Sound from "./sound"
|
||||
export * as Theme from "./theme"
|
||||
export * as i18n from "./i18n"
|
||||
export * as Notifications from "./notifications"
|
||||
|
||||
export { default as SettingsController } from "./settings"
|
||||
export { default as API } from "./api"
|
||||
export { default as Debug } from "./debug"
|
||||
export { default as Shortcuts } from "./shortcuts"
|
||||
export * as Render from "./render.extension.jsx"
|
||||
export * as Splash from "./splash"
|
@ -1,10 +1,11 @@
|
||||
import { Extension } from "evite"
|
||||
import React from "react"
|
||||
import { notification as Notf } from "antd"
|
||||
import { Icons, createIconRender } from "components/Icons"
|
||||
import { Translation } from "react-i18next"
|
||||
import { Haptics } from "@capacitor/haptics"
|
||||
|
||||
class NotificationController {
|
||||
export default class NotificationController extends Extension {
|
||||
getSoundVolume = () => {
|
||||
return (window.app.settings.get("notifications_sound_volume") ?? 50) / 100
|
||||
}
|
||||
@ -53,34 +54,21 @@ class NotificationController {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const extension = {
|
||||
key: "notification",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
app.NotificationController = new NotificationController()
|
||||
initializers = [
|
||||
function () {
|
||||
this.eventBus.on("changeNotificationsSoundVolume", (value) => {
|
||||
app.notifications.playAudio({ soundVolume: value })
|
||||
})
|
||||
this.eventBus.on("changeNotificationsVibrate", (value) => {
|
||||
app.notifications.playHaptic({
|
||||
vibrationEnabled: value,
|
||||
})
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
main.eventBus.on("changeNotificationsSoundVolume", (value) => {
|
||||
app.NotificationController.playAudio({ soundVolume: value })
|
||||
})
|
||||
main.eventBus.on("changeNotificationsVibrate", (value) => {
|
||||
app.NotificationController.playHaptic({
|
||||
vibrationEnabled: value,
|
||||
})
|
||||
})
|
||||
main.setToWindowContext("notifications", app.NotificationController)
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export {
|
||||
extension,
|
||||
NotificationController,
|
||||
}
|
||||
|
||||
export default extension
|
||||
window = {
|
||||
notifications: this
|
||||
}
|
||||
}
|
194
packages/app/src/extensions/render.extension.jsx
Normal file
194
packages/app/src/extensions/render.extension.jsx
Normal file
@ -0,0 +1,194 @@
|
||||
import React from "react"
|
||||
import { EvitePureComponent, Extension } from "evite"
|
||||
import progressBar from "nprogress"
|
||||
import routes from "virtual:generated-pages"
|
||||
|
||||
import NotFoundRender from "./staticsRenders/404"
|
||||
import CrashRender from "./staticsRenders/crash"
|
||||
|
||||
export const ConnectWithApp = (component) => {
|
||||
return window.app.bindContexts(component)
|
||||
}
|
||||
|
||||
export function GetRoutesComponentMap() {
|
||||
return routes.reduce((acc, route) => {
|
||||
const { path, component } = route
|
||||
|
||||
acc[path] = component
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export class RouteRender extends EvitePureComponent {
|
||||
state = {
|
||||
renderInitialization: true,
|
||||
renderComponent: null,
|
||||
renderError: null,
|
||||
//pageStatement: new PageStatement(),
|
||||
routes: GetRoutesComponentMap() ?? {},
|
||||
crash: null,
|
||||
}
|
||||
|
||||
handleBusEvents = {
|
||||
"render_initialization": () => {
|
||||
this.setState({ renderInitialization: true })
|
||||
},
|
||||
"render_initialization_done": () => {
|
||||
this.setState({ renderInitialization: false })
|
||||
},
|
||||
"crash": (message, error) => {
|
||||
this.setState({ crash: { message, error } })
|
||||
},
|
||||
"locationChange": (event) => {
|
||||
this.loadRender()
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._ismounted = true
|
||||
this._loadBusEvents()
|
||||
this.loadRender()
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._ismounted = false
|
||||
this._unloadBusEvents()
|
||||
}
|
||||
|
||||
loadRender = (path) => {
|
||||
if (!this._ismounted) {
|
||||
console.warn("RouteRender is not mounted, skipping render load")
|
||||
return false
|
||||
}
|
||||
|
||||
let componentModule = this.state.routes[path ?? this.props.path ?? window.location.pathname] ?? this.props.staticRenders?.NotFound ?? NotFoundRender
|
||||
|
||||
// TODO: in a future use, we can use `pageStatement` class for managing statement
|
||||
window.app.pageStatement = Object.freeze(componentModule.pageStatement) ?? Object.freeze({})
|
||||
|
||||
return this.setState({ renderComponent: componentModule })
|
||||
}
|
||||
|
||||
componentDidCatch(info, stack) {
|
||||
this.setState({ renderError: { info, stack } })
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.crash) {
|
||||
const StaticCrashRender = this.props.staticRenders?.Crash ?? CrashRender
|
||||
|
||||
return <StaticCrashRender crash={this.state.crash} />
|
||||
}
|
||||
|
||||
if (this.state.renderError) {
|
||||
if (this.props.staticRenders?.RenderError) {
|
||||
return React.createElement(this.props.staticRenders?.RenderError, { error: this.state.renderError })
|
||||
}
|
||||
|
||||
return JSON.stringify(this.state.renderError)
|
||||
}
|
||||
|
||||
if (this.state.renderInitialization) {
|
||||
const StaticInitializationRender = this.props.staticRenders?.initialization ?? null
|
||||
|
||||
return <StaticInitializationRender />
|
||||
}
|
||||
|
||||
if (!this.state.renderComponent) {
|
||||
return null
|
||||
}
|
||||
|
||||
return React.createElement(ConnectWithApp(this.state.renderComponent), this.props)
|
||||
}
|
||||
}
|
||||
|
||||
export class RenderExtension extends Extension {
|
||||
initializers = [
|
||||
async function () {
|
||||
const defaultTransitionDelay = 150
|
||||
|
||||
this.progressBar = progressBar.configure({ parent: "html", showSpinner: false })
|
||||
|
||||
this.history.listen((event) => {
|
||||
this.eventBus.emit("transitionDone", event)
|
||||
this.eventBus.emit("locationChange", event)
|
||||
this.progressBar.done()
|
||||
})
|
||||
|
||||
this.history.setLocation = (to, state, delay) => {
|
||||
const lastLocation = this.history.lastLocation
|
||||
|
||||
if (typeof lastLocation !== "undefined" && lastLocation?.pathname === to && lastLocation?.state === state) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.progressBar.start()
|
||||
this.eventBus.emit("transitionStart", delay)
|
||||
|
||||
setTimeout(() => {
|
||||
this.history.push({
|
||||
pathname: to,
|
||||
}, state)
|
||||
this.history.lastLocation = this.history.location
|
||||
}, delay ?? defaultTransitionDelay)
|
||||
}
|
||||
|
||||
this.setToWindowContext("setLocation", this.history.setLocation)
|
||||
},
|
||||
]
|
||||
|
||||
expose = {
|
||||
validateLocationSlash: (location) => {
|
||||
let key = location ?? window.location.pathname
|
||||
|
||||
while (key[0] === "/") {
|
||||
key = key.slice(1, key.length)
|
||||
}
|
||||
|
||||
return key
|
||||
},
|
||||
}
|
||||
|
||||
window = {
|
||||
isAppCapacitor: () => window.navigator.userAgent === "capacitor",
|
||||
bindContexts: (component) => {
|
||||
let contexts = {
|
||||
main: {},
|
||||
app: {},
|
||||
}
|
||||
|
||||
if (typeof component.bindApp === "string") {
|
||||
if (component.bindApp === "all") {
|
||||
Object.keys(app).forEach((key) => {
|
||||
contexts.app[key] = app[key]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(component.bindApp)) {
|
||||
component.bindApp.forEach((key) => {
|
||||
contexts.app[key] = app[key]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof component.bindMain === "string") {
|
||||
if (component.bindMain === "all") {
|
||||
Object.keys(main).forEach((key) => {
|
||||
contexts.main[key] = main[key]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(component.bindMain)) {
|
||||
component.bindMain.forEach((key) => {
|
||||
contexts.main[key] = main[key]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (props) => React.createElement(component, { ...props, contexts })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default RenderExtension
|
@ -1,225 +0,0 @@
|
||||
import React from "react"
|
||||
import { EvitePureComponent } from "evite"
|
||||
import routes from "virtual:generated-pages"
|
||||
import progressBar from "nprogress"
|
||||
|
||||
import NotFoundRender from "./statics/404"
|
||||
import CrashRender from "./statics/crash"
|
||||
|
||||
export const ConnectWithApp = (component) => {
|
||||
return window.app.bindContexts(component)
|
||||
}
|
||||
|
||||
export function GetRoutesMap() {
|
||||
return routes.map((route) => {
|
||||
const { path } = route
|
||||
route.name =
|
||||
path
|
||||
.replace(/^\//, "")
|
||||
.replace(/:/, "")
|
||||
.replace(/\//, "-")
|
||||
.replace("all(.*)", "not-found") || "home"
|
||||
|
||||
route.path = route.path.includes("*") ? "*" : route.path
|
||||
|
||||
return route
|
||||
})
|
||||
}
|
||||
|
||||
export function GetRoutesComponentMap() {
|
||||
return routes.reduce((acc, route) => {
|
||||
const { path, component } = route
|
||||
|
||||
acc[path] = component
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
// class PageStatement {
|
||||
// constructor() {
|
||||
// this.state = {}
|
||||
|
||||
// }
|
||||
|
||||
// getProxy() {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
export class RouteRender extends EvitePureComponent {
|
||||
state = {
|
||||
renderInitialization: true,
|
||||
renderComponent: null,
|
||||
renderError: null,
|
||||
//pageStatement: new PageStatement(),
|
||||
routes: GetRoutesComponentMap() ?? {},
|
||||
crash: null,
|
||||
}
|
||||
|
||||
handleBusEvents = {
|
||||
"render_initialization": () => {
|
||||
this.setState({ renderInitialization: true })
|
||||
},
|
||||
"render_initialization_done": () => {
|
||||
this.setState({ renderInitialization: false })
|
||||
},
|
||||
"crash": (message, error) => {
|
||||
this.setState({ crash: { message, error } })
|
||||
},
|
||||
"locationChange": (event) => {
|
||||
this.loadRender()
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._ismounted = true
|
||||
this._loadBusEvents()
|
||||
this.loadRender()
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._ismounted = false
|
||||
this._unloadBusEvents()
|
||||
}
|
||||
|
||||
loadRender = (path) => {
|
||||
if (!this._ismounted) {
|
||||
console.warn("RouteRender is not mounted, skipping render load")
|
||||
return false
|
||||
}
|
||||
|
||||
let componentModule = this.state.routes[path ?? this.props.path ?? window.location.pathname] ?? this.props.staticRenders?.NotFound ?? NotFoundRender
|
||||
|
||||
// TODO: in a future use, we can use `pageStatement` class for managing statement
|
||||
window.app.pageStatement = Object.freeze(componentModule.pageStatement) ?? Object.freeze({})
|
||||
|
||||
return this.setState({ renderComponent: componentModule })
|
||||
}
|
||||
|
||||
componentDidCatch(info, stack) {
|
||||
this.setState({ renderError: { info, stack } })
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.crash) {
|
||||
const StaticCrashRender = this.props.staticRenders?.Crash ?? CrashRender
|
||||
|
||||
return <StaticCrashRender crash={this.state.crash} />
|
||||
}
|
||||
|
||||
if (this.state.renderError) {
|
||||
if (this.props.staticRenders?.RenderError) {
|
||||
return React.createElement(this.props.staticRenders?.RenderError, { error: this.state.renderError })
|
||||
}
|
||||
|
||||
return JSON.stringify(this.state.renderError)
|
||||
}
|
||||
|
||||
if (this.state.renderInitialization) {
|
||||
const StaticInitializationRender = this.props.staticRenders?.initialization ?? null
|
||||
|
||||
return <StaticInitializationRender />
|
||||
}
|
||||
|
||||
if (!this.state.renderComponent) {
|
||||
return null
|
||||
}
|
||||
|
||||
return React.createElement(ConnectWithApp(this.state.renderComponent), this.props)
|
||||
}
|
||||
}
|
||||
|
||||
export const extension = {
|
||||
key: "customRender",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
app.bindContexts = (component) => {
|
||||
let contexts = {
|
||||
main: {},
|
||||
app: {},
|
||||
}
|
||||
|
||||
if (typeof component.bindApp === "string") {
|
||||
if (component.bindApp === "all") {
|
||||
Object.keys(app).forEach((key) => {
|
||||
contexts.app[key] = app[key]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(component.bindApp)) {
|
||||
component.bindApp.forEach((key) => {
|
||||
contexts.app[key] = app[key]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof component.bindMain === "string") {
|
||||
if (component.bindMain === "all") {
|
||||
Object.keys(main).forEach((key) => {
|
||||
contexts.main[key] = main[key]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(component.bindMain)) {
|
||||
component.bindMain.forEach((key) => {
|
||||
contexts.main[key] = main[key]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (props) => React.createElement(component, { ...props, contexts })
|
||||
}
|
||||
|
||||
main.setToWindowContext("bindContexts", app.bindContexts)
|
||||
},
|
||||
async (app, main) => {
|
||||
const defaultTransitionDelay = 150
|
||||
|
||||
main.progressBar = progressBar.configure({ parent: "html", showSpinner: false })
|
||||
|
||||
main.history.listen((event) => {
|
||||
main.eventBus.emit("transitionDone", event)
|
||||
main.eventBus.emit("locationChange", event)
|
||||
main.progressBar.done()
|
||||
})
|
||||
|
||||
main.history.setLocation = (to, state, delay) => {
|
||||
const lastLocation = main.history.lastLocation
|
||||
|
||||
if (typeof lastLocation !== "undefined" && lastLocation?.pathname === to && lastLocation?.state === state) {
|
||||
return false
|
||||
}
|
||||
|
||||
main.progressBar.start()
|
||||
main.eventBus.emit("transitionStart", delay)
|
||||
|
||||
setTimeout(() => {
|
||||
main.history.push({
|
||||
pathname: to,
|
||||
}, state)
|
||||
main.history.lastLocation = main.history.location
|
||||
}, delay ?? defaultTransitionDelay)
|
||||
}
|
||||
|
||||
main.setToWindowContext("setLocation", main.history.setLocation)
|
||||
},
|
||||
],
|
||||
mutateContext: {
|
||||
validateLocationSlash: (location) => {
|
||||
let key = location ?? window.location.pathname
|
||||
|
||||
while (key[0] === "/") {
|
||||
key = key.slice(1, key.length)
|
||||
}
|
||||
|
||||
return key
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default extension
|
@ -1,8 +1,10 @@
|
||||
import { Extension } from "evite"
|
||||
import store from "store"
|
||||
import defaultSettings from "schemas/defaultSettings.json"
|
||||
|
||||
class SettingsController {
|
||||
constructor() {
|
||||
export default class SettingsExtension extends Extension {
|
||||
constructor(app, main) {
|
||||
super(app, main)
|
||||
this.storeKey = "app_settings"
|
||||
this.settings = store.get(this.storeKey) ?? {}
|
||||
|
||||
@ -49,18 +51,8 @@ class SettingsController {
|
||||
|
||||
return this.settings[key]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
key: "settings",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
(app, main) => {
|
||||
app.settingsController = new SettingsController()
|
||||
window.app.settings = app.settingsController
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
window = {
|
||||
"settings": this
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
export class ShortcutsController {
|
||||
constructor() {
|
||||
import { Extension } from "evite"
|
||||
|
||||
export default class ShortcutsExtension extends Extension {
|
||||
constructor(app, main) {
|
||||
super(app, main)
|
||||
|
||||
this.shortcuts = {}
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
@ -15,15 +19,15 @@ export class ShortcutsController {
|
||||
if (typeof shortcut.shift === "boolean" && event.shiftKey !== shortcut.shift) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (typeof shortcut.alt === "boolean" && event.altKey !== shortcut.alt) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (typeof shortcut.meta === "boolean" && event.metaKey !== shortcut.meta) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (shortcut.preventDefault) {
|
||||
event.preventDefault()
|
||||
}
|
||||
@ -58,21 +62,8 @@ export class ShortcutsController {
|
||||
delete this.shortcuts[key]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const extension = {
|
||||
key: "shortcuts",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
(app, main) => {
|
||||
app.ShortcutsController = new ShortcutsController()
|
||||
|
||||
main.setToWindowContext("ShortcutsController", app.ShortcutsController)
|
||||
}
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export default extension
|
||||
window = {
|
||||
ShortcutsController: this
|
||||
}
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
import { Extension } from "evite"
|
||||
import { Howl } from "howler"
|
||||
import config from "config"
|
||||
|
||||
export class SoundEngine {
|
||||
constructor() {
|
||||
this.sounds = {}
|
||||
}
|
||||
|
||||
initialize = async () => {
|
||||
this.sounds = await this.getSounds()
|
||||
}
|
||||
export default class SoundEngineExtension extends Extension {
|
||||
sounds = {}
|
||||
|
||||
getSounds = async () => {
|
||||
// TODO: Load custom soundpacks manifests
|
||||
@ -35,19 +30,14 @@ export class SoundEngine {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const extension = {
|
||||
key: "soundEngine",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
app.SoundEngine = new SoundEngine()
|
||||
main.setToWindowContext("SoundEngine", app.SoundEngine)
|
||||
await app.SoundEngine.initialize()
|
||||
}
|
||||
]
|
||||
initializers = [
|
||||
async () => {
|
||||
this.sounds = await this.getSounds()
|
||||
}
|
||||
]
|
||||
|
||||
window = {
|
||||
SoundEngine: this
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
export const SplashComponent = ({ props = {}, logo }) => {
|
||||
return (
|
||||
<div className="splash_wrapper">
|
||||
<div {...props.logo} className="splash_logo">
|
||||
<img src={logo} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const extension = (params = {}) => {
|
||||
return {
|
||||
key: "splash",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
const fadeOutVelocity = params.velocity ?? 1000 //on milliseconds
|
||||
const splashElement = document.createElement("div")
|
||||
|
||||
splashElement.style = `
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
`
|
||||
|
||||
const show = () => {
|
||||
document.body.appendChild(splashElement)
|
||||
ReactDOM.render(<SplashComponent logo={params.logo} props={params.props} />, splashElement)
|
||||
}
|
||||
|
||||
const removeSplash = () => {
|
||||
splashElement.style.animation = `${params.preset ?? "fadeOut"} ${fadeOutVelocity}ms`
|
||||
|
||||
setTimeout(() => {
|
||||
splashElement.remove()
|
||||
}, fadeOutVelocity)
|
||||
}
|
||||
|
||||
main.eventBus.on("splash_show", show)
|
||||
main.eventBus.on("splash_close", removeSplash)
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
export default extension
|
@ -1,44 +0,0 @@
|
||||
.splash_wrapper {
|
||||
overflow: hidden;
|
||||
|
||||
//background-color: rgba(240, 242, 245, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
--webkit-backdrop-filter: blur(10px);
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1000;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.splash_logo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: fit-content;
|
||||
max-width: 50%;
|
||||
max-height: 50%;
|
||||
filter: drop-shadow(14px 10px 10px rgba(128, 128, 128, 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
import { Extension } from "evite"
|
||||
import config from "config"
|
||||
import store from "store"
|
||||
import { ConfigProvider } from "antd"
|
||||
|
||||
export class ThemeController {
|
||||
constructor(params) {
|
||||
this.params = { ...params }
|
||||
export default class ThemeExtension extends Extension {
|
||||
constructor(app, main) {
|
||||
super(app, main)
|
||||
|
||||
this.themeManifestStorageKey = "theme"
|
||||
this.modificationStorageKey = "themeModifications"
|
||||
@ -14,45 +15,59 @@ export class ThemeController {
|
||||
|
||||
this.mutation = null
|
||||
this.currentVariant = null
|
||||
|
||||
this.init()
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
initializers = [
|
||||
async () => {
|
||||
this.mainContext.eventBus.on("darkMode", (value) => {
|
||||
if (value) {
|
||||
this.applyVariant("dark")
|
||||
} else {
|
||||
this.applyVariant("light")
|
||||
}
|
||||
})
|
||||
this.mainContext.eventBus.on("modifyTheme", (value) => {
|
||||
this.update(value)
|
||||
this.setModifications(this.mutation)
|
||||
})
|
||||
|
||||
this.mainContext.eventBus.on("resetTheme", () => {
|
||||
this.resetDefault()
|
||||
})
|
||||
|
||||
let theme = this.getStoragedTheme()
|
||||
const modifications = this.getStoragedModifications()
|
||||
const variantKey = this.getStoragedVariant()
|
||||
|
||||
if (!theme) {
|
||||
// load default theme
|
||||
theme = this.getDefaultTheme()
|
||||
} else {
|
||||
// load URL and initialize theme
|
||||
}
|
||||
|
||||
// set global theme
|
||||
this.theme = theme
|
||||
|
||||
// override with static vars
|
||||
if (theme.staticVars) {
|
||||
this.update(theme.staticVars)
|
||||
}
|
||||
|
||||
// override theme with modifications
|
||||
if (modifications) {
|
||||
this.update(modifications)
|
||||
}
|
||||
|
||||
// apply variation
|
||||
this.applyVariant(variantKey)
|
||||
},
|
||||
]
|
||||
|
||||
static get currentVariant() {
|
||||
return document.documentElement.style.getPropertyValue("--themeVariant")
|
||||
}
|
||||
|
||||
init = () => {
|
||||
let theme = this.getStoragedTheme()
|
||||
const modifications = this.getStoragedModifications()
|
||||
const variantKey = this.getStoragedVariant()
|
||||
|
||||
if (!theme) {
|
||||
// load default theme
|
||||
theme = this.getDefaultTheme()
|
||||
} else {
|
||||
// load URL and initialize theme
|
||||
}
|
||||
|
||||
// set global theme
|
||||
this.theme = theme
|
||||
|
||||
// override with static vars
|
||||
if (theme.staticVars) {
|
||||
this.update(theme.staticVars)
|
||||
}
|
||||
|
||||
// override theme with modifications
|
||||
if (modifications) {
|
||||
this.update(modifications)
|
||||
}
|
||||
|
||||
// apply variation
|
||||
this.applyVariant(variantKey)
|
||||
}
|
||||
|
||||
getRootVariables = () => {
|
||||
let attributes = document.documentElement.getAttribute("style").trim().split(";")
|
||||
attributes = attributes.slice(0, (attributes.length - 1))
|
||||
@ -130,36 +145,8 @@ export class ThemeController {
|
||||
this.setVariant(variant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const extension = {
|
||||
key: "theme",
|
||||
expose: [
|
||||
{
|
||||
initialization: [
|
||||
async (app, main) => {
|
||||
app.ThemeController = new ThemeController()
|
||||
|
||||
main.eventBus.on("darkMode", (value) => {
|
||||
if (value) {
|
||||
app.ThemeController.applyVariant("dark")
|
||||
} else {
|
||||
app.ThemeController.applyVariant("light")
|
||||
}
|
||||
})
|
||||
main.eventBus.on("modifyTheme", (value) => {
|
||||
app.ThemeController.update(value)
|
||||
app.ThemeController.setModifications(app.ThemeController.mutation)
|
||||
})
|
||||
main.eventBus.on("resetTheme", () => {
|
||||
app.ThemeController.resetDefault()
|
||||
})
|
||||
|
||||
main.setToWindowContext("ThemeController", app.ThemeController)
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default extension
|
||||
window = {
|
||||
ThemeController: this
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user