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 { Avatar } from "./components/avatar.tsx";
|
2023-08-24 14:27:49 +00:00
|
|
|
import {
|
2024-03-16 07:09:30 +00:00
|
|
|
BackgroundColor_HoverButton,
|
2023-08-24 14:27:49 +00:00
|
|
|
DividerBackgroundColor,
|
|
|
|
PlaceholderColor,
|
|
|
|
PrimaryTextColor,
|
|
|
|
SecondaryBackgroundColor,
|
2024-03-16 07:09:30 +00:00
|
|
|
TextColor_Primary,
|
2023-08-24 14:27:49 +00:00
|
|
|
} 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";
|
2024-01-01 17:28:10 +00:00
|
|
|
import { PublicKey } from "../../libs/nostr.ts/key.ts";
|
2024-06-17 07:42:51 +00:00
|
|
|
import { robohash } from "../../libs/nostr.ts/nip11.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 interface ProfileSetter {
|
2024-03-15 13:44:17 +00:00
|
|
|
setProfile(profileEvent: Profile_Nostr_Event, relayURL: string): void;
|
2023-10-08 01:16:30 +00:00
|
|
|
}
|
|
|
|
|
2024-06-17 07:42:51 +00:00
|
|
|
export type func_GetProfileByPublicKey = (pubkey: PublicKey | string) => Profile_Nostr_Event | undefined;
|
2024-04-29 09:32:11 +00:00
|
|
|
export type func_GetProfilesByText = (input: string) => Profile_Nostr_Event[];
|
2023-10-06 00:05:35 +00:00
|
|
|
export interface ProfileGetter {
|
2024-04-29 09:32:11 +00:00
|
|
|
getProfilesByText: func_GetProfilesByText;
|
2024-04-12 09:04:58 +00:00
|
|
|
getProfileByPublicKey: func_GetProfileByPublicKey;
|
2024-03-15 13:44:17 +00:00
|
|
|
getUniqueProfileCount(): number;
|
2023-10-06 00:05:35 +00:00
|
|
|
}
|
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
type Props = {
|
|
|
|
placeholder: string;
|
2024-03-16 07:09:30 +00:00
|
|
|
profileGetter: ProfileGetter;
|
2023-09-10 17:56:37 +00:00
|
|
|
emit: emitFunc<SearchUpdate>;
|
|
|
|
};
|
|
|
|
|
|
|
|
type State = {
|
|
|
|
searchResults: Profile_Nostr_Event[] | PublicKey;
|
2024-03-16 07:09:30 +00:00
|
|
|
offset: number;
|
2023-09-10 17:56:37 +00:00
|
|
|
};
|
|
|
|
|
2024-03-16 07:09:30 +00:00
|
|
|
const page_size = 9;
|
2023-09-10 17:56:37 +00:00
|
|
|
export class Search extends Component<Props, State> {
|
2024-03-16 07:09:30 +00:00
|
|
|
state: State = {
|
|
|
|
searchResults: [],
|
|
|
|
offset: 0,
|
|
|
|
};
|
|
|
|
|
2024-03-15 15:18:36 +00:00
|
|
|
inputRef = createRef<HTMLInputElement>();
|
2024-03-16 07:09:30 +00:00
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
styles = {
|
2023-12-18 10:23:15 +00:00
|
|
|
container: `flex flex-col h-full w-full bg-[${SecondaryBackgroundColor}]`,
|
2023-09-10 17:56:37 +00:00
|
|
|
searchInput:
|
2023-12-18 10:23:15 +00:00
|
|
|
`p-2 w-full border-b border-[${DividerBackgroundColor}] focus-visible:outline-none bg-[${SecondaryBackgroundColor}] text-[${PrimaryTextColor}] placeholder-[${PlaceholderColor}]`,
|
2023-09-10 17:56:37 +00:00
|
|
|
result: {
|
2023-12-18 10:23:15 +00:00
|
|
|
container: `flex-1 list-none p-1 overflow-y-auto`,
|
2023-09-10 17:56:37 +00:00
|
|
|
item: {
|
|
|
|
container:
|
2023-12-18 10:23:15 +00:00
|
|
|
`w-full flex items-center px-4 py-2 text-[#B8B9BF] hover:bg-[#404249] rounded cursor-pointer`,
|
|
|
|
avatar: `w-8 h-8 mr-2`,
|
|
|
|
text: `truncate`,
|
2023-09-10 17:56:37 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2023-11-15 11:35:02 +00:00
|
|
|
componentDidMount() {
|
2024-03-15 15:18:36 +00:00
|
|
|
if (this.inputRef.current) {
|
|
|
|
this.inputRef.current.focus();
|
|
|
|
}
|
2023-11-15 11:35:02 +00:00
|
|
|
}
|
|
|
|
|
2023-09-10 17:56:37 +00:00
|
|
|
search = (e: h.JSX.TargetedEvent<HTMLInputElement, Event>) => {
|
2024-03-16 07:09:30 +00:00
|
|
|
this.setState({
|
|
|
|
offset: 0,
|
|
|
|
});
|
2023-09-10 17:56:37 +00:00
|
|
|
const text = e.currentTarget.value;
|
|
|
|
const pubkey = PublicKey.FromString(text);
|
|
|
|
if (pubkey instanceof Error) {
|
2024-03-16 07:09:30 +00:00
|
|
|
const profiles = this.props.profileGetter.getProfilesByText(text);
|
2023-09-10 17:56:37 +00:00
|
|
|
this.setState({
|
|
|
|
searchResults: profiles,
|
|
|
|
});
|
|
|
|
} else {
|
2024-04-12 09:04:58 +00:00
|
|
|
const profile_event = this.props.profileGetter.getProfileByPublicKey(pubkey);
|
2023-09-10 17:56:37 +00:00
|
|
|
this.setState({
|
|
|
|
searchResults: profile_event ? [profile_event] : pubkey,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
onSelect = (profile: Profile_Nostr_Event | PublicKey) => () => {
|
2024-03-15 15:18:36 +00:00
|
|
|
if (this.inputRef.current) {
|
|
|
|
this.inputRef.current.value = "";
|
|
|
|
}
|
2023-09-10 17:56:37 +00:00
|
|
|
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-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}
|
|
|
|
/>
|
2024-03-16 07:09:30 +00:00
|
|
|
<div class={`flex flex-row justify-evenly ${TextColor_Primary}`}>
|
|
|
|
<button
|
|
|
|
class={`border px-2 mt-1 rounded hover:${BackgroundColor_HoverButton}`}
|
|
|
|
onClick={() => {
|
|
|
|
const newOffset = this.state.offset - page_size;
|
|
|
|
if (newOffset >= 0) {
|
|
|
|
this.setState({
|
|
|
|
offset: newOffset,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
previous page
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
class={`border px-2 mt-1 rounded hover:${BackgroundColor_HoverButton}`}
|
|
|
|
onClick={() => {
|
|
|
|
if (this.state.searchResults instanceof PublicKey) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const newOffset = this.state.offset + page_size;
|
|
|
|
if (newOffset < this.state.searchResults.length) {
|
|
|
|
this.setState({
|
|
|
|
offset: newOffset,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
next page
|
|
|
|
</button>
|
|
|
|
</div>
|
2023-09-10 17:56:37 +00:00
|
|
|
{this.state.searchResults instanceof PublicKey
|
2024-03-16 07:09:30 +00:00
|
|
|
? this.pubkeyItem(this.state.searchResults)
|
2023-09-10 17:56:37 +00:00
|
|
|
: this.state.searchResults.length > 0
|
|
|
|
? (
|
|
|
|
<ul class={this.styles.result.container}>
|
2024-03-16 07:09:30 +00:00
|
|
|
{this.state.searchResults.slice(
|
|
|
|
this.state.offset,
|
|
|
|
this.state.offset + page_size,
|
|
|
|
).map(this.profileItem)}
|
2023-09-10 17:56:37 +00:00
|
|
|
</ul>
|
|
|
|
)
|
|
|
|
: undefined}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2024-03-16 07:09:30 +00:00
|
|
|
|
|
|
|
pubkeyItem = (pubkey: PublicKey) => {
|
|
|
|
return (
|
|
|
|
<li
|
|
|
|
onClick={this.onSelect(pubkey)}
|
|
|
|
class={this.styles.result.item.container}
|
|
|
|
>
|
|
|
|
<Avatar
|
|
|
|
class={this.styles.result.item.avatar}
|
|
|
|
picture={robohash(pubkey.hex)}
|
|
|
|
/>
|
|
|
|
<p class={this.styles.result.item.text}>
|
|
|
|
{pubkey.bech32()}
|
|
|
|
</p>
|
|
|
|
</li>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
profileItem = (result: Profile_Nostr_Event) => {
|
|
|
|
return (
|
|
|
|
<li
|
|
|
|
onClick={this.onSelect(result)}
|
|
|
|
class={this.styles.result.item.container}
|
|
|
|
>
|
|
|
|
<Avatar
|
|
|
|
class={this.styles.result.item.avatar}
|
|
|
|
picture={result.profile.picture || robohash(result.pubkey)}
|
|
|
|
/>
|
|
|
|
<p class={this.styles.result.item.text}>
|
2024-03-20 02:20:55 +00:00
|
|
|
{result.profile.name || result.profile.display_name}
|
2024-03-16 07:09:30 +00:00
|
|
|
</p>
|
|
|
|
</li>
|
|
|
|
);
|
|
|
|
};
|
2023-07-30 06:39:53 +00:00
|
|
|
}
|