mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
Merge pull request #59 from ragestudio/core-integration
Core integration
This commit is contained in:
commit
b48450e92f
@ -2,10 +2,11 @@ const path = require("path")
|
|||||||
const { builtinModules } = require("module")
|
const { builtinModules } = require("module")
|
||||||
|
|
||||||
const aliases = {
|
const aliases = {
|
||||||
"~/": `${path.resolve(__dirname, "src")}/`,
|
|
||||||
"__": __dirname,
|
"__": __dirname,
|
||||||
"@src": path.resolve(__dirname, "src"),
|
"~/": `${path.resolve(__dirname, "src")}/`,
|
||||||
schemas: path.resolve(__dirname, "constants"),
|
"@src": path.join(__dirname, "src"),
|
||||||
|
cores: path.join(__dirname, "src/cores"),
|
||||||
|
schemas: path.join(__dirname, "constants"),
|
||||||
config: path.join(__dirname, "config"),
|
config: path.join(__dirname, "config"),
|
||||||
extensions: path.resolve(__dirname, "src/extensions"),
|
extensions: path.resolve(__dirname, "src/extensions"),
|
||||||
pages: path.join(__dirname, "src/pages"),
|
pages: path.join(__dirname, "src/pages"),
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"antd-mobile": "^5.0.0-rc.17",
|
"antd-mobile": "^5.0.0-rc.17",
|
||||||
"chart.js": "3.7.0",
|
"chart.js": "3.7.0",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
"evite": "0.10.4",
|
"evite": "0.11.0",
|
||||||
"faye": "1.4.0",
|
"faye": "1.4.0",
|
||||||
"feather-reactjs": "2.0.13",
|
"feather-reactjs": "2.0.13",
|
||||||
"fuse.js": "6.5.3",
|
"fuse.js": "6.5.3",
|
||||||
|
@ -43,7 +43,7 @@ Promise.tasked = function (promises) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { CreateEviteApp, BindPropsProvider } from "evite"
|
import { EviteRuntime, BindPropsProvider } from "evite"
|
||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { ActionSheet, Toast } from "antd-mobile"
|
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 { Icons } from "components/Icons"
|
||||||
|
|
||||||
import Layout from "./layout"
|
import Layout from "./layout"
|
||||||
import * as Render from "extensions/render.extension.jsx"
|
|
||||||
|
import * as Render from "cores/render"
|
||||||
|
|
||||||
import "theme/index.less"
|
import "theme/index.less"
|
||||||
|
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
//static debugMode = true // this will enable debug mode of evite app (dah...)
|
|
||||||
|
|
||||||
sessionController = new Session()
|
sessionController = new Session()
|
||||||
|
|
||||||
userController = new User()
|
userController = new User()
|
||||||
@ -73,8 +72,6 @@ class App extends React.Component {
|
|||||||
user: null,
|
user: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingMessage = false
|
|
||||||
|
|
||||||
static initialize() {
|
static initialize() {
|
||||||
window.app.version = config.package.version
|
window.app.version = config.package.version
|
||||||
}
|
}
|
||||||
@ -101,7 +98,7 @@ class App extends React.Component {
|
|||||||
},
|
},
|
||||||
"destroyed_session": async function () {
|
"destroyed_session": async function () {
|
||||||
await this.flushState()
|
await this.flushState()
|
||||||
this.eventBus.emit("forceToLogin")
|
app.eventBus.emit("forceToLogin")
|
||||||
},
|
},
|
||||||
"forceToLogin": function () {
|
"forceToLogin": function () {
|
||||||
// if (window.location.pathname !== "/login") {
|
// if (window.location.pathname !== "/login") {
|
||||||
@ -116,7 +113,7 @@ class App extends React.Component {
|
|||||||
await this.flushState()
|
await this.flushState()
|
||||||
|
|
||||||
if (window.location.pathname !== "/login") {
|
if (window.location.pathname !== "/login") {
|
||||||
this.eventBus.emit("forceToLogin")
|
app.eventBus.emit("forceToLogin")
|
||||||
|
|
||||||
antd.notification.open({
|
antd.notification.open({
|
||||||
message: <Translation>
|
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 = {
|
static staticRenders = {
|
||||||
NotFound: (props) => {
|
NotFound: (props) => {
|
||||||
return <NotFound />
|
return <NotFound />
|
||||||
@ -246,7 +193,7 @@ class App extends React.Component {
|
|||||||
return <RenderError {...props} />
|
return <RenderError {...props} />
|
||||||
},
|
},
|
||||||
Crash: Crash.CrashWrapper,
|
Crash: Crash.CrashWrapper,
|
||||||
initialization: () => {
|
Initialization: () => {
|
||||||
return <div className="splash_wrapper">
|
return <div className="splash_wrapper">
|
||||||
<div className="splash_logo">
|
<div className="splash_logo">
|
||||||
<img src={config.logo.alt} />
|
<img src={config.logo.alt} />
|
||||||
@ -256,7 +203,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static publicMethods = {
|
static publicMethods = {
|
||||||
"openSettings": (goTo) => {
|
openSettings: (goTo) => {
|
||||||
window.app.DrawerController.open("settings", Settings, {
|
window.app.DrawerController.open("settings", Settings, {
|
||||||
props: {
|
props: {
|
||||||
width: "fit-content",
|
width: "fit-content",
|
||||||
@ -265,7 +212,43 @@ class App extends React.Component {
|
|||||||
goTo,
|
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 () => {
|
flushState = async () => {
|
||||||
@ -275,7 +258,7 @@ class App extends React.Component {
|
|||||||
componentDidMount = async () => {
|
componentDidMount = async () => {
|
||||||
if (window.app.isAppCapacitor()) {
|
if (window.app.isAppCapacitor()) {
|
||||||
window.addEventListener("statusTap", () => {
|
window.addEventListener("statusTap", () => {
|
||||||
this.eventBus.emit("statusTap")
|
app.eventBus.emit("statusTap")
|
||||||
})
|
})
|
||||||
|
|
||||||
StatusBar.setOverlaysWebView({ overlay: true })
|
StatusBar.setOverlaysWebView({ overlay: true })
|
||||||
@ -285,7 +268,7 @@ class App extends React.Component {
|
|||||||
const userAgentPlatform = window.navigator.userAgent.toLowerCase()
|
const userAgentPlatform = window.navigator.userAgent.toLowerCase()
|
||||||
|
|
||||||
if (userAgentPlatform.includes("mac")) {
|
if (userAgentPlatform.includes("mac")) {
|
||||||
window.app.ShortcutsController.register({
|
this.props.cores.ShortcutsCore.register({
|
||||||
key: ",",
|
key: ",",
|
||||||
meta: true,
|
meta: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
@ -293,7 +276,7 @@ class App extends React.Component {
|
|||||||
App.publicMethods.openSettings(...args)
|
App.publicMethods.openSettings(...args)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
window.app.ShortcutsController.register({
|
this.props.cores.ShortcutsCore.register({
|
||||||
key: ",",
|
key: ",",
|
||||||
ctrl: true,
|
ctrl: true,
|
||||||
preventDefault: 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()
|
await this.initialization()
|
||||||
|
|
||||||
this.eventBus.emit("app.render_initialization_done")
|
app.eventBus.emit("app.render_initialization_done")
|
||||||
}
|
}
|
||||||
|
|
||||||
initialization = async () => {
|
initialization = async () => {
|
||||||
@ -373,7 +356,10 @@ class App extends React.Component {
|
|||||||
|
|
||||||
await Promise.tasked(initializationTasks).catch((reason) => {
|
await Promise.tasked(initializationTasks).catch((reason) => {
|
||||||
console.error(`[App] Initialization failed: ${reason.cause}`)
|
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)
|
@ -8,7 +8,7 @@ export const CrashComponent = (props) => {
|
|||||||
|
|
||||||
return <Result
|
return <Result
|
||||||
status="error"
|
status="error"
|
||||||
title="Crash"
|
title="Well, we're sorry! The application has a fatal crash."
|
||||||
subTitle={crash.message}
|
subTitle={crash.message}
|
||||||
extra={[
|
extra={[
|
||||||
<Button type="primary" key="reload" onClick={() => window.location.reload()}>
|
<Button type="primary" key="reload" onClick={() => window.location.reload()}>
|
||||||
@ -16,9 +16,10 @@ export const CrashComponent = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<div>
|
{crash.details &&
|
||||||
<code>{crash.error}</code>
|
<div>
|
||||||
</div>
|
<code>{crash.details}</code>
|
||||||
|
</div>}
|
||||||
</Result>
|
</Result>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,5 +31,4 @@ export const CrashWrapper = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default CrashComponent
|
export default CrashComponent
|
@ -9,11 +9,53 @@
|
|||||||
|
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.7);
|
||||||
backdrop-filter: blur(10px);
|
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;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
animation: opacity-fade-in 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes opacity-fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,62 +1,53 @@
|
|||||||
import { Extension } from "evite"
|
import Core from "evite/src/core"
|
||||||
import config from "config"
|
import config from "config"
|
||||||
import { Bridge } from "linebridge/dist/client"
|
import { Bridge } from "linebridge/dist/client"
|
||||||
import { Session } from "models"
|
import { Session } from "models"
|
||||||
|
|
||||||
export default class ApiExtension extends Extension {
|
export default class ApiCore extends Core {
|
||||||
depends = ["SettingsExtension"]
|
apiBridge = this.createBridge()
|
||||||
|
|
||||||
constructor(app, main) {
|
WSInterface = {
|
||||||
super(app, main)
|
...this.apiBridge.wsInterface,
|
||||||
|
request: this.WSRequest,
|
||||||
//this.config = this.getServerOrigins()
|
listen: this.handleWSListener,
|
||||||
|
mainSocketConnected: false
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getServerOrigins = () => {
|
WSSockets = this.WSInterface.sockets
|
||||||
// TODO: try to get origins from settings
|
|
||||||
// const storagedOrigins = window.app.settings.get("serverOrigins")
|
publicMethods = {
|
||||||
|
api: this.apiBridge,
|
||||||
|
ws: this.WSInterface,
|
||||||
|
request: this.apiBridge.endpoints,
|
||||||
|
WSRequest: this.WSInterface.wsEndpoints,
|
||||||
}
|
}
|
||||||
|
|
||||||
initializers = [
|
async initialize() {
|
||||||
async () => {
|
this.WSSockets.main.on("authenticated", () => {
|
||||||
this.WSSockets.main.on("authenticated", () => {
|
console.debug("[WS] Authenticated")
|
||||||
console.debug("[WS] Authenticated")
|
})
|
||||||
})
|
this.WSSockets.main.on("authenticateFailed", (error) => {
|
||||||
this.WSSockets.main.on("authenticateFailed", (error) => {
|
console.error("[WS] Authenticate Failed", error)
|
||||||
console.error("[WS] Authenticate Failed", error)
|
})
|
||||||
})
|
|
||||||
|
|
||||||
this.WSSockets.main.on("connect", () => {
|
this.WSSockets.main.on("connect", () => {
|
||||||
window.app.eventBus.emit("websocket_connected")
|
this.ctx.eventBus.emit("websocket_connected")
|
||||||
this.WSInterface.mainSocketConnected = true
|
|
||||||
})
|
|
||||||
|
|
||||||
this.WSSockets.main.on("disconnect", (...context) => {
|
this.WSInterface.mainSocketConnected = true
|
||||||
window.app.eventBus.emit("websocket_disconnected", ...context)
|
})
|
||||||
this.WSInterface.mainSocketConnected = false
|
|
||||||
})
|
|
||||||
|
|
||||||
this.WSSockets.main.on("connect_error", (...context) => {
|
this.WSSockets.main.on("disconnect", (...context) => {
|
||||||
window.app.eventBus.emit("websocket_connection_error", ...context)
|
this.ctx.eventBus.emit("websocket_disconnected", ...context)
|
||||||
this.WSInterface.mainSocketConnected = false
|
|
||||||
})
|
|
||||||
|
|
||||||
this.mainContext.setToWindowContext("api", this.apiBridge)
|
this.WSInterface.mainSocketConnected = false
|
||||||
this.mainContext.setToWindowContext("ws", this.WSInterface)
|
})
|
||||||
|
|
||||||
this.mainContext.setToWindowContext("request", this.apiBridge.endpoints)
|
this.WSSockets.main.on("connect_error", (...context) => {
|
||||||
this.mainContext.setToWindowContext("WSRequest", this.WSInterface.wsEndpoints)
|
this.ctx.eventBus.emit("websocket_connection_error", ...context)
|
||||||
}
|
|
||||||
]
|
this.WSInterface.mainSocketConnected = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
createBridge() {
|
createBridge() {
|
||||||
const getSessionContext = async () => {
|
const getSessionContext = async () => {
|
||||||
@ -172,8 +163,4 @@ export default class ApiExtension extends Extension {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
window = {
|
|
||||||
ApiController: this
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Extension } from "evite"
|
import Core from "evite/src/core"
|
||||||
import config from "config"
|
import config from "config"
|
||||||
import i18n from "i18next"
|
import i18n from "i18next"
|
||||||
import { initReactI18next } from "react-i18next"
|
import { initReactI18next } from "react-i18next"
|
||||||
@ -14,8 +14,36 @@ export function extractLocaleFromPath(path = "") {
|
|||||||
|
|
||||||
const messageImports = import.meta.glob("./translations/*.json")
|
const messageImports = import.meta.glob("./translations/*.json")
|
||||||
|
|
||||||
export default class I18nExtension extends Extension {
|
export default class I18nCore extends Core {
|
||||||
depends = ["SettingsExtension"]
|
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) => {
|
importLocale = async (locale) => {
|
||||||
const [, importLocale] =
|
const [, importLocale] =
|
||||||
@ -40,34 +68,4 @@ export default class I18nExtension extends Extension {
|
|||||||
console.error(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)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
21
packages/app/src/cores/index.js
Normal file
21
packages/app/src/cores/index.js
Normal 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,
|
||||||
|
]
|
@ -1,11 +1,26 @@
|
|||||||
import { Extension } from "evite"
|
import Core from "evite/src/core"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { notification as Notf } from "antd"
|
import { notification as Notf } from "antd"
|
||||||
import { Icons, createIconRender } from "components/Icons"
|
import { Icons, createIconRender } from "components/Icons"
|
||||||
import { Translation } from "react-i18next"
|
import { Translation } from "react-i18next"
|
||||||
import { Haptics } from "@capacitor/haptics"
|
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 = () => {
|
getSoundVolume = () => {
|
||||||
return (window.app.settings.get("notifications_sound_volume") ?? 50) / 100
|
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()
|
const soundVolume = options.soundVolume ? options.soundVolume / 100 : this.getSoundVolume()
|
||||||
|
|
||||||
if (soundEnabled) {
|
if (soundEnabled) {
|
||||||
window.app.SoundEngine.play("notification", {
|
if (typeof window.app.sound?.play === "function") {
|
||||||
volume: soundVolume,
|
window.app.sound.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,
|
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
|
||||||
window = {
|
|
||||||
notifications: this
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
|
import Core from "evite/src/core"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { EvitePureComponent, Extension } from "evite"
|
import { EvitePureComponent } from "evite"
|
||||||
import progressBar from "nprogress"
|
import progressBar from "nprogress"
|
||||||
import routes from "virtual:generated-pages"
|
import routes from "virtual:generated-pages"
|
||||||
|
|
||||||
@ -89,8 +90,8 @@ export class RouteRender extends EvitePureComponent {
|
|||||||
return JSON.stringify(this.state.renderError)
|
return JSON.stringify(this.state.renderError)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.renderInitialization) {
|
if (this.state.renderInitialization && this.props.staticRenders?.Initialization) {
|
||||||
const StaticInitializationRender = this.props.staticRenders?.initialization ?? null
|
const StaticInitializationRender = this.props.staticRenders?.Initialization ?? <div>Loading...</div>
|
||||||
|
|
||||||
return <StaticInitializationRender />
|
return <StaticInitializationRender />
|
||||||
}
|
}
|
||||||
@ -103,92 +104,88 @@ export class RouteRender extends EvitePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RenderExtension extends Extension {
|
export class RenderCore extends Core {
|
||||||
initializers = [
|
progressBar = progressBar.configure({ parent: "html", showSpinner: false })
|
||||||
async function () {
|
|
||||||
const defaultTransitionDelay = 150
|
|
||||||
|
|
||||||
this.progressBar = progressBar.configure({ parent: "html", showSpinner: false })
|
publicMethods = {
|
||||||
|
setLocation: this.ctx.history.setLocation,
|
||||||
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 = {
|
initialize = () => {
|
||||||
isAppCapacitor: () => window.navigator.userAgent === "capacitor",
|
const defaultTransitionDelay = 150
|
||||||
bindContexts: (component) => {
|
|
||||||
let contexts = {
|
this.ctx.history.listen((event) => {
|
||||||
main: {},
|
this.ctx.eventBus.emit("transitionDone", event)
|
||||||
app: {},
|
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") {
|
this.progressBar.start()
|
||||||
if (component.bindApp === "all") {
|
this.ctx.eventBus.emit("transitionStart", delay)
|
||||||
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") {
|
setTimeout(() => {
|
||||||
if (component.bindMain === "all") {
|
this.ctx.history.push({
|
||||||
Object.keys(main).forEach((key) => {
|
pathname: to,
|
||||||
contexts.main[key] = main[key]
|
}, state)
|
||||||
})
|
this.ctx.history.lastLocation = this.history.location
|
||||||
}
|
}, delay ?? defaultTransitionDelay)
|
||||||
} else {
|
}
|
||||||
if (Array.isArray(component.bindMain)) {
|
}
|
||||||
component.bindMain.forEach((key) => {
|
|
||||||
contexts.main[key] = main[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
@ -1,18 +1,22 @@
|
|||||||
import { Extension } from "evite"
|
import Core from "evite/src/core"
|
||||||
import store from "store"
|
import store from "store"
|
||||||
import defaultSettings from "schemas/defaultSettings.json"
|
import defaultSettings from "schemas/defaultSettings.json"
|
||||||
import { Observable } from "rxjs"
|
import { Observable } from "rxjs"
|
||||||
|
|
||||||
export default class SettingsExtension extends Extension {
|
export default class SettingsCore extends Core {
|
||||||
constructor(app, main) {
|
storeKey = "app_settings"
|
||||||
super(app, main)
|
|
||||||
this.storeKey = "app_settings"
|
|
||||||
this.settings = store.get(this.storeKey) ?? {}
|
|
||||||
|
|
||||||
this._setDefaultUndefined()
|
settings = store.get(this.storeKey) ?? {}
|
||||||
|
|
||||||
|
publicMethods = {
|
||||||
|
settings: this
|
||||||
}
|
}
|
||||||
|
|
||||||
_setDefaultUndefined = () => {
|
initialize() {
|
||||||
|
this.fulfillUndefinedWithDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
fulfillUndefinedWithDefaults = () => {
|
||||||
Object.keys(defaultSettings).forEach((key) => {
|
Object.keys(defaultSettings).forEach((key) => {
|
||||||
const value = defaultSettings[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) => {
|
is = (key, value) => {
|
||||||
return this.settings[key] === value
|
return this.settings[key] === value
|
||||||
}
|
}
|
||||||
@ -53,6 +49,14 @@ export default class SettingsExtension extends Extension {
|
|||||||
return this.settings[key]
|
return this.settings[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefaults = (key) => {
|
||||||
|
if (typeof key === "undefined") {
|
||||||
|
return defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultSettings[key]
|
||||||
|
}
|
||||||
|
|
||||||
withEvent = (listenEvent, defaultValue) => {
|
withEvent = (listenEvent, defaultValue) => {
|
||||||
let value = defaultValue ?? this.settings[key] ?? false
|
let value = defaultValue ?? this.settings[key] ?? false
|
||||||
|
|
||||||
@ -69,8 +73,4 @@ export default class SettingsExtension extends Extension {
|
|||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
window = {
|
|
||||||
"settings": this
|
|
||||||
}
|
|
||||||
}
|
}
|
74
packages/app/src/cores/shortcuts/index.js
Normal file
74
packages/app/src/cores/shortcuts/index.js
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,18 @@
|
|||||||
import { Extension } from "evite"
|
import Core from "evite/src/core"
|
||||||
import { Howl } from "howler"
|
import { Howl } from "howler"
|
||||||
import config from "config"
|
import config from "config"
|
||||||
|
|
||||||
export default class SoundEngineExtension extends Extension {
|
export default class SoundCore extends Core {
|
||||||
sounds = {}
|
sounds = {}
|
||||||
|
|
||||||
|
publicMethods = {
|
||||||
|
sound: this,
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize() {
|
||||||
|
this.sounds = await this.getSounds()
|
||||||
|
}
|
||||||
|
|
||||||
getSounds = async () => {
|
getSounds = async () => {
|
||||||
// TODO: Load custom soundpacks manifests
|
// TODO: Load custom soundpacks manifests
|
||||||
let soundPack = config.defaultSoundPack ?? {}
|
let soundPack = config.defaultSoundPack ?? {}
|
||||||
@ -26,18 +34,8 @@ export default class SoundEngineExtension extends Extension {
|
|||||||
if (this.sounds[name]) {
|
if (this.sounds[name]) {
|
||||||
return this.sounds[name](options).play()
|
return this.sounds[name](options).play()
|
||||||
} else {
|
} else {
|
||||||
console.error(`Sound ${name} not found.`)
|
console.error(`Sound [${name}] not found or is not available.`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializers = [
|
|
||||||
async () => {
|
|
||||||
this.sounds = await this.getSounds()
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
window = {
|
|
||||||
SoundEngine: this
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,39 +1,33 @@
|
|||||||
import { Extension } from "evite"
|
import Core from "evite/src/core"
|
||||||
import config from "config"
|
import config from "config"
|
||||||
import store from "store"
|
import store from "store"
|
||||||
import { ConfigProvider } from "antd"
|
import { ConfigProvider } from "antd"
|
||||||
|
|
||||||
export default class ThemeExtension extends Extension {
|
export default class StyleCore extends Core {
|
||||||
constructor(app, main) {
|
themeManifestStorageKey = "theme"
|
||||||
super(app, main)
|
modificationStorageKey = "themeModifications"
|
||||||
|
|
||||||
this.themeManifestStorageKey = "theme"
|
theme = null
|
||||||
this.modificationStorageKey = "themeModifications"
|
mutation = null
|
||||||
|
currentVariant = null
|
||||||
|
|
||||||
this.theme = null
|
events = {
|
||||||
|
"theme.applyVariant": (value) => {
|
||||||
this.mutation = null
|
this.applyVariant(value)
|
||||||
this.currentVariant = null
|
this.setVariant(value)
|
||||||
|
},
|
||||||
|
"modifyTheme": (value) => {
|
||||||
|
this.update(value)
|
||||||
|
this.setModifications(this.mutation)
|
||||||
|
},
|
||||||
|
"resetTheme": () => {
|
||||||
|
this.resetDefault()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializers = [
|
publicMethods = {
|
||||||
async () => {
|
style: this
|
||||||
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()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
static get currentVariant() {
|
static get currentVariant() {
|
||||||
return document.documentElement.style.getPropertyValue("--themeVariant")
|
return document.documentElement.style.getPropertyValue("--themeVariant")
|
||||||
@ -145,8 +139,4 @@ export default class ThemeExtension extends Extension {
|
|||||||
this.update(values)
|
this.update(values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window = {
|
|
||||||
ThemeController: this
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user