update settigns

This commit is contained in:
SrGooglo 2023-02-24 14:26:19 +00:00
parent 108f99c897
commit 34f4623a36
19 changed files with 957 additions and 246 deletions

View File

@ -0,0 +1,109 @@
import React from "react"
import * as antd from "antd"
import moment from "moment"
import { Icons } from "components/Icons"
import config from "config"
import "./index.less"
const Footer = (props) => {
const isDevMode = window.__evite?.env?.NODE_ENV !== "production"
return <div className="footer">
<div>
<div>{config.app?.siteName}</div>
<div>
<antd.Tag>
<Icons.Tag />v{window.app.version}
</antd.Tag>
</div>
<div>
<antd.Tag color={isDevMode ? "magenta" : "green"}>
{isDevMode ? <Icons.Triangle /> : <Icons.Box />}
{isDevMode ? "development" : "stable"}
</antd.Tag>
</div>
</div>
</div>
}
export default {
id: "about",
icon: "Info",
label: "About",
group: "bottom",
render: () => {
const isProduction = import.meta.env.PROD
const [serverManifest, setServerManifest] = React.useState(null)
const checkServerVersion = async () => {
const serverManifest = await app.cores.api.customRequest("main")
setServerManifest(serverManifest.data)
}
React.useEffect(() => {
checkServerVersion()
}, [])
return <div className="about_app">
<div className="header">
<div className="branding">
<div className="logo">
<img
src={config.logo.alt}
alt="Logo"
/>
</div>
<div className="texts">
<h2>{config.app.siteName}</h2>
<span>{config.author}</span>
<span> Licensed with {config.package?.license ?? "unlicensed"} </span>
</div>
</div>
<div className="versions">
<antd.Tag><Icons.Tag />v{window.app.version ?? "experimental"}</antd.Tag>
<antd.Tag color={isProduction ? "green" : "magenta"}>
{isProduction ? <Icons.CheckCircle /> : <Icons.Triangle />}
{String(import.meta.env.MODE)}
</antd.Tag>
</div>
</div>
<div className="group">
<h3><Icons.Server />Server info</h3>
<div className="field">
Powered by Linebridge
<div className="value">
<antd.Tag>v{serverManifest?.LINEBRIDGE_SERVER_VERSION ?? "Unknown"}</antd.Tag>
</div>
</div>
<div className="field">
<span>
<Icons.Globe /> Origin address
</span>
<div className="value">
{app.cores.api?.namespaces.main.origin ?? "Unknown"}
</div>
</div>
<div className="field">
<span>
<Icons.Clock /> Server Time
</span>
<div className="value">
{moment(serverManifest?.requestTime).format("YYYY-MM-DD HH:mm:ss")}
</div>
</div>
</div>
<Footer />
</div>
}
}

View File

@ -0,0 +1,93 @@
.about_app {
display: flex;
flex-direction: column;
width: 100%;
padding: 0 20px 20px 20px;
background-color: var(--background-color-accent);
color: var(--text-color);
border-radius: 12px;
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-self: center;
width: 100%;
margin-bottom: 20px;
padding: 20px 0;
border-bottom: 1px solid var(--border-color);
.branding {
display: flex;
flex-direction: row;
align-items: center;
.logo {
width: 60px;
height: 100%;
margin-right: 20px;
img {
width: 100%;
height: 100%;
}
}
.texts {
display: flex;
flex-direction: column;
}
h1,
h2,
h3 {
height: fit-content;
line-height: 24px;
}
span {
height: fit-content;
color: var(--background-color-contrast);
font-size: 10px;
}
}
}
.group {
display: inline-flex;
flex-direction: column;
justify-content: center;
margin-bottom: 10px;
.field {
display: inline-flex;
flex-direction: column;
margin-bottom: 10px;
font-size: 0.9rem;
.value {
font-size: 0.8rem;
display: inline-flex;
flex-direction: row;
margin-left: 10px;
}
}
}
}

View File

@ -1,173 +1,477 @@
import React from "react" import React from "react"
import loadable from "@loadable/component" import loadable from "@loadable/component"
import { Modal } from "antd"
import UploadButton from "../components/uploadButton"
import "./index.less" import "./index.less"
export default { export default {
id: "apparence",
icon: "Eye", icon: "Eye",
label: "Apparence", label: "Apparence",
group: "app",
settings: [ settings: [
{ {
"id": "compactWidth", id: "sidebar.floating",
"title": "Compact Width", title: "Floating Sidebar",
"description": "Sets the width of the app to a compact width to facilitate the vision of components.", description: "Make the sidebar float over layout content.",
"component": "Switch", component: "Switch",
"icon": "MdCompress", icon: "MdOutlineLastPage",
"group": "layout", group: "layout",
"experimental": true, emitEvent: "app.softReload",
"storaged": true storaged: true,
}, },
{ {
"id": "sidebar.floating", id: "style.reduceAnimations",
"title": "Floating Sidebar", group: "animations",
"description": "Make the sidebar float over layout content.", component: "Switch",
"component": "Switch", icon: "MdOutlineSlowMotionVideo",
"icon": "MdOutlineLastPage", title: "Reduce animations",
"group": "layout", experimental: true,
"emitEvent": "app.softReload", storaged: true,
"storaged": true
}, },
{ {
"id": "reduceAnimations", id: "style.pageTransitionDuration",
"storaged": true, group: "animations",
"group": "animations", component: "Slider",
"component": "Switch", icon: "MdOutlineSpeed",
"icon": "MdOutlineSlowMotionVideo", title: "Page transition duration",
"title": "Reduce animation", description: "Change the duration of the page transition animation.",
"experimental": true props: {
},
{
"id": "pageTransitionDuration",
"storaged": true,
"group": "animations",
"component": "Slider",
"icon": "MdOutlineSpeed",
"title": "Page transition duration",
"description": "Change the duration of the page transition animation.",
"props": {
min: 0, min: 0,
max: 1000, max: 1000,
step: 50, step: 50,
marks: {
[app.cores.style.defaultVar("page-transition-duration").replace("ms", "")]: " ",
},
tooltip: { tooltip: {
formatter: (value) => `${value / 1000}s` formatter: (value) => `${value / 1000}s`
} }
}, },
"emitEvent": "modifyTheme", defaultValue: () => {
"emissionValueUpdate": (value) => { const value = app.cores.style.getValue("page-transition-duration")
return {
return value ? Number(value.replace("ms", "")) : 250
},
onUpdate: (value) => {
app.cores.style.modify({
"page-transition-duration": `${value}ms` "page-transition-duration": `${value}ms`
} })
}, },
storaged: true,
}, },
{ {
"id": "auto_darkMode", id: "style.auto_darkMode",
"experimental": true, group: "aspect",
"storaged": true, component: "Switch",
"group": "aspect", icon: "Moon",
"component": "Switch", title: "Sync with system",
"icon": "Moon", description: "Automatically switch to dark mode based on your system preference.",
"title": "Auto dark mode", emitEvent: "style.autoDarkModeToogle",
"emitEvent": "style.autoDarkModeToogle", storaged: true,
}, },
{ {
"experimental": true, id: "style.darkMode",
"dependsOn": { group: "aspect",
"auto_darkMode": false component: "Switch",
icon: "Moon",
title: "Dark mode",
description: "Change the theme variant of the application to dark.",
dependsOn: {
"style.auto_darkMode": false
}, },
"id": "darkMode", defaultValue: () => {
"storaged": true, return app.cores.style.currentVariant === "dark"
"group": "aspect",
"component": "Switch",
"icon": "Moon",
"title": "Dark mode",
"emitEvent": "theme.applyVariant",
"emissionValueUpdate": (value) => {
return value ? "dark" : "light"
}, },
onUpdate: (value) => {
app.cores.style.modify({
themeVariant: value ? "dark" : "light"
})
return value
},
storaged: true
}, },
{ {
"id": "primaryColor", id: "style.compactMode",
"storaged": true, group: "aspect",
"group": "aspect", component: "Switch",
"component": "SliderColorPicker", icon: "MdOutlineViewCompact",
"title": "Primary color", title: "Compact mode",
"description": "Change primary color of the application.", description: "Reduce the size of the application elements.",
"emitEvent": "modifyTheme", defaultValue: () => {
"reloadValueOnUpdateEvent": "resetTheme", return app.cores.style.getValue("compact-mode")
"emissionValueUpdate": (value) => { },
return { onUpdate: (value) => {
primaryColor: value app.cores.style.modify({
} "compact-mode": value
} })
return value
},
storaged: true
}, },
{ {
"id": "backgroundImage", id: "style.uiFont",
"storaged": true, group: "aspect",
"group": "aspect", component: "Select",
"title": "Background image", icon: "MdOutlineFontDownload",
"description": "Change background image of the application. You can use a local image or a remote image (URL).", title: "UI font",
"component": loadable(() => import("../components/ImageUploader")), description: "Change the font of the application.",
"props": { props: {
"noPreview": true, style: {
width: "100%"
},
options: [
{
label: "Varela Round (Default)",
value: "'Varela Round', sans-serif"
},
{
label: "Inter",
value: "'Inter', sans-serif"
},
]
}, },
"emitEvent": "modifyTheme", defaultValue: () => {
"emissionValueUpdate": (value) => { return app.cores.style.getValue("fontFamily")
return { },
onUpdate: (value) => {
app.cores.style.modify({
"fontFamily": value
})
return value
},
storaged: true
},
{
id: "style.colorPrimary",
group: "aspect",
component: "SliderColorPicker",
title: "Primary color",
description: "Change primary color of the application.",
defaultValue: () => {
return app.cores.style.getValue("colorPrimary")
},
onUpdate: (value) => {
app.cores.style.modify({
"colorPrimary": value
})
},
storaged: false,
},
{
id: "style.parallaxBackground",
group: "aspect",
component: "Switch",
icon: "MdOutline3DRotation",
title: "Parallax background",
description: "Create a parallax effect on the background.",
storaged: true,
},
{
id: "style.backgroundImage",
group: "aspect",
icon: "MdOutlineImage",
title: "Background image",
description: "Change background image of the application. You can use a local image or a remote image (URL).",
component: loadable(() => import("../components/urlInput")),
props: {
noPreview: true,
},
extraActions: [
{
id: "delete",
icon: "Delete",
title: "Remove",
onClick: (ctx) => {
return ctx.dispatchUpdate("")
}
},
UploadButton
],
defaultValue: () => {
const value = app.cores.style.getValue("backgroundImage")
console.log(value)
return value ? value.replace(/url\(|\)/g, "") : ""
},
onUpdate: (value) => {
app.cores.style.modify({
backgroundImage: `url(${value})` backgroundImage: `url(${value})`
} })
}, },
storaged: false,
}, },
{ {
"id": "backgroundBlur", id: "style.backgroundPattern",
"storaged": true, group: "aspect",
"group": "aspect", icon: "MdGrid4X4",
"component": "Slider", component: loadable(() => import("../components/backgroundSelector")),
"icon": "Eye", title: "Background pattern",
"title": "Background blur", description: "Change background pattern of the application.",
"description": "Create a blur effect on the background.", extraActions: [
"props": { {
id: "remove",
icon: "Delete",
title: "Remove",
onClick: () => {
app.cores.style.modify({
backgroundSVG: "unset"
})
}
}
],
storaged: false,
},
{
id: "style.backgroundBlur",
group: "aspect",
component: "Slider",
icon: "MdBlurOn",
title: "Background blur",
description: "Create a blur effect on the background.",
props: {
min: 0, min: 0,
max: 50, max: 50,
step: 5 step: 1
}, },
"emitEvent": "modifyTheme", defaultValue: () => {
"emissionValueUpdate": (value) => { const value = app.cores.style.getValue("backgroundBlur")
return {
backgroundBlur: `${value}px`, return value ? parseInt(value.replace("px", "")) : 0
}
}, },
onUpdate: (value) => {
app.cores.style.modify({
backgroundBlur: `${value}px`
})
},
storaged: false,
}, },
{ {
"id": "backgroundColorTransparency", id: "style.backgroundColorTransparency",
"storaged": true, group: "aspect",
"group": "aspect", component: "Slider",
"component": "Slider", icon: "Eye",
"icon": "Eye", title: "Background color transparency",
"title": "Background color transparency", description: "Adjust the transparency of the background color.",
"description": "Adjust the transparency of the background color.", props: {
"props": {
min: 0, min: 0,
max: 1, max: 1,
step: 0.1 step: 0.1
}, },
"emitEvent": "modifyTheme", defaultValue: () => {
"emissionValueUpdate": (value) => { const value = app.cores.style.getValue("backgroundColorTransparency")
return {
backgroundColorTransparency: value, return value ? parseFloat(value) : 1
}
}, },
onUpdate: (value) => {
app.cores.style.modify({
backgroundColorTransparency: value
})
},
storaged: false
}, },
{ {
"id": "resetTheme", id: "style.backgroundSize",
"storaged": true, group: "aspect",
"group": "aspect", component: "Select",
"component": "Button", icon: "MdOutlineImageAspectRatio",
"title": "Reset theme", title: "Background size",
"props": { description: "Adjust the size of the background image.",
"children": "Default Theme" props: {
style: {
width: "100%"
},
options: [
{
label: "Cover",
value: "cover"
},
{
label: "Contain",
value: "contain"
},
{
label: "Auto",
value: "auto"
},
{
label: "50%",
value: "50%"
},
{
label: "100%",
value: "100%"
},
{
label: "150%",
value: "150%"
},
]
}, },
"emitEvent": "resetTheme", defaultValue: () => {
"noUpdate": true, return app.cores.style.getValue("backgroundSize")
},
onUpdate: (value) => {
app.cores.style.modify({
backgroundSize: value
})
return value
},
storaged: false
},
{
id: "style.backgroundPosition",
group: "aspect",
component: "Select",
icon: "MdOutlineImageAspectRatio",
title: "Background position",
description: "Adjust the position of the background image.",
props: {
style: {
width: "100%"
},
options: [
{
label: "Left",
value: "left"
},
{
label: "Center",
value: "center"
},
{
label: "Right",
value: "right"
},
{
label: "Top",
value: "top"
},
]
},
defaultValue: () => {
return app.cores.style.getValue("backgroundPosition")
},
onUpdate: (value) => {
app.cores.style.modify({
backgroundPosition: value
})
return value
},
storaged: false
},
{
id: "style.backgroundRepeat",
group: "aspect",
component: "Select",
icon: "MdOutlineImageAspectRatio",
title: "Background repeat",
description: "Adjust the repeat of the background image.",
props: {
style: {
width: "100%"
},
options: [
{
label: "Repeat",
value: "repeat"
},
{
label: "No repeat",
value: "no-repeat"
},
{
label: "Repeat X",
value: "repeat-x"
},
{
label: "Repeat Y",
value: "repeat-y"
},
]
},
defaultValue: () => {
return app.cores.style.getValue("backgroundRepeat")
},
onUpdate: (value) => {
app.cores.style.modify({
backgroundRepeat: value
})
return value
},
storaged: false
},
{
id: "style.backgroundAttachment",
group: "aspect",
component: "Select",
icon: "MdOutlineImageAspectRatio",
title: "Background attachment",
description: "Adjust the attachment of the background image.",
props: {
style: {
width: "100%"
},
options: [
{
label: "Scroll",
value: "scroll"
},
{
label: "Fixed",
value: "fixed"
},
{
label: "Local",
value: "local"
},
{
label: "Initial",
value: "initial"
},
{
label: "Inherit",
value: "inherit"
},
]
},
defaultValue: () => {
return app.cores.style.getValue("backgroundAttachment")
},
onUpdate: (value) => {
app.cores.style.modify({
backgroundAttachment: value
})
return value
},
storaged: false
},
{
id: "resetTheme",
group: "aspect",
component: "Button",
title: "Reset theme",
props: {
children: "Default Theme"
},
onUpdate: (value) => {
Modal.confirm({
title: "Are you sure you want to reset the theme to the default theme ?",
description: "This action will reset the theme to the default theme. All your modifications will be lost.",
onOk: () => {
app.cores.style.setDefault()
}
})
},
storaged: false
} }
] ]
} }

View File

@ -1,71 +0,0 @@
import React from "react"
import { Button, Input, Upload } from "antd"
import { Icons } from "components/Icons"
import "./index.less"
export default (props) => {
const [value, setValue] = React.useState(props.ctx.currentValue)
const [uploading, setUploading] = React.useState(false)
const uploadImage = async (req) => {
setUploading(true)
const formData = new FormData()
formData.append("files", req.file)
const request = await window.app.api.withEndpoints("main").post.upload(formData, undefined).catch((error) => {
console.error(error)
app.message.error(error)
return false
})
setUploading(false)
if (request) {
setValue(request.files[0].url)
props.ctx.dispatchUpdate(request.files[0].url)
}
}
return <div className="imageUploader">
{
!props.noPreview && value && <div className="uploadPreview">
<img src={value} />
</div>
}
<Input.Group compact>
<Input
placeholder="Image URL..."
value={value}
onChange={(e) => setValue(e.target.value)}
onPressEnter={() => props.ctx.dispatchUpdate(value)}
/>
<Button
icon={<Icons.Save />}
onClick={() => props.ctx.dispatchUpdate(value)}
/>
</Input.Group>
or
<Upload
customRequest={uploadImage}
multiple={false}
accept="image/*"
progress={false}
fileList={[]}
>
<Button
icon={<Icons.Upload />}
loading={uploading}
>
Upload
</Button>
</Upload>
</div>
}

View File

@ -0,0 +1,57 @@
import React from "react"
import SVG from "react-inlinesvg"
import "./index.less"
const defaultBackgrounds = [
{
id: "topography",
label: "Topography",
src: "/assets/default_bg/topography.svg"
},
{
id: "meteors",
label: "Meteors",
src: "/assets/default_bg/meteors.svg"
},
{
id: "dots",
label: "Dots",
src: "/assets/default_bg/dots.svg"
},
{
id: "hideout",
label: "Hideout",
src: "/assets/default_bg/hideout.svg"
}
]
export default (props) => {
return <div className="background_selector">
<div className="background_selector_defaults">
{
defaultBackgrounds.map((background) => {
return <div className="background_selector_defaults__item">
<div className="background_selector_defaults__item_name">
<h3>{background.label}</h3>
</div>
<div
className="background_selector_defaults__item_preview"
onClick={() => {
app.cores.style.modify({
backgroundSVG: `url("${background.src}")`
})
}}
style={{
maskImage: `url("${background.src}")`,
WebkitMaskImage: `url("${background.src}")`
}}
/>
</div>
})
}
</div>
</div>
}

View File

@ -0,0 +1,65 @@
.background_selector {
display: flex;
flex-direction: column;
.background_selector_defaults {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
overflow-x: auto;
.background_selector_defaults__item {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
width: 10vw;
height: 300px;
transition: all 150ms ease-in-out;
margin-right: 20px;
background-color: var(--background-color-accent);
border-radius: 10px;
.background_selector_defaults__item_name {
display: flex;
flex-direction: row;
font-size: 1rem;
font-weight: 500;
margin: 10px;
}
.background_selector_defaults__item_preview {
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 10px;
cursor: pointer;
background-color: var(--text-color);
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
}
&:last-child {
margin-right: 0;
}
}
}
}

View File

@ -1,7 +1,7 @@
import React from "react" import React from "react"
import * as antd from "antd" import * as antd from "antd"
import { User } from "models" import { UserModel } from "models"
import { Icons } from "components/Icons" import { Icons } from "components/Icons"
import "./index.less" import "./index.less"
@ -31,7 +31,7 @@ const ChangePasswordComponent = (props) => {
setError(null) setError(null)
setLoading(true) setLoading(true)
const result = await User.changePassword({ currentPassword, newPassword }).catch((err) => { const result = await UserModel.changePassword({ currentPassword, newPassword }).catch((err) => {
console.error(err) console.error(err)
setError(err.response.data.message) setError(err.response.data.message)
return null return null

View File

@ -0,0 +1,69 @@
import React from "react"
import { Button, Upload } from "antd"
import { Icons } from "components/Icons"
export default (props) => {
const [uploading, setUploading] = React.useState(false)
const handleUpload = async (req) => {
console.log(req)
setUploading(true)
const formData = new FormData()
formData.append("files", req.file)
const request = await window.app.cores.api.customRequest({
url: "/upload",
method: "POST",
data: formData
}).catch((error) => {
console.error(error)
app.message.error(error.respose.data.message)
return false
})
setUploading(false)
if (request) {
// check failed uploads
if (request.failed.length > 0) {
request.failed.forEach((file) => {
app.notification.error({
message: "Failed to upload file",
description: `Could not upload file ${file.fileName} cause > ${file.error}`
})
})
}
props.ctx.dispatchUpdate(request.files[0].url)
}
}
return <Upload
customRequest={handleUpload}
multiple={false}
accept="image/*"
progress={false}
fileList={[]}
>
<Button
icon={props.icon ?? <Icons.Upload
style={{
margin: 0
}}
/>}
loading={uploading}
type={
props.type ?? "round"
}
>
{
props.children ?? "Upload"
}
</Button>
</Upload>
}

View File

@ -0,0 +1,31 @@
import React from "react"
import { Button, Input } from "antd"
import { Icons } from "components/Icons"
import "./index.less"
export default (props) => {
const [value, setValue] = React.useState(props.ctx.currentValue)
return <div className="imageUploader">
{
!props.noPreview && value && <div className="uploadPreview">
<img src={value} />
</div>
}
<Input.Group compact>
<Input
placeholder="Image URL..."
value={value}
onChange={(e) => setValue(e.target.value)}
onPressEnter={() => props.ctx.dispatchUpdate(value)}
/>
<Button
icon={<Icons.Save />}
onClick={() => props.ctx.dispatchUpdate(value)}
/>
</Input.Group>
</div>
}

View File

@ -2,8 +2,10 @@ import React from "react"
import loadable from "@loadable/component" import loadable from "@loadable/component"
export default { export default {
id: "extensions",
icon: "MdOutlineCode", icon: "MdOutlineCode",
label: "Extensions", label: "Extensions",
group: "advanced",
settings: [ settings: [
] ]

View File

@ -3,8 +3,10 @@ import config from "config"
import { Select } from "antd" import { Select } from "antd"
export default { export default {
id: "general",
icon: "Command", icon: "Command",
label: "App", label: "General",
group: "app",
settings: [ settings: [
{ {
"id": "language", "id": "language",

View File

@ -1,17 +1,24 @@
import AppSettings from "./app" import GeneralSettings from "./general"
import ProfileSettings from "./profile" import ProfileSettings from "./profile"
import SecuritySettings from "./security" import SecuritySettings from "./security"
import SubcriptionsSettings from "./subscriptions"
import NotificationsSettings from "./notifications" import NotificationsSettings from "./notifications"
import ApparenceSettings from "./apparence" import ApparenceSettings from "./apparence"
import ExtensionsSettings from "./extensions" import ExtensionsSettings from "./extensions"
import SyncSettings from "./sync" import SyncSettings from "./sync"
import PlayerSettings from "./player"
import AboutPage from "./about"
export default { export default {
app: AppSettings, general: GeneralSettings,
profile: ProfileSettings, profile: ProfileSettings,
apparence: ApparenceSettings, apparence: ApparenceSettings,
player: PlayerSettings,
security: SecuritySettings, security: SecuritySettings,
notifications: NotificationsSettings, notifications: NotificationsSettings,
extensions: ExtensionsSettings, extensions: ExtensionsSettings,
sync: SyncSettings, sync: SyncSettings,
subscriptions: SubcriptionsSettings,
about: AboutPage,
} }

View File

@ -1,8 +1,10 @@
import React from "react" import React from "react"
export default { export default {
id: "notifications",
icon: "Bell", icon: "Bell",
label: "Notifications", label: "Notifications",
group: "basic",
settings: [ settings: [
] ]

View File

@ -0,0 +1,40 @@
export default {
id: "player",
icon: "PlayCircleOutlined",
label: "Player",
group: "app",
settings: [
{
id: "player.allowVolumeOver100",
label: "Allow volume over 100%",
description: "Allow volume amplification over 100% (may cause distortion)",
component: "Switch",
storaged: true,
},
{
id: "player.crossfade",
label: "Crossfade",
description: "Enable crossfade between tracks",
component: "Slider",
props: {
min: 0,
max: 10,
step: 0.1,
marks: {
0: "Off",
1: "1s",
2: "2s",
3: "3s",
4: "4s",
5: "5s",
6: "6s",
7: "7s",
8: "8s",
9: "9s",
10: "10s",
},
},
storaged: true,
}
]
}

View File

@ -1,12 +1,15 @@
import React from "react" import React from "react"
import { User } from "models" import { UserModel } from "models"
import loadable from "@loadable/component" import loadable from "@loadable/component"
import UploadButton from "../components/uploadButton"
export default { export default {
id: "profile",
icon: "User", icon: "User",
label: "Profile", label: "Profile",
group: "basic",
ctxData: async () => { ctxData: async () => {
const userData = await User.data() const userData = await UserModel.data()
return { return {
userData userData
@ -26,47 +29,43 @@ export default {
}, },
}, },
{ {
"id": "fullName", id: "fullName",
"group": "account.basicInfo", group: "account.basicInfo",
"component": "Input", component: "Input",
"icon": "Edit3", icon: "Edit3",
"title": "Name", title: "Name",
"description": "Change your public name", description: "Change your public name",
"props": { props: {
// set max length // set max length
"maxLength": 120, "maxLength": 120,
"showCount": true, "showCount": true,
"allowClear": true, "allowClear": true,
"placeholder": "Enter your name. e.g. John Doe", "placeholder": "Enter your name. e.g. John Doe",
}, },
"defaultValue": (ctx) => { defaultValue: (ctx) => {
return ctx.userData.fullName return ctx.userData.fullName
}, },
"onUpdate": async (value) => { onUpdate: async (value) => {
const selfId = await User.selfUserId() const result = await UserModel.updateData({
fullName: value
const result = window.app.api.withEndpoints("main").post.updateUser({
_id: selfId,
update: {
fullName: value
}
}) })
if (result) { if (result) {
return result return result
} }
}, },
"extraActions": [ extraActions: [
{ {
"id": "unset", "id": "unset",
"icon": "Delete", "icon": "Delete",
"title": "Unset", "title": "Unset",
"onClick": async () => { "onClick": async () => {
window.app.api.withEndpoints("main").post.unsetPublicName() await UserModel.unsetFullName()
} }
} }
], ],
"debounced": true, debounced: true,
storaged: false,
}, },
{ {
"id": "email", "id": "email",
@ -85,13 +84,8 @@ export default {
return ctx.userData.email return ctx.userData.email
}, },
"onUpdate": async (value) => { "onUpdate": async (value) => {
const selfId = await User.selfUserId() const result = await UserModel.updateData({
email: value
const result = window.app.api.withEndpoints("main").post.updateUser({
_id: selfId,
update: {
email: value
}
}) })
if (result) { if (result) {
@ -106,18 +100,16 @@ export default {
"icon": "Image", "icon": "Image",
"title": "Avatar", "title": "Avatar",
"description": "Change your avatar (Upload an image or use an URL)", "description": "Change your avatar (Upload an image or use an URL)",
"component": loadable(() => import("../components/ImageUploader")), "component": loadable(() => import("../components/urlInput")),
extraActions: [
UploadButton
],
"defaultValue": (ctx) => { "defaultValue": (ctx) => {
return ctx.userData.avatar return ctx.userData.avatar
}, },
"onUpdate": async (value) => { "onUpdate": async (value) => {
const selfId = await User.selfUserId() const result = await UserModel.updateData({
avatar: value
const result = window.app.api.withEndpoints("main").post.updateUser({
_id: selfId,
update: {
avatar: value
}
}) })
if (result) { if (result) {
@ -133,18 +125,16 @@ export default {
"icon": "Image", "icon": "Image",
"title": "Cover", "title": "Cover",
"description": "Change your profile cover (Upload an image or use an URL)", "description": "Change your profile cover (Upload an image or use an URL)",
"component": loadable(() => import("../components/ImageUploader")), "component": loadable(() => import("../components/urlInput")),
extraActions: [
UploadButton
],
"defaultValue": (ctx) => { "defaultValue": (ctx) => {
return ctx.userData.cover return ctx.userData.cover
}, },
"onUpdate": async (value) => { "onUpdate": async (value) => {
const selfId = await User.selfUserId() const result = await UserModel.updateData({
cover: value
const result = window.app.api.withEndpoints("main").post.updateUser({
_id: selfId,
update: {
cover: value
}
}) })
if (result) { if (result) {
@ -171,13 +161,8 @@ export default {
return ctx.userData.description return ctx.userData.description
}, },
"onUpdate": async (value) => { "onUpdate": async (value) => {
const selfId = await User.selfUserId() const result = await UserModel.updateData({
description: value
const result = window.app.api.withEndpoints("main").post.updateUser({
_id: selfId,
update: {
description: value
}
}) })
if (result) { if (result) {
@ -185,6 +170,7 @@ export default {
} }
}, },
"debounced": true, "debounced": true,
storaged: false,
}, },
] ]
} }

View File

@ -1,11 +1,14 @@
import React from "react" import React from "react"
import loadable from "@loadable/component" import loadable from "@loadable/component"
import AuthModel from "models/auth"
// TODO: Make logout button require a valid session to be not disabled // TODO: Make logout button require a valid session to be not disabled
export default { export default {
id: "security",
icon: "Shield", icon: "Shield",
label: "Security", label: "Security",
group: "basic",
settings: [ settings: [
{ {
"id": "change-password", "id": "change-password",
@ -39,7 +42,9 @@ export default {
"icon": "LogOut", "icon": "LogOut",
"title": "Logout", "title": "Logout",
"description": "Logout from your account", "description": "Logout from your account",
"emitEvent": "session.logout", onUpdate: async () => {
await AuthModel.logout()
}
} }
] ]
} }

View File

@ -0,0 +1,8 @@
export default {
id: "subscriptions",
label: "Subscriptions",
icon: "MdLoyalty",
group: "other",
settings: [
]
}

View File

@ -5,8 +5,10 @@ import SyncModel from "models/sync"
// TODO: Make logout button require a valid session to be not disabled // TODO: Make logout button require a valid session to be not disabled
export default { export default {
id: "sync",
icon: "MdSync", icon: "MdSync",
label: "Sync", label: "Sync",
group: "advanced",
ctxData: async () => { ctxData: async () => {
const spotifyAccount = await SyncModel.spotifyCore.getData().catch((err) => { const spotifyAccount = await SyncModel.spotifyCore.getData().catch((err) => {
return null return null