update ui & bad typos

This commit is contained in:
SrGooglo 2023-06-30 14:20:39 +00:00
parent 6b913e3d60
commit 55c9b7fb17
49 changed files with 788 additions and 507 deletions

View File

@ -59,7 +59,7 @@ export default {
icon: "Moon", icon: "Moon",
title: "Sync with system", title: "Sync with system",
description: "Automatically switch to dark mode based on your system preference.", description: "Automatically switch to dark mode based on your system preference.",
emitEvent: "style.autoDarkModeToogle", emitEvent: "style.autoDarkModetoggle",
storaged: true, storaged: true,
}, },
{ {

View File

@ -124,7 +124,11 @@ const ChangePasswordComponent = (props) => {
export default (props) => { export default (props) => {
const onClick = () => { const onClick = () => {
app.SidedrawerController.open("passwordChange", ChangePasswordComponent) if (app.isMobile) {
return app.DrawerController.open("passwordChange", ChangePasswordComponent)
}
return app.layout.sidedrawer.open("passwordChange", ChangePasswordComponent)
} }
return <antd.Button onClick={onClick}> return <antd.Button onClick={onClick}>

View File

@ -32,7 +32,7 @@ export default class WidgetsView extends React.Component {
}) })
}} }}
onChangeVisible={(visible) => { onChangeVisible={(visible) => {
app.cores.widgets.toogleVisibility(manifest._id, visible) app.cores.widgets.toggleVisibility(manifest._id, visible)
}} }}
/> />
</React.Fragment> </React.Fragment>

View File

@ -157,6 +157,23 @@ class ComtyApp extends React.Component {
static publicMethods = { static publicMethods = {
controls: { controls: {
toogleUIVisibility: (to) => {
if (app.layout.sidebar) {
app.layout.sidebar.toggleVisibility(to)
}
if (app.layout.tools_bar) {
app.layout.tools_bar.toggleVisibility(to)
}
if (app.layout.top_bar) {
app.layout.top_bar.toggleVisibility(to)
}
if (app.layout.floatingStack) {
app.layout.floatingStack.toggleGlobalVisibility(to)
}
},
openLoginForm: async (options = {}) => { openLoginForm: async (options = {}) => {
app.DrawerController.open("login", Login, { app.DrawerController.open("login", Login, {
defaultLocked: options.defaultLocked ?? false, defaultLocked: options.defaultLocked ?? false,
@ -178,7 +195,7 @@ class ComtyApp extends React.Component {
}, },
// Opens the notification window and sets up the UI for the notification to be displayed // Opens the notification window and sets up the UI for the notification to be displayed
openNotifications: () => { openNotifications: () => {
window.app.SidedrawerController.open("notifications", NotificationsCenter, { window.app.layout.sidedrawer.open("notifications", NotificationsCenter, {
props: { props: {
width: "fit-content", width: "fit-content",
}, },

View File

@ -51,7 +51,7 @@ export default class FormGenerator extends React.Component {
}, },
clearForm: () => { clearForm: () => {
this.ctx.clearErrors() this.ctx.clearErrors()
this.ctx.toogleValidation(false) this.ctx.toggleValidation(false)
this.ref.current.resetFields() this.ref.current.resetFields()
}, },
finish: () => this.ref.current.submit(), finish: () => this.ref.current.submit(),
@ -61,7 +61,7 @@ export default class FormGenerator extends React.Component {
shake: (id) => { shake: (id) => {
this.formItemShake(id) this.formItemShake(id)
}, },
toogleValidation: (to) => { toggleValidation: (to) => {
if (typeof to !== "undefined") { if (typeof to !== "undefined") {
return this.setState({ validating: to }) return this.setState({ validating: to })
} }

View File

@ -15,6 +15,19 @@ import CreatorView from "pages/@mobile-views/creator"
import "./index.less" import "./index.less"
const tourSteps = [
{
title: "Open quick nav",
description: "Xd",
ref: React.createRef(),
},
{
title: "Account button",
description: "Xd",
ref: React.createRef(),
}
]
const openPlayerView = () => { const openPlayerView = () => {
app.DrawerController.open("player", PlayerView) app.DrawerController.open("player", PlayerView)
} }
@ -53,7 +66,7 @@ const PlayerButton = (props) => {
</div> </div>
} }
const AccountButton = (props) => { const AccountButton = React.forwardRef((props, ref) => {
const user = app.userData const user = app.userData
const ActionSheetRef = React.useRef() const ActionSheetRef = React.useRef()
@ -101,6 +114,7 @@ const AccountButton = (props) => {
key="account" key="account"
id="account" id="account"
className="item" className="item"
ref={ref}
onClick={handleClick} onClick={handleClick}
onContextMenu={handleHold} onContextMenu={handleHold}
context-menu="ignore" context-menu="ignore"
@ -111,7 +125,7 @@ const AccountButton = (props) => {
} }
</div> </div>
</div> </div>
} })
export default (props) => { export default (props) => {
return <WithPlayerContext> return <WithPlayerContext>
@ -128,6 +142,7 @@ export class BottomBar extends React.Component {
visible: false, visible: false,
quickNavVisible: false, quickNavVisible: false,
render: null, render: null,
tourOpen: false,
} }
busEvents = { busEvents = {
@ -136,9 +151,14 @@ export class BottomBar extends React.Component {
} }
} }
refs = {
homeBtn: React.createRef(),
accountBtn: React.createRef(),
}
componentDidMount = () => { componentDidMount = () => {
this.interface = app.layout.bottom_bar = { this.interface = app.layout.bottom_bar = {
toogleVisible: this.toggleVisibility, toggleVisible: this.toggleVisibility,
isVisible: () => this.state.visible, isVisible: () => this.state.visible,
render: (fragment) => { render: (fragment) => {
this.setState({ render: fragment }) this.setState({ render: fragment })
@ -146,6 +166,9 @@ export class BottomBar extends React.Component {
clear: () => { clear: () => {
this.setState({ render: null }) this.setState({ render: null })
}, },
toggleTour: () => {
this.setState({ tourOpen: !this.state.tourOpen })
},
} }
setTimeout(() => { setTimeout(() => {
@ -278,6 +301,16 @@ export class BottomBar extends React.Component {
} }
} }
toggleTour = (to) => {
if (typeof to === "undefined") {
to = !this.state.tourOpen
}
this.setState({
tourOpen: to
})
}
render() { render() {
if (this.state.render) { if (this.state.render) {
return <div className="bottomBar"> return <div className="bottomBar">
@ -358,7 +391,7 @@ export class BottomBar extends React.Component {
</div> </div>
</div> </div>
<AccountButton /> <AccountButton/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -195,8 +195,8 @@
.icon { .icon {
border-radius: 360px; border-radius: 360px;
height: @app_bottomBar_iconSize; height: @bottomBar_iconSize;
width: @app_bottomBar_iconSize; width: @bottomBar_iconSize;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -120,7 +120,7 @@ export class Drawer extends React.Component {
} }
} }
toogleVisibility = (to) => { toggleVisibility = (to) => {
this.setState({ visible: to ?? !this.state.visible }) this.setState({ visible: to ?? !this.state.visible })
} }
@ -137,7 +137,7 @@ export class Drawer extends React.Component {
return console.warn("Cannot close a locked drawer") return console.warn("Cannot close a locked drawer")
} }
this.toogleVisibility(false) this.toggleVisibility(false)
this.events.emit("beforeClose") this.events.emit("beforeClose")

View File

@ -5,3 +5,4 @@ export { default as Sidebar } from "./sidebar"
export { default as Sidedrawer } from "./sidedrawer" export { default as Sidedrawer } from "./sidedrawer"
export { default as Modal } from "./modal" export { default as Modal } from "./modal"
export { default as Header } from "./header" export { default as Header } from "./header"
export { default as ToolsBar } from "./toolsBar"

View File

@ -90,6 +90,9 @@ export default class Sidebar extends React.Component {
topItems: generateTopItems(), topItems: generateTopItems(),
bottomItems: [], bottomItems: [],
lockAutocollapse: false,
navigationRender: null,
} }
sidebarRef = React.createRef() sidebarRef = React.createRef()
@ -97,10 +100,49 @@ export default class Sidebar extends React.Component {
collapseDebounce = null collapseDebounce = null
interface = window.app.layout.sidebar = { interface = window.app.layout.sidebar = {
toggleVisibility: this.toggleVisibility, toggleVisibility: (to) => {
toggleCollapse: this.toggleExpanded, this.setState({ visible: to ?? !this.state.visible })
},
toggleCollapse: (to, force) => {
to = to ?? !this.state.expanded
if (this.collapseDebounce) {
clearTimeout(this.collapseDebounce)
this.collapseDebounce = null
}
if (!to & this.state.dropdownOpen && !force) {
// FIXME: This is a walkaround for a bug in antd, causing when dropdown set to close, item click event is not fired
// The desing defines when sidebar should be collapsed, dropdown should be closed, but in this case, gonna to keep it open untils dropdown is closed
//this.setState({ dropdownOpen: false })
return false
}
if (!to) {
if (this.state.lockAutocollapse) {
return false
}
this.collapseDebounce = setTimeout(() => {
this.setState({ expanded: to })
}, window.app.cores.settings.get("sidebar.collapse_delay_time") ?? 500)
} else {
this.setState({ expanded: to })
}
app.eventBus.emit("sidebar.expanded", to)
},
isVisible: () => this.state.visible, isVisible: () => this.state.visible,
isExpanded: () => this.state.expanded, isExpanded: () => this.state.expanded,
renderNavigationBar: (component, options) => {
this.setState({
navigationRender: {
component,
options,
}
})
},
updateBottomItemProps: (id, newProps) => { updateBottomItemProps: (id, newProps) => {
let updatedValue = this.state.bottomItems let updatedValue = this.state.bottomItems
@ -153,17 +195,37 @@ export default class Sidebar extends React.Component {
}, },
} }
events = {
"sidedrawers.visible": (has) => {
this.setState({
lockAutocollapse: has
})
if (!has && this.state.expanded) {
this.interface.toggleCollapse(false)
}
}
}
componentDidMount = async () => { componentDidMount = async () => {
for (const [event, handler] of Object.entries(this.events)) {
app.eventBus.on(event, handler)
}
setTimeout(() => { setTimeout(() => {
this.toggleVisibility(true) this.interface.toggleVisibility(true)
if (app.cores.settings.is("sidebar.collapsable", false)) { if (app.cores.settings.is("sidebar.collapsable", false)) {
this.toggleExpanded(true) this.interface.toggleCollapse(true)
} }
}, 10) }, 10)
} }
componentWillUnmount = () => { componentWillUnmount = () => {
for (const [event, handler] of Object.entries(this.events)) {
app.eventBus.off(event, handler)
}
delete app.layout.sidebar delete app.layout.sidebar
} }
@ -192,49 +254,18 @@ export default class Sidebar extends React.Component {
return window.app.location.push(`/${item.path ?? e.key}`, 150) return window.app.location.push(`/${item.path ?? e.key}`, 150)
} }
toggleExpanded = (to, force) => {
to = to ?? !this.state.expanded
if (this.collapseDebounce) {
clearTimeout(this.collapseDebounce)
this.collapseDebounce = null
}
if (!to & this.state.dropdownOpen && !force) {
// FIXME: This is a walkaround for a bug in antd, causing when dropdown set to close, item click event is not fired
// The desing defines when sidebar should be collapsed, dropdown should be closed, but in this case, gonna to keep it open untils dropdown is closed
//this.setState({ dropdownOpen: false })
return false
}
if (!to) {
this.collapseDebounce = setTimeout(() => {
this.setState({ expanded: to })
}, window.app.cores.settings.get("sidebar.collapse_delay_time") ?? 500)
} else {
this.setState({ expanded: to })
}
app.eventBus.emit("sidebar.expanded", to)
}
toggleVisibility = (to) => {
this.setState({ visible: to ?? !this.state.visible })
}
onMouseEnter = () => { onMouseEnter = () => {
if (!this.state.visible) return if (!this.state.visible) return
if (window.app.cores.settings.is("sidebar.collapsable", false)) { if (window.app.cores.settings.is("sidebar.collapsable", false)) {
if (!this.state.expanded) { if (!this.state.expanded) {
this.toggleExpanded(true) this.interface.toggleCollapse(true)
} }
return return
} }
this.toggleExpanded(true) this.interface.toggleCollapse(true)
} }
handleMouseLeave = () => { handleMouseLeave = () => {
@ -242,13 +273,13 @@ export default class Sidebar extends React.Component {
if (window.app.cores.settings.is("sidebar.collapsable", false)) return if (window.app.cores.settings.is("sidebar.collapsable", false)) return
this.toggleExpanded(false) this.interface.toggleCollapse(false)
} }
onDropdownOpenChange = (to) => { onDropdownOpenChange = (to) => {
// this is another walkaround for a bug in antd, causing when dropdown set to close, item click event is not fired // this is another walkaround for a bug in antd, causing when dropdown set to close, item click event is not fired
if (!to && this.state.expanded) { if (!to && this.state.expanded) {
this.toggleExpanded(false, true) this.interface.toggleCollapse(false, true)
} }
this.setState({ dropdownOpen: to }) this.setState({ dropdownOpen: to })
@ -265,126 +296,133 @@ export default class Sidebar extends React.Component {
render() { render() {
const defaultSelectedKey = window.location.pathname.replace("/", "") const defaultSelectedKey = window.location.pathname.replace("/", "")
return <Motion style={{ return <>
x: spring(!this.state.visible ? 100 : 0), <Motion style={{
}}> x: spring(!this.state.visible ? 100 : 0),
{({ x }) => { }}>
return <div {({ x }) => {
className="app_sidebar_wrapper" return <div
style={{
transform: `translateX(-${x}%)`,
}}
>
<div
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.handleMouseLeave}
className={classnames( className={classnames(
"app_sidebar", "app_sidebar_wrapper",
{ {
["expanded"]: this.state.visible && this.state.expanded, visible: this.state.visible,
["hidden"]: !this.state.visible,
} }
) )}
} style={{
ref={this.sidebarRef} transform: `translateX(-${x}%)`,
}}
> >
<div className="app_sidebar_header">
<div className="app_sidebar_header_logo">
<img src={config.logo?.alt} />
</div>
</div>
<div key="menu" className="app_sidebar_menu_wrapper">
<Menu
mode="inline"
onClick={this.handleClick}
defaultSelectedKeys={[defaultSelectedKey]}
items={this.state.topItems}
selectable
/>
</div>
<div <div
key="bottom" onMouseEnter={this.onMouseEnter}
onMouseLeave={this.handleMouseLeave}
className={classnames( className={classnames(
"app_sidebar_menu_wrapper", "app_sidebar",
"bottom" {
)} ["expanded"]: this.state.visible && this.state.expanded,
["hidden"]: !this.state.visible,
}
)
}
ref={this.sidebarRef}
> >
<Menu
selectable={false} <div className="app_sidebar_header">
mode="inline" <div className="app_sidebar_header_logo">
onClick={this.handleClick} <img src={config.logo?.alt} />
</div>
</div>
<div key="menu" className="app_sidebar_menu_wrapper">
<Menu
mode="inline"
onClick={this.handleClick}
defaultSelectedKeys={[defaultSelectedKey]}
items={this.state.topItems}
selectable
/>
</div>
<div
key="bottom"
className={classnames(
"app_sidebar_menu_wrapper",
"bottom"
)}
> >
{ <Menu
this.state.bottomItems.map((item) => { selectable={false}
if (item.noContainer) { mode="inline"
return React.createElement(item.children, item.childrenProps) onClick={this.handleClick}
} >
{
return <Menu.Item this.state.bottomItems.map((item) => {
key={item.id} if (item.noContainer) {
className="extra_bottom_item" return React.createElement(item.children, item.childrenProps)
icon={createIconRender(item.icon)}
disabled={item.disabled ?? false}
{...item.containerProps}
>
{
React.createElement(item.children, item.childrenProps)
} }
</Menu.Item>
})
}
<Menu.Item key="search" icon={<Icons.Search />} >
<Translation>
{(t) => t("Search")}
</Translation>
</Menu.Item>
<Menu.Item key="notifications" icon={<Icons.Bell />}>
<Translation>
{t => t("Notifications")}
</Translation>
</Menu.Item>
<Menu.Item key="settings" icon={<Icons.Settings />}>
<Translation>
{t => t("Settings")}
</Translation>
</Menu.Item>
{ return <Menu.Item
app.userData && <Dropdown key={item.id}
menu={{ className="extra_bottom_item"
items: ActionMenuItems, icon={createIconRender(item.icon)}
onClick: this.onClickDropdownItem disabled={item.disabled ?? false}
}} {...item.containerProps}
autoFocus >
placement="top" {
trigger={["click"]} React.createElement(item.children, item.childrenProps)
onOpenChange={this.onDropdownOpenChange} }
> </Menu.Item>
<Menu.Item })
key="account" }
className="user_avatar" <Menu.Item key="search" icon={<Icons.Search />} >
ignoreClick
>
<Avatar shape="square" src={app.userData?.avatar} />
</Menu.Item>
</Dropdown>
}
{
!app.userData && <Menu.Item key="login" icon={<Icons.LogIn />}>
<Translation> <Translation>
{t => t("Login")} {(t) => t("Search")}
</Translation> </Translation>
</Menu.Item> </Menu.Item>
} <Menu.Item key="notifications" icon={<Icons.Bell />}>
</Menu> <Translation>
{t => t("Notifications")}
</Translation>
</Menu.Item>
<Menu.Item key="settings" icon={<Icons.Settings />}>
<Translation>
{t => t("Settings")}
</Translation>
</Menu.Item>
{
app.userData && <Dropdown
menu={{
items: ActionMenuItems,
onClick: this.onClickDropdownItem
}}
autoFocus
placement="top"
trigger={["click"]}
onOpenChange={this.onDropdownOpenChange}
>
<Menu.Item
key="account"
className="user_avatar"
ignoreClick
>
<Avatar shape="square" src={app.userData?.avatar} />
</Menu.Item>
</Dropdown>
}
{
!app.userData && <Menu.Item key="login" icon={<Icons.LogIn />}>
<Translation>
{t => t("Login")}
</Translation>
</Menu.Item>
}
</Menu>
</div>
</div> </div>
</div> </div>
</div> }}
}} </Motion>
</Motion> </>
} }
} }

View File

@ -8,12 +8,21 @@
z-index: 1000; z-index: 1000;
display: flex;
flex-direction: row;
align-items: center;
gap: 20px;
width: fit-content; width: fit-content;
height: 100vh; height: 100vh;
height: 100dvh; height: 100dvh;
padding: 10px; &.visible {
padding: 10px;
}
} }
.app_sidebar { .app_sidebar {

View File

@ -1,62 +1,79 @@
import React from "react" import React from "react"
import classnames from "classnames" import classnames from "classnames"
import { Motion, spring } from "react-motion"
import "./index.less" import "./index.less"
export const Sidedrawer = (props) => { export class Sidedrawer extends React.Component {
const sidedrawerId = props.id ?? props.key state = {
visible: false,
}
return <div toggleVisibility = (to) => {
key={sidedrawerId} this.setState({ visible: to ?? !this.state.visible })
id={sidedrawerId} }
style={props.style}
className={ render() {
classnames("sidedrawer", { return <Motion style={{
"hided": !props.defaultVisible, x: spring(!this.state.visible ? 100 : 0),
"first": props.first opacity: spring(!this.state.visible ? 0 : 1),
}) }}>
} {({ x, opacity }) => {
> return <div
{ key={this.props.id}
React.createElement(props.children, { id={this.props.id}
...props.props, className={classnames(
close: props.close, "sidedrawer",
}) {
} "first": this.props.first
</div> }
)}
style={{
...this.props.style,
transform: `translateX(-${x}%)`,
opacity: opacity,
}}
>
{
React.createElement(this.props.children, {
...this.props.props,
close: this.props.close,
})
}
</div>
}}
</Motion>
}
} }
export default class SidedrawerController extends React.Component { export default class SidedrawerController extends React.Component {
state = {
drawers: [],
lockedIds: [],
}
constructor(props) { constructor(props) {
super(props) super(props)
this.controller = window.app["SidedrawerController"] = { this.interface = app.layout.sidedrawer = {
open: this.open, open: this.open,
close: this.close, close: this.close,
closeAll: this.closeAll, closeAll: this.closeAll,
hasDrawers: this.state.drawers.length > 0, hasDrawers: this.state.drawers.length > 0,
toggleGlobalVisibility: () => {
this.setState({
globalVisible: !this.state.globalVisible,
})
}
} }
} }
state = {
globalVisible: true,
drawers: [],
lockedIds: [],
}
componentDidMount = () => { componentDidMount = () => {
this.listenEscape() this.listenEscape()
} }
componentDidUpdate() {
this.controller.hasDrawers = this.state.drawers.length > 0
if (this.controller.hasDrawers) {
window.app.eventBus.emit("sidedrawer.hasDrawers")
} else {
window.app.eventBus.emit("sidedrawer.noDrawers")
}
}
componentWillUnmount = () => { componentWillUnmount = () => {
this.unlistenEscape() this.unlistenEscape()
} }
@ -84,7 +101,7 @@ export default class SidedrawerController extends React.Component {
id = component.key ?? component.id ?? Math.random().toString(36).substr(2, 9) id = component.key ?? component.id ?? Math.random().toString(36).substr(2, 9)
} }
let drawers = this.state.drawers const drawers = this.state.drawers
// check if id is already in use // check if id is already in use
// but only if its allowed to be used multiple times // but only if its allowed to be used multiple times
@ -111,56 +128,56 @@ export default class SidedrawerController extends React.Component {
id = newId id = newId
} }
drawers.push(React.createElement( const drawerProps = {
Sidedrawer, id: id,
{ allowMultiples: options.allowMultiples ?? false,
key: id, escClosable: options.escClosable ?? true,
id: id, first: drawers.length === 0,
first: drawers.length === 0, style: {
style: { zIndex: 100 - drawers.length,
zIndex: 100 - drawers.length,
},
allowMultiples: options.allowMultiples ?? false,
...options.props,
close: this.close,
escClosable: options.escClosable ?? true,
defaultVisible: false,
selfLock: () => {
this.lockDrawerId(id)
},
selfUnlock: () => {
this.unlockDrawer(id)
}
}, },
component ref: React.createRef(),
)) close: this.close,
lock: () => this.lockDrawerId(id),
unlock: () => this.unlockDrawer(id),
}
drawers.push(React.createElement(Sidedrawer, drawerProps, component))
if (options.lock) { if (options.lock) {
this.lockDrawerId(id) this.lockDrawerId(id)
} }
await this.setState({ drawers }) await this.setState({
drawers,
})
setTimeout(() => { setTimeout(() => {
this.toggleDrawerVisibility(id, true) this.toggleDrawerVisibility(id, true)
}, 10) }, 10)
window.app.eventBus.emit("sidedrawer.open") window.app.eventBus.emit("sidedrawer.open")
if (this.state.drawers.length > 0) {
app.eventBus.emit("sidedrawers.visible", true)
}
} }
toggleDrawerVisibility = (id, to) => { toggleDrawerVisibility = (id, to) => {
const drawer = document.getElementById(id) // find drawer
const drawerClasses = drawer.classList const drawer = this.state.drawers.find(drawer => drawer.props.id === id)
if (to) { if (!drawer) {
app.cores.sound.useUIAudio("sidebar.expand") console.warn(`Sidedrawer with id "${id}" does not exist.`)
return
drawerClasses.remove("hided")
} else {
app.cores.sound.useUIAudio("sidebar.collapse")
drawerClasses.add("hided")
} }
if (!drawer.ref.current) {
console.warn(`Sidedrawer with id "${id}" has not valid ref.`)
return
}
return drawer.ref.current.toggleVisibility(to)
} }
close = (id) => { close = (id) => {
@ -186,7 +203,7 @@ export default class SidedrawerController extends React.Component {
// emit event // emit event
window.app.eventBus.emit("sidedrawer.close") window.app.eventBus.emit("sidedrawer.close")
// toogleVisibility off // toggleVisibility off
this.toggleDrawerVisibility(drawerId, false) this.toggleDrawerVisibility(drawerId, false)
// await drawer transition // await drawer transition
@ -195,6 +212,10 @@ export default class SidedrawerController extends React.Component {
drawers = drawers.filter(drawer => drawer.props.id !== drawerId) drawers = drawers.filter(drawer => drawer.props.id !== drawerId)
this.setState({ drawers }) this.setState({ drawers })
if (this.state.drawers.length === 0) {
app.eventBus.emit("sidedrawers.visible", false)
}
}, 500) }, 500)
} }
@ -231,7 +252,7 @@ export default class SidedrawerController extends React.Component {
className={classnames( className={classnames(
"sidedrawers-wrapper", "sidedrawers-wrapper",
{ {
["floating-sidebar"]: window.app?.cores.settings.get("sidebar.floating") ["hidden"]: !this.state.drawers.length || this.state.globalVisible,
} }
)} )}
> >

View File

@ -1,50 +1,48 @@
@import "theme/vars.less"; @import "theme/vars.less";
.sidedrawers-wrapper { .sidedrawers-wrapper {
position: relative;
top: 0;
right: 0;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
&.floating-sidebar { padding: @sidebar_padding;
z-index: 950;
position: absolute; height: 100dvh;
margin-left: @app_sidebar_width; height: 100vh;
}
.sidedrawer { .sidedrawer {
position: relative; position: absolute;
width: auto; // by default z-index: 100;
height: 100vh;
top: 0;
left: 0;
bottom: 0;
margin-top: @sidebar_padding;
height: calc(100% - @sidebar_padding * 2);
width: auto;
min-width: 400px; min-width: 400px;
max-width: 15vw;
z-index: 20;
background-color: var(--sidedrawer-background-color); background-color: var(--sidedrawer-background-color);
border-radius: 0 @app_sidebar_borderRadius @app_sidebar_borderRadius 0; border-radius: @sidebar_borderRadius;
padding: 20px; padding: 20px;
padding-left: 30px;
transform: translate(-20px, 0);
overflow-x: hidden; overflow-x: hidden;
overflow-y: overlay; overflow-y: overlay;
word-break: break-all; box-shadow: @card-shadow;
transition: all 150ms ease-in-out; }
// create shadow on the right side &.hidden{
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important; width: 0;
padding: 0;
&.first {
transform: translate(-10px, 0);
}
&.hided {
width: 0;
min-width: 0;
padding: 0;
}
} }
} }

View File

@ -0,0 +1,89 @@
import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import { Motion, spring } from "react-motion"
import { Translation } from "react-i18next"
import { Icons } from "components/Icons"
import { HashtagTrendings, FeaturedEventsAnnouncements, ConnectedFriends } from "components"
import WidgetsWrapper from "components/WidgetsWrapper"
import "./index.less"
export default class ToolsBar extends React.Component {
state = {
visible: false,
}
componentDidMount() {
app.layout.tools_bar = this.interface
setTimeout(() => {
this.setState({
visible: true,
})
}, 10)
}
componentWillUnmount() {
delete app.layout.tools_bar
}
interface = {
toggleVisibility: (to) => {
this.setState({
visible: to ?? !this.state.visible,
})
},
}
render() {
return <Motion style={{
x: spring(this.state.visible ? 0 : 100),
width: spring(this.state.visible ? 100 : 0),
}}>
{({ x, width }) => {
return <div
id="tools_bar"
className={classnames(
"tools-bar",
{
visible: this.state.visible,
}
)}
style={{
width: `${width}%`,
transform: `translateX(${x}%)`,
}}
>
<FeaturedEventsAnnouncements />
<div className="card" id="trendings">
<div className="header">
<h2>
<Icons.TrendingUp />
<Translation>{(t) => t("Trendings")}</Translation>
</h2>
</div>
<HashtagTrendings />
</div>
<div className="card" id="onlineFriends">
<div className="header">
<h2>
<Icons.MdPeopleAlt />
<Translation>{(t) => t("Online Friends")}</Translation>
</h2>
</div>
<ConnectedFriends />
</div>
<WidgetsWrapper />
</div>
}}
</Motion>
}
}

View File

@ -0,0 +1,67 @@
.tools-bar {
position: relative;
display: flex;
flex-direction: column;
max-width: 20vw;
height: 100vh;
height: 100dvh;
border-left: 1px solid var(--border-color);
gap: 20px;
&.visible {
padding: 20px;
}
.card {
display: flex;
flex-direction: column;
background-color: var(--background-color-accent);
border-radius: 12px;
padding: 20px;
isolation: isolate;
h1,
h2 {
width: fit-content;
margin: 0;
}
&.header {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
margin-bottom: 10px;
z-index: 50;
-webkit-box-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
-moz-box-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
box-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
}
&.content {
position: relative;
transform: translateY(-30px);
padding-top: 35px;
z-index: 45;
}
}
}

View File

@ -4,25 +4,10 @@ import { Motion, spring } from "react-motion"
import useLayoutInterface from "hooks/useLayoutInterface" import useLayoutInterface from "hooks/useLayoutInterface"
import useDefaultVisibility from "hooks/useDefaultVisibility" import useDefaultVisibility from "hooks/useDefaultVisibility"
import useTopBar from "hooks/useTopBar"
import "./index.less" import "./index.less"
export const UseTopBar = (props) => {
app.layout.top_bar.render(
<React.Fragment>
{props.children}
</React.Fragment>,
props.options)
React.useEffect(() => {
return () => {
app.layout.top_bar.renderDefault()
}
}, [])
return null
}
export default (props) => { export default (props) => {
const [visible, setVisible] = useDefaultVisibility() const [visible, setVisible] = useDefaultVisibility()
const [shouldUseTopBarSpacer, setShouldUseTopBarSpacer] = React.useState(true) const [shouldUseTopBarSpacer, setShouldUseTopBarSpacer] = React.useState(true)
@ -45,7 +30,7 @@ export default (props) => {
setRender(null) setRender(null)
}, },
shouldUseTopBarSpacer: (to) => { shouldUseTopBarSpacer: (to) => {
app.layout.toogleTopBarSpacer(to) app.layout.toggleTopBarSpacer(to)
setShouldUseTopBarSpacer(to) setShouldUseTopBarSpacer(to)
} }
}) })
@ -69,27 +54,27 @@ export default (props) => {
React.useEffect(() => { React.useEffect(() => {
if (!shouldUseTopBarSpacer) { if (!shouldUseTopBarSpacer) {
app.layout.tooglePagePanelSpacer(true) app.layout.togglePagePanelSpacer(true)
} else { } else {
app.layout.tooglePagePanelSpacer(false) app.layout.togglePagePanelSpacer(false)
} }
}, [shouldUseTopBarSpacer]) }, [shouldUseTopBarSpacer])
React.useEffect(() => { React.useEffect(() => {
if (shouldUseTopBarSpacer) { if (shouldUseTopBarSpacer) {
if (visible) { if (visible) {
app.layout.toogleTopBarSpacer(true) app.layout.toggleTopBarSpacer(true)
} else { } else {
app.layout.toogleTopBarSpacer(false) app.layout.toggleTopBarSpacer(false)
} }
} else { } else {
if (visible) { if (visible) {
app.layout.tooglePagePanelSpacer(true) app.layout.togglePagePanelSpacer(true)
} else { } else {
app.layout.tooglePagePanelSpacer(false) app.layout.togglePagePanelSpacer(false)
} }
app.layout.toogleTopBarSpacer(false) app.layout.toggleTopBarSpacer(false)
} }
}, [visible]) }, [visible])

View File

@ -51,12 +51,12 @@ export default class Login extends React.Component {
} }
this.clearError() this.clearError()
this.toogleLoading(true) this.toggleLoading(true)
const loginProcess = await AuthModel.login(payload).catch((error) => { const loginProcess = await AuthModel.login(payload).catch((error) => {
console.error(error, error.response) console.error(error, error.response)
this.toogleLoading(false) this.toggleLoading(false)
this.onError(error.response.data.message) this.onError(error.response.data.message)
return false return false
@ -86,7 +86,7 @@ export default class Login extends React.Component {
app.eventBus.emit("app.createRegister") app.eventBus.emit("app.createRegister")
} }
toogleLoading = (to) => { toggleLoading = (to) => {
if (typeof to === "undefined") { if (typeof to === "undefined") {
to = !this.state.loading to = !this.state.loading
} }
@ -129,11 +129,11 @@ export default class Login extends React.Component {
const phase = phasesToSteps[this.state.phase] const phase = phasesToSteps[this.state.phase]
if (typeof stepsValidations[phase] === "function") { if (typeof stepsValidations[phase] === "function") {
this.toogleLoading(true) this.toggleLoading(true)
const result = await stepsValidations[phase](this.state.loginInputs) const result = await stepsValidations[phase](this.state.loginInputs)
this.toogleLoading(false) this.toggleLoading(false)
if (!result) { if (!result) {
this.formRef.current.setFields([ this.formRef.current.setFields([

View File

@ -10,13 +10,13 @@ const NavMenu = (props) => {
} }
return <div className="navmenu_wrapper"> return <div className="navmenu_wrapper">
{ <div className="card">
props.header && <div className="card header" id="navMenu"> {
{props.header} props.header && <div className="card_header">
</div> {props.header}
} </div>
}
<div className="card content" id="navMenu">
<antd.Menu <antd.Menu
mode="inline" mode="inline"
selectedKeys={[props.activeKey]} selectedKeys={[props.activeKey]}

View File

@ -2,6 +2,60 @@
.navmenu_wrapper { .navmenu_wrapper {
position: relative; position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
.card {
display: flex;
flex-direction: column;
background-color: var(--background-color-accent);
border-radius: 12px;
padding: 20px;
width: 100%;
h1,
h2 {
width: fit-content;
margin: 0;
}
.card_header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
view-transition-name: main-header;
h1 {
view-transition-name: main-header-text;
}
}
&.content {
position: relative;
transform: translateY(-30px);
padding-top: 35px;
z-index: 45;
}
}
&.card:last-child {
margin-bottom: 0;
}
} }
.__mobile__navmenu_wrapper { .__mobile__navmenu_wrapper {

View File

@ -4,7 +4,6 @@ import * as antd from "antd"
import { createIconRender } from "components/Icons" import { createIconRender } from "components/Icons"
import { UseTopBar } from "components/Layout/topBar"
import NavMenu from "./components/NavMenu" import NavMenu from "./components/NavMenu"
import "./index.less" import "./index.less"
@ -33,12 +32,16 @@ export class PagePanelWithNavMenu extends React.Component {
componentDidMount() { componentDidMount() {
if (app.isMobile) { if (app.isMobile) {
app.layout.top_bar.shouldUseTopBarSpacer(false) app.layout.top_bar.shouldUseTopBarSpacer(false)
} else {
app.layout.toggleCenteredContent(true)
} }
} }
componentWillUnmount() { componentWillUnmount() {
if (app.isMobile) { if (app.isMobile) {
app.layout.top_bar.shouldUseTopBarSpacer(true) app.layout.top_bar.shouldUseTopBarSpacer(true)
} else {
app.layout.toggleCenteredContent(false)
} }
} }

View File

@ -55,7 +55,7 @@
.pagePanels { .pagePanels {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 3fr;
grid-template-rows: 1fr; grid-template-rows: 1fr;
grid-column-gap: 20px; grid-column-gap: 20px;
grid-row-gap: 0px; grid-row-gap: 0px;
@ -104,57 +104,4 @@
} }
} }
} }
.card {
display: flex;
flex-direction: column;
background-color: var(--background-color-accent);
border-radius: 12px;
padding: 20px;
width: 20vw;
min-width: 250px;
isolation: isolate;
h1,
h2 {
width: fit-content;
margin: 0;
}
&.header {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
margin-bottom: 10px;
z-index: 50;
-webkit-box-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
-moz-box-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
box-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
}
&.content {
position: relative;
transform: translateY(-30px);
padding-top: 35px;
z-index: 45;
}
}
&.card:last-child {
margin-bottom: 0;
}
} }

View File

@ -37,7 +37,7 @@ export class BackgroundMediaPlayer extends React.Component {
events = { events = {
"sidebar.expanded": (to) => { "sidebar.expanded": (to) => {
if (!to) { if (!to) {
this.toogleExpand(false) this.toggleExpand(false)
} }
} }
} }
@ -46,7 +46,7 @@ export class BackgroundMediaPlayer extends React.Component {
app.cores.player.minimize() app.cores.player.minimize()
} }
toogleExpand = (to) => { toggleExpand = (to) => {
if (typeof to !== "boolean") { if (typeof to !== "boolean") {
to = !this.state.expanded to = !this.state.expanded
} }
@ -91,7 +91,7 @@ export class BackgroundMediaPlayer extends React.Component {
<div <div
className="background_media_player__row" className="background_media_player__row"
onClick={this.toogleExpand} onClick={this.toggleExpand}
> >
<div <div
id="sidebar_item_icon" id="sidebar_item_icon"
@ -166,7 +166,7 @@ export class BackgroundMediaPlayer extends React.Component {
type="ghost" type="ghost"
shape="circle" shape="circle"
icon={this.context.playbackStatus === "playing" ? <Icons.MdPause /> : <Icons.MdPlayArrow />} icon={this.context.playbackStatus === "playing" ? <Icons.MdPause /> : <Icons.MdPlayArrow />}
onClick={app.cores.player.playback.toogle} onClick={app.cores.player.playback.toggle}
/> />
<antd.Button <antd.Button

View File

@ -59,7 +59,7 @@ export default ({
type="primary" type="primary"
shape="circle" shape="circle"
icon={streamMode ? <Icons.MdStop /> : playbackStatus === "playing" ? <Icons.MdPause /> : <Icons.MdPlayArrow />} icon={streamMode ? <Icons.MdStop /> : playbackStatus === "playing" ? <Icons.MdPause /> : <Icons.MdPlayArrow />}
onClick={() => onClickActionsButton("toogle")} onClick={() => onClickActionsButton("toggle")}
className="playButton" className="playButton"
disabled={syncModeLocked} disabled={syncModeLocked}
> >

View File

@ -57,8 +57,8 @@ export class AudioPlayer extends React.Component {
app.cores.player.volume(value) app.cores.player.volume(value)
} }
toogleMute = () => { toggleMute = () => {
app.cores.player.toogleMute() app.cores.player.toggleMute()
} }
onClickPlayButton = () => { onClickPlayButton = () => {
@ -66,7 +66,7 @@ export class AudioPlayer extends React.Component {
return app.cores.player.playback.stop() return app.cores.player.playback.stop()
} }
app.cores.player.playback.toogle() app.cores.player.playback.toggle()
} }
onClickPreviousButton = () => { onClickPreviousButton = () => {
@ -168,10 +168,10 @@ export class AudioPlayer extends React.Component {
audioMuted={this.context.audioMuted} audioMuted={this.context.audioMuted}
audioVolume={this.context.audioVolume} audioVolume={this.context.audioVolume}
onVolumeUpdate={this.updateVolume} onVolumeUpdate={this.updateVolume}
onMuteUpdate={this.toogleMute} onMuteUpdate={this.toggleMute}
controls={{ controls={{
previous: this.onClickPreviousButton, previous: this.onClickPreviousButton,
toogle: this.onClickPlayButton, toggle: this.onClickPlayButton,
next: this.onClickNextButton, next: this.onClickNextButton,
}} }}
/> />

View File

@ -104,8 +104,8 @@ export default class PostCard extends React.PureComponent {
to = !this.state.open to = !this.state.open
} }
if (typeof this.props.events?.onToogleOpen === "function") { if (typeof this.props.events?.ontoggleOpen === "function") {
this.props.events?.onToogleOpen(to, this.props.data) this.props.events?.ontoggleOpen(to, this.props.data)
} }
this.setState({ this.setState({

View File

@ -41,7 +41,7 @@ export default class PostCreator extends React.Component {
}) })
} }
toogleUploaderVisibility = (to) => { toggleUploaderVisibility = (to) => {
to = to ?? !this.state.uploaderVisible to = to ?? !this.state.uploaderVisible
if (to === this.state.uploaderVisible) { if (to === this.state.uploaderVisible) {
@ -115,7 +115,7 @@ export default class PostCreator extends React.Component {
uploadFile = async (req) => { uploadFile = async (req) => {
// hide uploader // hide uploader
this.toogleUploaderVisibility(false) this.toggleUploaderVisibility(false)
const request = await app.cores.remoteStorage.uploadFile(req.file) const request = await app.cores.remoteStorage.uploadFile(req.file)
.catch(error => { .catch(error => {
@ -180,7 +180,7 @@ export default class PostCreator extends React.Component {
switch (change.file.status) { switch (change.file.status) {
case "uploading": { case "uploading": {
this.toogleUploaderVisibility(false) this.toggleUploaderVisibility(false)
this.setState({ this.setState({
pending: [...this.state.pending, change.file.uid] pending: [...this.state.pending, change.file.uid]
@ -377,11 +377,11 @@ export default class PostCreator extends React.Component {
console.log(event) console.log(event)
if (event.type === "dragenter") { if (event.type === "dragenter") {
this.toogleUploaderVisibility(true) this.toggleUploaderVisibility(true)
} else if (event.type === "dragleave") { } else if (event.type === "dragleave") {
// check if mouse is over the uploader or outside the creatorRef // check if mouse is over the uploader or outside the creatorRef
if (this.state.uploaderVisible && !this.creatorRef.current.contains(event.target)) { if (this.state.uploaderVisible && !this.creatorRef.current.contains(event.target)) {
this.toogleUploaderVisibility(false) this.toggleUploaderVisibility(false)
} }
} }
} }

View File

@ -342,7 +342,7 @@ export class PostsListsComponent extends React.Component {
} }
onLikePost = async (data) => { onLikePost = async (data) => {
let result = await PostModel.toogleLike({ post_id: data._id }).catch(() => { let result = await PostModel.toggleLike({ post_id: data._id }).catch(() => {
antd.message.error("Failed to like post") antd.message.error("Failed to like post")
return false return false
@ -352,7 +352,7 @@ export class PostsListsComponent extends React.Component {
} }
onSavePost = async (data) => { onSavePost = async (data) => {
let result = await PostModel.toogleSave({ post_id: data._id }).catch(() => { let result = await PostModel.toggleSave({ post_id: data._id }).catch(() => {
antd.message.error("Failed to save post") antd.message.error("Failed to save post")
return false return false
@ -376,7 +376,7 @@ export class PostsListsComponent extends React.Component {
}) })
} }
onToogleOpen = (to, data) => { ontoggleOpen = (to, data) => {
if (typeof this.props.onOpenPost === "function") { if (typeof this.props.onOpenPost === "function") {
this.props.onOpenPost(to, data) this.props.onOpenPost(to, data)
} }

View File

@ -111,10 +111,10 @@ class DefaultWindowRender extends React.Component {
this.setState({ position: this.getCenterPosition() }) this.setState({ position: this.getCenterPosition() })
} }
this.toogleVisibility(true) this.toggleVisibility(true)
} }
toogleVisibility = (to) => { toggleVisibility = (to) => {
this.setState({ visible: to ?? !this.state.visible }) this.setState({ visible: to ?? !this.state.visible })
} }

View File

@ -1,4 +1,6 @@
.shortable-list { .shortable-list {
position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -105,7 +105,7 @@ export default class SyncRoomCard extends React.Component {
app.cores.sync.music.leaveRoom() app.cores.sync.music.leaveRoom()
} }
toogleChatVisibility = (to) => { toggleChatVisibility = (to) => {
if (typeof to !== "boolean") { if (typeof to !== "boolean") {
to = !this.state.chatVisible to = !this.state.chatVisible
} }
@ -182,7 +182,7 @@ export default class SyncRoomCard extends React.Component {
<Button <Button
size="small" size="small"
icon={<Icons.MdChat />} icon={<Icons.MdChat />}
onClick={this.toogleChatVisibility} onClick={this.toggleChatVisibility}
/> />
</Badge> </Badge>
</div> </div>

View File

@ -80,7 +80,7 @@ export const WidgetBrowser = (props) => {
}) })
}} }}
onChangeVisible={(visible) => { onChangeVisible={(visible) => {
app.cores.widgets.toogleVisibility(widget._id, visible) app.cores.widgets.toggleVisibility(widget._id, visible)
}} }}
/> />
</React.Fragment> </React.Fragment>

View File

@ -207,6 +207,27 @@ export default class WidgetsWrapper extends React.Component {
widgetsRender: getWidgets(), widgetsRender: getWidgets(),
} }
events = {
"widgets:installed": () => {
this.loadWidgets()
},
"widgets:uninstalled": () => {
this.loadWidgets()
}
}
componentDidMount() {
for (const [eventName, eventHandler] of Object.entries(this.events)) {
app.eventBus.on(eventName, eventHandler)
}
}
componentWillUnmount() {
for (const [eventName, eventHandler] of Object.entries(this.events)) {
app.eventBus.off(eventName, eventHandler)
}
}
handleOnSortEnd = (widgetsRender) => { handleOnSortEnd = (widgetsRender) => {
this.setState({ this.setState({
widgetsRender widgetsRender
@ -215,6 +236,12 @@ export default class WidgetsWrapper extends React.Component {
app.cores.widgets.sort(widgetsRender) app.cores.widgets.sort(widgetsRender)
} }
loadWidgets = () => {
this.setState({
widgetsRender: getWidgets(),
})
}
render() { render() {
return <div className="widgets_wrapper"> return <div className="widgets_wrapper">
<SortableList <SortableList

View File

@ -1,7 +1,7 @@
.widgets_wrapper { .widgets_wrapper {
gap: 20px; position: relative;
width: 20vw; gap: 20px;
.widgets_wrapper_list { .widgets_wrapper_list {
display: flex; display: flex;

View File

@ -135,13 +135,13 @@ class MediaSession {
// External controls (iOS only) // External controls (iOS only)
case "music-controls-toggle-play-pause": { case "music-controls-toggle-play-pause": {
return app.cores.player.playback.toogle() return app.cores.player.playback.toggle()
} }
// Headset events (Android only) // Headset events (Android only)
// All media button events are listed below // All media button events are listed below
case "music-controls-media-button": { case "music-controls-media-button": {
return app.cores.player.playback.toogle() return app.cores.player.playback.toggle()
} }
case "music-controls-headset-unplugged": { case "music-controls-headset-unplugged": {
return app.cores.player.playback.pause() return app.cores.player.playback.pause()
@ -211,8 +211,8 @@ export default class Player extends Core {
audioContext: this.audioContext, audioContext: this.audioContext,
attachPlayerComponent: this.attachPlayerComponent.bind(this), attachPlayerComponent: this.attachPlayerComponent.bind(this),
detachPlayerComponent: this.detachPlayerComponent.bind(this), detachPlayerComponent: this.detachPlayerComponent.bind(this),
toogleMute: this.toogleMute.bind(this), toggleMute: this.toggleMute.bind(this),
minimize: this.toogleMinimize.bind(this), minimize: this.toggleMinimize.bind(this),
volume: this.volume.bind(this), volume: this.volume.bind(this),
start: this.start.bind(this), start: this.start.bind(this),
startPlaylist: this.startPlaylist.bind(this), startPlaylist: this.startPlaylist.bind(this),
@ -267,7 +267,7 @@ export default class Player extends Core {
return this.state.playbackMode return this.state.playbackMode
}.bind(this), }.bind(this),
toogle: function () { toggle: function () {
if (!this.currentAudioInstance) { if (!this.currentAudioInstance) {
console.error("No audio instance") console.error("No audio instance")
return null return null
@ -304,7 +304,7 @@ export default class Player extends Core {
duration: this.duration.bind(this), duration: this.duration.bind(this),
velocity: this.velocity.bind(this), velocity: this.velocity.bind(this),
close: this.close.bind(this), close: this.close.bind(this),
toogleSyncMode: this.toogleSyncMode.bind(this), toggleSyncMode: this.toggleSyncMode.bind(this),
currentState: this.currentState.bind(this), currentState: this.currentState.bind(this),
setSampleRate: this.setSampleRate.bind(this), setSampleRate: this.setSampleRate.bind(this),
} }
@ -1073,7 +1073,7 @@ export default class Player extends Core {
this.detachPlayerComponent() this.detachPlayerComponent()
} }
toogleMute(to) { toggleMute(to) {
if (app.isMobile) { if (app.isMobile) {
console.warn("Cannot mute on mobile") console.warn("Cannot mute on mobile")
return false return false
@ -1088,7 +1088,7 @@ export default class Player extends Core {
return this.state.audioMuted return this.state.audioMuted
} }
toogleMinimize(to) { toggleMinimize(to) {
this.state.minimized = to ?? !this.state.minimized this.state.minimized = to ?? !this.state.minimized
return this.state.minimized return this.state.minimized
@ -1202,7 +1202,7 @@ export default class Player extends Core {
return this.state.collapsed return this.state.collapsed
} }
toogleSyncMode(to, lock) { toggleSyncMode(to, lock) {
if (typeof to !== "boolean") { if (typeof to !== "boolean") {
console.warn("Sync mode must be a boolean") console.warn("Sync mode must be a boolean")
return false return false

View File

@ -154,7 +154,7 @@ export default class StyleCore extends Core {
} }
onEvents = { onEvents = {
"style.autoDarkModeToogle": (value) => { "style.autoDarkModetoggle": (value) => {
if (value === true) { if (value === true) {
return this.applyVariant(StyleCore.variant) return this.applyVariant(StyleCore.variant)
} }

View File

@ -65,7 +65,7 @@ class MusicSyncSubCore {
this.currentRoomData = data this.currentRoomData = data
// check if user is owner // check if user is owner
app.cores.player.toogleSyncMode(true, data.ownerUserId !== app.userData._id) app.cores.player.toggleSyncMode(true, data.ownerUserId !== app.userData._id)
this.startSendStateInterval() this.startSendStateInterval()
@ -78,7 +78,7 @@ class MusicSyncSubCore {
this.currentRoomData = null this.currentRoomData = null
app.cores.player.toogleSyncMode(false, false) app.cores.player.toggleSyncMode(false, false)
this.eventBus.emit("room:left", data) this.eventBus.emit("room:left", data)
}, },
@ -96,7 +96,7 @@ class MusicSyncSubCore {
type: "error", type: "error",
}) })
app.cores.player.toogleSyncMode(false, false) app.cores.player.toggleSyncMode(false, false)
}, },
"room:user:joined": (data) => { "room:user:joined": (data) => {
@ -114,7 +114,7 @@ class MusicSyncSubCore {
"room:owner:changed": (data) => { "room:owner:changed": (data) => {
const isSelf = data.ownerUserId === app.userData._id const isSelf = data.ownerUserId === app.userData._id
app.cores.player.toogleSyncMode(true, !isSelf) app.cores.player.toggleSyncMode(true, !isSelf)
app.cores.player.playback.stop() app.cores.player.playback.stop()
@ -173,7 +173,7 @@ class MusicSyncSubCore {
this.currentRoomData = null this.currentRoomData = null
app.cores.player.toogleSyncMode(false, false) app.cores.player.toggleSyncMode(false, false)
app.notification.new({ app.notification.new({
title: "Kicked", title: "Kicked",

View File

@ -14,7 +14,7 @@ export default class WidgetsCore extends Core {
isInstalled: this.isInstalled.bind(this), isInstalled: this.isInstalled.bind(this),
install: this.install.bind(this), install: this.install.bind(this),
uninstall: this.uninstall.bind(this), uninstall: this.uninstall.bind(this),
toogleVisibility: this.toogleVisibility.bind(this), toggleVisibility: this.toggleVisibility.bind(this),
isVisible: this.isVisible.bind(this), isVisible: this.isVisible.bind(this),
sort: this.sort.bind(this), sort: this.sort.bind(this),
} }
@ -154,7 +154,7 @@ export default class WidgetsCore extends Core {
return true return true
} }
toogleVisibility(widget_id, to) { toggleVisibility(widget_id, to) {
if (!widget_id || typeof widget_id !== "string") { if (!widget_id || typeof widget_id !== "string") {
throw new Error("Widget id must be a string.") throw new Error("Widget id must be a string.")
} }

View File

@ -2,10 +2,10 @@ import React from "react"
export default () => { export default () => {
React.useEffect(() => { React.useEffect(() => {
app.layout.toogleCenteredContent(true) app.layout.toggleCenteredContent(true)
return () => { return () => {
app.layout.toogleCenteredContent(false) app.layout.toggleCenteredContent(false)
} }
}, []) }, [])
} }

View File

@ -0,0 +1,17 @@
import React from "react"
export default (props) => {
app.layout.top_bar.render(
<React.Fragment>
{props.children}
</React.Fragment>,
props.options)
React.useEffect(() => {
return () => {
app.layout.top_bar.renderDefault()
}
}, [])
return null
}

View File

@ -45,8 +45,12 @@ export default class Layout extends React.PureComponent {
transitionLayer.classList.remove("fade-opacity-leave") transitionLayer.classList.remove("fade-opacity-leave")
}, },
"router.navigate": (path, options) => { "router.navigate": async (path, options) => {
this.makePageTransition(path, options) this.progressBar.start()
await this.makePageTransition(options)
this.progressBar.done()
}, },
} }
@ -57,11 +61,11 @@ export default class Layout extends React.PureComponent {
}) })
if (app.isMobile) { if (app.isMobile) {
this.layoutInterface.toogleMobileStyle(true) this.layoutInterface.toggleMobileStyle(true)
} }
if (app.cores.settings.get("reduceAnimations")) { if (app.cores.settings.get("reduceAnimations")) {
this.layoutInterface.toogleRootContainerClassname("reduce-animations", true) this.layoutInterface.toggleRootContainerClassname("reduce-animations", true)
} }
} }
@ -76,26 +80,31 @@ export default class Layout extends React.PureComponent {
this.setState({ renderError: { info, stack } }) this.setState({ renderError: { info, stack } })
} }
makePageTransition(path, options = {}) { async makePageTransition(options = {}) {
this.progressBar.start() if (document.startViewTransition) {
return document.startViewTransition(async () => {
await new Promise((resolve) => {
setTimeout(resolve, options.state?.transitionDelay ?? 250)
})
})
}
const content_layout = document.getElementById("content_layout") const content_layout = document.getElementById("content_layout")
if (!content_layout) { if (!content_layout) {
console.warn("content_layout not found, no animation will be played") console.warn("content_layout not found, no animation will be played")
this.progressBar.done()
return false return false
} }
content_layout.classList.add("fade-transverse-leave") content_layout.classList.add("fade-transverse-leave")
setTimeout(() => { return await new Promise((resolve) => {
this.progressBar.done() setTimeout(() => {
resolve()
content_layout.classList.remove("fade-transverse-leave") content_layout.classList.remove("fade-transverse-leave")
}, options.state?.transitionDelay ?? 250) }, options.state?.transitionDelay ?? 250)
})
} }
layoutInterface = window.app.layout = { layoutInterface = window.app.layout = {
@ -110,22 +119,22 @@ export default class Layout extends React.PureComponent {
layoutType: layout, layoutType: layout,
}) })
}, },
toogleCenteredContent: (to) => { toggleCenteredContent: (to) => {
return this.layoutInterface.toogleRootContainerClassname("centered-content", to) return this.layoutInterface.toggleRootContainerClassname("centered-content", to)
}, },
toogleMobileStyle: (to) => { toggleMobileStyle: (to) => {
return this.layoutInterface.toogleRootContainerClassname("mobile", to) return this.layoutInterface.toggleRootContainerClassname("mobile", to)
}, },
toogleReducedAnimations: (to) => { toggleReducedAnimations: (to) => {
return this.layoutInterface.toogleRootContainerClassname("reduce-animations", to) return this.layoutInterface.toggleRootContainerClassname("reduce-animations", to)
}, },
toogleTopBarSpacer: (to) => { toggleTopBarSpacer: (to) => {
return this.layoutInterface.toogleRootContainerClassname("top-bar-spacer", to) return this.layoutInterface.toggleRootContainerClassname("top-bar-spacer", to)
}, },
tooglePagePanelSpacer: (to) => { togglePagePanelSpacer: (to) => {
return this.layoutInterface.toogleRootContainerClassname("page-panel-spacer", to) return this.layoutInterface.toggleRootContainerClassname("page-panel-spacer", to)
}, },
toogleRootContainerClassname: (classname, to) => { toggleRootContainerClassname: (classname, to) => {
const root = document.getElementById("root") const root = document.getElementById("root")
if (!root) { if (!root) {

View File

@ -80,7 +80,7 @@ export default class FloatingStack extends React.Component {
return true return true
}, },
toogleGlobalVisibility: (to) => { toggleGlobalVisibility: (to) => {
if (typeof to !== "boolean") { if (typeof to !== "boolean") {
to = !this.state.globalVisibility to = !this.state.globalVisibility
} }

View File

@ -2,7 +2,7 @@ import React from "react"
import classnames from "classnames" import classnames from "classnames"
import { Layout } from "antd" import { Layout } from "antd"
import { Sidebar, Drawer, Sidedrawer, Modal, BottomBar, TopBar } from "components/Layout" import { Sidebar, Drawer, Sidedrawer, Modal, BottomBar, TopBar, ToolsBar } from "components/Layout"
import BackgroundDecorator from "components/BackgroundDecorator" import BackgroundDecorator from "components/BackgroundDecorator"
@ -40,6 +40,7 @@ const DesktopLayout = (props) => {
React.cloneElement(props.children, props) React.cloneElement(props.children, props)
} }
</Layout.Content> </Layout.Content>
<ToolsBar />
</Layout> </Layout>
</> </>
} }
@ -64,7 +65,6 @@ const MobileLayout = (props) => {
</Layout.Content> </Layout.Content>
<BottomBar /> <BottomBar />
<Sidedrawer />
<Drawer /> <Drawer />
</Layout> </Layout>
} }

View File

@ -144,7 +144,7 @@ export default class Account extends React.Component {
} }
onClickFollow = async () => { onClickFollow = async () => {
const result = await FollowsModel.toogleFollow({ const result = await FollowsModel.toggleFollow({
username: this.state.requestedUser, username: this.state.requestedUser,
}).catch((error) => { }).catch((error) => {
console.error(error) console.error(error)
@ -159,7 +159,7 @@ export default class Account extends React.Component {
}) })
} }
toogleCoverExpanded = async (to) => { toggleCoverExpanded = async (to) => {
this.setState({ this.setState({
coverExpanded: to ?? !this.state.coverExpanded, coverExpanded: to ?? !this.state.coverExpanded,
}) })
@ -217,7 +217,7 @@ export default class Account extends React.Component {
})} })}
ref={this.coverComponent} ref={this.coverComponent}
style={{ backgroundImage: `url("${user.cover}")` }} style={{ backgroundImage: `url("${user.cover}")` }}
onClick={() => this.toogleCoverExpanded()} onClick={() => this.toggleCoverExpanded()}
id="profile-cover" id="profile-cover"
/> />
} }

View File

@ -27,41 +27,9 @@ export default class Home extends React.Component {
</antd.Button> </antd.Button>
</> </>
const extraPanel = {
children: <>
<div className="card" id="trendings">
<div className="header">
<h2>
<Icons.TrendingUp />
<Translation>{(t) => t("Trendings")}</Translation>
</h2>
</div>
<HashtagTrendings />
</div>
<div className="card" id="onlineFriends">
<div className="header">
<h2>
<Icons.MdPeopleAlt />
<Translation>{(t) => t("Online Friends")}</Translation>
</h2>
</div>
<ConnectedFriends />
</div>
<WidgetsWrapper />
</>
}
return <PagePanelWithNavMenu return <PagePanelWithNavMenu
tabs={Tabs} tabs={Tabs}
navMenuHeader={navMenuHeader} navMenuHeader={navMenuHeader}
extraMenuItems={[
<FeaturedEventsAnnouncements />
]}
extraPanel={extraPanel}
primaryPanelClassName="full" primaryPanelClassName="full"
useSetQueryType useSetQueryType
transition transition

View File

@ -128,7 +128,7 @@ class PlayerController extends React.Component {
app.cores.player.playback.next() app.cores.player.playback.next()
} }
onClickTooglePlayButton = () => { onClicktogglePlayButton = () => {
if (this.state?.playbackStatus === "playing") { if (this.state?.playbackStatus === "playing") {
app.cores.player.playback.pause() app.cores.player.playback.pause()
} else { } else {
@ -140,8 +140,8 @@ class PlayerController extends React.Component {
app.cores.player.volume(value) app.cores.player.volume(value)
} }
toogleMute = () => { toggleMute = () => {
app.cores.player.toogleMute() app.cores.player.toggleMute()
} }
componentDidMount() { componentDidMount() {
@ -250,7 +250,7 @@ class PlayerController extends React.Component {
className="player_controller_controls" className="player_controller_controls"
controls={{ controls={{
previous: this.onClickPreviousButton, previous: this.onClickPreviousButton,
toogle: this.onClickTooglePlayButton, toggle: this.onClicktogglePlayButton,
next: this.onClickNextButton, next: this.onClickNextButton,
}} }}
syncModeLocked={this.state.syncModeLocked} syncModeLocked={this.state.syncModeLocked}
@ -259,7 +259,7 @@ class PlayerController extends React.Component {
audioVolume={this.state.audioVolume} audioVolume={this.state.audioVolume}
audioMuted={this.state.audioMuted} audioMuted={this.state.audioMuted}
onVolumeUpdate={this.updateVolume} onVolumeUpdate={this.updateVolume}
onMuteUpdate={this.toogleMute} onMuteUpdate={this.toggleMute}
/> />
</div> </div>
@ -341,7 +341,7 @@ export default class SyncLyrics extends React.Component {
} }
} }
toogleClassName = (className, to) => { toggleClassName = (className, to) => {
if (typeof to === "undefined") { if (typeof to === "undefined") {
to = !this.state.classnames[className] to = !this.state.classnames[className]
} }
@ -379,16 +379,16 @@ export default class SyncLyrics extends React.Component {
} }
} }
toogleVideoCanvas = (to) => { toggleVideoCanvas = (to) => {
return this.toogleClassName("video-canvas-enabled", to) return this.toggleClassName("video-canvas-enabled", to)
} }
toogleCenteredControllerMode = (to) => { toggleCenteredControllerMode = (to) => {
return this.toogleClassName("centered-player", to) return this.toggleClassName("centered-player", to)
} }
toogleCinematicMode = (to) => { toggleCinematicMode = (to) => {
return this.toogleClassName("cinematic-mode", to) return this.toggleClassName("cinematic-mode", to)
} }
isCurrentLine = (line) => { isCurrentLine = (line) => {
@ -461,27 +461,27 @@ export default class SyncLyrics extends React.Component {
//app.message.info("Video canvas loaded") //app.message.info("Video canvas loaded")
console.log(`[SyncLyrics] Video canvas loaded`) console.log(`[SyncLyrics] Video canvas loaded`)
this.toogleVideoCanvas(true) this.toggleVideoCanvas(true)
} else { } else {
//app.message.info("No video canvas available for this song") //app.message.info("No video canvas available for this song")
console.log(`[SyncLyrics] No video canvas available for this song`) console.log(`[SyncLyrics] No video canvas available for this song`)
this.toogleVideoCanvas(false) this.toggleVideoCanvas(false)
} }
// if has no lyrics or are unsynced, toogle cinematic mode off and center controller // if has no lyrics or are unsynced, toggle cinematic mode off and center controller
if (data.lines.length === 0 || data.syncType !== "LINE_SYNCED") { if (data.lines.length === 0 || data.syncType !== "LINE_SYNCED") {
//app.message.info("No lyrics available for this song") //app.message.info("No lyrics available for this song")
console.log(`[SyncLyrics] No lyrics available for this song, sync type [${data.syncType}]`) console.log(`[SyncLyrics] No lyrics available for this song, sync type [${data.syncType}]`)
this.toogleCinematicMode(false) this.toggleCinematicMode(false)
this.toogleCenteredControllerMode(true) this.toggleCenteredControllerMode(true)
} else { } else {
//app.message.info("Lyrics loaded, starting sync...") //app.message.info("Lyrics loaded, starting sync...")
console.log(`[SyncLyrics] Starting sync with type [${data.syncType}]`) console.log(`[SyncLyrics] Starting sync with type [${data.syncType}]`)
this.toogleCenteredControllerMode(false) this.toggleCenteredControllerMode(false)
this.startLyricsSync() this.startLyricsSync()
} }
@ -519,7 +519,7 @@ export default class SyncLyrics extends React.Component {
if (!hasStartedFirst) { if (!hasStartedFirst) {
if (this.state.canvas_url) { if (this.state.canvas_url) {
this.toogleCinematicMode(true) this.toggleCinematicMode(true)
} }
return false return false
@ -567,15 +567,15 @@ export default class SyncLyrics extends React.Component {
if (line.words === "♪" || line.words === "♫" || line.words === " " || line.words === "") { if (line.words === "♪" || line.words === "♫" || line.words === " " || line.words === "") {
//console.log(`[SyncLyrics] Toogling cinematic mode on because line is empty`) //console.log(`[SyncLyrics] Toogling cinematic mode on because line is empty`)
this.toogleCinematicMode(true) this.toggleCinematicMode(true)
} else { } else {
//console.log(`[SyncLyrics] Toogling cinematic mode off because line is not empty`) //console.log(`[SyncLyrics] Toogling cinematic mode off because line is not empty`)
this.toogleCinematicMode(false) this.toggleCinematicMode(false)
} }
} else { } else {
if (this.state.classnames["cinematic-mode"] === true) { if (this.state.classnames["cinematic-mode"] === true) {
this.toogleCinematicMode(false) this.toggleCinematicMode(false)
} }
} }
} }
@ -602,11 +602,7 @@ export default class SyncLyrics extends React.Component {
}) })
if (app.layout.sidebar) { if (app.layout.sidebar) {
app.layout.sidebar.toggleVisibility(false) app.controls.toogleUIVisibility(false)
}
if (app.layout.floatingStack) {
app.layout.floatingStack.toogleGlobalVisibility(false)
} }
app.cores.style.compactMode(true) app.cores.style.compactMode(true)
@ -625,9 +621,9 @@ export default class SyncLyrics extends React.Component {
}) })
window._hacks = { window._hacks = {
toogleVideoCanvas: this.toogleVideoCanvas, toggleVideoCanvas: this.toggleVideoCanvas,
toogleCinematicMode: this.toogleCinematicMode, toggleCinematicMode: this.toggleCinematicMode,
toogleCenteredControllerMode: this.toogleCenteredControllerMode, toggleCenteredControllerMode: this.toggleCenteredControllerMode,
} }
await this.loadLyrics() await this.loadLyrics()
@ -647,11 +643,7 @@ export default class SyncLyrics extends React.Component {
delete window._hacks delete window._hacks
if (app.layout.sidebar) { if (app.layout.sidebar) {
app.layout.sidebar.toggleVisibility(true) app.controls.toogleUIVisibility(true)
}
if (app.layout.floatingStack) {
app.layout.floatingStack.toogleGlobalVisibility(true)
} }
app.cores.style.compactMode(false) app.cores.style.compactMode(false)

View File

@ -193,7 +193,7 @@ export default class SettingItemComponent extends React.PureComponent {
return {} return {}
} }
toogleLoading = (to) => { toggleLoading = (to) => {
if (typeof to === "undefined") { if (typeof to === "undefined") {
to = !this.state.loading to = !this.state.loading
} }
@ -206,7 +206,7 @@ export default class SettingItemComponent extends React.PureComponent {
initialize = async () => { initialize = async () => {
this.perf.start(`init tooks`) this.perf.start(`init tooks`)
this.toogleLoading(true) this.toggleLoading(true)
if (this.props.setting.storaged) { if (this.props.setting.storaged) {
this.perf.start(`get value from storaged`) this.perf.start(`get value from storaged`)
@ -221,13 +221,13 @@ export default class SettingItemComponent extends React.PureComponent {
if (typeof this.props.setting.defaultValue === "function") { if (typeof this.props.setting.defaultValue === "function") {
this.perf.start(`execute default value fn`) this.perf.start(`execute default value fn`)
this.toogleLoading(true) this.toggleLoading(true)
this.setState({ this.setState({
value: await this.props.setting.defaultValue(this.props.ctx) value: await this.props.setting.defaultValue(this.props.ctx)
}) })
this.toogleLoading(false) this.toggleLoading(false)
this.perf.end(`execute default value fn`) this.perf.end(`execute default value fn`)
} }
@ -285,7 +285,7 @@ export default class SettingItemComponent extends React.PureComponent {
this.perf.end(`Reinitializing setting [${this.props.setting.id}]`) this.perf.end(`Reinitializing setting [${this.props.setting.id}]`)
} }
this.toogleLoading(false) this.toggleLoading(false)
this.perf.end(`init tooks`) this.perf.end(`init tooks`)

View File

@ -5,7 +5,7 @@ import { Translation } from "react-i18next"
import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey" import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey"
import { Icons, createIconRender } from "components/Icons" import { Icons, createIconRender } from "components/Icons"
import { UseTopBar } from "components/Layout/topBar" import UseTopBar from "hooks/useTopBar"
import { import {
composedSettingsByGroups as settingsGroups, composedSettingsByGroups as settingsGroups,

View File

@ -6,20 +6,20 @@ import "./index.less"
export default (props) => { export default (props) => {
const [streamingKeyVisibility, setStreamingKeyVisibility] = React.useState(false) const [streamingKeyVisibility, setStreamingKeyVisibility] = React.useState(false)
const toogleVisibility = (to) => { const toggleVisibility = (to) => {
setStreamingKeyVisibility(to ?? !streamingKeyVisibility) setStreamingKeyVisibility(to ?? !streamingKeyVisibility)
} }
return <div className="streamingKeyString"> return <div className="streamingKeyString">
{streamingKeyVisibility ? {streamingKeyVisibility ?
<> <>
<Icons.EyeOff onClick={() => toogleVisibility()} /> <Icons.EyeOff onClick={() => toggleVisibility()} />
<code> <code>
{props.streamingKey ?? "No streaming key available"} {props.streamingKey ?? "No streaming key available"}
</code> </code>
</> : </> :
<div <div
onClick={() => toogleVisibility()} onClick={() => toggleVisibility()}
> >
<Icons.Eye /> <Icons.Eye />
Click to show key Click to show key

View File

@ -121,9 +121,9 @@ function generatePageElementWrapper(route, element, bindProps) {
} }
if (routeDeclaration.centeredContent) { if (routeDeclaration.centeredContent) {
app.layout.toogleCenteredContent(true) app.layout.toggleCenteredContent(true)
} else { } else {
app.layout.toogleCenteredContent(false) app.layout.toggleCenteredContent(false)
} }
} }