redesign EditProfile (#446)

Co-authored-by: BlowaterNostr <blowater.nostr@proton.me>
This commit is contained in:
Bob 2024-03-29 14:59:34 +08:00 committed by GitHub
parent 728675690b
commit 7044128342
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 157 additions and 161 deletions

View File

@ -309,12 +309,6 @@ type AppProps = {
};
export class AppComponent extends Component<AppProps> {
events = this.props.eventBus.onChange();
componentWillUnmount() {
this.events.close();
}
render(props: AppProps) {
const t = Date.now();
const model = props.model;
@ -404,22 +398,11 @@ export class AppComponent extends Component<AppProps> {
);
}
const final = (
<div class={`h-screen w-full flex`}>
<NavBar
publicKey={app.ctx.publicKey}
profile={app.database.getProfilesByPublicKey(myAccountCtx.publicKey)}
emit={app.eventBus.emit}
installPrompt={props.installPrompt}
currentRelay={model.currentRelay}
activeNav={model.navigationModel.activeNav}
pool={app.pool}
/>
let profileEditorNode: VNode | undefined;
if (model.navigationModel.activeNav == "Profile") {
profileEditorNode = (
<div
class={`h-full px-[3rem] sm:px-4 bg-[${SecondaryBackgroundColor}] flex-1 overflow-auto${
model.navigationModel.activeNav == "Profile" ? " block" : " hidden"
}`}
class={`h-full px-[3rem] sm:px-4 bg-[${SecondaryBackgroundColor}] flex-1 overflow-auto block`}
>
<div
class={`max-w-[35rem] h-full m-auto`}
@ -432,6 +415,21 @@ export class AppComponent extends Component<AppProps> {
/>
</div>
</div>
);
}
const final = (
<div class={`h-screen w-full flex`}>
<NavBar
publicKey={app.ctx.publicKey}
profile={app.database.getProfilesByPublicKey(myAccountCtx.publicKey)}
emit={app.eventBus.emit}
installPrompt={props.installPrompt}
currentRelay={model.currentRelay}
activeNav={model.navigationModel.activeNav}
pool={app.pool}
/>
{profileEditorNode}
{publicNode}
{dmVNode}
{aboutNode}

View File

@ -1,24 +1,17 @@
import { createRef, Fragment, h } from "https://esm.sh/preact@10.17.1";
import {
CenterClass,
DividerClass,
InputClass,
LinearGradientsClass,
NoOutlineClass,
} from "./components/tw.ts";
import { createRef, h } from "https://esm.sh/preact@10.17.1";
import { ProfileData } from "../features/profile.ts";
import {
DividerBackgroundColor,
ErrorColor,
HintLinkColor,
HintTextColor,
HoverButtonBackgroundColor,
PlaceholderColor,
PrimaryTextColor,
SecondaryBackgroundColor,
} from "./style/colors.ts";
import { Component, ComponentChildren } from "https://esm.sh/preact@10.11.3";
import { Component } from "https://esm.sh/preact@10.11.3";
import { emitFunc } from "../event-bus.ts";
import { NostrAccountContext } from "../../libs/nostr.ts/nostr.ts";
import { UserIcon } from "./icons/user-icon.tsx";
export type SaveProfile = {
type: "SaveProfile";
@ -26,12 +19,6 @@ export type SaveProfile = {
ctx: NostrAccountContext;
};
type profileItem = {
key: string;
value?: string;
hint?: ComponentChildren;
};
type Props = {
ctx: NostrAccountContext;
profile: ProfileData;
@ -49,134 +36,112 @@ export class EditProfile extends Component<Props, State> {
profileData: undefined,
};
newFieldKey = createRef<HTMLInputElement>();
newFieldValue = createRef<HTMLInputElement>();
componentDidMount(): void {
this.setState({
profileData: this.props.profile,
});
}
render() {
const profileItems: profileItem[] = [
{
key: "name",
value: this.state.profileData?.name,
},
{
key: "banner",
value: this.state.profileData?.banner,
},
{
key: "picture",
value: this.state.profileData?.picture,
hint: (
<span class={this.styles.field.hint.text}>
render(props: Props, state: State) {
return (
<form class={`py-4 bg-[${SecondaryBackgroundColor}]`} onSubmit={this.onSubmit}>
<img
src={`${
this.state.profileData?.banner ||
"https://images.unsplash.com/photo-1468581264429-2548ef9eb732"
}`}
class="w-full h-[300px] object-cover rounded-lg"
/>
<img
src={`${this.state.profileData?.picture || "/logo.webp"}`}
class="w-20 h-20 rounded-full mx-auto border-[3px] border-white
object-cover
transform -translate-y-10 bg-white"
/>
<div
class={`my-4 p-4 rounded-2xl border-[2px] border-[${DividerBackgroundColor}] `}
>
<div class={`flex justify-start items-center gap-2`}>
<UserIcon class="w-8 h-8 text-[#FF772B]" />
<div
class={`text-[${PrimaryTextColor}] text-[1.3125rem] font-not-italic font-700 leading-[1.5rem] tracking--0.21px`}
>
Profile
</div>
</div>
<h3 class={`text-[${PrimaryTextColor}] mt-8`}>
Name
</h3>
<input
value={state.profileData?.name}
name="name"
onInput={(e) => this.onInput(e, "name")}
type="text"
class={`focus:outline-none focus:border-white w-full px-4 py-3 rounded-lg resize-y bg-transparent border-[2px] border-[${DividerBackgroundColor}] placeholder-[${PlaceholderColor}] text-[${PrimaryTextColor}] `}
/>
<h3 class={`text-[${PrimaryTextColor}] mt-8`}>
Profile Image URL
</h3>
<input
value={state.profileData?.picture}
name="picture"
onInput={(e) => this.onInput(e, "picture")}
type="text"
class={`focus:outline-none focus:border-white w-full px-4 py-3 rounded-lg resize-y bg-transparent border-[2px] border-[${DividerBackgroundColor}] placeholder-[${PlaceholderColor}] text-[${PrimaryTextColor}] `}
/>
<span class={`text-sm text-[${HintTextColor}]`}>
You can upload your images on websites like{" "}
<a class={this.styles.field.hint.link} href="https://nostr.build/" target="_blank">
<a class={`text-[${HintLinkColor}]`} href="https://nostr.build/" target="_blank">
nostr.build
</a>
</span>
),
},
{
key: "about",
value: this.state.profileData?.about,
},
{
key: "website",
value: this.state.profileData?.website,
},
];
const items = profileItems.map((item) => (
<Fragment>
<h3 class={this.styles.field.title} style={{ textTransform: "capitalize" }}>
{item.key}
</h3>
<textarea
placeholder={item.key}
rows={item.value?.split("\n")?.length || 1}
value={item.value}
onInput={(e) => this.onInput(e, item.key)}
type="text"
class={this.styles.field.input}
>
</textarea>
{item.hint}
</Fragment>
));
return (
<div class={this.styles.container}>
{items}
<div class={this.styles.divider}></div>
<p class={this.styles.custom.title}>Custom Fields</p>
<span class={this.styles.custom.text}>
Create your own custom fields, anything goes!
</span>
<h3 class={this.styles.field.title}>
Field name
<h3 class={`text-[${PrimaryTextColor}] mt-8`}>
Banner Image URL
</h3>
<input
ref={this.newFieldKey}
placeholder="e.g. hobbies"
value={state.profileData?.banner}
name="banner"
onInput={(e) => this.onInput(e, "banner")}
type="text"
class={this.styles.field.input}
class={`focus:outline-none focus:border-white w-full px-4 py-3 rounded-lg resize-y bg-transparent border-[2px] border-[${DividerBackgroundColor}] placeholder-[${PlaceholderColor}] text-[${PrimaryTextColor}]`}
/>
<span class={this.styles.custom.error}>{this.state.newFieldKeyError}</span>
<h3 class={this.styles.field.title}>
Field value
<h3 class={`text-[${PrimaryTextColor}] mt-8`}>
About
</h3>
<textarea
ref={this.newFieldValue}
placeholder="e.g. Sports, Reading, Design"
rows={1}
onInput={(e) => this.onInput(e)}
<input
placeholder={"tell us about yourself"}
value={state.profileData?.about}
name="about"
onInput={(e) => this.onInput(e, "about")}
type="text"
class={this.styles.field.input}
>
</textarea>
<button class={this.styles.addButton} onClick={this.addField}>Add Field</button>
<div class={`${DividerClass}`}></div>
<button class={this.styles.submitButton} onClick={this.onSubmit}>Update Profile</button>
class={`focus:outline-none focus:border-white w-full px-4 py-3 rounded-lg resize-y bg-transparent border-[2px] border-[${DividerBackgroundColor}] placeholder-[${PlaceholderColor}] text-[${PrimaryTextColor}] `}
/>
<h3 class={`text-[${PrimaryTextColor}] mt-8`}>
Website
</h3>
<input
value={state.profileData?.website}
name="website"
onInput={(e) => this.onInput(e, "website")}
type="text"
class={`focus:outline-none focus:border-white w-full px-4 py-3 rounded-lg resize-y bg-transparent border-[2px] border-[${DividerBackgroundColor}] placeholder-[${PlaceholderColor}] text-[${PrimaryTextColor}]`}
/>
</div>
<button
class={`w-full p-3 rounded-lg text-[${PrimaryTextColor}] flex items-center justify-center bg-gradient-to-r from-[#FF762C] via-[#FF3A5E] to-[#FF01A9] hover:bg-gradient-to-l`}
type="submit"
>
Update Profile
</button>
</form>
);
}
styles = {
container: `py-4 bg-[${SecondaryBackgroundColor}]`,
banner: {
container: `h-72 w-full rounded-lg mb-20 relative`,
},
field: {
title: `text-[${PrimaryTextColor}] mt-8`,
input: `${InputClass}`,
hint: {
text: `text-sm text-[${HintTextColor}]`,
link: `text-[${HintLinkColor}]`,
},
},
addButton:
`w-full mt-6 p-3 rounded-lg ${NoOutlineClass} text-[${PrimaryTextColor}] bg-[${DividerBackgroundColor}] hover:bg-[${HoverButtonBackgroundColor}] ${CenterClass}`,
submitButton:
`w-full p-3 rounded-lg ${NoOutlineClass} text-[${PrimaryTextColor}] ${CenterClass} ${LinearGradientsClass} hover:bg-gradient-to-l`,
divider: `${DividerClass}`,
custom: {
title: `text-[${PrimaryTextColor}] font-bold text-sm`,
text: `text-[${HintTextColor}] text-sm`,
error: `text-sm text-[${ErrorColor}]`,
},
};
newFieldKey = createRef<HTMLInputElement>();
newFieldValue = createRef<HTMLTextAreaElement>();
onInput = (e: h.JSX.TargetedEvent<HTMLTextAreaElement, Event>, key?: string) => {
onInput = (e: h.JSX.TargetedEvent<HTMLTextAreaElement | HTMLInputElement, Event>, key?: string) => {
const lines = e.currentTarget.value.split("\n");
e.currentTarget.setAttribute(
"rows",
@ -205,6 +170,9 @@ export class EditProfile extends Component<Props, State> {
return;
}
console.log(`Adding field ${this.newFieldKey.current.value}`);
console.log(`Adding field ${this.newFieldValue.current.value}`);
this.setState({
profileData: {
...this.state.profileData,
@ -217,7 +185,8 @@ export class EditProfile extends Component<Props, State> {
this.newFieldValue.current.value = "";
};
onSubmit = () => {
onSubmit = (e: h.JSX.TargetedEvent) => {
e.preventDefault();
this.props.emit({
type: "SaveProfile",
ctx: this.props.ctx,

View File

@ -0,0 +1,29 @@
/** @jsx h */
import { h } from "https://esm.sh/preact@10.17.1";
export function PlusCircleIcon(props: {
class?: string | h.JSX.SignalLike<string | undefined> | undefined;
style?:
| string
| h.JSX.CSSProperties
| h.JSX.SignalLike<string | h.JSX.CSSProperties>
| undefined;
}) {
return (
<svg
class={props.class}
style={props.style}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 8V16M8 12H16M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z"
stroke="currentColor"
stroke-width="2.67"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
}

View File

@ -13,17 +13,17 @@ export function UserIcon(props: {
<svg
class={props.class}
style={props.style}
viewBox="0 0 32 32"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g id="user-03">
<path
d="M4 26.6667C7.11438 23.3634 11.3427 21.3333 16 21.3333C20.6573 21.3333 24.8856 23.3634 28 26.6667M22 10C22 13.3137 19.3137 16 16 16C12.6863 16 10 13.3137 10 10C10 6.68629 12.6863 4 16 4C19.3137 4 22 6.68629 22 10Z"
d="M3 20C5.33579 17.5226 8.50702 16 12 16C15.493 16 18.6642 17.5226 21 20M16.5 7.5C16.5 9.98528 14.4853 12 12 12C9.51472 12 7.5 9.98528 7.5 7.5C7.5 5.01472 9.51472 3 12 3C14.4853 3 16.5 5.01472 16.5 7.5Z"
stroke="currentColor"
stroke-width="2.67"
stroke-linecap="round"
stroke-linejoin="round"
/>
</g>
</svg>
);
}