2023-06-30 14:05:57 +00:00
|
|
|
/** @jsx h */
|
2023-09-10 17:56:37 +00:00
|
|
|
import { createRef, h } from "https://esm.sh/preact@10.17.1";
|
2023-06-30 14:05:57 +00:00
|
|
|
import { tw } from "https://esm.sh/twind@0.16.16";
|
|
|
|
import { Avatar } from "./components/avatar.tsx";
|
2023-08-24 14:27:49 +00:00
|
|
|
import {
|
|
|
|
DividerBackgroundColor,
|
|
|
|
PlaceholderColor,
|
|
|
|
PrimaryTextColor,
|
|
|
|
SecondaryBackgroundColor,
|
|
|
|
} from "./style/colors.ts";
|
2023-09-10 17:56:37 +00:00
|
|
|
import { Component } from "https://esm.sh/preact@10.17.1";
|
|
|
|
import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
|
|
|
import { emitFunc } from "../event-bus.ts";
|
|
|
|
import { SearchUpdate } from "./search_model.ts";
|
|
|
|
import { Profile_Nostr_Event } from "../nostr.ts";
|
2023-10-06 00:05:35 +00:00
|
|
|
import { PublicKey } from "../lib/nostr-ts/key.ts";
|
2023-09-10 17:56:37 +00:00
|
|
|
|
|
|
|
export type SearchResultChannel = Channel<SearchResult[]>;
|
|
|
|
|
|
|
|
type SearchResult = {
|
|
|
|
picture: string | undefined;
|
|
|
|
text: string;
|
|
|
|
id: string;
|
|
|
|
};
|
|
|
|
|
2023-10-08 01:16:30 +00:00
|
|
|
export type ProfileController = ProfileSetter & ProfileGetter;
|
|
|
|
|
|
|
|
export interface ProfileSetter {
|
|
|
|
setProfile(profileEvent: Profile_Nostr_Event): void;
|
|
|
|
}
|
|
|
|
|
2023-10-06 00:05:35 +00:00
|
|
|
export interface ProfileGetter {
|
|
|
|
getProfilesByText(input: string): Profile_Nostr_Event[];
|
|
|
|
getProfilesByPublicKey(pubkey: PublicKey): Profile_Nostr_Event | undefined;
|
|
|
|
}
|
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
type Props = {
|
|
|
|
placeholder: string;
|
2023-10-06 00:05:35 +00:00
|
|
|
db: ProfileGetter;
|
2023-09-10 17:56:37 +00:00
|
|
|
emit: emitFunc<SearchUpdate>;
|
|
|
|
};
|
|
|
|
|
|
|
|
type State = {
|
|
|
|
searchResults: Profile_Nostr_Event[] | PublicKey;
|
|
|
|
};
|
|
|
|
|
|
|
|
export class Search extends Component<Props, State> {
|
|
|
|
state: State = { searchResults: [] };
|
|
|
|
inputRef = createRef();
|
|
|
|
styles = {
|
|
|
|
container: tw`flex flex-col h-full w-full bg-[${SecondaryBackgroundColor}]`,
|
|
|
|
searchInput:
|
|
|
|
tw`p-2 w-full border-b border-[${DividerBackgroundColor}] focus-visible:outline-none bg-[${SecondaryBackgroundColor}] text-[${PrimaryTextColor}] placeholder-[${PlaceholderColor}]`,
|
|
|
|
result: {
|
|
|
|
container: tw`flex-1 list-none p-1 overflow-y-auto`,
|
|
|
|
item: {
|
|
|
|
container:
|
|
|
|
tw`w-full flex items-center px-4 py-2 text-[#B8B9BF] hover:bg-[#404249] rounded cursor-pointer`,
|
|
|
|
avatar: tw`w-8 h-8 mr-2`,
|
|
|
|
text: tw`truncate`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2023-11-15 11:35:02 +00:00
|
|
|
componentDidMount() {
|
|
|
|
this.inputRef.current?.focus();
|
|
|
|
}
|
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
search = (e: h.JSX.TargetedEvent<HTMLInputElement, Event>) => {
|
|
|
|
const text = e.currentTarget.value;
|
|
|
|
const pubkey = PublicKey.FromString(text);
|
|
|
|
if (pubkey instanceof Error) {
|
2023-10-06 00:05:35 +00:00
|
|
|
const profiles = this.props.db.getProfilesByText(text);
|
2023-09-10 17:56:37 +00:00
|
|
|
this.setState({
|
|
|
|
searchResults: profiles,
|
|
|
|
});
|
|
|
|
} else {
|
2023-10-06 00:05:35 +00:00
|
|
|
const profile_event = this.props.db.getProfilesByPublicKey(pubkey);
|
2023-09-10 17:56:37 +00:00
|
|
|
this.setState({
|
|
|
|
searchResults: profile_event ? [profile_event] : pubkey,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
onSelect = (profile: Profile_Nostr_Event | PublicKey) => () => {
|
|
|
|
this.inputRef.current.value = "";
|
|
|
|
this.setState({
|
|
|
|
searchResults: [],
|
|
|
|
});
|
|
|
|
this.props.emit({
|
2023-09-23 21:33:30 +00:00
|
|
|
type: "SelectConversation",
|
2023-09-10 17:56:37 +00:00
|
|
|
pubkey: profile instanceof PublicKey ? profile : profile.publicKey,
|
2023-10-04 21:34:43 +00:00
|
|
|
isGroupChat: false, // todo
|
2023-09-10 17:56:37 +00:00
|
|
|
});
|
|
|
|
};
|
2023-07-30 06:39:53 +00:00
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div class={this.styles.container}>
|
|
|
|
<input
|
|
|
|
ref={this.inputRef}
|
|
|
|
onInput={this.search}
|
|
|
|
type="text"
|
|
|
|
class={this.styles.searchInput}
|
|
|
|
placeholder={this.props.placeholder}
|
|
|
|
/>
|
|
|
|
{this.state.searchResults instanceof PublicKey
|
|
|
|
? (
|
|
|
|
<li
|
|
|
|
onClick={this.onSelect(this.state.searchResults)}
|
|
|
|
class={this.styles.result.item.container}
|
|
|
|
>
|
|
|
|
<Avatar
|
|
|
|
class={this.styles.result.item.avatar}
|
|
|
|
picture={undefined}
|
|
|
|
/>
|
|
|
|
<p class={this.styles.result.item.text}>
|
|
|
|
{this.state.searchResults.bech32()}
|
|
|
|
</p>
|
|
|
|
</li>
|
|
|
|
)
|
|
|
|
: this.state.searchResults.length > 0
|
|
|
|
? (
|
|
|
|
<ul class={this.styles.result.container}>
|
|
|
|
{this.state.searchResults.map((result) => {
|
|
|
|
return (
|
|
|
|
<li
|
|
|
|
onClick={this.onSelect(result)}
|
|
|
|
class={this.styles.result.item.container}
|
|
|
|
>
|
|
|
|
<Avatar
|
|
|
|
class={this.styles.result.item.avatar}
|
|
|
|
picture={result.profile.picture}
|
|
|
|
/>
|
|
|
|
<p class={this.styles.result.item.text}>
|
|
|
|
{result.profile.name}
|
|
|
|
</p>
|
|
|
|
</li>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</ul>
|
|
|
|
)
|
|
|
|
: undefined}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2023-07-30 06:39:53 +00:00
|
|
|
}
|