1use crate::i18n::{MessageKey, tr};
6use crate::state::{AppState, Message, NavGroup, ViewId};
7use crate::views;
8use iced::Element;
9use lucide_icons::Icon as LucideIcon;
10use snora::{AppLayout, Icon, LayoutDirection, SideBar, SideBarItem, Tab, TabBar, render,
11 widget::{app_side_bar, app_tab_bar}};
12
13fn tab_action_to_msg(action: snora::TabAction<ViewId>) -> Message {
14 let snora::TabAction::Pressed(id) = action;
15 Message::Switch(id)
16}
17
18fn build_tab_bar(tabs: Vec<Tab<ViewId>>, active: ViewId) -> Element<'static, Message> {
19 app_tab_bar(
20 TabBar { tabs, active },
21 &tab_action_to_msg,
22 LayoutDirection::Ltr,
23 )
24}
25
26#[derive(Default)]
28pub struct OrbokApp {
29 pub state: AppState,
30}
31
32impl OrbokApp {
33 pub fn with_state(state: AppState) -> Self {
34 Self { state }
35 }
36
37 pub fn update(&mut self, message: Message) {
38 self.state.update(&message);
39 }
40
41 pub fn view(&self) -> Element<'_, Message> {
42 let locale = self.state.locale;
43
44 if self.state.wizard.is_some() {
46 return views::wizard_view(&self.state);
47 }
48
49 let sidebar_items: Vec<SideBarItem<Message, NavGroup>> = vec![
51 SideBarItem {
52 view_id: NavGroup::Search,
53 icon: Icon::Lucide(LucideIcon::Search),
54 tooltip: tr(locale, MessageKey::NavSearch).to_string(),
55 on_press: Message::SwitchGroup(NavGroup::Search),
56 },
57 SideBarItem {
58 view_id: NavGroup::Ai,
59 icon: Icon::Lucide(LucideIcon::BrainCircuit),
60 tooltip: tr(locale, MessageKey::NavAi).to_string(),
61 on_press: Message::SwitchGroup(NavGroup::Ai),
62 },
63 SideBarItem {
64 view_id: NavGroup::Settings,
65 icon: Icon::Lucide(LucideIcon::Settings),
66 tooltip: tr(locale, MessageKey::NavSettings).to_string(),
67 on_press: Message::SwitchGroup(NavGroup::Settings),
68 },
69 ];
70 let side_bar = app_side_bar(
71 SideBar {
72 items: sidebar_items,
73 active: self.state.active_view.group(),
74 },
75 LayoutDirection::Ltr,
76 );
77
78 let tab_bar_el: Option<Element<'_, Message>> =
80 match self.state.active_view.group() {
81 NavGroup::Search => {
82 Some(build_tab_bar(
83 vec![
84 Tab { id: ViewId::Search, label: tr(locale, MessageKey::NavSearch).to_string(), icon: None },
85 Tab { id: ViewId::Sources, label: tr(locale, MessageKey::NavSources).to_string(), icon: None },
86 ],
87 self.state.active_view,
88 ))
89 }
90 NavGroup::Ai => {
91 Some(build_tab_bar(
92 vec![
93 Tab { id: ViewId::Indexing, label: tr(locale, MessageKey::NavIndexing).to_string(), icon: None },
94 Tab { id: ViewId::Storage, label: tr(locale, MessageKey::NavStorage).to_string(), icon: None },
95 Tab { id: ViewId::Models, label: tr(locale, MessageKey::NavModels).to_string(), icon: None },
96 ],
97 self.state.active_view,
98 ))
99 }
100 NavGroup::Settings => None,
101 };
102
103 let page_body = match self.state.active_view {
105 ViewId::Search => views::search_view(&self.state),
106 ViewId::Sources => views::sources_view(&self.state),
107 ViewId::Indexing => views::indexing_view(&self.state),
108 ViewId::Storage => views::storage_view(&self.state),
109 ViewId::Models => views::models_view(&self.state),
110 ViewId::Settings => views::settings_view(&self.state),
111 };
112
113 let body: Element<'_, Message> = if let Some(tabs) = tab_bar_el {
115 iced::widget::column![tabs, page_body]
116 .spacing(0)
117 .into()
118 } else {
119 page_body
120 };
121
122 render(AppLayout::new(body).side_bar(side_bar))
123 }
124
125 pub fn title(&self) -> String {
126 tr(self.state.locale, MessageKey::AppTitle).to_string()
127 }
128}