Merge pull request #59 from ragestudio/core-integration

Core integration
This commit is contained in:
srgooglo 2022-05-31 00:18:53 +02:00 committed by GitHub
commit b48450e92f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 423 additions and 528 deletions

View File

@ -2,10 +2,11 @@ const path = require("path")
const { builtinModules } = require("module")
const aliases = {
"~/": `${path.resolve(__dirname, "src")}/`,
"__": __dirname,
"@src": path.resolve(__dirname, "src"),
schemas: path.resolve(__dirname, "constants"),
"~/": `${path.resolve(__dirname, "src")}/`,
"@src": path.join(__dirname, "src"),
cores: path.join(__dirname, "src/cores"),
schemas: path.join(__dirname, "constants"),
config: path.join(__dirname, "config"),
extensions: path.resolve(__dirname, "src/extensions"),
pages: path.join(__dirname, "src/pages"),

View File

@ -31,7 +31,7 @@
"antd-mobile": "^5.0.0-rc.17",
"chart.js": "3.7.0",
"classnames": "2.3.1",
"evite": "0.10.4",
"evite": "0.11.0",
"faye": "1.4.0",
"feather-reactjs": "2.0.13",
"fuse.js": "6.5.3",

View File

@ -43,7 +43,7 @@ Promise.tasked = function (promises) {
}
import React from "react"
import { CreateEviteApp, BindPropsProvider } from "evite"
import { EviteRuntime, BindPropsProvider } from "evite"
import { Helmet } from "react-helmet"
import * as antd from "antd"
import { ActionSheet, Toast } from "antd-mobile"
@ -57,13 +57,12 @@ import { NotFound, RenderError, Crash, Settings, Navigation, Login } from "compo
import { Icons } from "components/Icons"
import Layout from "./layout"
import * as Render from "extensions/render.extension.jsx"
import * as Render from "cores/render"
import "theme/index.less"
class App extends React.Component {
//static debugMode = true // this will enable debug mode of evite app (dah...)
sessionController = new Session()
userController = new User()
@ -73,8 +72,6 @@ class App extends React.Component {
user: null,
}
loadingMessage = false
static initialize() {
window.app.version = config.package.version
}
@ -101,7 +98,7 @@ class App extends React.Component {
},
"destroyed_session": async function () {
await this.flushState()
this.eventBus.emit("forceToLogin")
app.eventBus.emit("forceToLogin")
},
"forceToLogin": function () {
// if (window.location.pathname !== "/login") {
@ -116,7 +113,7 @@ class App extends React.Component {
await this.flushState()
if (window.location.pathname !== "/login") {
this.eventBus.emit("forceToLogin")
app.eventBus.emit("forceToLogin")
antd.notification.open({
message: <Translation>
@ -188,56 +185,6 @@ class App extends React.Component {
},
}
static windowContext() {
return {
// TODO: Open with popup controller instead drawer controller
openNavigationMenu: () => window.app.DrawerController.open("navigation", Navigation),
openSettings: App.publicMethods.openSettings,
goMain: () => {
return window.app.setLocation(config.app.mainPath)
},
goToAccount: (username) => {
return window.app.setLocation(`/account`, { username })
},
setStatusBarStyleDark: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] setStatusBarStyleDark is only available on capacitor")
return false
}
return await StatusBar.setStyle({ style: Style.Dark })
},
setStatusBarStyleLight: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] setStatusBarStyleLight is not supported on this platform")
return false
}
return await StatusBar.setStyle({ style: Style.Light })
},
hideStatusBar: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] hideStatusBar is not supported on this platform")
return false
}
return await StatusBar.hide()
},
showStatusBar: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] showStatusBar is not supported on this platform")
return false
}
return await StatusBar.show()
},
}
}
static appContext() {
return {
renderRef: this.renderRef,
sessionController: this.sessionController,
userController: this.userController,
}
}
static staticRenders = {
NotFound: (props) => {
return <NotFound />
@ -246,7 +193,7 @@ class App extends React.Component {
return <RenderError {...props} />
},
Crash: Crash.CrashWrapper,
initialization: () => {
Initialization: () => {
return <div className="splash_wrapper">
<div className="splash_logo">
<img src={config.logo.alt} />
@ -256,7 +203,7 @@ class App extends React.Component {
}
static publicMethods = {
"openSettings": (goTo) => {
openSettings: (goTo) => {
window.app.DrawerController.open("settings", Settings, {
props: {
width: "fit-content",
@ -265,7 +212,43 @@ class App extends React.Component {
goTo,
}
})
}
},
openNavigationMenu: () => window.app.DrawerController.open("navigation", Navigation),
goMain: () => {
return window.app.setLocation(config.app.mainPath)
},
goToAccount: (username) => {
return window.app.setLocation(`/account`, { username })
},
isAppCapacitor: () => window.navigator.userAgent === "capacitor",
setStatusBarStyleDark: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] setStatusBarStyleDark is only available on capacitor")
return false
}
return await StatusBar.setStyle({ style: Style.Dark })
},
setStatusBarStyleLight: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] setStatusBarStyleLight is not supported on this platform")
return false
}
return await StatusBar.setStyle({ style: Style.Light })
},
hideStatusBar: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] hideStatusBar is not supported on this platform")
return false
}
return await StatusBar.hide()
},
showStatusBar: async () => {
if (!window.app.isAppCapacitor()) {
console.warn("[App] showStatusBar is not supported on this platform")
return false
}
return await StatusBar.show()
},
}
flushState = async () => {
@ -275,7 +258,7 @@ class App extends React.Component {
componentDidMount = async () => {
if (window.app.isAppCapacitor()) {
window.addEventListener("statusTap", () => {
this.eventBus.emit("statusTap")
app.eventBus.emit("statusTap")
})
StatusBar.setOverlaysWebView({ overlay: true })
@ -285,7 +268,7 @@ class App extends React.Component {
const userAgentPlatform = window.navigator.userAgent.toLowerCase()
if (userAgentPlatform.includes("mac")) {
window.app.ShortcutsController.register({
this.props.cores.ShortcutsCore.register({
key: ",",
meta: true,
preventDefault: true,
@ -293,7 +276,7 @@ class App extends React.Component {
App.publicMethods.openSettings(...args)
})
} else {
window.app.ShortcutsController.register({
this.props.cores.ShortcutsCore.register({
key: ",",
ctrl: true,
preventDefault: true,
@ -302,11 +285,11 @@ class App extends React.Component {
})
}
this.eventBus.emit("app.render_initialization")
app.eventBus.emit("app.render_initialization")
await this.initialization()
this.eventBus.emit("app.render_initialization_done")
app.eventBus.emit("app.render_initialization_done")
}
initialization = async () => {
@ -373,7 +356,10 @@ class App extends React.Component {
await Promise.tasked(initializationTasks).catch((reason) => {
console.error(`[App] Initialization failed: ${reason.cause}`)
app.eventBus.emit("app.crash", reason)
app.eventBus.emit("runtime.crash", {
message: `App initialization failed`,
details: reason.cause,
})
})
}
@ -433,4 +419,4 @@ class App extends React.Component {
}
}
export default CreateEviteApp(App)
export default new EviteRuntime(App)

View File

@ -8,7 +8,7 @@ export const CrashComponent = (props) => {
return <Result
status="error"
title="Crash"
title="Well, we're sorry! The application has a fatal crash."
subTitle={crash.message}
extra={[
<Button type="primary" key="reload" onClick={() => window.location.reload()}>
@ -16,9 +16,10 @@ export const CrashComponent = (props) => {
</Button>
]}
>
<div>
<code>{crash.error}</code>
</div>
{crash.details &&
<div>
<code>{crash.details}</code>
</div>}
</Result>
}
@ -30,5 +31,4 @@ export const CrashWrapper = (props) => {
</div>
}
export default CrashComponent

View File

@ -3,17 +3,59 @@
width: 100vw;
height: 100vh;
top: 0;
left: 0;
z-index: 10000;
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
color: #fff;
.ant-result,
.ant-result-title,
.ant-result-subtitle,
.ant-result-extra {
color: #fff;
}
.ant-result-content {
background-color: rgba(0, 0, 0, 0.7) !important;
}
code {
color: #fff;
font-family: "DM Mono", monospace;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
span,
pre {
color: #fff;
}
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
animation: opacity-fade-in 0.3s ease-in-out;
}
@keyframes opacity-fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -1,62 +1,53 @@
import { Extension } from "evite"
import Core from "evite/src/core"
import config from "config"
import { Bridge } from "linebridge/dist/client"
import { Session } from "models"
export default class ApiExtension extends Extension {
depends = ["SettingsExtension"]
export default class ApiCore extends Core {
apiBridge = this.createBridge()
constructor(app, main) {
super(app, main)
//this.config = this.getServerOrigins()
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
WSInterface = {
...this.apiBridge.wsInterface,
request: this.WSRequest,
listen: this.handleWSListener,
mainSocketConnected: false
}
getServerOrigins = () => {
// TODO: try to get origins from settings
// const storagedOrigins = window.app.settings.get("serverOrigins")
WSSockets = this.WSInterface.sockets
publicMethods = {
api: this.apiBridge,
ws: this.WSInterface,
request: this.apiBridge.endpoints,
WSRequest: this.WSInterface.wsEndpoints,
}
async initialize() {
this.WSSockets.main.on("authenticated", () => {
console.debug("[WS] Authenticated")
})
this.WSSockets.main.on("authenticateFailed", (error) => {
console.error("[WS] Authenticate Failed", error)
})
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", () => {
this.ctx.eventBus.emit("websocket_connected")
this.WSSockets.main.on("connect", () => {
window.app.eventBus.emit("websocket_connected")
this.WSInterface.mainSocketConnected = true
})
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("disconnect", (...context) => {
this.ctx.eventBus.emit("websocket_disconnected", ...context)
this.WSSockets.main.on("connect_error", (...context) => {
window.app.eventBus.emit("websocket_connection_error", ...context)
this.WSInterface.mainSocketConnected = false
})
this.WSInterface.mainSocketConnected = false
})
this.mainContext.setToWindowContext("api", this.apiBridge)
this.mainContext.setToWindowContext("ws", this.WSInterface)
this.WSSockets.main.on("connect_error", (...context) => {
this.ctx.eventBus.emit("websocket_connection_error", ...context)
this.mainContext.setToWindowContext("request", this.apiBridge.endpoints)
this.mainContext.setToWindowContext("WSRequest", this.WSInterface.wsEndpoints)
}
]
this.WSInterface.mainSocketConnected = false
})
}
createBridge() {
const getSessionContext = async () => {
@ -172,8 +163,4 @@ export default class ApiExtension extends Extension {
})
})
}
window = {
ApiController: this
}
}

View File

@ -1,4 +1,4 @@
import { Extension } from "evite"
import Core from "evite/src/core"
import config from "config"
import i18n from "i18next"
import { initReactI18next } from "react-i18next"
@ -14,8 +14,36 @@ export function extractLocaleFromPath(path = "") {
const messageImports = import.meta.glob("./translations/*.json")
export default class I18nExtension extends Extension {
depends = ["SettingsExtension"]
export default class I18nCore extends Core {
events = {
"changeLanguage": (locale) => {
this.loadAsyncLanguage(locale)
}
}
initialize = 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
},
})
}
importLocale = async (locale) => {
const [, importLocale] =
@ -40,34 +68,4 @@ export default class I18nExtension extends Extension {
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)
})
},
]
}

View File

@ -0,0 +1,21 @@
import SettingsCore from "./settings"
import APICore from "./api"
import StyleCore from "./style"
import Render from "./render"
import I18nCore from "./i18n"
import NotificationsCore from "./notifications"
import ShortcutsCore from "./shortcuts"
import SoundCore from "./sound"
// DEFINE LOAD ORDER HERE
export default [
SettingsCore,
APICore,
StyleCore,
I18nCore,
SoundCore,
NotificationsCore,
ShortcutsCore,
Render,
]

View File

@ -1,11 +1,26 @@
import { Extension } from "evite"
import Core from "evite/src/core"
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"
export default class NotificationController extends Extension {
export default class NotificationCore extends Core {
events = {
"changeNotificationsSoundVolume": (value) => {
this.playAudio({ soundVolume: value })
},
"changeNotificationsVibrate": (value) => {
this.playHaptic({
vibrationEnabled: value,
})
}
}
publicMethods = {
notification: this
}
getSoundVolume = () => {
return (window.app.settings.get("notifications_sound_volume") ?? 50) / 100
}
@ -49,26 +64,11 @@ export default class NotificationController extends Extension {
const soundVolume = options.soundVolume ? options.soundVolume / 100 : this.getSoundVolume()
if (soundEnabled) {
window.app.SoundEngine.play("notification", {
volume: soundVolume,
})
}
}
initializers = [
function () {
this.eventBus.on("changeNotificationsSoundVolume", (value) => {
app.notifications.playAudio({ soundVolume: value })
})
this.eventBus.on("changeNotificationsVibrate", (value) => {
app.notifications.playHaptic({
vibrationEnabled: value,
if (typeof window.app.sound?.play === "function") {
window.app.sound.play("notification", {
volume: soundVolume,
})
})
}
}
]
window = {
notifications: this
}
}

View File

@ -1,5 +1,6 @@
import Core from "evite/src/core"
import React from "react"
import { EvitePureComponent, Extension } from "evite"
import { EvitePureComponent } from "evite"
import progressBar from "nprogress"
import routes from "virtual:generated-pages"
@ -89,8 +90,8 @@ export class RouteRender extends EvitePureComponent {
return JSON.stringify(this.state.renderError)
}
if (this.state.renderInitialization) {
const StaticInitializationRender = this.props.staticRenders?.initialization ?? null
if (this.state.renderInitialization && this.props.staticRenders?.Initialization) {
const StaticInitializationRender = this.props.staticRenders?.Initialization ?? <div>Loading...</div>
return <StaticInitializationRender />
}
@ -103,92 +104,88 @@ export class RouteRender extends EvitePureComponent {
}
}
export class RenderExtension extends Extension {
initializers = [
async function () {
const defaultTransitionDelay = 150
export class RenderCore extends Core {
progressBar = progressBar.configure({ parent: "html", showSpinner: false })
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
},
publicMethods = {
setLocation: this.ctx.history.setLocation,
}
window = {
isAppCapacitor: () => window.navigator.userAgent === "capacitor",
bindContexts: (component) => {
let contexts = {
main: {},
app: {},
initialize = () => {
const defaultTransitionDelay = 150
this.ctx.history.listen((event) => {
this.ctx.eventBus.emit("transitionDone", event)
this.ctx.eventBus.emit("locationChange", event)
this.progressBar.done()
})
this.ctx.history.setLocation = (to, state, delay) => {
const lastLocation = this.ctx.history.lastLocation
if (typeof lastLocation !== "undefined" && lastLocation?.pathname === to && lastLocation?.state === state) {
return false
}
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]
})
}
}
this.progressBar.start()
this.ctx.eventBus.emit("transitionStart", delay)
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]
})
}
}
setTimeout(() => {
this.ctx.history.push({
pathname: to,
}, state)
this.ctx.history.lastLocation = this.history.location
}, delay ?? defaultTransitionDelay)
}
}
return (props) => React.createElement(component, { ...props, contexts })
},
validateLocationSlash = (location) => {
let key = location ?? window.location.pathname
while (key[0] === "/") {
key = key.slice(1, key.length)
}
return key
}
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
export default RenderCore

View File

@ -1,18 +1,22 @@
import { Extension } from "evite"
import Core from "evite/src/core"
import store from "store"
import defaultSettings from "schemas/defaultSettings.json"
import { Observable } from "rxjs"
export default class SettingsExtension extends Extension {
constructor(app, main) {
super(app, main)
this.storeKey = "app_settings"
this.settings = store.get(this.storeKey) ?? {}
export default class SettingsCore extends Core {
storeKey = "app_settings"
this._setDefaultUndefined()
settings = store.get(this.storeKey) ?? {}
publicMethods = {
settings: this
}
_setDefaultUndefined = () => {
initialize() {
this.fulfillUndefinedWithDefaults()
}
fulfillUndefinedWithDefaults = () => {
Object.keys(defaultSettings).forEach((key) => {
const value = defaultSettings[key]
@ -23,14 +27,6 @@ export default class SettingsExtension extends Extension {
})
}
defaults = (key) => {
if (typeof key === "undefined") {
return defaultSettings
}
return defaultSettings[key]
}
is = (key, value) => {
return this.settings[key] === value
}
@ -53,6 +49,14 @@ export default class SettingsExtension extends Extension {
return this.settings[key]
}
getDefaults = (key) => {
if (typeof key === "undefined") {
return defaultSettings
}
return defaultSettings[key]
}
withEvent = (listenEvent, defaultValue) => {
let value = defaultValue ?? this.settings[key] ?? false
@ -69,8 +73,4 @@ export default class SettingsExtension extends Extension {
return value
})
}
window = {
"settings": this
}
}

View File

@ -0,0 +1,74 @@
import Core from "evite/src/core"
export default class ShortcutsCore extends Core {
shortcuts = {}
publicMethods = {
shortcuts: this
}
initialize() {
document.addEventListener("keydown", this.handleEvent)
}
handleEvent = (event) => {
// FIXME: event.key sometimes is not defined
const key = event.key.toLowerCase()
const shortcut = this.shortcuts[key]
if (shortcut) {
if (typeof shortcut.ctrl === "boolean" && event.ctrlKey !== shortcut.ctrl) {
return
}
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()
}
if (typeof shortcut.fn === "function") {
shortcut.fn()
}
}
}
register = (keybind = {}, fn) => {
if (typeof keybind === "string") {
keybind = {
key: keybind,
}
}
this.shortcuts[keybind.key] = {
...keybind,
fn,
}
}
remove = (array) => {
if (typeof array === "string") {
array = [array]
}
array.forEach(key => {
delete this.shortcuts[key]
})
}
window = {
ShortcutsController: this
}
}

View File

@ -1,10 +1,18 @@
import { Extension } from "evite"
import Core from "evite/src/core"
import { Howl } from "howler"
import config from "config"
export default class SoundEngineExtension extends Extension {
export default class SoundCore extends Core {
sounds = {}
publicMethods = {
sound: this,
}
async initialize() {
this.sounds = await this.getSounds()
}
getSounds = async () => {
// TODO: Load custom soundpacks manifests
let soundPack = config.defaultSoundPack ?? {}
@ -26,18 +34,8 @@ export default class SoundEngineExtension extends Extension {
if (this.sounds[name]) {
return this.sounds[name](options).play()
} else {
console.error(`Sound ${name} not found.`)
console.error(`Sound [${name}] not found or is not available.`)
return false
}
}
initializers = [
async () => {
this.sounds = await this.getSounds()
}
]
window = {
SoundEngine: this
}
}

View File

@ -1,39 +1,33 @@
import { Extension } from "evite"
import Core from "evite/src/core"
import config from "config"
import store from "store"
import { ConfigProvider } from "antd"
export default class ThemeExtension extends Extension {
constructor(app, main) {
super(app, main)
export default class StyleCore extends Core {
themeManifestStorageKey = "theme"
modificationStorageKey = "themeModifications"
this.themeManifestStorageKey = "theme"
this.modificationStorageKey = "themeModifications"
theme = null
mutation = null
currentVariant = null
this.theme = null
this.mutation = null
this.currentVariant = null
events = {
"theme.applyVariant": (value) => {
this.applyVariant(value)
this.setVariant(value)
},
"modifyTheme": (value) => {
this.update(value)
this.setModifications(this.mutation)
},
"resetTheme": () => {
this.resetDefault()
}
}
initializers = [
async () => {
this.mainContext.eventBus.on("theme.applyVariant", (value) => {
this.applyVariant(value)
this.setVariant(value)
})
this.mainContext.eventBus.on("modifyTheme", (value) => {
this.update(value)
this.setModifications(this.mutation)
})
this.mainContext.eventBus.on("resetTheme", () => {
this.resetDefault()
})
await this.initialize()
},
]
publicMethods = {
style: this
}
static get currentVariant() {
return document.documentElement.style.getPropertyValue("--themeVariant")
@ -145,8 +139,4 @@ export default class ThemeExtension extends Extension {
this.update(values)
}
}
window = {
ThemeController: this
}
}

View File

@ -1,129 +0,0 @@
import { Extension } from "evite"
import React from "react"
import { Window } from "components"
import { Skeleton, Tabs } from "antd"
class DebuggerUI extends React.Component {
state = {
loading: true,
error: null,
debuggers: null,
active: null,
}
toogleLoading = (to = !this.state.loading ?? false) => {
this.setState({ loading: to })
}
loadDebuggers = async () => {
this.toogleLoading(true)
const debuggers = await import(`~/debugComponents`)
let renders = {}
Object.keys(debuggers).forEach((key) => {
renders[key] = debuggers[key]
})
this.setState({ debuggers: renders }, () => {
this.toogleLoading(false)
})
}
componentDidMount = async () => {
await this.loadDebuggers()
}
componentDidCatch = (error, info) => {
this.setState({ error })
}
onChangeTab = (key) => {
console.debug(`Changing tab to ${key}`)
this.setState({ active: key, error: null })
}
renderError = (key, error) => {
return (
<div>
<h2>Debugger Error</h2>
<i>
<h4>
Catch on [<strong>{key}</strong>]
</h4>
</i>
`<code>{error.message}</code>`
<hr />
<code>{error.stack}</code>
</div>
)
}
renderTabs = () => {
return Object.keys(this.state.debuggers).map((key) => {
return <Tabs.TabPane tab={key} key={key} />
})
}
renderDebugger = (_debugger) => {
try {
return React.createElement(window.app.bindContexts(_debugger))
} catch (error) {
return this.renderError(key, error)
}
}
render() {
const { loading, error } = this.state
if (loading) {
return <Skeleton active />
}
return (
<div>
<Tabs
onChange={this.onChangeTab}
activeKey={this.state.active}
>
{this.renderTabs()}
</Tabs>
{error && this.renderError(this.state.active, error)}
{!this.state.active ? (
<div> Select an debugger to start </div>
) : (
this.renderDebugger(this.state.debuggers[this.state.active])
)}
</div>
)
}
}
class Debugger {
constructor(mainContext, params = {}) {
this.mainContext = mainContext
this.params = { ...params }
this.bindings = {}
}
openWindow = () => {
new Window.DOMWindow({ id: "debugger", children: window.app.bindContexts(DebuggerUI) }).create()
}
bind = (id, binding) => {
this.bindings[id] = binding
return binding
}
unbind = (id) => {
delete this.bindings[id]
}
}
export default class VisualDebugger extends Extension {
window = {
debug: new Debugger(this.mainContext)
}
}

View File

@ -1,70 +0,0 @@
import { Extension } from "evite"
export default class ShortcutsExtension extends Extension {
constructor(app, main) {
super(app, main)
this.shortcuts = {}
document.addEventListener("keydown", (event) => {
// FIXME: event.key sometimes is not defined
const key = event.key.toLowerCase()
const shortcut = this.shortcuts[key]
if (shortcut) {
if (typeof shortcut.ctrl === "boolean" && event.ctrlKey !== shortcut.ctrl) {
return
}
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()
}
if (typeof shortcut.fn === "function") {
shortcut.fn()
}
}
})
}
register = (keybind = {}, fn) => {
if (typeof keybind === "string") {
keybind = {
key: keybind,
}
}
this.shortcuts[keybind.key] = {
...keybind,
fn,
}
}
remove = (array) => {
if (typeof array === "string") {
array = [array]
}
array.forEach(key => {
delete this.shortcuts[key]
})
}
window = {
ShortcutsController: this
}
}