mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
improve settings desing
This commit is contained in:
parent
1e23aad5b4
commit
4a38afb45a
@ -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"
|
||||
}
|
||||
}
|
70
packages/app/src/components/UserShareBadge/index.jsx
Normal file
70
packages/app/src/components/UserShareBadge/index.jsx
Normal 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
|
60
packages/app/src/components/UserShareBadge/index.less
Normal file
60
packages/app/src/components/UserShareBadge/index.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,6 +436,7 @@ 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>
|
||||
}
|
||||
@ -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">
|
||||
<>
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -78,6 +78,10 @@ export default {
|
||||
width: "100%"
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: "Noto Sans",
|
||||
value: "'Noto Sans', sans-serif"
|
||||
},
|
||||
{
|
||||
label: "Inter (Default)",
|
||||
value: "'Inter', sans-serif"
|
||||
|
@ -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>
|
||||
</>
|
||||
}
|
@ -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>
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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: [
|
||||
|
@ -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}
|
||||
/>
|
||||
]}
|
||||
/>
|
||||
}
|
@ -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"
|
||||
|
@ -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,
|
||||
}
|
||||
]
|
||||
}
|
@ -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');
|
||||
|
Loading…
x
Reference in New Issue
Block a user