mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 15:43:20 +00:00
refactor EditProfile (#255)
This commit is contained in:
parent
114cf7a2c7
commit
82c9457ed2
10
UI/app.tsx
10
UI/app.tsx
@ -502,11 +502,11 @@ export function AppComponent(props: {
|
||||
<div
|
||||
class={tw`max-w-[35rem] h-full m-auto`}
|
||||
>
|
||||
{EditProfile({
|
||||
emit: app.eventBus.emit,
|
||||
myProfile: model.myProfile,
|
||||
newProfileField: model.newProfileField,
|
||||
})}
|
||||
<EditProfile
|
||||
ctx={model.app.ctx}
|
||||
profileGetter={app.database}
|
||||
emit={props.eventBus.emit}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{dmVNode}
|
||||
|
@ -12,7 +12,6 @@ import { DirectedMessageController, sendDMandImages } from "../features/dm.ts";
|
||||
import { notify } from "./notification.ts";
|
||||
import { emitFunc, EventBus } from "../event-bus.ts";
|
||||
import { ContactUpdate } from "./conversation-list.tsx";
|
||||
import { MyProfileUpdate } from "./edit-profile.tsx";
|
||||
import { EditorEvent, EditorModel, new_DM_EditorModel, SendMessage } from "./editor.tsx";
|
||||
import { DirectMessagePanelUpdate } from "./message-panel.tsx";
|
||||
import { NavigationUpdate } from "./nav.tsx";
|
||||
@ -46,6 +45,7 @@ import { GroupMessageController } from "../features/gm.ts";
|
||||
import { ChatMessage } from "./message.ts";
|
||||
import { InviteUsersToGroup } from "./invite-button.tsx";
|
||||
import { IS_BETA_VERSION } from "./config.js";
|
||||
import { SaveProfile } from "./edit-profile.tsx";
|
||||
|
||||
export type UI_Interaction_Event =
|
||||
| SearchUpdate
|
||||
@ -54,7 +54,7 @@ export type UI_Interaction_Event =
|
||||
| NavigationUpdate
|
||||
| DirectMessagePanelUpdate
|
||||
| BackToContactList
|
||||
| MyProfileUpdate
|
||||
| SaveProfile
|
||||
| PinConversation
|
||||
| UnpinConversation
|
||||
| SignInEvent
|
||||
@ -243,23 +243,14 @@ export async function* UI_Interaction_Update(args: {
|
||||
console.log(editor);
|
||||
} //
|
||||
//
|
||||
// MyProfile
|
||||
// Profile
|
||||
//
|
||||
else if (event.type == "EditMyProfile") {
|
||||
model.myProfile = Object.assign(model.myProfile || {}, event.profile);
|
||||
} else if (event.type == "SaveMyProfile") {
|
||||
InsertNewProfileField(app.model);
|
||||
else if (event.type == "SaveProfile") {
|
||||
await saveProfile(
|
||||
event.profile,
|
||||
app.ctx,
|
||||
event.ctx,
|
||||
pool,
|
||||
);
|
||||
} else if (event.type == "EditNewProfileFieldKey") {
|
||||
model.newProfileField.key = event.key;
|
||||
} else if (event.type == "EditNewProfileFieldValue") {
|
||||
model.newProfileField.value = event.value;
|
||||
} else if (event.type == "InsertNewProfileField") {
|
||||
InsertNewProfileField(app.model);
|
||||
} //
|
||||
//
|
||||
// Navigation
|
||||
@ -662,18 +653,6 @@ export async function* Database_Update(
|
||||
}
|
||||
}
|
||||
|
||||
function InsertNewProfileField(model: Model) {
|
||||
if (model.newProfileField.key && model.newProfileField.value) {
|
||||
model.myProfile = Object.assign(model.myProfile || {}, {
|
||||
[model.newProfileField.key]: model.newProfileField.value,
|
||||
});
|
||||
model.newProfileField = {
|
||||
key: "",
|
||||
value: "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function handle_SendMessage(
|
||||
event: SendMessage,
|
||||
ctx: NostrAccountContext,
|
||||
|
@ -120,7 +120,7 @@ export class ConversationList extends Component<Props, State> {
|
||||
class={tw`w-full h-10 ${CenterClass} text-sm text-[${PrimaryTextColor}] !hover:bg-transparent hover:font-bold group`}
|
||||
>
|
||||
<ChatIcon
|
||||
class={tw`w-4 h-4m mr-1 text-[${PrimaryTextColor}] stroke-current`}
|
||||
class={tw`w-4 h-4 mr-1 text-[${PrimaryTextColor}] stroke-current`}
|
||||
style={{
|
||||
fill: "none",
|
||||
}}
|
||||
|
@ -1,13 +1,10 @@
|
||||
/** @jsx h */
|
||||
import { Fragment, h } from "https://esm.sh/preact@10.17.1";
|
||||
import { createRef, Fragment, h } from "https://esm.sh/preact@10.17.1";
|
||||
import { tw } from "https://esm.sh/twind@0.16.16";
|
||||
import { Avatar } from "./components/avatar.tsx";
|
||||
import { emitFunc, EventEmitter } from "../event-bus.ts";
|
||||
import {
|
||||
ButtonClass,
|
||||
CenterClass,
|
||||
DividerClass,
|
||||
inputBorderClass,
|
||||
InputClass,
|
||||
LinearGradientsClass,
|
||||
NoOutlineClass,
|
||||
@ -15,309 +12,263 @@ import {
|
||||
import { ProfileData } from "../features/profile.ts";
|
||||
import {
|
||||
DividerBackgroundColor,
|
||||
ErrorColor,
|
||||
HintLinkColor,
|
||||
HintTextColor,
|
||||
HoverButtonBackgroudColor,
|
||||
PlaceholderColor,
|
||||
PrimaryTextColor,
|
||||
} from "./style/colors.ts";
|
||||
import { Component, ComponentChildren } from "https://esm.sh/preact@10.11.3";
|
||||
import { ProfileGetter } from "./search.tsx";
|
||||
import { NostrAccountContext } from "../lib/nostr-ts/nostr.ts";
|
||||
import { emitFunc } from "../event-bus.ts";
|
||||
|
||||
export type MyProfileUpdate =
|
||||
| Edit
|
||||
| Save
|
||||
| EditNewProfileFieldKey
|
||||
| EditNewProfileFieldValue
|
||||
| InsertNewProfileField;
|
||||
|
||||
export type Edit = {
|
||||
type: "EditMyProfile";
|
||||
export type SaveProfile = {
|
||||
type: "SaveProfile";
|
||||
profile: ProfileData;
|
||||
ctx: NostrAccountContext;
|
||||
};
|
||||
|
||||
type Save = {
|
||||
type: "SaveMyProfile";
|
||||
profile: ProfileData;
|
||||
};
|
||||
|
||||
export type EditNewProfileFieldKey = {
|
||||
type: "EditNewProfileFieldKey";
|
||||
type profileItem = {
|
||||
key: string;
|
||||
value?: string;
|
||||
hint?: ComponentChildren;
|
||||
};
|
||||
|
||||
export type EditNewProfileFieldValue = {
|
||||
type: "EditNewProfileFieldValue";
|
||||
value: string;
|
||||
type Props = {
|
||||
ctx: NostrAccountContext;
|
||||
profileGetter: ProfileGetter;
|
||||
emit: emitFunc<SaveProfile>;
|
||||
};
|
||||
|
||||
export type InsertNewProfileField = {
|
||||
type: "InsertNewProfileField";
|
||||
type State = {
|
||||
profile: ProfileData | undefined;
|
||||
newFieldKeyError: string;
|
||||
};
|
||||
|
||||
export function EditProfile(props: {
|
||||
emit: emitFunc<MyProfileUpdate>;
|
||||
myProfile: ProfileData | undefined;
|
||||
newProfileField: {
|
||||
key: string;
|
||||
value: string;
|
||||
export class EditProfile extends Component<Props, State> {
|
||||
styles = {
|
||||
container: tw`py-4`,
|
||||
banner: {
|
||||
container: tw`h-72 w-full rounded-lg mb-20 relative`,
|
||||
avatar:
|
||||
tw`w-24 h-24 m-auto absolute top-60 left-1/2 box-border border-2 border-[${PrimaryTextColor}] -translate-x-2/4`,
|
||||
},
|
||||
avatar: tw`w-24 h-24 m-auto box-border border-2 border-[${PrimaryTextColor}]`,
|
||||
field: {
|
||||
title: tw`text-[${PrimaryTextColor}] mt-8`,
|
||||
input: tw`${InputClass}`,
|
||||
hint: {
|
||||
text: tw`text-sm text-[${HintTextColor}]`,
|
||||
link: tw`text-[${HintLinkColor}]`,
|
||||
},
|
||||
},
|
||||
addButton:
|
||||
tw`w-full mt-6 p-3 rounded-lg ${NoOutlineClass} text-[${PrimaryTextColor}] bg-[${DividerBackgroundColor}] hover:bg-[${HoverButtonBackgroudColor}] ${CenterClass}`,
|
||||
submitButton:
|
||||
tw`w-full p-3 rounded-lg ${NoOutlineClass} text-[${PrimaryTextColor}] ${CenterClass} ${LinearGradientsClass} hover:bg-gradient-to-l`,
|
||||
divider: tw`${DividerClass}`,
|
||||
custom: {
|
||||
title: tw`text-[${PrimaryTextColor}] font-bold text-sm`,
|
||||
text: tw`text-[${HintTextColor}] text-sm`,
|
||||
error: tw`text-sm text-[${ErrorColor}]`,
|
||||
},
|
||||
};
|
||||
}) {
|
||||
return (
|
||||
<div class={tw`py-[3rem]`}>
|
||||
{props.myProfile?.banner
|
||||
? (
|
||||
<div
|
||||
class={tw`h-[18.75rem] w-full rounded-lg relative`}
|
||||
style={{
|
||||
background: `url(${
|
||||
props.myProfile?.banner ? props.myProfile.banner : "default-bg.png"
|
||||
}) no-repeat center center / cover`,
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
picture={props.myProfile?.picture}
|
||||
class={tw`w-[6.25rem] h-[6.25rem] m-auto absolute top-[15.62rem] left-[50%] box-border border-[3px] border-[${PrimaryTextColor}]`}
|
||||
style={{
|
||||
transform: "translate(-50%, 0%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<Avatar
|
||||
picture={props.myProfile?.picture}
|
||||
class={tw`w-[6.25rem] h-[6.25rem] m-auto box-border border-[3px] border-[${PrimaryTextColor}]`}
|
||||
/>
|
||||
)}
|
||||
<h3 class={tw`text-[${PrimaryTextColor}] mt-[4.5rem]`}>
|
||||
Name
|
||||
</h3>
|
||||
<textarea
|
||||
placeholder="Name"
|
||||
rows={props.myProfile?.name?.split("\n")?.length || 1}
|
||||
value={props.myProfile?.name}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditMyProfile",
|
||||
profile: {
|
||||
name: e.currentTarget.value,
|
||||
},
|
||||
});
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
>
|
||||
</textarea>
|
||||
<h3 class={tw`mt-[1.5rem] text-[${PrimaryTextColor}]`}>
|
||||
Picture
|
||||
</h3>
|
||||
<textarea
|
||||
placeholder="Profile Image URL"
|
||||
rows={props.myProfile?.picture?.split("\n")?.length || 1}
|
||||
value={props.myProfile?.picture}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditMyProfile",
|
||||
profile: {
|
||||
picture: e.currentTarget.value,
|
||||
},
|
||||
});
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
>
|
||||
</textarea>
|
||||
<span class={tw`text-[0.875rem] text-[${HintTextColor}]`}>
|
||||
You can upload your images on websites like{" "}
|
||||
<a class={tw`text-[${HintLinkColor}]`} href="https://nostr.build/" target="_blank">
|
||||
nostr.build
|
||||
</a>
|
||||
</span>
|
||||
<h3 class={tw`mt-[1.5rem] text-[${PrimaryTextColor}]`}>
|
||||
About
|
||||
</h3>
|
||||
<textarea
|
||||
placeholder="About"
|
||||
rows={props.myProfile?.about?.split("\n")?.length || 1}
|
||||
value={props.myProfile?.about}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditMyProfile",
|
||||
profile: {
|
||||
about: e.currentTarget.value,
|
||||
},
|
||||
});
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
>
|
||||
</textarea>
|
||||
<h3 class={tw`mt-[1.5rem] text-[${PrimaryTextColor}]`}>
|
||||
Website
|
||||
</h3>
|
||||
<textarea
|
||||
placeholder="Website"
|
||||
rows={props.myProfile?.website?.split("\n")?.length || 1}
|
||||
value={props.myProfile?.website}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditMyProfile",
|
||||
profile: {
|
||||
website: e.currentTarget.value,
|
||||
},
|
||||
});
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
>
|
||||
</textarea>
|
||||
<h3 class={tw`mt-[1.5rem] text-[${PrimaryTextColor}]`}>
|
||||
Banner
|
||||
</h3>
|
||||
<textarea
|
||||
placeholder="Banner Image Url"
|
||||
rows={props.myProfile?.banner?.split("\n")?.length || 1}
|
||||
value={props.myProfile?.banner}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditMyProfile",
|
||||
profile: {
|
||||
banner: e.currentTarget.value,
|
||||
},
|
||||
});
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
>
|
||||
</textarea>
|
||||
{props.myProfile
|
||||
? Object.entries(props.myProfile).map(([key, value]) => {
|
||||
if (["name", "picture", "about", "website", "banner"].includes(key) || !value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h3 class={tw`mt-[1.5rem] text-[${PrimaryTextColor}]`}>
|
||||
{key}
|
||||
</h3>
|
||||
<textarea
|
||||
placeholder={key}
|
||||
rows={value.toString().split("\n").length}
|
||||
value={value}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditMyProfile",
|
||||
profile: {
|
||||
[key]: e.currentTarget.value,
|
||||
},
|
||||
});
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
>
|
||||
</textarea>
|
||||
</Fragment>
|
||||
);
|
||||
})
|
||||
: undefined}
|
||||
<div class={tw`${DividerClass}`}></div>
|
||||
<p class={tw`text-[${PrimaryTextColor}] font-blod text-[0.8125rem]`}>Custom Fields</p>
|
||||
<span class={tw`text-[${HintTextColor}] text-[0.875rem]`}>
|
||||
Create your own custom fields, anything goes!
|
||||
</span>
|
||||
<h3 class={tw`text-[${PrimaryTextColor}] mt-[1.5rem]`}>
|
||||
Field name
|
||||
</h3>
|
||||
<input
|
||||
placeholder="e.g. hobbies"
|
||||
value={props.newProfileField.key}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditNewProfileFieldKey",
|
||||
key: e.currentTarget.value,
|
||||
});
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
/>
|
||||
<h3 class={tw`mt-[1.5rem] text-[${PrimaryTextColor}]`}>
|
||||
Field value
|
||||
</h3>
|
||||
<textarea
|
||||
placeholder="e.g. Sports, Reading, Design"
|
||||
rows={1}
|
||||
value={props.newProfileField.value}
|
||||
onInput={(e) => {
|
||||
props.emit({
|
||||
type: "EditNewProfileFieldValue",
|
||||
value: e.currentTarget.value,
|
||||
});
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
}}
|
||||
type="text"
|
||||
class={tw`${InputClass}`}
|
||||
>
|
||||
</textarea>
|
||||
<button
|
||||
class={tw`w-full mt-[1.5rem] p-[0.75rem] rounded-lg ${NoOutlineClass} text-[${PrimaryTextColor}] bg-[${DividerBackgroundColor}] hover:bg-[${HoverButtonBackgroudColor}] ${CenterClass}`}
|
||||
onClick={() => {
|
||||
props.emit({
|
||||
type: "InsertNewProfileField",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Add Field
|
||||
</button>
|
||||
<div class={tw`${DividerClass}`}></div>
|
||||
<div class={tw`mt-[1.5rem] flex justify-end`}>
|
||||
<button
|
||||
class={tw`w-full p-[0.75rem] rounded-lg ${NoOutlineClass} text-[${PrimaryTextColor}] ${CenterClass} ${LinearGradientsClass} hover:bg-gradient-to-l`}
|
||||
onClick={async () => {
|
||||
if (props.myProfile) {
|
||||
props.emit({
|
||||
type: "SaveMyProfile",
|
||||
profile: props.myProfile,
|
||||
});
|
||||
}
|
||||
componentDidMount() {
|
||||
const { ctx, profileGetter } = this.props;
|
||||
this.setState({
|
||||
profile: profileGetter.getProfilesByPublicKey(ctx.publicKey)?.profile,
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(_: Readonly<Props>, nextState: Readonly<State>, __: any): boolean {
|
||||
return JSON.stringify(this.state.profile) != JSON.stringify(nextState.profile);
|
||||
}
|
||||
|
||||
newFieldKey = createRef<HTMLInputElement>();
|
||||
newFieldValue = createRef<HTMLTextAreaElement>();
|
||||
|
||||
onInput = (e: h.JSX.TargetedEvent<HTMLTextAreaElement, Event>, key?: string) => {
|
||||
const lines = e.currentTarget.value.split("\n");
|
||||
e.currentTarget.setAttribute(
|
||||
"rows",
|
||||
`${lines.length}`,
|
||||
);
|
||||
if (key) {
|
||||
const value = e.currentTarget.value;
|
||||
this.setState({
|
||||
profile: {
|
||||
...this.state.profile,
|
||||
[key]: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
addField = () => {
|
||||
if (!this.newFieldKey.current || !this.newFieldValue.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.newFieldKey.current.value.trim() == "") {
|
||||
this.setState({
|
||||
newFieldKeyError: "Key is required.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
profile: {
|
||||
...this.state.profile,
|
||||
[this.newFieldKey.current.value]: this.newFieldValue.current.value,
|
||||
},
|
||||
newFieldKeyError: "",
|
||||
});
|
||||
|
||||
this.newFieldKey.current.value = "";
|
||||
this.newFieldValue.current.value = "";
|
||||
};
|
||||
|
||||
onSubmit = () => {
|
||||
if (!this.state.profile) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.emit({
|
||||
type: "SaveProfile",
|
||||
ctx: this.props.ctx,
|
||||
profile: this.state.profile,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const profileItems: profileItem[] = [
|
||||
{
|
||||
key: "name",
|
||||
value: this.state.profile?.name,
|
||||
},
|
||||
{
|
||||
key: "picture",
|
||||
value: this.state.profile?.picture,
|
||||
hint: (
|
||||
<span class={this.styles.field.hint.text}>
|
||||
You can upload your images on websites like{" "}
|
||||
<a class={this.styles.field.hint.link} href="https://nostr.build/" target="_blank">
|
||||
nostr.build
|
||||
</a>
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "about",
|
||||
value: this.state.profile?.about,
|
||||
},
|
||||
{
|
||||
key: "website",
|
||||
value: this.state.profile?.website,
|
||||
},
|
||||
{
|
||||
key: "banner",
|
||||
value: this.state.profile?.banner,
|
||||
},
|
||||
];
|
||||
|
||||
if (this.state.profile) {
|
||||
for (const [key, value] of Object.entries(this.state.profile)) {
|
||||
if (["name", "picture", "about", "website", "banner"].includes(key) || !value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
profileItems.push({
|
||||
key: key,
|
||||
value: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const banner = this.state.profile?.banner
|
||||
? (
|
||||
<div
|
||||
class={this.styles.banner.container}
|
||||
style={{
|
||||
background: `url(${
|
||||
this.state.profile?.banner ? this.state.profile.banner : "default-bg.png"
|
||||
}) no-repeat center center / cover`,
|
||||
}}
|
||||
>
|
||||
Update Profile
|
||||
</button>
|
||||
<Avatar
|
||||
picture={this.state.profile?.picture}
|
||||
class={this.styles.banner.avatar}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<Avatar
|
||||
picture={this.state.profile?.picture}
|
||||
class={this.styles.avatar}
|
||||
/>
|
||||
);
|
||||
|
||||
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}>
|
||||
{banner}
|
||||
{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>
|
||||
<input
|
||||
ref={this.newFieldKey}
|
||||
placeholder="e.g. hobbies"
|
||||
type="text"
|
||||
class={this.styles.field.input}
|
||||
/>
|
||||
<span class={this.styles.custom.error}>{this.state.newFieldKeyError}</span>
|
||||
|
||||
<h3 class={this.styles.field.title}>
|
||||
Field value
|
||||
</h3>
|
||||
<textarea
|
||||
ref={this.newFieldValue}
|
||||
placeholder="e.g. Sports, Reading, Design"
|
||||
rows={1}
|
||||
onInput={(e) => this.onInput(e)}
|
||||
type="text"
|
||||
class={this.styles.field.input}
|
||||
>
|
||||
</textarea>
|
||||
|
||||
<button class={this.styles.addButton} onClick={this.addField}>Add Field</button>
|
||||
|
||||
<div class={tw`${DividerClass}`}></div>
|
||||
|
||||
<button class={this.styles.submitButton} onClick={this.onSubmit}>Update Profile</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export function ProfileCard(props: {
|
||||
profile: {
|
||||
container: tw`flex`,
|
||||
avatar: tw`w-10 h-10`,
|
||||
name: tw`text-[1.2rem] font-blod leading-10 truncate ml-2`,
|
||||
name: tw`text-[1.2rem] font-bold leading-10 truncate ml-2`,
|
||||
},
|
||||
divider: tw`${DividerClass} my-[0.5rem]`,
|
||||
about: tw`text-[0.8rem]`,
|
||||
|
Loading…
Reference in New Issue
Block a user