mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44: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 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"),
|
||||
|
@ -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",
|
||||
|
@ -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)
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
})
|
||||
},
|
||||
]
|
||||
}
|
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 { 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
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
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 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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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