Yoda 0.12.4

Browser for Gemini Protocol
mod action;
mod client;
mod database;
mod page;

use super::{Action as TabAction, WindowAction};
use crate::Profile;
use action::Action;
use adw::TabPage;
use anyhow::Result;
use client::Client;
use gtk::{
    Box,
    prelude::{ActionExt, ActionMapExt, BoxExt, EditableExt},
};
use page::Page;
use sqlite::Transaction;
use std::rc::Rc;

pub struct Item {
    // Multi-protocol handler
    pub client: Rc<Client>,
    // Components
    pub page: Rc<Page>,
    pub action: Rc<Action>,
    pub tab_page: TabPage,
}

impl Item {
    // Constructors

    /// Build new `Self`
    pub fn build(
        (tab_page, target_child): (&TabPage, &Box),
        profile: &Rc<Profile>,
        (window_action, tab_action): (&Rc<WindowAction>, &Rc<TabAction>),
        request: Option<&str>,
        is_load: bool,
    ) -> Self {
        // Init components
        let action = Rc::new(Action::new());

        tab_action.simple_action_group.add_action(&action.home);
        tab_action.simple_action_group.add_action(&action.reload);
        tab_action.simple_action_group.add_action(&action.identity);

        tab_action
            .simple_action_group
            .add_action(&action.history.back);

        tab_action
            .simple_action_group
            .add_action(&action.history.forward);

        // Create new `Page` implementation for `TabPage`
        let page = Rc::new(Page::build(
            profile,
            (window_action, tab_action, &action),
            tab_page,
        ));

        target_child.append(&page.navigation.g_box);
        target_child.append(&page.content.g_box);
        target_child.append(&page.search.g_box);
        target_child.append(&page.input.clamp);

        // Update tab loading indicator
        let client = Rc::new(Client::init(profile, &page));

        // Connect events
        action.home.connect_enabled_notify({
            let window_action = window_action.clone();
            move |this| {
                window_action
                    .home
                    .simple_action
                    .set_enabled(this.is_enabled())
            }
        });

        action.home.connect_activate({
            let client = client.clone();
            let page = page.clone();
            move |this, _| {
                this.set_enabled(false);
                match page.navigation.request.home() {
                    Some(uri) => {
                        let request = uri.to_string();
                        // prevent `changed` event extra emission
                        // but make sure the entry is always up-to-date
                        if page.navigation.request.entry.text() != request {
                            page.navigation.request.entry.set_text(&request)
                        }
                        client.handle(&request, true, false)
                    }
                    None => panic!(), // unexpected
                }
            }
        });

        action.load.connect_activate({
            let c = client.clone();
            let e = page.navigation.request.entry.clone();
            move |request, is_snap_history, is_redirect| {
                match request {
                    Some(request) => {
                        // prevent `changed` event extra emission
                        // but make sure the entry is always up-to-date
                        if e.text() != request {
                            e.set_text(&request)
                        }
                        c.handle(&request, is_snap_history, is_redirect)
                    }
                    None => panic!(), // unexpected
                }
            }
        });

        action.identity.connect_activate({
            let page = page.clone();
            move |_, _| page.navigation.show_identity_dialog()
        });

        action.reload.connect_activate({
            let page = page.clone();
            let client = client.clone();
            move |_, _| client.handle(&page.navigation.request.entry.text(), true, false)
        });

        action.reload.connect_enabled_notify({
            let window_action = window_action.clone();
            move |this| {
                window_action
                    .reload
                    .simple_action
                    .set_enabled(this.is_enabled())
            }
        });

        action.history.back.connect_enabled_notify({
            let window_action = window_action.clone();
            move |this| {
                window_action
                    .history_back
                    .simple_action
                    .set_enabled(this.is_enabled())
            }
        });

        action.history.forward.connect_enabled_notify({
            let window_action = window_action.clone();
            move |this| {
                window_action
                    .history_forward
                    .simple_action
                    .set_enabled(this.is_enabled())
            }
        });

        // Handle immediately on request
        if is_load && let Some(request) = request {
            // prevent `changed` event extra emission
            // but make sure the entry is always up-to-date
            if page.navigation.request.entry.text() != request {
                page.navigation.request.entry.set_text(request)
            }
            client.handle(request, true, false)
        }

        Self {
            client,
            page,
            action,
            tab_page: tab_page.clone(),
        }
    }

    pub fn clean(&self, transaction: &Transaction, app_browser_window_tab_id: i64) -> Result<()> {
        for record in database::select(transaction, app_browser_window_tab_id)? {
            database::delete(transaction, record.id)?;
            // Delegate clean action to the item childs
            self.page.clean(transaction, record.id)?;
        }
        Ok(())
    }

    pub fn save(
        &self,
        transaction: &Transaction,
        app_browser_window_tab_id: i64,
        page_position: i32,
    ) -> Result<()> {
        let id = database::insert(
            transaction,
            app_browser_window_tab_id,
            page_position,
            self.tab_page.is_pinned(),
            self.tab_page.is_selected(),
        )?;
        self.page.save(transaction, id)?;
        Ok(())
    }
}

// Tools
pub fn migrate(tx: &Transaction) -> Result<()> {
    // Migrate self components
    database::init(tx)?;

    // Delegate migration to childs
    page::migrate(tx)?;

    // Success
    Ok(())
}

// This feature restore require parental implementation
// * see `super::Tab::restore()`
pub fn restore(
    transaction: &Transaction,
    app_browser_window_tab_id: i64,
) -> Result<Vec<database::Table>> {
    database::select(transaction, app_browser_window_tab_id)
}