Skip to content

Commit 25fd009

Browse files
Add basic history page
1 parent 7ce701b commit 25fd009

File tree

10 files changed

+146
-8
lines changed

10 files changed

+146
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## Next
4+
- Add basic history page, accessible via `Cmd/Ctrl+Y`.
45
- Fix selection staying in the same position when videos update
56
- Keep video selected if it moves position
67
- Fix settings link in Get Started popup

bindings.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export function checkNow() {
3838
return invoke()<null>("check_now")
3939
}
4040

41+
export function getHistory() {
42+
return invoke()<UndoHistory>("get_history")
43+
}
44+
4145
export function getVideos(options: Options, after: After | null) {
4246
return invoke()<Video[]>("get_videos", { options,after })
4347
}
@@ -55,4 +59,6 @@ export type Options = { show_all: boolean; show_archived: boolean; channel_filte
5559
export type Video = { id: string; title: string; description: string; publishTimeMs: number; durationMs: number; thumbnailStandard: boolean; thumbnailMaxres: boolean; channelId: string; channelName: string; unread: boolean; archived: boolean }
5660
export type After = { publishTimeMs: number; id: string }
5761
export type Channel = { id: string; name: string; icon: string; uploads_playlist_id: string; from_time: number; refresh_rate_ms: number; tags: string[] }
62+
export type UndoHistory = { entries: ([number, Action])[] }
5863
export type AddChannelOptions = { url: string; from_time: number; refresh_rate_ms: number; tags: string[] }
64+
export type Action = "CheckNow" | { Archive: string } | { Unarchive: string } | { AddChannel: string } | { UpdateOrDeleteChannels: string }

src-tauri/src/data.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ use crate::settings::{Channel, Settings, VersionedSettings};
33
use crate::{api, background, throw};
44
use atomicwrites::{AtomicFile, OverwriteBehavior};
55
use scraper::{Html, Selector};
6-
use serde::Deserialize;
6+
use serde::{Deserialize, Serialize};
77
use specta::Type;
88
use sqlx::SqlitePool;
99
use std::collections::HashSet;
10+
use std::convert::TryInto;
1011
use std::env;
1112
use std::io::Write;
1213
use std::path::{Path, PathBuf};
1314
use std::sync::Arc;
15+
use std::time::{SystemTime, UNIX_EPOCH};
1416
use tauri::{command, Config, State};
1517
use tokio::sync::Mutex;
1618
use url::Url;
@@ -41,6 +43,7 @@ pub struct Data {
4143
pub versioned_settings: VersionedSettings,
4244
pub paths: AppPaths,
4345
pub window: tauri::Window,
46+
pub user_history: UndoHistory,
4447
}
4548
impl Data {
4649
pub fn settings(&mut self) -> &mut Settings {
@@ -122,6 +125,7 @@ pub async fn tags(data: DataState<'_>) -> Result<Vec<String>, String> {
122125
pub async fn check_now(data: DataState<'_>) -> Result<(), String> {
123126
let mut data = data.0.lock().await;
124127
data.check_now()?;
128+
data.user_history.push(Action::CheckNow);
125129
Ok(())
126130
}
127131

@@ -227,6 +231,8 @@ pub async fn set_channels(channels: Vec<Channel>, data: DataState<'_>) -> Result
227231
let mut data = data.0.lock().await;
228232
data.settings().channels = channels;
229233
data.save_settings()?;
234+
data.user_history
235+
.push(Action::UpdateOrDeleteChannels("".to_string()));
230236
Ok(())
231237
}
232238

@@ -256,7 +262,7 @@ pub async fn add_channel(options: AddChannelOptions, data: DataState<'_>) -> Res
256262
};
257263

258264
settings.channels.push(Channel {
259-
id: channel.id,
265+
id: channel.id.clone(),
260266
name: channel.snippet.title,
261267
icon: channel.snippet.thumbnails.medium.url,
262268
uploads_playlist_id: channel.contentDetails.relatedPlaylists.uploads,
@@ -265,6 +271,7 @@ pub async fn add_channel(options: AddChannelOptions, data: DataState<'_>) -> Res
265271
tags: options.tags,
266272
});
267273
data.save_settings()?;
274+
data.user_history.push(Action::AddChannel(channel.id));
268275
Ok(())
269276
}
270277

@@ -291,3 +298,42 @@ impl ArcData {
291298
Self(Arc::new(Mutex::new(data)))
292299
}
293300
}
301+
302+
#[derive(Serialize, Clone, Type)]
303+
pub struct UndoHistory {
304+
pub entries: Vec<(u32, Action)>,
305+
}
306+
307+
impl UndoHistory {
308+
pub fn new() -> Self {
309+
Self { entries: vec![] }
310+
}
311+
pub fn push(&mut self, action: Action) {
312+
let time: u32 = SystemTime::now()
313+
.duration_since(UNIX_EPOCH)
314+
.unwrap()
315+
.as_secs()
316+
.try_into()
317+
.unwrap();
318+
self.entries.push((time, action));
319+
if self.entries.len() > 100 {
320+
self.entries.remove(0);
321+
}
322+
}
323+
}
324+
325+
#[derive(Serialize, Clone, Type)]
326+
pub enum Action {
327+
CheckNow,
328+
Archive(String),
329+
Unarchive(String),
330+
AddChannel(String),
331+
UpdateOrDeleteChannels(String),
332+
}
333+
334+
#[command]
335+
#[specta::specta]
336+
pub async fn get_history(data: DataState<'_>) -> Result<UndoHistory, String> {
337+
let data = data.0.lock().await;
338+
Ok(data.user_history.clone())
339+
}

src-tauri/src/db.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::api::playlist_items;
2-
use crate::data::{AppPaths, DataState};
2+
use crate::data::{Action, AppPaths, DataState};
33
use crate::throw;
44
use serde::{Deserialize, Serialize};
55
use specta::Type;
@@ -225,19 +225,23 @@ async fn set_archived(pool: &SqlitePool, id: &str, value: bool) -> Result<(), St
225225
#[command]
226226
#[specta::specta]
227227
pub async fn archive(id: String, data: DataState<'_>) -> Result<(), String> {
228-
let data = data.0.lock().await;
228+
let mut data = data.0.lock().await;
229229
match set_archived(&data.db_pool, &id, true).await {
230-
Ok(()) => Ok(()),
230+
Ok(()) => (),
231231
Err(e) => throw!("Error archiving video: {}", e),
232232
}
233+
data.user_history.push(Action::Archive(id));
234+
Ok(())
233235
}
234236

235237
#[command]
236238
#[specta::specta]
237239
pub async fn unarchive(id: String, data: DataState<'_>) -> Result<(), String> {
238-
let data = data.0.lock().await;
240+
let mut data = data.0.lock().await;
239241
match set_archived(&data.db_pool, &id, false).await {
240-
Ok(()) => Ok(()),
242+
Ok(()) => (),
241243
Err(e) => throw!("Error unarchiving video: {}", e),
242244
}
245+
data.user_history.push(Action::Unarchive(id));
246+
Ok(())
243247
}

src-tauri/src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use crate::data::{AppPaths, ArcData, Data};
77
use crate::settings::yt_email_notifier;
88
use crate::settings::VersionedSettings;
9+
use data::UndoHistory;
910
use tauri::api::{dialog, shell};
1011
#[cfg(target_os = "macos")]
1112
use tauri::AboutMetadata;
@@ -93,6 +94,7 @@ async fn main() {
9394
data::add_channel,
9495
data::set_general_settings,
9596
data::check_now,
97+
data::get_history,
9698
db::get_videos,
9799
db::archive,
98100
db::unarchive
@@ -137,6 +139,7 @@ async fn main() {
137139
data::add_channel,
138140
data::set_general_settings,
139141
data::check_now,
142+
data::get_history,
140143
db::get_videos,
141144
db::archive,
142145
db::unarchive,
@@ -184,6 +187,7 @@ async fn main() {
184187
versioned_settings: settings,
185188
paths: app_paths,
186189
window: win.clone(),
190+
user_history: UndoHistory::new(),
187191
};
188192
app.manage(ArcData::new(data));
189193

@@ -269,6 +273,10 @@ async fn main() {
269273
.accelerator("Alt+CmdOrCtrl+A")
270274
.into(),
271275
MenuItem::Separator.into(),
276+
CustomMenuItem::new("History", "History")
277+
.accelerator("CmdOrCtrl+Y")
278+
.into(),
279+
MenuItem::Separator.into(),
272280
MenuItem::EnterFullScreen.into(),
273281
]),
274282
)),

src/lib/Link.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<style lang="sass">
1818
button
1919
font-size: 13px
20+
font-size: inherit
2021
margin: 0px
2122
background-color: transparent
2223
padding: 0px

src/lib/modals/Settings.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
<p class="sub">
3434
Kadium has a default API key, but it's vulnerable to abuse and could run out of quota.
3535
<Link on:click={() => (keyGuideVisible = true)}>
36-
<div>Get your own key</div>
36+
<div style="font-size: 13px;">Get your own key</div>
3737
</Link>
3838
</p>
3939
<input class="textbox" type="text" bind:value={apiKey} placeholder="AIzaSyNq5Y9knL..." />

src/routes/+layout.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
goto('/', { replaceState: true })
5454
} else if (payload === 'Channels') {
5555
goto('/channels', { replaceState: true })
56+
} else if (payload === 'History') {
57+
goto('/history', { replaceState: true })
5658
} else if (payload === 'Preferences...' || payload === 'Options...') {
5759
$settingsOpen = true
5860
} else if (payload === 'Add Channel...') {

src/routes/channels/+page.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@
200200
color: hsla(231, 20%, 100%, 0.5)
201201
.edit
202202
padding: 10px 0px
203+
font-size: 13px
203204
input
204205
height: 28px
205206
box-sizing: border-box

src/routes/history/+page.svelte

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script lang="ts">
2+
import { getHistory } from '../../../bindings'
3+
import Link from '$lib/Link.svelte'
4+
import { shell } from '@tauri-apps/api'
5+
6+
const history = getHistory()
7+
</script>
8+
9+
<main>
10+
<h1>Basic-ass history page</h1>
11+
12+
<p class="dark">The history is lost when you close the app</p>
13+
14+
{#await history then history}
15+
{#if history.entries.length === 0}
16+
Empty! When you do things, it will show here.
17+
{/if}
18+
<table>
19+
{#each history.entries.reverse() as [timestamp, action]}
20+
<tr>
21+
<td class="timestamp dark">
22+
{new Date(timestamp * 1000).toLocaleString()}
23+
</td>
24+
<td>
25+
{#if action === 'CheckNow'}
26+
Manually check for videos
27+
{:else if 'Archive' in action}
28+
{@const id = action.Archive}
29+
Archive video ID <Link
30+
on:click={() => shell.open(`https://www.youtube.com/watch?v=${id}`)}
31+
>{action.Archive}</Link
32+
>
33+
{:else if 'Unarchive' in action}
34+
{@const id = action.Unarchive}
35+
Unarchive video ID <Link
36+
on:click={() => shell.open(`https://www.youtube.com/watch?v=${id}`)}
37+
>{action.Unarchive}</Link
38+
>
39+
{:else if 'AddChannel' in action}
40+
{@const id = action.AddChannel}
41+
Added channel <Link
42+
on:click={() => shell.open(`https://www.youtube.com/channel/${id}`)}
43+
>{action.AddChannel}</Link
44+
>
45+
{:else if 'UpdateOrDeleteChannels' in action}
46+
Updated or deleted channel(s)
47+
{/if}
48+
</td>
49+
</tr>
50+
{/each}
51+
</table>
52+
{#if history.entries.length >= 100}
53+
<p>The end. Only 100 entries are shown.</p>
54+
{/if}
55+
{:catch error}
56+
Error {error}
57+
{/await}
58+
</main>
59+
60+
<style lang="sass">
61+
main
62+
padding: 20px
63+
padding-top: 0px
64+
overflow-y: auto
65+
.dark
66+
color: hsl(210, 8%, 80%)
67+
.timestamp
68+
padding-right: 10px
69+
</style>

0 commit comments

Comments
 (0)