Skip to content

Commit 3de33ff

Browse files
authored
📜 Firefox History (#152)
This plugin lets you search and open entried from your Firefox history. This resolves #25.
1 parent c13276e commit 3de33ff

File tree

9 files changed

+203
-70
lines changed

9 files changed

+203
-70
lines changed

‎README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ plugin:
116116
plugin:
117117
firefox_bookmarks:
118118
enable: true
119+
firefox_history:
120+
enable: true
119121
```
120122

121123
### Git Repositories
@@ -259,6 +261,8 @@ You can specify alternative configuration locations through:
259261
enable: true
260262
firefox_bookmarks:
261263
enable: true
264+
firefox_history:
265+
enable: true
262266
git_repositories:
263267
enable: true
264268
commands:
@@ -314,6 +318,9 @@ You can specify alternative configuration locations through:
314318
firefox_bookmarks = {
315319
enable = true;
316320
};
321+
firefox_history = {
322+
enable = true;
323+
};
317324
git_repositories = {
318325
enable = true;
319326
commands = [

‎client/src/main.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ impl Application for Centerpiece {
189189
>());
190190
}
191191

192+
if self.settings.plugin.firefox_history.enable {
193+
subscriptions.push(crate::plugin::utils::spawn::<
194+
crate::plugin::firefox::history::HistoryPlugin,
195+
>());
196+
}
197+
192198
if self.settings.plugin.git_repositories.enable {
193199
subscriptions.push(crate::plugin::utils::spawn::<
194200
crate::plugin::git_repositories::GitRepositoriesPlugin,

‎client/src/plugin/brave/history.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ impl Plugin for HistoryPlugin {
1515
}
1616

1717
fn title() -> &'static str {
18-
"󰃃 History"
18+
"ó°‹š History"
1919
}
2020

2121
fn entries(&self) -> Vec<crate::model::Entry> {

‎client/src/plugin/firefox/bookmarks.rs‎

Lines changed: 1 addition & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,73 +5,6 @@ pub struct BookmarksPlugin {
55
entries: Vec<crate::model::Entry>,
66
}
77

8-
#[derive(serde::Deserialize, Debug)]
9-
#[serde(untagged)]
10-
#[allow(dead_code)]
11-
enum Section {
12-
#[serde(rename_all = "PascalCase")]
13-
Profile {
14-
name: String,
15-
is_relative: String,
16-
path: String,
17-
default: Option<String>,
18-
},
19-
#[serde(rename_all = "PascalCase")]
20-
General {
21-
start_with_last_profile: String,
22-
version: Option<String>,
23-
},
24-
25-
#[serde(rename_all = "PascalCase")]
26-
Install { default: String, locked: String },
27-
}
28-
29-
fn profile_path() -> anyhow::Result<String> {
30-
let home_directory = std::env::var("HOME")?;
31-
32-
let profiles_file_path = format!("{home_directory}/.mozilla/firefox/profiles.ini");
33-
let profiles_file = std::fs::File::open(profiles_file_path)?;
34-
let profiles_file_contents: std::collections::HashMap<String, Section> =
35-
serde_ini::from_read(profiles_file)?;
36-
37-
let mut default_profile = profiles_file_contents
38-
.values()
39-
.find(|section| match section {
40-
Section::Profile { default, .. } => {
41-
default.clone().unwrap_or(String::from("")) == String::from("1")
42-
}
43-
_ => false,
44-
});
45-
46-
if default_profile.is_none() {
47-
default_profile = profiles_file_contents
48-
.values()
49-
.find(|section| match section {
50-
Section::Profile { .. } => true,
51-
_ => false,
52-
});
53-
}
54-
55-
if default_profile.is_none() {
56-
return Err(anyhow::anyhow!("Could not find a firefox profile."));
57-
}
58-
59-
match default_profile.unwrap() {
60-
Section::Profile {
61-
is_relative, path, ..
62-
} => {
63-
if is_relative.eq(&String::from("1")) {
64-
Ok(format!("{home_directory}/.mozilla/firefox/{path}"))
65-
} else {
66-
Ok(path.clone())
67-
}
68-
}
69-
_ => {
70-
unreachable!("A non-profile section should be parsed as a profile.");
71-
}
72-
}
73-
}
74-
758
impl Plugin for BookmarksPlugin {
769
fn id() -> &'static str {
7710
"firefox_bookmarks"
@@ -99,8 +32,7 @@ impl Plugin for BookmarksPlugin {
9932

10033
fn update_entries(&mut self) -> anyhow::Result<()> {
10134
self.entries.clear();
102-
let profile_path = profile_path()?;
103-
35+
let profile_path = crate::plugin::firefox::utils::profile_path()?;
10436
let bookmarks_file_path = format!("{profile_path}/places.sqlite");
10537
let cache_directory = crate::plugin::utils::centerpiece_cache_directory()?;
10638
let bookmarks_cache_file_path = format!("{cache_directory}/firefox-bookmarks.sqlite");
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use crate::plugin::utils::Plugin;
2+
use anyhow::Context;
3+
4+
pub struct HistoryPlugin {
5+
entries: Vec<crate::model::Entry>,
6+
}
7+
8+
impl Plugin for HistoryPlugin {
9+
fn id() -> &'static str {
10+
"firefox_history"
11+
}
12+
13+
fn priority() -> u32 {
14+
0
15+
}
16+
17+
fn title() -> &'static str {
18+
"ó°‹š History"
19+
}
20+
21+
fn entries(&self) -> Vec<crate::model::Entry> {
22+
self.entries.clone()
23+
}
24+
25+
fn set_entries(&mut self, entries: Vec<crate::model::Entry>) {
26+
self.entries = entries;
27+
}
28+
29+
fn new() -> Self {
30+
Self { entries: vec![] }
31+
}
32+
33+
fn update_entries(&mut self) -> anyhow::Result<()> {
34+
self.entries.clear();
35+
let profile_path = crate::plugin::firefox::utils::profile_path()?;
36+
let history_file_path = format!("{profile_path}/places.sqlite");
37+
let cache_directory = crate::plugin::utils::centerpiece_cache_directory()?;
38+
let history_cache_file_path = format!("{cache_directory}/firefox-history.sqlite");
39+
40+
std::fs::copy(history_file_path, &history_cache_file_path)
41+
.context("Error while creating cache directory")?;
42+
43+
let connection = sqlite::open(history_cache_file_path)?;
44+
let query = "
45+
SELECT title, url
46+
FROM moz_places
47+
GROUP BY title
48+
ORDER BY visit_count DESC";
49+
50+
connection.execute(query)?;
51+
52+
let url_rows = connection
53+
.prepare(query)
54+
.unwrap()
55+
.into_iter()
56+
.map(|row| row.unwrap());
57+
58+
self.entries = url_rows
59+
.map(|row| {
60+
let title = row.read::<Option<&str>, _>("title");
61+
let url = row.read::<&str, _>("url");
62+
63+
crate::model::Entry {
64+
id: url.to_string(),
65+
title: title.unwrap_or(url).to_string(),
66+
action: String::from("open"),
67+
meta: String::from("History"),
68+
command: None,
69+
}
70+
})
71+
.collect();
72+
73+
Ok(())
74+
}
75+
76+
fn activate(
77+
&mut self,
78+
entry: crate::model::Entry,
79+
plugin_channel_out: &mut iced::futures::channel::mpsc::Sender<crate::Message>,
80+
) -> anyhow::Result<()> {
81+
std::process::Command::new("firefox")
82+
.arg(&entry.id)
83+
.spawn()
84+
.context(format!(
85+
"Failed to launch firefox while activating entry with id '{}'.",
86+
entry.id
87+
))?;
88+
89+
plugin_channel_out
90+
.try_send(crate::Message::Exit)
91+
.context(format!(
92+
"Failed to send message to exit application while activating entry with id '{}'.",
93+
entry.id
94+
))?;
95+
96+
Ok(())
97+
}
98+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
pub mod bookmarks;
2+
pub mod history;
3+
pub mod utils;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#[derive(serde::Deserialize, Debug)]
2+
#[serde(untagged)]
3+
#[allow(dead_code)]
4+
enum Section {
5+
#[serde(rename_all = "PascalCase")]
6+
Profile {
7+
name: String,
8+
is_relative: String,
9+
path: String,
10+
default: Option<String>,
11+
},
12+
#[serde(rename_all = "PascalCase")]
13+
General {
14+
start_with_last_profile: String,
15+
version: Option<String>,
16+
},
17+
18+
#[serde(rename_all = "PascalCase")]
19+
Install { default: String, locked: String },
20+
}
21+
22+
pub fn profile_path() -> anyhow::Result<String> {
23+
let home_directory = std::env::var("HOME")?;
24+
25+
let profiles_file_path = format!("{home_directory}/.mozilla/firefox/profiles.ini");
26+
let profiles_file = std::fs::File::open(profiles_file_path)?;
27+
let profiles_file_contents: std::collections::HashMap<String, Section> =
28+
serde_ini::from_read(profiles_file)?;
29+
30+
let mut default_profile = profiles_file_contents
31+
.values()
32+
.find(|section| match section {
33+
Section::Profile { default, .. } => {
34+
default.clone().unwrap_or(String::from("")) == String::from("1")
35+
}
36+
_ => false,
37+
});
38+
39+
if default_profile.is_none() {
40+
default_profile = profiles_file_contents
41+
.values()
42+
.find(|section| match section {
43+
Section::Profile { .. } => true,
44+
_ => false,
45+
});
46+
}
47+
48+
if default_profile.is_none() {
49+
return Err(anyhow::anyhow!("Could not find a firefox profile."));
50+
}
51+
52+
match default_profile.unwrap() {
53+
Section::Profile {
54+
is_relative, path, ..
55+
} => {
56+
if is_relative.eq(&String::from("1")) {
57+
Ok(format!("{home_directory}/.mozilla/firefox/{path}"))
58+
} else {
59+
Ok(path.clone())
60+
}
61+
}
62+
_ => {
63+
unreachable!("A non-profile section should be parsed as a profile.");
64+
}
65+
}
66+
}

‎client/src/settings.rs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ impl Default for FirefoxBookmarksPluginSettings {
9393
}
9494
}
9595

96+
#[derive(Debug, Deserialize)]
97+
pub struct FirefoxHistoryPluginSettings {
98+
#[serde(default = "default_true")]
99+
pub enable: bool,
100+
}
101+
102+
impl Default for FirefoxHistoryPluginSettings {
103+
fn default() -> Self {
104+
Self { enable: true }
105+
}
106+
}
107+
96108
#[derive(Debug, Deserialize)]
97109
pub struct GitRepositoriesPluginSettings {
98110
#[serde(default = "default_true")]
@@ -257,6 +269,8 @@ pub struct PluginSettings {
257269
#[serde(default)]
258270
pub firefox_bookmarks: FirefoxBookmarksPluginSettings,
259271
#[serde(default)]
272+
pub firefox_history: FirefoxHistoryPluginSettings,
273+
#[serde(default)]
260274
pub git_repositories: GitRepositoriesPluginSettings,
261275
#[serde(default)]
262276
pub gitmoji: GitmojiPluginSettings,

‎home-manager-module.nix‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ in {
5656
};
5757
};
5858

59+
firefox_history = {
60+
enable = lib.mkOption {
61+
default = true;
62+
type = lib.types.bool;
63+
description = lib.mdDoc "Enable / disable the plugin.";
64+
};
65+
};
66+
5967
git_repositories = {
6068
enable = lib.mkOption {
6169
default = true;

0 commit comments

Comments
 (0)