improve settings desing

This commit is contained in:
SrGooglo 2024-09-11 01:14:24 +00:00
parent 1e23aad5b4
commit 4a38afb45a
16 changed files with 395 additions and 193 deletions

View File

@ -1,20 +1,20 @@
{
"app": {
"label": "Application Settings"
"label": "Application"
},
"basic": {
"label": "Basic Settings"
"label": "Basic"
},
"security": {
"label": "Security Settings"
"label": "Security"
},
"privacy": {
"label": "Privacy Settings"
"label": "Privacy"
},
"advanced": {
"label": "Advanced Settings"
"label": "Advanced"
},
"other": {
"label": "Other Settings"
"label": "Other"
}
}

View File

@ -0,0 +1,70 @@
import React from "react"
import * as antd from "antd"
import Image from "@components/Image"
import UserBadges from "@components/UserBadges"
import { Icons, createIconRender } from "@components/Icons"
import ContrastYIQ from "@utils/contrastYIQ"
import "./index.less"
const UserShareBadge = (props) => {
const { user } = props
const [loading, setLoading] = React.useState(true)
const [contrastColor, setContrastColor] = React.useState(null)
async function initialize(params) {
setLoading(true)
const contrastYIQ = await ContrastYIQ.fromUrl(user.cover)
setContrastColor(contrastYIQ)
setLoading(false)
}
React.useEffect(() => {
initialize()
}, [])
if (loading) {
return <div className="user-share-badge">
<antd.Skeleton active />
</div>
}
return <div
className="user-share-badge"
style={{
backgroundImage: `url("${user.cover}")`,
color: contrastColor
}}
>
<div className="user-share-badge-info">
<div className="user-share-badge-avatar">
<Image
src={user.avatar}
/>
</div>
<div className="user-share-badge-username">
<h1>
{user.public_name || user.username}
{user.verified && <Icons.verifiedBadge />}
</h1>
<span>
@{user.username}
</span>
</div>
{
user.badges?.length > 0 && <UserBadges user_id={user._id} />
}
</div>
</div>
}
export default UserShareBadge

View File

@ -0,0 +1,60 @@
.user-share-badge {
width: 400px;
height: 240px;
border-radius: 24px;
// background-color: rgb(255, 96, 100);
// background-image: radial-gradient(circle, currentcolor 0%, transparent 110%);
outline: 2px solid var(--border-color);
padding: 20px;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
h1,
h2,
h3,
h4,
h5,
h6,
p,
span {
color: currentColor;
margin: 0;
}
.user-share-badge-info {
display: flex;
flex-direction: column;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
background-color: rgba(var(--bg_color_4), 0.4);
width: fit-content;
padding: 10px;
border-radius: 12px;
.user-share-badge-avatar {
display: flex;
flex-direction: column;
img {
border-radius: 12px;
}
}
.user-share-badge-username {
display: flex;
flex-direction: column;
font-size: 1.3rem;
}
}
}

View File

@ -360,6 +360,14 @@ export default class SettingItemComponent extends React.PureComponent {
}))
}
computeSwitchEnablerDefault = () => {
if (typeof this.props.setting.switchDefault === "function") {
return this.props.setting.switchDefault()
}
return this.props.setting.switchDefault
}
render() {
if (!this.props.setting) {
console.error(`Item [${this.props.setting.id}] has no an setting!`)
@ -428,8 +436,9 @@ export default class SettingItemComponent extends React.PureComponent {
{(t) => t(this.props.setting.title ?? this.props.setting.id)}
</Translation>
</h1>
{
this.props.setting.experimental && <antd.Tag> Experimental </antd.Tag>
this.props.setting.experimental && <antd.Tag>Experimental</antd.Tag>
}
</div>
<div className="setting_item_header_description">
@ -441,10 +450,9 @@ export default class SettingItemComponent extends React.PureComponent {
</div>
</div>
<div className="setting_item_header_actions">
{
this.props.setting.extraActions && <div className="setting_item_header_actions">
{
this.props.setting.extraActions.map((action, index) => {
this.props.setting.extraActions && this.props.setting.extraActions.map((action, index) => {
if (typeof action === "function") {
return React.createElement(action, {
ctx: {
@ -478,9 +486,15 @@ export default class SettingItemComponent extends React.PureComponent {
</antd.Button>
})
}
</div>
{
typeof this.props.setting.onEnabledChange === "function" && <antd.Switch
defaultChecked={this.computeSwitchEnablerDefault()}
onChange={this.props.setting.onEnabledChange}
/>
}
</div>
</div>
<div className="setting_item_content">
<>

View File

@ -45,12 +45,13 @@
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
color: var(--background-color-contrast);
gap: 5px;
h1 {
font-size: 1rem;
margin: 0;

View File

@ -55,7 +55,7 @@ const generateMenuItems = () => {
return {
key: item.id,
type: "item",
label: <div {...item.props}>
label: <div {...item.props} className="menu-item-content">
{createIconRender(item.icon ?? "Settings")}
{item.label}
</div>,
@ -126,7 +126,9 @@ export default () => {
if (app.layout.tools_bar) {
app.layout.tools_bar.toggleVisibility(false)
}
}, [activeKey])
React.useEffect(() => {
return () => {
if (app.layout.tools_bar) {
app.layout.tools_bar.toggleVisibility(true)

View File

@ -29,6 +29,43 @@
padding: 0 30px;
gap: 3px;
.ant-menu-item-group-list {
display: flex;
flex-direction: column;
width: 100%;
gap: 3px;
}
.menu-item-content {
display: flex;
flex-direction: row;
align-items: center;
height: fit-content;
gap: 10px;
svg {
margin: 0 !important;
}
}
.ant-menu-item {
padding: 0 !important;
padding-inline: 20px !important;
margin: 0;
border-radius: 24px;
.ant-menu-title-content {
height: fit-content;
}
}
.ant-menu-item-danger {
.ant-menu-title-content {
svg {
@ -59,7 +96,7 @@
border-radius: 12px;
padding: 20px;
padding: 15px;
gap: 15px;

View File

@ -78,6 +78,10 @@ export default {
width: "100%"
},
options: [
{
label: "Noto Sans",
value: "'Noto Sans', sans-serif"
},
{
label: "Inter (Default)",
value: "'Inter', sans-serif"

View File

@ -88,11 +88,29 @@ export default (props) => {
}, [selectedPreset])
return <>
<Sliders
{...props}
/>
<div
style={{
display: "inline-flex",
flexDirection: "column",
alignItems: "center",
gap: "5px",
width: "100%",
backgroundColor: "rgba(var(--bg_color_3), 0.5)",
padding: "5px 10px",
borderRadius: "12px",
}}
>
<div
style={{
display: "inline-flex",
flexDirection: "row",
alignItems: "center",
gap: "5px",
width: "100%",
}}
>
<Icons.MdList
@ -100,6 +118,25 @@ export default (props) => {
margin: "0"
}}
/>
<h4
style={{
margin: "0",
}}
>
Preset
</h4>
</div>
<div
style={{
display: "inline-flex",
flexDirection: "row",
alignItems: "center",
gap: "5px",
width: "100%",
}}
>
<Select
style={{
width: "50%"
@ -120,9 +157,6 @@ export default (props) => {
disabled={selectedPreset === "default"}
/>
</div>
<Sliders
{...props}
/>
</div>
</>
}

View File

@ -7,14 +7,14 @@ import WidgetItemPreview from "@components/WidgetItemPreview"
import "./index.less"
export default class WidgetsView extends React.Component {
export default class WidgetsManager extends React.Component {
state = {
loadedWidgets: this.props.ctx.currentValue ?? [],
loadedWidgets: app.cores.widgets.getInstalled() ?? [],
}
render() {
return <div className="widgets_load">
<div className="widgets_load_list">
return <div className="widgets-manager">
<div className="widgets-manager-list">
{
Array.isArray(this.state.loadedWidgets) && this.state.loadedWidgets.map((manifest) => {
return <React.Fragment>
@ -52,7 +52,7 @@ export default class WidgetsView extends React.Component {
icon={<Icons.Plus />}
onClick={openWidgetsBrowserModal}
>
Add widget
Install more
</antd.Button>
</div>
</div>

View File

@ -1,16 +1,16 @@
.widgets_load {
.widgets-manager {
display: flex;
flex-direction: column;
gap: 20px;
.widgets_load_list {
.widgets-manager-list {
display: flex;
flex-direction: column;
gap: 10px;
.widget_load_list_item {
.widgets-manager-list-item {
display: flex;
flex-direction: row;
@ -22,14 +22,14 @@
border: 1px var(--border-color) solid;
border-radius: 12px;
.widget_load_list_item_info {
.widgets-manager-list-item-info {
display: flex;
flex-direction: row;
gap: 20px;
}
.widget_load_list_item_icon {
.widgets-manager-list-item-icon {
display: flex;
flex-direction: row;
@ -47,7 +47,6 @@
object-fit: contain;
}
}
}
}

View File

@ -7,17 +7,35 @@ export default {
group: "app",
settings: [
{
id: "player.allowVolumeOver100",
title: "Allow volume over 100%",
id: "player.gain",
title: "Gain",
icon: "MdGraphicEq",
group: "general",
icon: "MdHearing",
description: "Allow volume amplification over 100% (may cause distortion)",
component: "Switch",
storaged: true,
description: "Adjust gain for audio output",
component: "Slider",
props: {
min: 1,
max: 2,
step: 0.1,
marks: {
1: "Normal",
1.5: "+50%",
2: "+100%"
}
},
defaultValue: () => {
return app.cores.player.gain.values().gain
},
onUpdate: (value) => {
app.cores.player.gain.modifyValues({
gain: value
})
},
storaged: false,
},
{
id: "player.sample_rate",
title: "Sample rate",
title: "Sample Rate",
icon: "MdHearing",
group: "general",
description: "Internal sample rate for audio output",
@ -52,54 +70,9 @@ export default {
},
storaged: false,
},
{
id: "player.crossfade",
title: "Crossfade",
icon: "MdSwapHoriz",
group: "general",
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,
disabled: true,
},
{
id: "player.compressor",
title: "Compression",
icon: "MdGraphicEq",
group: "general",
description: "Enable compression for audio output",
component: "Switch",
experimental: true,
beforeSave: (value) => {
if (value) {
app.cores.player.compressor.attach()
} else {
app.cores.player.compressor.detach()
}
},
storaged: true,
},
{
id: "player.compressor.values",
title: "Compression adjustment",
title: "Compression",
icon: "Sliders",
group: "general",
description: "Adjust compression values (Warning: may cause distortion when changing values)",
@ -108,6 +81,18 @@ export default {
"player.compressor": true
},
component: loadable(() => import("./items/player.compressor")),
switchDefault: () => {
return app.cores.settings.get("player.compressor")
},
onEnabledChange: (enabled) => {
if (enabled === true) {
app.cores.settings.set("player.compressor", true)
app.cores.player.compressor.attach()
} else {
app.cores.settings.set("player.compressor", false)
app.cores.player.compressor.detach()
}
},
props: {
valueFormat: (value) => `${value}dB`,
sliders: [
@ -149,7 +134,7 @@ export default {
extraActions: [
{
id: "reset",
title: "Reset",
title: "Default",
icon: "MdRefresh",
onClick: async (ctx) => {
const values = await app.cores.player.compressor.resetDefaultValues()
@ -165,33 +150,7 @@ export default {
},
storaged: false,
},
{
id: "player.gain",
title: "Gain",
icon: "MdGraphicEq",
group: "general",
description: "Adjust gain for audio output",
component: "Slider",
props: {
min: 1,
max: 2,
step: 0.1,
marks: {
1: "Off",
1.5: "50%",
2: "100%"
}
},
defaultValue: () => {
return app.cores.player.gain.values().gain
},
onUpdate: (value) => {
app.cores.player.gain.modifyValues({
gain: value
})
},
storaged: false,
},
{
id: "player.eq",
title: "Equalizer",
@ -211,7 +170,9 @@ export default {
}
},
],
usePadding: false,
dependsOn: {
"player.equalizer": true
},
props: {
valueFormat: (value) => `${value}dB`,
marks: [

View File

@ -1,8 +1,14 @@
import { Switch } from "antd"
import SlidersWithPresets from "../../../components/slidersWithPresets"
export default (props) => {
return <SlidersWithPresets
{...props}
controller={app.cores.player.compressor}
extraHeaderItems={[
<Switch
onChange={props.onEnabledChange}
/>
]}
/>
}

View File

@ -3,15 +3,16 @@ import * as antd from "antd"
import classnames from "classnames"
import NFCModel from "comty.js/models/nfc"
import StepsContext from "./context"
import { Icons } from "@components/Icons"
import UserShareBadge from "@components/UserShareBadge"
import CheckRegister from "./steps/check_register"
import DataEditor from "./steps/data_editor"
import TagWritter from "./steps/tag_writter"
import Success from "./steps/success"
import StepsContext from "./context"
import "./index.less"
const RegisterNewTagSteps = [
@ -306,14 +307,29 @@ const TapShareRender = () => {
<Icons.MdSpoke /> Registered Tags
</h1>
</div>
{
app.cores.nfc.scanning && <span className="tip">
<Icons.MdInfo /> You can quickly edit your tags by tapping them.
</span>
}
<OwnTags />
</div>
<div className="tap-share-field">
<div className="tap-share-field_header">
<h1>
<Icons.MdBadge /> Your Badge
</h1>
</div>
<UserShareBadge
user={app.userData}
editMode
/>
</div>
{
app.isMobile && <antd.Button
type="primary"

View File

@ -1,26 +1,23 @@
import loadable from "@loadable/component"
import React from "react"
import WidgetsManager from "../components/widgetsManager"
export default {
id: "widgets",
icon: "List",
label: "Widgets",
group: "app",
settings: [
{
id: "widgets.urls",
title: "Widgets",
group: "general",
icon: "List",
component: loadable(() => import("../components/widgetsView")),
defaultValue: () => {
if (typeof app.cores.widgets === "undefined") {
return []
render: () => {
React.useEffect(() => {
if (app.layout.tools_bar) {
app.layout.tools_bar.toggleVisibility(true)
}
}, [])
return app.cores.widgets.getInstalled()
return <div>
<h1>Widgets</h1>
<WidgetsManager />
</div>
},
reloadValueOnUpdateEvent: "widgets:update",
storaged: false,
}
]
}

View File

@ -1,6 +1,7 @@
/* Selectable fonts for users */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Varela+Round&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap');
/* Required secondary fonts */
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap');