blowater/app/UI/nav.tsx

177 lines
5.9 KiB
TypeScript
Raw Normal View History

2023-06-30 14:05:57 +00:00
/** @jsx h */
import { ComponentChild, h } from "https://esm.sh/preact@10.17.1";
2024-01-01 17:28:10 +00:00
import { PublicKey } from "../../libs/nostr.ts/key.ts";
2023-06-30 14:05:57 +00:00
import {
PrimaryBackgroundColor,
PrimaryTextColor,
SecondaryBackgroundColor,
SecondaryTextColor,
} from "./style/colors.ts";
2023-11-11 11:19:21 +00:00
import { ChatIcon } from "./icons/chat-icon.tsx";
import { UserIcon } from "./icons/user-icon.tsx";
import { SettingIcon } from "./icons/setting-icon.tsx";
2023-11-02 13:10:16 +00:00
import { Component } from "https://esm.sh/preact@10.17.1";
2023-11-11 11:19:21 +00:00
import { AboutIcon } from "./icons/about-icon.tsx";
2023-11-02 13:10:16 +00:00
import { CenterClass, NoOutlineClass } from "./components/tw.ts";
import { emitFunc } from "../event-bus.ts";
2023-11-27 13:35:01 +00:00
import { DownloadIcon } from "./icons/download-icon.tsx";
import { Profile_Nostr_Event } from "../nostr.ts";
import { ConnectionPool } from "../../libs/nostr.ts/relay-pool.ts";
import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts";
import { RelaySwitchList } from "./relay-switch-list.tsx";
import { SocialIcon } from "./icons/social-icon.tsx";
import { SearchIcon } from "./icons/search-icon.tsx";
import { StartSearch } from "./search_model.ts";
import { setState } from "./_helper.ts";
2023-11-27 13:35:01 +00:00
export type InstallPrompt = {
event: Event | undefined;
};
2023-06-30 14:05:57 +00:00
2023-11-02 13:10:16 +00:00
export type NavigationUpdate = {
type: "ChangeNavigation";
id: NavTabID;
};
2023-06-30 14:05:57 +00:00
export type SelectRelay = {
type: "SelectRelay";
relay: SingleRelayConnection;
};
2023-06-30 14:05:57 +00:00
export type NavigationModel = {
2023-11-02 13:10:16 +00:00
activeNav: NavTabID;
2023-06-30 14:05:57 +00:00
};
2023-11-02 13:10:16 +00:00
type Props = {
publicKey: PublicKey;
profile: Profile_Nostr_Event | undefined;
emit: emitFunc<NavigationUpdate | SelectRelay | StartSearch>;
2023-11-27 13:35:01 +00:00
installPrompt: InstallPrompt;
pool: ConnectionPool;
currentRelay?: string;
activeNav: NavTabID;
2023-06-30 14:05:57 +00:00
};
2023-11-02 13:10:16 +00:00
type State = {
2023-11-27 13:35:01 +00:00
installPrompt: InstallPrompt;
2023-06-30 14:05:57 +00:00
};
type NavTabID = "Public" | "DM" | "Search" | "Profile" | "About" | "Setting";
2023-11-02 13:10:16 +00:00
type NavTab = {
icon: (active: boolean) => ComponentChild;
id: NavTabID;
};
export class NavBar extends Component<Props, State> {
styles = {
container:
2023-12-21 17:17:03 +00:00
`h-screen w-16 flex flex-col gap-y-4 overflow-y-auto bg-[${PrimaryBackgroundColor}] py-8 items-center`,
2023-11-02 13:10:16 +00:00
icons: (active: boolean, fill?: boolean) => (
2023-12-18 10:23:15 +00:00
`w-6 h-6 ${fill ? "fill-current" : "stroke-current"} text-[${
2023-11-02 13:10:16 +00:00
active ? PrimaryTextColor : SecondaryTextColor
}]`
),
2023-12-18 10:23:15 +00:00
avatar: `w-12 h-12`,
2023-11-27 13:35:01 +00:00
tabsContainer:
2023-12-18 10:23:15 +00:00
`last:flex-1 last:flex last:items-end last:flex last:flex-col last:justify-end last:gap-y-4`,
2023-11-02 13:10:16 +00:00
tabs: (active: boolean) =>
2023-12-18 10:23:15 +00:00
`rounded-lg w-10 h-10 ${
2023-11-02 13:10:16 +00:00
active ? `bg-[${SecondaryBackgroundColor}]` : ""
} hover:bg-[${SecondaryBackgroundColor}] ${CenterClass} ${NoOutlineClass}`,
2023-12-18 10:23:15 +00:00
mobileContainer: `h-[4.5rem] flex justify-evenly bg-[${PrimaryBackgroundColor}] items-start pt-2`,
2023-11-02 13:10:16 +00:00
};
2023-11-27 13:35:01 +00:00
state: State = {
installPrompt: this.props.installPrompt,
};
2023-11-02 13:10:16 +00:00
tabs: NavTab[] = [
{
icon: (active: boolean) => <SocialIcon class={this.styles.icons(active)} />,
id: "Public",
},
2023-11-02 13:10:16 +00:00
{
icon: (active: boolean) => <ChatIcon class={this.styles.icons(active)} />,
id: "DM",
},
{
icon: (active: boolean) => <SearchIcon class={this.styles.icons(active)} />,
id: "Search",
},
2023-11-02 13:10:16 +00:00
{
icon: (active: boolean) => <UserIcon class={this.styles.icons(active)} />,
id: "Profile",
},
{
icon: (active: boolean) => <AboutIcon class={this.styles.icons(active, true)} />,
id: "About",
},
{
icon: (active: boolean) => <SettingIcon class={this.styles.icons(active)} />,
id: "Setting",
},
];
changeTab = async (activeNav: NavTabID) => {
if (activeNav == this.props.activeNav) {
2023-11-02 13:10:16 +00:00
return;
}
this.props.emit({
type: "ChangeNavigation",
id: activeNav,
2023-11-02 13:10:16 +00:00
});
};
2023-06-30 14:05:57 +00:00
2023-11-27 13:35:01 +00:00
install = async () => {
if (!this.props.installPrompt.event) {
return;
}
// @ts-ignore
const res = await this.props.installPrompt.event.prompt();
if (res && res.outcome == "accepted") {
this.setState({
installPrompt: {
event: undefined,
},
});
}
};
render(props: Props) {
2023-11-02 13:10:16 +00:00
return (
<div class={this.styles.container}>
{/* <Avatar class={this.styles.avatar} picture={this.props.profile?.profile?.picture} /> */}
{<RelaySwitchList emit={props.emit} pool={props.pool} currentRelay={props.currentRelay} />}
{this.tabs.map(({ icon, id }) => (
<div class={this.styles.tabsContainer}>
{id === "Setting" && this.state.installPrompt.event
? (
<button class={this.styles.tabs(false)} onClick={this.install}>
<DownloadIcon class={this.styles.icons(false)} />
2023-11-02 13:10:16 +00:00
</button>
)
: undefined}
<button
onClick={() => {
if (id === "Search") {
props.emit({
type: "StartSearch",
});
} else {
this.changeTab(id);
}
}}
class={this.styles.tabs(this.props.activeNav === id)}
>
{icon(this.props.activeNav === id)}
</button>
2023-11-02 13:10:16 +00:00
</div>
))}
</div>
2023-11-02 13:10:16 +00:00
);
}
2023-06-30 14:05:57 +00:00
}