blowater/app/UI/nav.tsx

183 lines
6.3 KiB
TypeScript
Raw Normal View History

2023-06-30 14:05:57 +00:00
/** @jsx h */
2023-11-27 13:35:01 +00:00
import { ComponentChild, Fragment, h } from "https://esm.sh/preact@10.17.1";
2023-06-30 14:05:57 +00:00
import { Avatar } from "./components/avatar.tsx";
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";
import { ProfileGetter } from "./search.tsx";
import { ProfileData } from "../features/profile.ts";
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";
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 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;
profileGetter: ProfileGetter;
emit: emitFunc<NavigationUpdate>;
isMobile?: boolean;
2023-11-27 13:35:01 +00:00
installPrompt: InstallPrompt;
2023-06-30 14:05:57 +00:00
};
2023-11-02 13:10:16 +00:00
type State = {
activeIndex: number;
2023-11-27 13:35:01 +00:00
installPrompt: InstallPrompt;
2023-06-30 14:05:57 +00:00
};
2023-11-24 07:06:27 +00:00
type NavTabID = "DM" | "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 = {
activeIndex: 0,
installPrompt: this.props.installPrompt,
};
2023-11-02 13:10:16 +00:00
myProfile: ProfileData | undefined;
tabs: NavTab[] = [
{
icon: (active: boolean) => <ChatIcon class={this.styles.icons(active)} />,
id: "DM",
},
{
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",
},
];
componentWillMount() {
this.myProfile = this.props.profileGetter.getProfilesByPublicKey(this.props.publicKey)?.profile;
}
changeTab = (activeIndex: number) => {
if (activeIndex == this.state.activeIndex) {
return;
}
this.props.emit({
type: "ChangeNavigation",
id: this.tabs[activeIndex].id,
});
this.setState({
activeIndex: activeIndex,
});
};
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,
},
});
}
};
2023-11-02 13:10:16 +00:00
render() {
return (
this.props.isMobile
? (
<div class={this.styles.mobileContainer}>
{this.tabs.map((tab, index) => (
2023-11-27 13:35:01 +00:00
<Fragment>
{index == this.tabs.length - 1 && this.state.installPrompt.event
? (
<button class={this.styles.tabs(false)} onClick={this.install}>
<DownloadIcon class={this.styles.icons(false)} />
</button>
)
: undefined}
<button
onClick={() => this.changeTab(index)}
class={this.styles.tabs(this.state.activeIndex == index)}
>
{tab.icon(this.state.activeIndex == index)}
</button>
</Fragment>
2023-11-02 13:10:16 +00:00
))}
</div>
)
: (
<div class={this.styles.container}>
<Avatar class={this.styles.avatar} picture={this.myProfile?.picture} />
{this.tabs.map((tab, index) => (
<div class={this.styles.tabsContainer}>
2023-11-27 13:35:01 +00:00
{index == this.tabs.length - 1 && this.state.installPrompt.event
? (
<button class={this.styles.tabs(false)} onClick={this.install}>
<DownloadIcon class={this.styles.icons(false)} />
</button>
)
: undefined}
2023-11-02 13:10:16 +00:00
<button
onClick={() => this.changeTab(index)}
class={this.styles.tabs(this.state.activeIndex == index)}
>
{tab.icon(this.state.activeIndex == index)}
</button>
</div>
))}
</div>
)
);
}
2023-06-30 14:05:57 +00:00
}