Skip to content

Commit

Permalink
feat: Bookmarks
Browse files Browse the repository at this point in the history
TODO:
- Making OkuNet posts
- Setting up an OkuNet identity
- Viewing OkuNet profiles
- Following & blocking OkuNet users
  • Loading branch information
emmyoh committed Oct 27, 2024
1 parent b77493a commit 5499894
Show file tree
Hide file tree
Showing 18 changed files with 1,210 additions and 25 deletions.
2 changes: 2 additions & 0 deletions data/hicolor/scalable/actions/bookmark-filled-symbolic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions data/hicolor/scalable/actions/editor-symbolic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions resources.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@
<file preprocess="xml-stripblanks">user-home-symbolic.svg</file>
<file preprocess="xml-stripblanks">note-symbolic.svg</file>
<file preprocess="xml-stripblanks">folder-remote-symbolic.svg</file>
<file preprocess="xml-stripblanks">bookmark-filled-symbolic.svg</file>
<file preprocess="xml-stripblanks">pencil-and-paper-small-symbolic.svg</file>
<file preprocess="xml-stripblanks">editor-symbolic.svg</file>
</gresource>
</gresources>
150 changes: 150 additions & 0 deletions src/bookmark_item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use glib::clone;
use glib::property::PropertySet;
use glib::subclass::object::ObjectImpl;
use glib::subclass::types::ObjectSubclass;
use glib::subclass::types::ObjectSubclassExt;
use glib::subclass::types::ObjectSubclassIsExt;
use glib::value::ToValue;
use glib::ParamSpec;
use glib::ParamSpecBoxed;
use glib::ParamSpecBuilderExt;
use glib::ParamSpecObject;
use glib::ParamSpecString;
use glib::Value;
use once_cell::sync::Lazy;
use std::cell::RefCell;
use uuid::Uuid;
use webkit2gtk::functions::uri_for_display;

pub mod imp {
use super::*;

#[derive(Default, Debug)]
pub struct BookmarkItem {
pub(crate) url: RefCell<String>,
pub(crate) title: RefCell<String>,
pub(crate) body: RefCell<String>,
pub(crate) tags: RefCell<Vec<String>>,
pub(crate) favicon: RefCell<Option<gdk::Texture>>,
}

#[glib::object_subclass]
impl ObjectSubclass for BookmarkItem {
const NAME: &'static str = "OkuBookmarkItem";
type Type = super::BookmarkItem;
}

impl ObjectImpl for BookmarkItem {
fn properties() -> &'static [ParamSpec] {
static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
vec![
ParamSpecString::builder("url").readwrite().build(),
ParamSpecString::builder("title").readwrite().build(),
ParamSpecString::builder("body").readwrite().build(),
ParamSpecBoxed::builder::<Vec<String>>("tags")
.readwrite()
.build(),
ParamSpecObject::builder::<gdk::Texture>("favicon")
.readwrite()
.build(),
]
});
PROPERTIES.as_ref()
}

fn set_property(&self, _id: usize, value: &Value, pspec: &ParamSpec) {
match pspec.name() {
"url" => {
let url = value.get::<String>().unwrap();
self.url.set(
html_escape::encode_text(&uri_for_display(&url).unwrap_or(url.into()))
.to_string(),
);
}
"title" => {
let title = value.get::<String>().unwrap();
self.title.set(html_escape::encode_text(&title).to_string());
}
"body" => {
let body = value.get::<String>().unwrap();
self.body.set(html_escape::encode_text(&body).to_string());
}
"tags" => {
let tags = value.get::<Vec<String>>().unwrap();
self.tags.set(
tags.iter()
.map(|x| html_escape::encode_text(x).to_string())
.collect(),
);
}
"favicon" => {
let favicon = value.get::<gdk::Texture>().ok();
self.favicon.set(favicon);
}
_ => unimplemented!(),
}
}

fn property(&self, _id: usize, pspec: &ParamSpec) -> Value {
let obj = self.obj();
match pspec.name() {
"url" => obj.url().to_string().to_value(),
"title" => obj.title().to_value(),
"body" => obj.body().to_value(),
"tags" => obj.tags().to_value(),
"favicon" => obj.favicon().to_value(),
_ => unimplemented!(),
}
}
}
}

glib::wrapper! {
pub struct BookmarkItem(ObjectSubclass<imp::BookmarkItem>);
}

impl BookmarkItem {
pub fn url(&self) -> String {
self.imp().url.borrow().to_string()
}
pub fn title(&self) -> String {
self.imp().title.borrow().to_string()
}
pub fn body(&self) -> String {
self.imp().body.borrow().to_string()
}
pub fn tags(&self) -> Vec<String> {
self.imp().tags.borrow().to_owned()
}
pub fn favicon(&self) -> Option<gdk::Texture> {
self.imp().favicon.borrow().clone()
}
pub fn new(
url: String,
title: String,
body: String,
tags: Vec<String>,
favicon_database: &webkit2gtk::FaviconDatabase,
) -> Self {
let bookmark_item = glib::Object::builder::<Self>()
.property("url", url.clone())
.property("title", title)
.property("body", body)
.property("tags", tags.clone())
.build();

favicon_database.favicon(
&url,
Some(&gio::Cancellable::new()),
clone!(
#[weak]
bookmark_item,
move |favicon_result| {
bookmark_item.imp().favicon.set(favicon_result.ok());
}
),
);

bookmark_item
}
}
35 changes: 28 additions & 7 deletions src/database/bookmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tantivy::{
collector::TopDocs,
directory::MmapDirectory,
query::QueryParser,
schema::{Field, Schema, Value, TEXT},
schema::{Field, Schema, Value, STORED, TEXT},
Directory, Index, IndexReader, IndexWriter, TantivyDocument, Term,
};
use tokio::sync::Mutex;
Expand All @@ -24,10 +24,13 @@ pub(crate) static BOOKMARK_INDEX_PATH: LazyLock<PathBuf> =
pub(crate) static BOOKMARK_SCHEMA: LazyLock<(Schema, HashMap<&str, Field>)> = LazyLock::new(|| {
let mut schema_builder = Schema::builder();
let fields = HashMap::from([
("url", schema_builder.add_text_field("url", TEXT)),
("title", schema_builder.add_text_field("title", TEXT)),
("body", schema_builder.add_text_field("body", TEXT)),
("tag", schema_builder.add_text_field("tag", TEXT)),
("url", schema_builder.add_text_field("url", TEXT | STORED)),
(
"title",
schema_builder.add_text_field("title", TEXT | STORED),
),
("body", schema_builder.add_text_field("body", TEXT | STORED)),
("tag", schema_builder.add_text_field("tag", TEXT | STORED)),
]);
let schema = schema_builder.build();
(schema, fields)
Expand Down Expand Up @@ -139,11 +142,26 @@ impl BrowserDatabase {
.collect())
}

pub fn rebuild_bookmark_index(&self) -> miette::Result<()> {
let mut index_writer = BOOKMARK_INDEX_WRITER
.clone()
.try_lock_owned()
.into_diagnostic()?;
index_writer.delete_all_documents().into_diagnostic()?;
self.get_bookmarks()?
.into_par_iter()
.filter_map(|x| index_writer.add_document(x.into()).ok())
.collect::<Vec<_>>();
index_writer.commit().into_diagnostic()?;
Ok(())
}

pub fn upsert_bookmark(&self, bookmark: Bookmark) -> miette::Result<Option<Bookmark>> {
let rw: transaction::RwTransaction<'_> =
self.database.rw_transaction().into_diagnostic()?;
let old_value: Option<Bookmark> = rw.upsert(bookmark.clone()).into_diagnostic()?;
rw.commit().into_diagnostic()?;
self.bookmark_sender.send_replace(());

let mut index_writer = BOOKMARK_INDEX_WRITER
.clone()
Expand All @@ -170,6 +188,7 @@ impl BrowserDatabase {
.filter_map(|bookmark| rw.upsert(bookmark).ok())
.collect();
rw.commit().into_diagnostic()?;
self.bookmark_sender.send_replace(());

let mut index_writer = BOOKMARK_INDEX_WRITER
.clone()
Expand All @@ -189,6 +208,7 @@ impl BrowserDatabase {
let rw = self.database.rw_transaction().into_diagnostic()?;
let removed_bookmark = rw.remove(bookmark).into_diagnostic()?;
rw.commit().into_diagnostic()?;
self.bookmark_sender.send_replace(());

let mut index_writer = BOOKMARK_INDEX_WRITER
.clone()
Expand All @@ -207,6 +227,7 @@ impl BrowserDatabase {
.filter_map(|bookmark| rw.remove(bookmark).ok())
.collect();
rw.commit().into_diagnostic()?;
self.bookmark_sender.send_replace(());

let mut index_writer = BOOKMARK_INDEX_WRITER
.clone()
Expand All @@ -231,8 +252,8 @@ impl BrowserDatabase {
.into_diagnostic()?)
}

pub fn get_bookmark(&self, original_uri: String) -> miette::Result<Option<Bookmark>> {
pub fn get_bookmark(&self, url: String) -> miette::Result<Option<Bookmark>> {
let r = self.database.r_transaction().into_diagnostic()?;
Ok(r.get().primary(original_uri).into_diagnostic()?)
Ok(r.get().primary(url).into_diagnostic()?)
}
}
5 changes: 5 additions & 0 deletions src/database/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(crate) static MODELS: LazyLock<Models> = LazyLock::new(|| {
pub struct BrowserDatabase {
pub(super) database: Database<'static>,
pub history_sender: tokio::sync::watch::Sender<()>,
pub bookmark_sender: tokio::sync::watch::Sender<()>,
}

impl BrowserDatabase {
Expand All @@ -28,12 +29,16 @@ impl BrowserDatabase {
.create(&MODELS, &*DATABASE_PATH)
.into_diagnostic()?,
history_sender: tokio::sync::watch::channel(()).0,
bookmark_sender: tokio::sync::watch::channel(()).0,
};
if database.get_history_records()?.len() as u64
!= HISTORY_RECORD_INDEX_READER.searcher().num_docs()
{
database.rebuild_history_record_index()?;
}
if database.get_bookmarks()?.len() as u64 != BOOKMARK_INDEX_READER.searcher().num_docs() {
database.rebuild_bookmark_index()?;
}
Ok(database)
}

Expand Down
23 changes: 23 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
html_logo_url = "https://github.com/OkuBrowser/oku/raw/master/branding/logo-filled.svg",
html_favicon_url = "https://github.com/OkuBrowser/oku/raw/master/branding/logo-filled.svg"
)]
pub mod bookmark_item;
pub mod config;
pub mod database;
pub mod history_item;
Expand Down Expand Up @@ -320,6 +321,28 @@ async fn main() {
application.connect_window_added(clone!(move |_, window| {
let window: widgets::window::Window = window.clone().downcast().unwrap();
let ctx = glib::MainContext::default();
let mut bookmark_rx = DATABASE.bookmark_sender.subscribe();
ctx.spawn_local_with_priority(
glib::source::Priority::HIGH,
clone!(
#[weak]
window,
async move {
loop {
bookmark_rx.borrow_and_update();
info!("Bookmarks updated … ");
window.bookmarks_updated();
match bookmark_rx.changed().await {
Ok(_) => continue,
Err(e) => {
error!("{}", e);
break;
}
}
}
}
),
);
let mut history_rx = DATABASE.history_sender.subscribe();
ctx.spawn_local_with_priority(
glib::source::Priority::HIGH,
Expand Down
Loading

0 comments on commit 5499894

Please sign in to comment.