Filtering: Add includePreviouslyWatched setting

This commit is contained in:
IRHM
2024-02-03 02:13:46 +00:00
parent 4d3da51ae7
commit 5146af2672
5 changed files with 102 additions and 17 deletions
+4
View File
@@ -80,6 +80,10 @@ type UserSettings struct {
PrivateThoughts *bool `gorm:"default:false" json:"privateThoughts"`
// If ui 'spoilers' should be shown
HideSpoilers *bool `gorm:"default:false" json:"hideSpoilers"`
// If user wants previously watched items to show in 'Finished' filter,
// even if the watched item state has since been changed. Used only in
// frontend.
IncludePreviouslyWatched *bool `gorm:"default:false" json:"includePreviouslyWatched"`
}
// We use a separate struct for registration to avoid confusion
+15 -2
View File
@@ -55,8 +55,16 @@ func userUpdate(db *gorm.DB, userId uint, ur UserSettings) (UserSettings, error)
if ur.PrivateThoughts != nil {
user.PrivateThoughts = ur.PrivateThoughts
}
if ur.IncludePreviouslyWatched != nil {
user.IncludePreviouslyWatched = ur.IncludePreviouslyWatched
}
db.Save(&user)
return UserSettings{Private: user.Private, PrivateThoughts: user.PrivateThoughts, HideSpoilers: user.HideSpoilers}, nil
return UserSettings{
Private: user.Private,
PrivateThoughts: user.PrivateThoughts,
HideSpoilers: user.HideSpoilers,
IncludePreviouslyWatched: user.IncludePreviouslyWatched,
}, nil
}
func userGetSettings(db *gorm.DB, userId uint) (UserSettings, error) {
@@ -67,7 +75,12 @@ func userGetSettings(db *gorm.DB, userId uint) (UserSettings, error) {
slog.Error("user get failed", "user_id", userId, "error", res.Error)
return UserSettings{}, errors.New("failed to retrieve user")
}
return UserSettings{Private: user.Private, PrivateThoughts: user.PrivateThoughts, HideSpoilers: user.HideSpoilers}, nil
return UserSettings{
Private: user.Private,
PrivateThoughts: user.PrivateThoughts,
HideSpoilers: user.HideSpoilers,
IncludePreviouslyWatched: user.IncludePreviouslyWatched,
}, nil
}
func userSearch(db *gorm.DB, currentUsersId uint, q string) ([]PublicUser, error) {
+63 -15
View File
@@ -3,7 +3,7 @@
import Icon from "@/lib/Icon.svelte";
import Poster from "@/lib/poster/Poster.svelte";
import PosterList from "@/lib/poster/PosterList.svelte";
import { activeFilters, activeSort } from "@/store";
import { activeFilters, activeSort, userSettings } from "@/store";
import type { Watched, WatchedSeason } from "@/types";
export let list: Watched[];
@@ -11,8 +11,39 @@
$: sort = $activeSort;
$: filters = $activeFilters;
$: watched = list
.sort((a, b) => {
$: watched = list;
$: settings = $userSettings;
function contentWatchedPreviously(w: Watched) {
let wp = false;
const relatedActivity = w.activity.filter(
(a) =>
a.type === "ADDED_WATCHED" ||
a.type === "IMPORTED_ADDED_WATCHED" ||
a.type === "IMPORTED_WATCHED"
);
for (let i = 0; i < relatedActivity.length; i++) {
const ra = relatedActivity[i];
if (ra.type === "IMPORTED_ADDED_WATCHED") {
wp = true;
break;
} else if (ra.type === "ADDED_WATCHED" || ra.type === "IMPORTED_WATCHED") {
const data = JSON.parse(ra.data);
if (data?.status == "FINISHED") {
wp = true;
break;
}
}
}
return wp;
}
// Monsterous code for filters. Soz.
$: (watched, filters), filt();
function filt() {
// Set watched to list and sort it.
watched = list.sort((a, b) => {
if (sort[0] === "DATEADDED" && sort[1] === "UP") {
return Date.parse(a.createdAt) - Date.parse(b.createdAt);
} else if (sort[0] === "ALPHA") {
@@ -27,21 +58,38 @@
}
// default DATEADDED DOWN
return Date.parse(b.createdAt) - Date.parse(a.createdAt);
})
.filter((w) => {
if (filters.status.length <= 0 && filters.type.length <= 0) return w;
if (filters.status.length > 0 && filters.type.length > 0) {
return (
filters.status.includes(w.status?.toLowerCase()) && filters.type.includes(w.content.type)
});
// Now apply filters to watch list.
if (filters.status.length > 0 && filters.type.length > 0) {
// If status and type filters applied, combine both.
if (settings?.includePreviouslyWatched && filters.status.includes("finished")) {
watched = watched.filter(
(w) =>
(filters.status.includes(w.status?.toLowerCase()) &&
filters.type.includes(w.content.type)) ||
contentWatchedPreviously(w)
);
} else {
watched = watched.filter(
(w) =>
filters.status.includes(w.status?.toLowerCase()) &&
filters.type.includes(w.content.type)
);
}
if (filters.type.length > 0) {
return filters.type.includes(w.content.type);
} else if (filters.type.length > 0) {
// Only filter type
watched = watched.filter((w) => filters.type.includes(w.content.type));
} else if (filters.status.length > 0) {
// Only filter status
if (settings?.includePreviouslyWatched && filters.status.includes("finished")) {
watched = watched.filter(
(w) => filters.status.includes(w.status?.toLowerCase()) || contentWatchedPreviously(w)
);
} else {
watched = watched.filter((w) => filters.status.includes(w.status?.toLowerCase()));
}
if (filters.status.length > 0) {
return filters.status.includes(w.status?.toLowerCase());
}
});
}
}
// Get biggest season watching or biggest season watched.
// This could probably be simpler but -_-
+19
View File
@@ -23,6 +23,7 @@
let privateThoughtsDisabled = false;
let exportDisabled = false;
let hideSpoilersDisabled = false;
let includePreviouslyWatchedDisabled = false;
let pwChangeModalOpen = false;
async function getProfile() {
@@ -236,6 +237,24 @@
}}
/>
</Setting>
<Setting
title="Include Previously Watched"
desc="Do you want to include previously watched content in the 'finished' filter, even if their status has now been changed?"
row
>
<Checkbox
name="includePreviouslyWatched"
disabled={includePreviouslyWatchedDisabled}
value={settings?.includePreviouslyWatched}
toggled={(on) => {
includePreviouslyWatchedDisabled = true;
updateUserSetting("includePreviouslyWatched", on, () => {
includePreviouslyWatchedDisabled = false;
});
}}
/>
</Setting>
<div class="row btns">
<button on:click={() => goto("/import")}>Import</button>
<button on:click={() => downloadWatchedList()} disabled={exportDisabled}>Export</button>
+1
View File
@@ -120,6 +120,7 @@ export interface UserSettings {
private: boolean;
privateThoughts: boolean;
hideSpoilers: boolean;
includePreviouslyWatched: boolean;
}
export interface ChangePasswordForm {