mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 07:33:22 +00:00
fix profile state (#420)
This commit is contained in:
parent
1bc41d6852
commit
83a81fdf81
@ -240,12 +240,6 @@ export class App {
|
||||
forever(sync_kind_1(this.pool, this.database));
|
||||
}
|
||||
|
||||
/* my profile */
|
||||
const myProfileEvent = this.database.getProfilesByPublicKey(this.ctx.publicKey);
|
||||
if (myProfileEvent != undefined) {
|
||||
this.model.myProfile = myProfileEvent.profile;
|
||||
}
|
||||
|
||||
// Database
|
||||
(async () => {
|
||||
let i = 0;
|
||||
@ -415,7 +409,7 @@ export class AppComponent extends Component<AppProps> {
|
||||
>
|
||||
<EditProfile
|
||||
ctx={model.app.ctx}
|
||||
profileGetter={app.database}
|
||||
profile={app.database.getProfilesByPublicKey(myAccountCtx.publicKey)?.profile}
|
||||
emit={props.eventBus.emit}
|
||||
/>
|
||||
</div>
|
||||
|
@ -15,7 +15,6 @@ export type Model = {
|
||||
social: Social_Model;
|
||||
|
||||
// profile
|
||||
myProfile?: ProfileData;
|
||||
newProfileField: {
|
||||
key: string;
|
||||
value: string;
|
||||
@ -42,6 +41,5 @@ export function initialModel(): Model {
|
||||
navigationModel: {
|
||||
activeNav: "Public",
|
||||
},
|
||||
myProfile: undefined,
|
||||
};
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import { OtherConfig } from "./config-other.ts";
|
||||
import { DM_List } from "./conversation-list.ts";
|
||||
import { ContactUpdate } from "./conversation-list.tsx";
|
||||
import { StartInvite } from "./dm.tsx";
|
||||
import { EditGroup, StartEditGroupChatProfile } from "./edit-group.tsx";
|
||||
import { SaveProfile } from "./edit-profile.tsx";
|
||||
import { EditorEvent, SendMessage } from "./editor.tsx";
|
||||
import { EventDetail, EventDetailItem } from "./event-detail.tsx";
|
||||
@ -70,7 +69,6 @@ export type UI_Interaction_Event =
|
||||
| UnpinConversation
|
||||
| SignInEvent
|
||||
| RelayConfigChange
|
||||
| StartEditGroupChatProfile
|
||||
| StartInvite
|
||||
| ViewRelayDetail
|
||||
| ViewRecommendedRelaysList
|
||||
@ -280,24 +278,23 @@ const handle_update_event = async (chan: PutChannel<true>, args: {
|
||||
// Profile
|
||||
//
|
||||
else if (event.type == "SaveProfile") {
|
||||
if (current_relay.status() != "Open") {
|
||||
app.toastInputChan.put(() => `${current_relay.url} is not connected yet`);
|
||||
} else if (event.profile == undefined) {
|
||||
if (event.profile == undefined) {
|
||||
app.toastInputChan.put(() => "profile is empty");
|
||||
} else {
|
||||
const result = await saveProfile(
|
||||
saveProfile(
|
||||
event.profile,
|
||||
event.ctx,
|
||||
current_relay,
|
||||
);
|
||||
app.popOverInputChan.put({ children: undefined });
|
||||
if (result instanceof Error) {
|
||||
app.toastInputChan.put(
|
||||
SendingEventRejection(eventBus.emit, current_relay.url, result.message),
|
||||
);
|
||||
} else {
|
||||
app.toastInputChan.put(() => "profile has been updated");
|
||||
}
|
||||
).then((result) => {
|
||||
app.popOverInputChan.put({ children: undefined });
|
||||
if (result instanceof Error) {
|
||||
app.toastInputChan.put(
|
||||
SendingEventRejection(eventBus.emit, current_relay.url, result.message),
|
||||
);
|
||||
} else {
|
||||
app.toastInputChan.put(() => "profile has been updated");
|
||||
}
|
||||
});
|
||||
}
|
||||
} //
|
||||
//
|
||||
@ -336,16 +333,6 @@ const handle_update_event = async (chan: PutChannel<true>, args: {
|
||||
);
|
||||
} else if (event.type == "OpenNote") {
|
||||
open(`https://nostrapp.link/#${NoteID.FromHex(event.event.id).bech32()}?select=true`);
|
||||
} else if (event.type == "StartEditGroupChatProfile") {
|
||||
app.popOverInputChan.put({
|
||||
children: (
|
||||
<EditGroup
|
||||
emit={eventBus.emit}
|
||||
ctx={event.ctx}
|
||||
profileGetter={app.database}
|
||||
/>
|
||||
),
|
||||
});
|
||||
} else if (event.type == "StartInvite") {
|
||||
app.popOverInputChan.put({
|
||||
children: <div></div>,
|
||||
@ -508,16 +495,7 @@ export async function* Database_Update(
|
||||
lamport.set(t);
|
||||
}
|
||||
if (e.kind == NostrKind.META_DATA || e.kind == NostrKind.DIRECT_MESSAGE) {
|
||||
if (e.kind == NostrKind.META_DATA) {
|
||||
// my profile update
|
||||
if (ctx && e.pubkey == ctx.publicKey.hex) {
|
||||
const newProfile = database.getProfilesByPublicKey(ctx.publicKey);
|
||||
if (newProfile == undefined) {
|
||||
throw new Error("impossible");
|
||||
}
|
||||
model.myProfile = newProfile.profile;
|
||||
}
|
||||
} else if (e.kind == NostrKind.DIRECT_MESSAGE) {
|
||||
if (e.kind == NostrKind.DIRECT_MESSAGE) {
|
||||
console.log("add event");
|
||||
const err = await dmController.addEvent({
|
||||
...e,
|
||||
|
@ -11,9 +11,9 @@ export type RelayRecord = {
|
||||
export class DexieDatabase extends Dexie implements EventsAdapter, RelayRecorder, EventMarker, EventRemover {
|
||||
// 'events' is added by dexie when declaring the stores()
|
||||
// We just tell the typing system this is the case
|
||||
events!: Table<NostrEvent>;
|
||||
relayRecords!: Table<RelayRecord>;
|
||||
eventMarks!: Table<EventMark>;
|
||||
private events!: Table<NostrEvent>;
|
||||
private relayRecords!: Table<RelayRecord>;
|
||||
private eventMarks!: Table<EventMark>;
|
||||
|
||||
constructor() {
|
||||
super("Events");
|
||||
@ -28,7 +28,8 @@ export class DexieDatabase extends Dexie implements EventsAdapter, RelayRecorder
|
||||
return this.events.toArray();
|
||||
}
|
||||
async get(keys: Indices) {
|
||||
return this.events.get(keys);
|
||||
const e = await this.events.get(keys);
|
||||
return e;
|
||||
}
|
||||
async put(e: NostrEvent<NostrKind, Tag>): Promise<void> {
|
||||
await this.events.put(e);
|
||||
|
@ -1,21 +0,0 @@
|
||||
import { h, render } from "https://esm.sh/preact@10.17.1";
|
||||
import { InMemoryAccountContext } from "../../libs/nostr.ts/nostr.ts";
|
||||
import { test_db_view, testEventBus } from "./_setup.test.ts";
|
||||
import { EditGroup } from "./edit-group.tsx";
|
||||
|
||||
const database = await test_db_view();
|
||||
|
||||
const ctx = InMemoryAccountContext.Generate();
|
||||
|
||||
render(
|
||||
<EditGroup
|
||||
emit={testEventBus.emit}
|
||||
ctx={ctx}
|
||||
profileGetter={database}
|
||||
/>,
|
||||
document.body,
|
||||
);
|
||||
|
||||
for await (const e of testEventBus.onChange()) {
|
||||
console.log(e);
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/** @jsx h */
|
||||
import { h } from "https://esm.sh/preact@10.17.1";
|
||||
import { PrimaryTextColor, SecondaryBackgroundColor, TitleIconColor } from "./style/colors.ts";
|
||||
import { GroupIcon } from "./icons/group-icon.tsx";
|
||||
import { emitFunc } from "../event-bus.ts";
|
||||
import { ProfileGetter } from "./search.tsx";
|
||||
import { EditProfile, SaveProfile } from "./edit-profile.tsx";
|
||||
import { NostrAccountContext } from "../../libs/nostr.ts/nostr.ts";
|
||||
|
||||
export type StartEditGroupChatProfile = {
|
||||
type: "StartEditGroupChatProfile";
|
||||
ctx: NostrAccountContext;
|
||||
};
|
||||
|
||||
export function EditGroup(props: {
|
||||
ctx: NostrAccountContext;
|
||||
profileGetter: ProfileGetter;
|
||||
emit: emitFunc<SaveProfile>;
|
||||
}) {
|
||||
const styles = {
|
||||
container: `py-6 px-4 bg-[${SecondaryBackgroundColor}]`,
|
||||
header: {
|
||||
container: `text-[${PrimaryTextColor}] text-xl flex`,
|
||||
icon: `w-8 h-8 mr-4 text-[${TitleIconColor}] fill-current`,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={styles.container}>
|
||||
<p class={styles.header.container}>
|
||||
<GroupIcon class={styles.header.icon} />
|
||||
Update Group
|
||||
</p>
|
||||
<EditProfile ctx={props.ctx} profileGetter={props.profileGetter} emit={props.emit} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -38,16 +38,21 @@ type profileItem = {
|
||||
|
||||
type Props = {
|
||||
ctx: NostrAccountContext;
|
||||
profileGetter: ProfileGetter;
|
||||
profile: ProfileData | undefined;
|
||||
emit: emitFunc<SaveProfile>;
|
||||
};
|
||||
|
||||
type State = {
|
||||
profile: ProfileData | undefined;
|
||||
profileData: ProfileData;
|
||||
newFieldKeyError: string;
|
||||
};
|
||||
|
||||
export class EditProfile extends Component<Props, State> {
|
||||
state: Readonly<State> = {
|
||||
newFieldKeyError: "",
|
||||
profileData: {},
|
||||
};
|
||||
|
||||
styles = {
|
||||
container: `py-4 bg-[${SecondaryBackgroundColor}]`,
|
||||
banner: {
|
||||
@ -73,17 +78,6 @@ export class EditProfile extends Component<Props, State> {
|
||||
},
|
||||
};
|
||||
|
||||
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>();
|
||||
|
||||
@ -96,8 +90,8 @@ export class EditProfile extends Component<Props, State> {
|
||||
if (key) {
|
||||
const value = e.currentTarget.value;
|
||||
this.setState({
|
||||
profile: {
|
||||
...this.state.profile,
|
||||
profileData: {
|
||||
...this.state.profileData,
|
||||
[key]: value,
|
||||
},
|
||||
});
|
||||
@ -117,8 +111,8 @@ export class EditProfile extends Component<Props, State> {
|
||||
}
|
||||
|
||||
this.setState({
|
||||
profile: {
|
||||
...this.state.profile,
|
||||
profileData: {
|
||||
...this.state.profileData,
|
||||
[this.newFieldKey.current.value]: this.newFieldValue.current.value,
|
||||
},
|
||||
newFieldKeyError: "",
|
||||
@ -132,7 +126,7 @@ export class EditProfile extends Component<Props, State> {
|
||||
this.props.emit({
|
||||
type: "SaveProfile",
|
||||
ctx: this.props.ctx,
|
||||
profile: this.state.profile,
|
||||
profile: this.state.profileData,
|
||||
});
|
||||
};
|
||||
|
||||
@ -140,15 +134,15 @@ export class EditProfile extends Component<Props, State> {
|
||||
const profileItems: profileItem[] = [
|
||||
{
|
||||
key: "name",
|
||||
value: this.state.profile?.name,
|
||||
value: this.state.profileData.name || this.props.profile?.name,
|
||||
},
|
||||
{
|
||||
key: "banner",
|
||||
value: this.state.profile?.banner,
|
||||
value: this.state.profileData.banner || this.props.profile?.banner,
|
||||
},
|
||||
{
|
||||
key: "picture",
|
||||
value: this.state.profile?.picture,
|
||||
value: this.state.profileData.picture || this.props.profile?.picture,
|
||||
hint: (
|
||||
<span class={this.styles.field.hint.text}>
|
||||
You can upload your images on websites like{" "}
|
||||
@ -160,50 +154,14 @@ export class EditProfile extends Component<Props, State> {
|
||||
},
|
||||
{
|
||||
key: "about",
|
||||
value: this.state.profile?.about,
|
||||
value: this.state.profileData.about || this.props.profile?.about,
|
||||
},
|
||||
{
|
||||
key: "website",
|
||||
value: this.state.profile?.website,
|
||||
value: this.state.profileData.website || this.props.profile?.website,
|
||||
},
|
||||
];
|
||||
|
||||
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`,
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
picture={this.state.profile?.picture || robohash(this.props.ctx.publicKey.hex)}
|
||||
class={`w-24 h-24 m-auto absolute top-60 left-1/2 rounded-full box-border border-2 border-[${PrimaryTextColor}] -translate-x-2/4`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<Avatar
|
||||
picture={this.state.profile?.picture || robohash(this.props.ctx.publicKey.hex)}
|
||||
class={`w-24 h-24 m-auto box-border border-2 border-[${PrimaryTextColor}]`}
|
||||
/>
|
||||
);
|
||||
|
||||
const items = profileItems.map((item) => (
|
||||
<Fragment>
|
||||
<h3 class={this.styles.field.title} style={{ textTransform: "capitalize" }}>
|
||||
@ -224,7 +182,6 @@ export class EditProfile extends Component<Props, State> {
|
||||
|
||||
return (
|
||||
<div class={this.styles.container}>
|
||||
{banner}
|
||||
{items}
|
||||
|
||||
<div class={this.styles.divider}></div>
|
||||
|
@ -2,7 +2,6 @@ import { NostrAccountContext } from "../../libs/nostr.ts/nostr.ts";
|
||||
import { ConnectionPool } from "../../libs/nostr.ts/relay-pool.ts";
|
||||
import { parseJSON } from "../features/profile.ts";
|
||||
import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts";
|
||||
import { damus } from "../../libs/nostr.ts/relay-list.test.ts";
|
||||
|
||||
export const default_blowater_relay = "wss://blowater.nostr1.com";
|
||||
|
||||
@ -43,11 +42,6 @@ export class RelayConfig {
|
||||
console.error(res);
|
||||
}
|
||||
});
|
||||
config.add(damus).then((res) => {
|
||||
if (res instanceof Error) {
|
||||
console.error(res);
|
||||
}
|
||||
});
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ Deno.test("Database", async () => {
|
||||
const stream = db.subscribe();
|
||||
const event_to_add = await prepareNormalNostrEvent(ctx, { kind: NostrKind.TEXT_NOTE, content: "1" });
|
||||
await db.addEvent(event_to_add);
|
||||
const e1 = db.get({ id: event_to_add.id });
|
||||
const e1 = db.getEventByID(event_to_add.id);
|
||||
if (!e1) {
|
||||
fail();
|
||||
}
|
||||
@ -115,20 +115,20 @@ Deno.test("mark removed event", async () => {
|
||||
const event_to_add = await prepareNormalNostrEvent(ctx, { kind: NostrKind.TEXT_NOTE, content: "1" });
|
||||
|
||||
const parsed_event = await db.addEvent(event_to_add);
|
||||
const retrieved_event = db.get({ id: event_to_add.id });
|
||||
const retrieved_event = db.getEventByID(event_to_add.id);
|
||||
if (retrieved_event == undefined) fail();
|
||||
|
||||
assertEquals(parsed_event, retrieved_event);
|
||||
assertEquals(retrieved_event.id, event_to_add.id);
|
||||
|
||||
await db.remove(event_to_add.id);
|
||||
const retrieved_event_2 = db.get({ id: event_to_add.id });
|
||||
const retrieved_event_2 = db.getEventByID(event_to_add.id);
|
||||
assertEquals(retrieved_event_2, undefined);
|
||||
|
||||
const added_event = await db.addEvent(event_to_add);
|
||||
assertEquals(added_event, false);
|
||||
|
||||
const retrieved_event_3 = db.get({ id: event_to_add.id });
|
||||
const retrieved_event_3 = db.getEventByID(event_to_add.id);
|
||||
assertEquals(retrieved_event_3, undefined);
|
||||
});
|
||||
|
||||
|
@ -61,10 +61,8 @@ export interface RelayRecordGetter {
|
||||
getRelayRecord: (eventID: string) => Set<string>;
|
||||
}
|
||||
|
||||
export class Database_View
|
||||
implements ProfileSetter, ProfileGetter, EventGetter, EventRemover, RelayRecordGetter {
|
||||
//
|
||||
public readonly sourceOfChange = csp.chan<{ event: Parsed_Event; relay?: string }>(buffer_size);
|
||||
export class Database_View implements ProfileSetter, ProfileGetter, EventRemover, RelayRecordGetter {
|
||||
private readonly sourceOfChange = csp.chan<{ event: Parsed_Event; relay?: string }>(buffer_size);
|
||||
private readonly caster = csp.multi<{ event: Parsed_Event; relay?: string }>(this.sourceOfChange);
|
||||
private readonly profiles = new Map<string, Profile_Nostr_Event>();
|
||||
|
||||
@ -131,13 +129,6 @@ export class Database_View
|
||||
return db;
|
||||
}
|
||||
|
||||
get(keys: Indices): Parsed_Event | undefined {
|
||||
if (this.removedEvents.has(keys.id)) {
|
||||
return;
|
||||
}
|
||||
return this.events.get(keys.id);
|
||||
}
|
||||
|
||||
getEventByID = (id: string | NoteID) => {
|
||||
if (id instanceof NoteID) {
|
||||
id = id.hex;
|
||||
@ -232,12 +223,14 @@ export class Database_View
|
||||
};
|
||||
|
||||
// check if the event exists
|
||||
const storedEvent = await this.eventsAdapter.get({ id: event.id });
|
||||
if (storedEvent) { // event exist
|
||||
if (new_relay_record) {
|
||||
this.sourceOfChange.put({ event: parsedEvent, relay: url });
|
||||
{
|
||||
const storedEvent = this.getEventByID(event.id);
|
||||
if (storedEvent) { // event exist
|
||||
if (new_relay_record) {
|
||||
this.sourceOfChange.put({ event: parsedEvent, relay: url });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// add event to database and notify subscribers
|
||||
@ -246,8 +239,7 @@ export class Database_View
|
||||
this.events.set(parsedEvent.id, parsedEvent);
|
||||
|
||||
if (parsedEvent.kind == NostrKind.META_DATA) {
|
||||
// @ts-ignore
|
||||
const pEvent = parseProfileEvent(parsedEvent);
|
||||
const pEvent = parseProfileEvent(parsedEvent as NostrEvent<NostrKind.META_DATA>);
|
||||
if (pEvent instanceof Error) {
|
||||
return pEvent;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Parsed_Event, Profile_Nostr_Event } from "../nostr.ts";
|
||||
import { prepareNormalNostrEvent } from "../../libs/nostr.ts/event.ts";
|
||||
|
||||
import { ConnectionPool } from "../../libs/nostr.ts/relay-pool.ts";
|
||||
import { NostrAccountContext, NostrKind } from "../../libs/nostr.ts/nostr.ts";
|
||||
import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user