experimental global filter (#425)

This commit is contained in:
BlowaterNostr 2024-03-19 23:29:59 +08:00 committed by GitHub
parent f9ee637d35
commit ae537311e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 91 additions and 151 deletions

View File

@ -25,7 +25,7 @@ import { LamportTime } from "../time.ts";
import { InstallPrompt, NavBar } from "./nav.tsx";
import { Component } from "https://esm.sh/preact@10.17.1";
import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts";
import { ChannelContainer } from "./channel-container.tsx";
import { PublicMessageContainer } from "./public-message-container.tsx";
import { ChatMessage } from "./message.ts";
import { filter, forever, map } from "./_helper.ts";
import { RightPanel } from "./components/right-panel.tsx";
@ -342,12 +342,12 @@ export class AppComponent extends Component<AppProps> {
}
}
let socialNode: VNode | undefined;
let publicNode: VNode | undefined;
if (model.navigationModel.activeNav == "Public" && model.currentRelay) {
socialNode = (
<ChannelContainer
publicNode = (
<PublicMessageContainer
ctx={myAccountCtx}
{...model.social}
{...model.public}
getters={{
convoListRetriever: app.conversationLists,
newMessageChecker: app.conversationLists,
@ -414,7 +414,7 @@ export class AppComponent extends Component<AppProps> {
/>
</div>
</div>
{socialNode}
{publicNode}
{dmVNode}
{aboutNode}
{Setting({

View File

@ -2,7 +2,7 @@ import { NavigationModel } from "./nav.tsx";
import { ProfileData } from "../features/profile.ts";
import { DM_Model } from "./dm.tsx";
import { Social_Model } from "./channel-container.tsx";
import { Public_Model } from "./public-message-container.tsx";
import { App } from "./app.tsx";
import { PublicKey } from "../../libs/nostr.ts/key.ts";
import { default_blowater_relay } from "./relay-config.ts";
@ -12,7 +12,7 @@ export type Model = {
currentRelay: string;
dm: DM_Model;
social: Social_Model;
public: Public_Model;
// profile
newProfileField: {
@ -31,7 +31,7 @@ export function initialModel(): Model {
dm: {
currentConversation: undefined,
},
social: {
public: {
relaySelectedChannel: new Map(),
},
newProfileField: {

View File

@ -49,13 +49,14 @@ import { TagSelected } from "./contact-tags.tsx";
import { BlockUser, UnblockUser, UserDetail } from "./user-detail.tsx";
import { RelayRecommendList } from "./relay-recommend-list.tsx";
import { HidePopOver } from "./components/popover.tsx";
import { Social_Model } from "./channel-container.tsx";
import { Public_Model } from "./public-message-container.tsx";
import { SyncEvent } from "./message-panel.tsx";
import { SendingEventRejection, ToastChannel } from "./components/toast.tsx";
import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts";
import { default_blowater_relay } from "./relay-config.ts";
import { forever } from "./_helper.ts";
import { func_GetEventByID } from "./message-list.tsx";
import { FilterContent } from "./filter.tsx";
export type UI_Interaction_Event =
| SearchUpdate
@ -63,7 +64,6 @@ export type UI_Interaction_Event =
| EditorEvent
| NavigationUpdate
| DirectMessagePanelUpdate
| BackToChannelList
| BackToContactList
| SaveProfile
| PinConversation
@ -78,11 +78,9 @@ export type UI_Interaction_Event =
| UnblockUser
| SelectRelay
| HidePopOver
| SyncEvent;
| SyncEvent
| FilterContent;
type BackToChannelList = {
type: "BackToChannelList";
};
type BackToContactList = {
type: "BackToContactList";
};
@ -306,15 +304,6 @@ const handle_update_event = async (chan: PutChannel<true>, args: {
model.navigationModel.activeNav = event.id;
} //
//
// Channel
//
else if (event.type == "SelectChannel") {
model.social.relaySelectedChannel.set(model.currentRelay, event.channel);
app.popOverInputChan.put({ children: undefined });
} else if (event.type == "BackToChannelList") {
model.social.relaySelectedChannel.delete(model.currentRelay);
app.popOverInputChan.put({ children: undefined });
} //
// DM
//
else if (event.type == "ViewUserDetail") {
@ -559,7 +548,7 @@ export async function handle_SendMessage(
db: Database_View,
args: {
navigationModel: NavigationModel;
social: Social_Model;
public: Public_Model;
dm: {
currentConversation: PublicKey | undefined;
};

View File

@ -1,14 +0,0 @@
import { h, render } from "https://esm.sh/preact@10.17.1";
import { ChannelList } from "./channel-list.tsx";
import { testEventBus } from "./_setup.test.ts";
render(
<ChannelList
emit={testEventBus.emit}
relay="test"
currentSelected="test"
channels={["general", "games", "work"]}
>
</ChannelList>,
document.body,
);

View File

@ -1,49 +0,0 @@
import { Component, h } from "https://esm.sh/preact@10.17.1";
import { SelectChannel } from "./search_model.ts";
import { emitFunc } from "../event-bus.ts";
type ChannelListProps = {
relay: string;
currentSelected: string | undefined;
emit: emitFunc<SelectChannel>;
channels: string[];
};
export class ChannelList extends Component<ChannelListProps> {
render() {
return (
<div>
{this.props.channels.map((c) =>
this.ChannelListItem(this.props, c, c == this.props.currentSelected)
)}
</div>
);
}
ChannelListItem(props: ChannelListProps, name: string, isSelected: boolean) {
const selected = isSelected ? " bg-[#404248] text-[#fff]" : "";
return (
<div
class={`m-2 p-1
rounded
text-[#959BA3]
hover:text-[#fff]
hover:bg-[#36373C]
hover:cursor-pointer` + selected}
onClick={selectChannel(
props.emit,
name,
)}
>
# {name}
</div>
);
}
}
const selectChannel = (emit: emitFunc<SelectChannel>, channel: string) => () => {
return emit({
type: "SelectChannel",
channel,
});
};

4
app/UI/filter.test.tsx Normal file
View File

@ -0,0 +1,4 @@
import { h, render } from "https://esm.sh/preact@10.17.1";
import { Filter } from "./filter.tsx";
render(<Filter></Filter>, document.body);

32
app/UI/filter.tsx Normal file
View File

@ -0,0 +1,32 @@
import { Component, h } from "https://esm.sh/preact@10.17.1";
import { emitFunc } from "../event-bus.ts";
type Props = {
emit: emitFunc<FilterContent>;
};
export class Filter extends Component<Props, {}> {
render() {
return (
<div class="border flex flex-col items-center">
<div class="flex flex-row border">
<input
placeholder={"search content"}
onInput={(e) => {
this.props.emit({
type: "FilterContent",
content: e.currentTarget.value,
});
}}
>
</input>
</div>
</div>
);
}
}
export type FilterContent = {
type: "FilterContent";
content: string;
};

View File

@ -51,6 +51,13 @@ export class MessageList extends Component<MessageListProps, MessageListState> {
jitter = new JitterPrevention(100);
async componentWillReceiveProps(nextPrpos: MessageListProps) {
if (nextPrpos.messages.length != this.props.messages.length) {
await setState(this, { offset: 0 });
await this.goToLastPage();
}
}
async componentDidUpdate(previousProps: Readonly<MessageListProps>) {
const newest = last(this.props.messages);
const pre_newest = last(previousProps.messages);
@ -69,6 +76,7 @@ export class MessageList extends Component<MessageListProps, MessageListState> {
}
render() {
console.log(this.state.offset, this.props.messages.length);
const messages_to_render = this.sortAndSliceMessage();
const groups = groupContinuousMessages(messages_to_render, (pre, cur) => {
const sameAuthor = pre.event.pubkey == cur.event.pubkey;

View File

@ -1,6 +1,6 @@
import { fail } from "https://deno.land/std@0.176.0/testing/asserts.ts";
import { h, render } from "https://esm.sh/preact@10.17.1";
import { ChannelContainer } from "./channel-container.tsx";
import { PublicMessageContainer } from "./public-message-container.tsx";
import { relays } from "../../libs/nostr.ts/relay-list.test.ts";
import { ConnectionPool } from "../../libs/nostr.ts/relay-pool.ts";
import { testEventBus } from "./_setup.test.ts";
@ -41,7 +41,7 @@ dm_list.addEvents([e], true);
dm_list.addEvents(Array.from(database.getAllEvents()), true);
render(
<ChannelContainer
<PublicMessageContainer
ctx={ctx}
relay={relay}
bus={testEventBus}

View File

@ -1,7 +1,6 @@
import { Component, h } from "https://esm.sh/preact@10.17.1";
import { ChannelList } from "./channel-list.tsx";
import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts";
import { EventBus } from "../event-bus.ts";
import { emitFunc, EventBus } from "../event-bus.ts";
import { UI_Interaction_Event } from "./app_update.tsx";
import { setState } from "./_helper.ts";
import { ProfileGetter } from "./search.tsx";
@ -11,20 +10,19 @@ import { NewMessageChecker } from "./conversation-list.tsx";
import { ConversationListRetriever } from "./conversation-list.tsx";
import { NostrAccountContext } from "../../libs/nostr.ts/nostr.ts";
import { IconButtonClass } from "./components/tw.ts";
import { LeftArrowIcon } from "./icons/left-arrow-icon.tsx";
import { MessagePanel } from "./message-panel.tsx";
import { PublicKey } from "../../libs/nostr.ts/key.ts";
import { ChatMessage } from "./message.ts";
import { func_GetEventByID } from "./message-list.tsx";
import { Filter, FilterContent } from "./filter.tsx";
export type Social_Model = {
export type Public_Model = {
relaySelectedChannel: Map<string, /* relay url */ string /* channel name */>;
};
export type func_IsUserBlocked = (pubkey: PublicKey) => boolean;
type ChannelContainerProps = {
type Props = {
ctx: NostrAccountContext;
relay: SingleRelayConnection;
bus: EventBus<UI_Interaction_Event>;
@ -37,78 +35,64 @@ type ChannelContainerProps = {
isUserBlocked: func_IsUserBlocked;
getEventByID: func_GetEventByID;
};
} & Social_Model;
} & Public_Model;
type ChannelContainerState = {
type State = {
currentSelectedChannel: string /*channel name*/ | undefined;
currentEditor: {
text: string;
};
filter: FilterContent | undefined;
};
export class ChannelContainer extends Component<ChannelContainerProps, ChannelContainerState> {
state: ChannelContainerState = {
export class PublicMessageContainer extends Component<Props, State> {
state: State = {
currentSelectedChannel: "general",
currentEditor: {
text: "",
},
filter: undefined,
};
async componentDidMount() {
for await (const e of this.props.bus.onChange()) {
if (e.type == "SelectChannel") {
if (e.type == "FilterContent") {
await setState(this, {
currentSelectedChannel: e.channel,
filter: e,
});
} else if (e.type == "SelectRelay") {
await setState(this, {
currentSelectedChannel: "general", // this.props.relaySelectedChannel.get(e.relay.url),
});
} else if (e.type == "BackToChannelList") {
// await setState(this, {
// currentSelectedChannel: undefined,
// });
}
}
}
render(props: ChannelContainerProps, state: ChannelContainerState) {
render(props: Props, state: State) {
let msgs = props.messages;
const filter = this.state.filter;
if (filter) {
msgs = this.props.messages.filter((msg) => {
if (filter.content == "") {
return true;
}
return msg.content.toLowerCase().includes(filter.content.toLowerCase());
});
}
return (
<div class="flex flex-row h-full w-full flex bg-[#36393F] overflow-hidden">
{
/* <div
class={`h-screen w-60 max-sm:w-full
flex flex-col bg-[${SecondaryBackgroundColor}] `}
>
<div
class={`flex items-center w-full h-20 font-bold text-xl text-[${PrimaryTextColor}] m-1 p-3 border-b border-[#36393F]`}
>
{new URL(props.relay.url).host}
</div>
<ChannelList
relay={props.relay.url}
currentSelected={state.currentSelectedChannel}
channels={["general", "games", "work"]}
emit={props.bus.emit}
/>
</div> */
}
{this.state.currentSelectedChannel
? (
<div class={`flex flex-col flex-1 overflow-hidden`}>
<TopBar
bus={props.bus}
currentSelected={state.currentSelectedChannel}
profileGetter={props.getters.profileGetter}
emit={props.bus.emit}
/>
<div class={`flex-1 overflow-auto`}>
{
<MessagePanel
key={props.relay.url}
myPublicKey={props.ctx.publicKey}
emit={props.bus.emit}
eventSub={props.bus}
getters={props.getters}
messages={props.messages}
messages={msgs}
/>
}
</div>
@ -121,9 +105,8 @@ export class ChannelContainer extends Component<ChannelContainerProps, ChannelCo
}
function TopBar(props: {
bus: EventBus<UI_Interaction_Event>;
currentSelected: string | undefined;
profileGetter: ProfileGetter;
emit: emitFunc<FilterContent>;
}) {
return (
<div
@ -131,24 +114,15 @@ function TopBar(props: {
items-center justify-between bg-[#2F3136]`}
>
<div class={`flex items-center overflow-hidden`}>
{
/* <button
class={`w-6 h-6 mx-2 ${IconButtonClass}`}
>
<LeftArrowIcon
class={`w-4 h-4`}
style={{
fill: "rgb(185, 187, 190)",
}}
/>
</button> */
}
<span
class={`text-[#F3F4EA] text-[1.2rem] mx-4
whitespace-nowrap truncate`}
>
{props.currentSelected}
</span>
<div>
<Filter {...props}></Filter>
</div>
</div>
</div>
);

View File

@ -1,6 +1,6 @@
import { PublicKey } from "../../libs/nostr.ts/key.ts";
export type SearchUpdate = SelectChannel | SelectConversation | StartSearch;
export type SearchUpdate = SelectConversation | StartSearch;
export type StartSearch = {
type: "StartSearch";
};
@ -8,7 +8,3 @@ export type SelectConversation = {
type: "SelectConversation";
pubkey: PublicKey;
};
export type SelectChannel = {
type: "SelectChannel";
channel: string;
};