mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-15 09:10:08 +00:00
implement records list builder, make some members public, drop extra getters
This commit is contained in:
parent
38c3b01736
commit
6b5e712cdf
@ -28,6 +28,11 @@ package = "gtk4"
|
|||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
features = ["v4_10"]
|
features = ["v4_10"]
|
||||||
|
|
||||||
|
[dependencies.gio]
|
||||||
|
package = "gio"
|
||||||
|
version = "0.20.4"
|
||||||
|
features = ["v2_70"]
|
||||||
|
|
||||||
[dependencies.sqlite]
|
[dependencies.sqlite]
|
||||||
package = "rusqlite"
|
package = "rusqlite"
|
||||||
version = "0.32.1"
|
version = "0.32.1"
|
||||||
|
@ -2,11 +2,15 @@ mod widget;
|
|||||||
use widget::Widget;
|
use widget::Widget;
|
||||||
|
|
||||||
use crate::profile::Profile;
|
use crate::profile::Profile;
|
||||||
use gtk::{glib::Uri, prelude::IsA};
|
use gtk::{
|
||||||
|
gio::{prelude::TlsCertificateExt, TlsCertificate},
|
||||||
|
glib::Uri,
|
||||||
|
prelude::IsA,
|
||||||
|
};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Gemini {
|
pub struct Gemini {
|
||||||
profile: Rc<Profile>,
|
// profile: Rc<Profile>,
|
||||||
widget: Rc<Widget>,
|
widget: Rc<Widget>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,8 +19,42 @@ impl Gemini {
|
|||||||
|
|
||||||
/// Create new `Self` for given Profile
|
/// Create new `Self` for given Profile
|
||||||
pub fn new(profile: Rc<Profile>, auth_uri: Uri) -> Self {
|
pub fn new(profile: Rc<Profile>, auth_uri: Uri) -> Self {
|
||||||
|
// Init widget
|
||||||
let widget = Rc::new(Widget::new());
|
let widget = Rc::new(Widget::new());
|
||||||
|
|
||||||
|
// Add new identity option
|
||||||
|
widget.form.list.append(None, "Create new..");
|
||||||
|
|
||||||
|
// Collect additional options from database
|
||||||
|
match profile.identity.gemini.database.records() {
|
||||||
|
Ok(identities) => {
|
||||||
|
for identity in identities {
|
||||||
|
// Get certificate details
|
||||||
|
let certificate = match TlsCertificate::from_pem(&identity.pem) {
|
||||||
|
Ok(certificate) => certificate,
|
||||||
|
Err(reason) => todo!("{reason}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get expiration time
|
||||||
|
let expires = certificate
|
||||||
|
.not_valid_after()
|
||||||
|
.unwrap()
|
||||||
|
.format_iso8601()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Append record option
|
||||||
|
widget.form.list.append(
|
||||||
|
Some(identity.id),
|
||||||
|
&match identity.name {
|
||||||
|
Some(name) => format!("{name} ({expires})"),
|
||||||
|
None => format!("{expires}"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => todo!(),
|
||||||
|
}
|
||||||
|
|
||||||
// Init events
|
// Init events
|
||||||
widget.connect_response({
|
widget.connect_response({
|
||||||
let profile = profile.clone();
|
let profile = profile.clone();
|
||||||
@ -33,7 +71,10 @@ impl Gemini {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Return activated `Self`
|
// Return activated `Self`
|
||||||
Self { profile, widget }
|
Self {
|
||||||
|
// profile,
|
||||||
|
widget,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -7,6 +7,7 @@ use adw::{
|
|||||||
AlertDialog, ResponseAppearance,
|
AlertDialog, ResponseAppearance,
|
||||||
};
|
};
|
||||||
use gtk::prelude::IsA;
|
use gtk::prelude::IsA;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
const HEADING: &str = "Ident";
|
const HEADING: &str = "Ident";
|
||||||
@ -17,13 +18,11 @@ const RESPONSE_APPLY: (&str, &str) = ("apply", "Apply");
|
|||||||
const RESPONSE_CANCEL: (&str, &str) = ("cancel", "Cancel");
|
const RESPONSE_CANCEL: (&str, &str) = ("cancel", "Cancel");
|
||||||
// const RESPONSE_MANAGE: (&str, &str) = ("manage", "Manage");
|
// const RESPONSE_MANAGE: (&str, &str) = ("manage", "Manage");
|
||||||
|
|
||||||
// List options
|
|
||||||
const OPTION_CREATE: (Option<i64>, &str) = (None, "Create new..");
|
|
||||||
|
|
||||||
// Select options
|
// Select options
|
||||||
|
|
||||||
pub struct Widget {
|
pub struct Widget {
|
||||||
gobject: AlertDialog,
|
pub form: Rc<Form>,
|
||||||
|
pub gobject: AlertDialog,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget {
|
impl Widget {
|
||||||
@ -31,12 +30,8 @@ impl Widget {
|
|||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Collect identity certificates
|
|
||||||
let mut options: Vec<(Option<i64>, String, bool)> = Vec::new();
|
|
||||||
options.push((OPTION_CREATE.0, OPTION_CREATE.1.to_owned(), false));
|
|
||||||
|
|
||||||
// Init child container
|
// Init child container
|
||||||
let form = Form::new(options);
|
let form = Rc::new(Form::new());
|
||||||
|
|
||||||
// Init main `GObject`
|
// Init main `GObject`
|
||||||
let gobject = AlertDialog::builder()
|
let gobject = AlertDialog::builder()
|
||||||
@ -44,7 +39,7 @@ impl Widget {
|
|||||||
.body(BODY)
|
.body(BODY)
|
||||||
.close_response(RESPONSE_CANCEL.0)
|
.close_response(RESPONSE_CANCEL.0)
|
||||||
.default_response(RESPONSE_APPLY.0)
|
.default_response(RESPONSE_APPLY.0)
|
||||||
.extra_child(form.gobject())
|
.extra_child(&form.gobject)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Set response variants
|
// Set response variants
|
||||||
@ -62,7 +57,7 @@ impl Widget {
|
|||||||
gobject.set_response_appearance(RESPONSE_CANCEL.0, ResponseAppearance::Destructive);
|
gobject.set_response_appearance(RESPONSE_CANCEL.0, ResponseAppearance::Destructive);
|
||||||
|
|
||||||
// Return new activated `Self`
|
// Return new activated `Self`
|
||||||
Self { gobject }
|
Self { form, gobject }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -8,36 +8,37 @@ use gtk::{
|
|||||||
prelude::{BoxExt, WidgetExt},
|
prelude::{BoxExt, WidgetExt},
|
||||||
Box, Orientation,
|
Box, Orientation,
|
||||||
};
|
};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Form {
|
pub struct Form {
|
||||||
gobject: Box,
|
pub gobject: Box,
|
||||||
|
pub list: Rc<List>,
|
||||||
|
// pub name: Rc<Name>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Form {
|
impl Form {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn new(items: Vec<(Option<i64>, String, bool)>) -> Self {
|
pub fn new() -> Self {
|
||||||
// Init components
|
// Init components
|
||||||
let list = List::new();
|
let list = Rc::new(List::new());
|
||||||
let name = Name::new();
|
let name = Rc::new(Name::new());
|
||||||
|
|
||||||
// Init main container
|
// Init main container
|
||||||
let gobject = Box::builder().orientation(Orientation::Vertical).build();
|
let gobject = Box::builder().orientation(Orientation::Vertical).build();
|
||||||
|
|
||||||
gobject.append(list.gobject());
|
gobject.append(&list.gobject);
|
||||||
gobject.append(name.gobject());
|
gobject.append(&name.gobject);
|
||||||
|
|
||||||
// Connect events
|
// Connect events
|
||||||
list.on_select(move |key| name.gobject().set_visible(key.is_none()));
|
list.connect_selected_notify(move |key| name.gobject.set_visible(key.is_none()));
|
||||||
|
|
||||||
// Return activated `Self`
|
// Return activated `Self`
|
||||||
Self { gobject }
|
Self {
|
||||||
|
gobject,
|
||||||
|
list,
|
||||||
|
// name,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
|
||||||
|
|
||||||
pub fn gobject(&self) -> &Box {
|
|
||||||
&self.gobject
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use gtk::{gio::ListStore, prelude::ObjectExt, DropDown, Label};
|
use gtk::{gio::ListStore, DropDown, Label};
|
||||||
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
const PROPERTY_KEY_NAME: &str = "key"; // Store item key as GTK property
|
|
||||||
const PROPERTY_KEY_NONE_VALUE: i64 = -1; // C-type conversion for `None` values
|
|
||||||
|
|
||||||
pub struct List {
|
pub struct List {
|
||||||
gobject: DropDown,
|
pub gobject: DropDown,
|
||||||
model: ListStore,
|
model: ListStore,
|
||||||
|
index: Rc<RefCell<HashMap<Label, Option<i64>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl List {
|
impl List {
|
||||||
@ -13,74 +12,41 @@ impl List {
|
|||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let index = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let model = ListStore::new::<Label>();
|
let model = ListStore::new::<Label>();
|
||||||
let gobject = DropDown::builder().model(&model).build();
|
let gobject = DropDown::builder().model(&model).build();
|
||||||
|
|
||||||
Self { model, gobject }
|
Self {
|
||||||
|
model,
|
||||||
|
index,
|
||||||
|
gobject,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
/// Append new item with `profile_identity_gemini_id` as `key` and name as `value`
|
/// Append new item with `profile_identity_gemini_id` as `key` and label as `value`
|
||||||
pub fn append(&self, key: Option<i64>, value: &str) {
|
pub fn append(&self, profile_identity_gemini_id: Option<i64>, label: &str) {
|
||||||
// Create new label for item
|
// Create new label for item
|
||||||
let item = Label::new(Some(value));
|
let item = Label::new(Some(label));
|
||||||
|
|
||||||
// Store key as property
|
// Register ID in hash map index
|
||||||
item.set_property(
|
self.index
|
||||||
PROPERTY_KEY_NAME,
|
.borrow_mut()
|
||||||
match key {
|
.insert(item.clone(), profile_identity_gemini_id);
|
||||||
Some(key) => key,
|
|
||||||
None => PROPERTY_KEY_NONE_VALUE,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set value as label
|
|
||||||
item.set_label(value);
|
|
||||||
|
|
||||||
// Append formatted record
|
// Append formatted record
|
||||||
self.model.append(&item);
|
self.model.append(&item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @TODO not in use
|
|
||||||
/// Get selected `key` or panic on selection not found
|
|
||||||
/// * return `None` if current selection key match `PROPERTY_KEY_NONE_VALUE`
|
|
||||||
pub fn selected(&self) -> Option<i64> {
|
|
||||||
selected(&self.gobject)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
|
|
||||||
/// Run callback function on `connect_selected_notify`
|
/// Run callback function on `connect_selected_notify` event
|
||||||
/// * return formatted key as result
|
/// * return formatted `profile_identity_gemini_id` match selected
|
||||||
pub fn on_select(&self, callback: impl Fn(Option<i64>) + 'static) {
|
pub fn connect_selected_notify(&self, callback: impl Fn(Option<i64>) + 'static) {
|
||||||
self.gobject
|
self.gobject.connect_selected_notify({
|
||||||
.connect_selected_notify(move |this| callback(selected(this)));
|
let index = self.index.clone();
|
||||||
}
|
move |list| callback(*index.borrow().get(&list.selected_item().unwrap()).unwrap())
|
||||||
|
});
|
||||||
// Getters
|
|
||||||
|
|
||||||
pub fn gobject(&self) -> &DropDown {
|
|
||||||
&self.gobject
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tools
|
|
||||||
|
|
||||||
/// Get selected `key` or panic on selection not found
|
|
||||||
/// * return `None` if current selection key match `PROPERTY_KEY_NONE_VALUE`
|
|
||||||
fn selected(list: &DropDown) -> Option<i64> {
|
|
||||||
match list.selected_item() {
|
|
||||||
Some(this) => {
|
|
||||||
// Convert back from C-based GObject type
|
|
||||||
let key = this.property::<i64>(PROPERTY_KEY_NAME);
|
|
||||||
|
|
||||||
if key == PROPERTY_KEY_NONE_VALUE {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => panic!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ const PLACEHOLDER_TEXT: &str = "Identity name (optional)";
|
|||||||
const MARGIN: i32 = 8;
|
const MARGIN: i32 = 8;
|
||||||
|
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
gobject: Entry,
|
pub gobject: Entry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Name {
|
impl Name {
|
||||||
@ -20,10 +20,4 @@ impl Name {
|
|||||||
.build(),
|
.build(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
|
||||||
|
|
||||||
pub fn gobject(&self) -> &Entry {
|
|
||||||
&self.gobject
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ pub struct Table {
|
|||||||
pub id: i64,
|
pub id: i64,
|
||||||
//pub profile_identity_id: i64,
|
//pub profile_identity_id: i64,
|
||||||
pub pem: String,
|
pub pem: String,
|
||||||
pub name: String,
|
pub name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Storage for Gemini auth certificates
|
/// Storage for Gemini auth certificates
|
||||||
|
Loading…
x
Reference in New Issue
Block a user