Skip to content

Hiding Watched Videos on Channel View #7366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 51 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6e4fecf
Toggle Switch created with proper label, imported from component list…
palharesf Apr 28, 2025
fcaddb8
Make toggle only visible on the Videos tab
palharesf Apr 28, 2025
bbddf3c
Adjusted vertical position of the toggle
palharesf Apr 28, 2025
b8d85a6
Adjusted styling to separate toggle and dropdowns
palharesf Apr 28, 2025
ee82ca2
Refactor variable names for better readability
palharesf Apr 28, 2025
f7d5d93
Created the function to filter watched videos with check logic in the…
palharesf May 5, 2025
dd46933
Fixed hideWatchedToggle variable initialization and updating; also co…
palharesf May 5, 2025
6604ab9
Updated the toggle helper function to update the ref values; created …
palharesf May 5, 2025
6565d6c
Removed autoRefresh options
palharesf May 5, 2025
3bc97bc
Created helper function to check if video exists in history
palharesf May 5, 2025
97ec3f0
Updated helper function 'filterWatchedVideos' to be able to get histo…
palharesf May 5, 2025
d991ed6
Added some comments about limitations found during testing
palharesf May 5, 2025
d159660
Fixing localization key in the app and adding localization key in the…
palharesf May 5, 2025
fadc362
Added logic to filter watched videos when getting video list from Inv…
palharesf May 5, 2025
1962830
Missing space
palharesf May 7, 2025
3af3a23
Changed the template reference from the latestVideos array to a compu…
palharesf May 7, 2025
fcb0088
Removed all the logic that changed the original latestVideos (replace…
palharesf May 7, 2025
464407a
Same as last commit, but I forgot to include this last bit of code
palharesf May 7, 2025
20107d1
Added the button for both shorts and live pages, but functionality st…
palharesf May 7, 2025
4f7cc1c
Implemented filtering logic through computed field for Shorts
palharesf May 7, 2025
69df855
Implemented computed field for Lives, working properly
palharesf May 7, 2025
8fe1a72
Since 'Hide Watched' is being used outside of the Videos tab, I incre…
palharesf May 7, 2025
1e5b5d3
Since the helper function is not specific to videos, I renamed it to …
palharesf May 7, 2025
8a8f855
Added a visibility flag to hide the toggle when the videoPanel is bei…
palharesf May 7, 2025
1cd18e6
Adjusted computer variable to force a screen refresh if the filteredA…
palharesf May 7, 2025
7a0ec56
Returned filteredVideos to a pure computed function
palharesf May 8, 2025
c51626b
Update src/renderer/views/Channel/Channel.vue
palharesf May 7, 2025
ccccfde
Update src/renderer/views/Channel/Channel.vue
palharesf May 7, 2025
a608731
Update src/renderer/views/Channel/Channel.vue
palharesf May 7, 2025
6b53c2c
Hide the 'Sort By' dropdown if there's only one video loaded on the v…
palharesf May 8, 2025
6331f0c
Refactor - simplify logic for when to show the toggle on videos page
palharesf May 10, 2025
feaf4ba
Refactor - simplify logic for when to show the toggle on shorts page
palharesf May 10, 2025
083da11
Refactor - simplify logic for when to show the toggle on live page
palharesf May 10, 2025
6e695af
Refactor - updated variable name for better readability
palharesf May 10, 2025
7fd310c
Refactor, adjusted case for function name
palharesf May 11, 2025
2bf29b8
Refactor, adjusted case in function call
palharesf May 11, 2025
2eaca07
Removed the switch from the Channel page
palharesf May 22, 2025
12b063a
Removed the CSS rules created for proper positioning of the toggle bu…
palharesf May 22, 2025
e446c06
Removed redundant import (since we're not rendering the toggle anymore)
palharesf May 22, 2025
db6e48d
Removed unused sentence from the Channel page
palharesf May 22, 2025
a1275f2
Moved toggle element from subscription to distractionfree component
palharesf May 22, 2025
8af16ab
Moved computed functions and methods between components
palharesf May 22, 2025
77343ba
Updated localization settings
palharesf May 22, 2025
38bcb24
Updated variable declaration to access value from the store (global s…
palharesf May 22, 2025
1eb673b
Refactor all variable referencs to the new variable name; removed old…
palharesf May 22, 2025
468062c
Implemented tooltip and localization
palharesf May 22, 2025
72768e7
Merge branch 'development' into feature/4497-hide-watched-videos
PikachuEXE May 27, 2025
135cc6f
Update static/locales/en-US.yaml
palharesf May 28, 2025
59c8dd0
Update src/renderer/views/Channel/Channel.vue
palharesf May 28, 2025
f49bbe9
Move translations for Hide Videos on Watch key
efb4f5ff-1298-471a-8973-3d47447115dc May 28, 2025
727b7a9
Merge branch 'development' into feature/4497-hide-watched-videos
PikachuEXE Jun 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@
/>
</div>
<div class="switchColumn">
<FtToggleSwitch
:label="$t('Settings.Subscription Settings.Hide Videos on Watch')"
:default-value="hideWatchedSubs"
compact
@change="updateHideWatchedSubs"
/>
<FtToggleSwitch
:label="$t('Settings.Subscription Settings.Limit the number of videos displayed for each channel')"
:default-value="onlyShowLatestFromChannel"
Expand Down Expand Up @@ -93,16 +87,6 @@ function updateUnsubscriptionPopupStatus(value) {
store.dispatch('updateUnsubscriptionPopupStatus', value)
}
/** @type {import('vue').ComputedRef<boolean>} */
const hideWatchedSubs = computed(() => store.getters.getHideWatchedSubs)
/**
* @param {boolean} value
*/
function updateHideWatchedSubs(value) {
store.dispatch('updateHideWatchedSubs', value)
}
/** @type {import('vue').ComputedRef<boolean>} */
const onlyShowLatestFromChannel = computed(() => store.getters.getOnlyShowLatestFromChannel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ export default defineComponent({
settingsSection: this.$t('Settings.Distraction Free Settings.Distraction Free Settings')
})
},
hideWatchedSubs: function () {
return this.$store.getters.getHideWatchedSubs
},
},
mounted: function () {
this.verifyChannelsHidden()
Expand Down Expand Up @@ -245,6 +248,7 @@ export default defineComponent({
'updateHideSubscriptionsCommunity',
'updateShowAddedChannelsHidden',
'updateShowAddedForbiddenTitles',
])
}
'updateHideWatchedSubs',
]),
},
})
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@
:default-value="hideSharingActions"
@change="updateHideSharingActions"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Videos on Watch')"
:default-value="hideWatchedSubs"
:compact="true"
:tooltip="$t('Tooltips.Distraction Free Settings.Hide Videos on Watch')"
@change="updateHideWatchedSubs"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
Expand Down
44 changes: 38 additions & 6 deletions src/renderer/views/Channel/Channel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<div class="select-container">
<FtSelect
v-if="showVideoSortBy"
v-show="currentTab === 'videos' && latestVideos.length > 0"
v-show="currentTab === 'videos' && (hideWatchedSubs ? filteredVideos.length > 1 : filteredVideos.length > 0)"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea why we need different logic based on Hide Videos on Watch enabled or not
Probably just need filteredVideos.length > 1 here

Or do you mean the opposite?
When watched entries are hidden, allow changing the sorting due to having 1 entry does not mean 1 entry only in other orders (e.g. all except 1 watched, but all oldest still not watched)
However if this is the case the criteria should be on NOT filtered entries = latestVideos.length > 1 (i.e. more than 1 entries from remote = possibility to have unwatched entries in other sorting orders

I might get some of this wrong let me know

Copy link
Member

@efb4f5ff-1298-471a-8973-3d47447115dc efb4f5ff-1298-471a-8973-3d47447115dc May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current behavior in dev branch is that the sort by dropdown gets hidden when there is only one video available in the tabs. With the addition of this feature it should basically do the same. So when there is one video available in the tab because all others are marked as watched, hide sort by dropdown

Edit: oh wait a sec...idk why im just noticing now or maybe it was bad guidance of me prior to this but it hides the sort by dropdown when fetch more results is available, which means sorting is still possible. Sort by dropdown should only be hidden when there is one video listed in the tab AND when there fetch more results button isnt there. So if im reading the code correctly Pika is right, the logic we had in place is sufficient

Copy link
Author

@palharesf palharesf May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, there's no need to differentiate the behavior depending on the state of hideWatchedSubs -- as long as filteredVideos.lenght >1 is met, the dropdown should appear.

I believe when I started working on the feature, the dropdown still appeared even when there was only one video in the page, and I didn't want to touch the original condition, so that would explain why I added another condition looking at hideWatchedSubs, but there's no good reason to keep it that way

I will try to push this fix (without forcing it) and let you know if I have trouble doing so

Copy link
Member

@absidue absidue May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original code checks YouTube's response for the dropdown, which only hides it when there is only 1 or 0 videos and there are no continuations. You need to ensure that the original behaviour still exists when the hide watched setting is disabled.

So the important thing for this pull requests is that you cannot hide it just based on filteredVideos.length < 2, because if there is a continuation available, you don't actually know that all videos are hidden, because there may be some visible ones that were not fetched yet.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My proposal was to only show the dropdown if we are in one of the tabs it would appear (currentTab === 'videos' and the same for shorts and lives) and if the array containing the videos to be displayed, after computing for hiding watched videos, has more than one video (`filteredVideos.length >1)

So basically just switching
v-show="currentTab === 'videos' && (hideWatchedSubs ? filteredVideos.length > 1 : filteredVideos.length > 0)"

for
` v-show="currentTab === 'videos' && filteredVideos.length > 1"

It makes sense to me unless I'm missing something, and I just tested and it works. Let me know if you see any issues with that approach

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also realized why I was not able to push my previous changes cleanly, my forked repo was behind the actual origin repo so that's why simply pulling the changes was not working. I have just synced my repo with the origin one, and when I try to pull the changes into my repo I finally get the conflicts.

I assume that I can accept the remote repository changes by using git checkout --theirs src/renderer/components/subscription-settings/subscription-settings.js and git checkout --theirs src/renderer/components/subscription-settings/subscription-settings.vue, but I'm afraid of doing that without checking with you all and breaking things. If its easier to just suggest the changes I outlined in my previous comment directly into the code, I'm also happy with that approach

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I re-read both of your comments and understand I was overlooking the case where there was the possibility of fetching more videos. I need to think how to incorporate that into the logic and test the implementation, as it's not coming to me immediately. I'll try to sync my branch to the origin before doing that so I don't rely on you guys to push every little change

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @palharesf just checking in. What is the status on this? Do you need any additional help?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I still haven't been able to properly merge the changes from the remote branch onto my local copy of the repo, as outlined here.

It's unclear what I can do without breaking things, and as I have already broken stuff here in the past I decided to wait for guidance.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just rename your existing branch (delete it later)
Check out this pull request (you can use gh pr checkout 7366 if you got gh installed) and work from there
If you got something in the old branch committed but not pushed yet you can see try to apply those on new branch once you have done the above

:value="videoSortBy"
:select-names="videoLiveShortSelectNames"
:select-values="videoLiveShortSelectValues"
Expand All @@ -51,7 +51,7 @@
/>
<FtSelect
v-if="!hideChannelShorts && showShortSortBy"
v-show="currentTab === 'shorts' && latestShorts.length > 0"
v-show="currentTab === 'shorts' && (hideWatchedSubs ? filteredShorts.length > 1 : filteredShorts.length > 0)"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

:value="shortSortBy"
:select-names="videoLiveShortSelectNames"
:select-values="videoLiveShortSelectValues"
Expand All @@ -61,7 +61,7 @@
/>
<FtSelect
v-if="!hideLiveStreams && showLiveSortBy"
v-show="currentTab === 'live' && latestLive.length > 0"
v-show="currentTab === 'live' && (hideWatchedSubs ? filteredShorts.length > 1 : filteredShorts.length > 0)"
:value="liveSortBy"
:select-names="videoLiveShortSelectNames"
:select-values="videoLiveShortSelectValues"
Expand Down Expand Up @@ -97,7 +97,7 @@
<FtElementList
v-show="currentTab === 'videos'"
id="videoPanel"
:data="latestVideos"
:data="filteredVideos"
:use-channels-hidden-preference="false"
role="tabpanel"
aria-labelledby="videosTab"
Expand All @@ -112,7 +112,7 @@
<FtElementList
v-if="!hideChannelShorts && currentTab === 'shorts'"
id="shortPanel"
:data="latestShorts"
:data="filteredShorts"
:use-channels-hidden-preference="false"
role="tabpanel"
aria-labelledby="shortsTab"
Expand All @@ -128,7 +128,7 @@
v-if="!hideLiveStreams"
v-show="currentTab === 'live'"
id="livePanel"
:data="latestLive"
:data="filteredLive"
:use-channels-hidden-preference="false"
role="tabpanel"
aria-labelledby="liveTab"
Expand Down Expand Up @@ -469,6 +469,9 @@ const hideChannelPlaylists = computed(() => store.getters.getHideChannelPlaylist
/** @type {import('vue').ComputedRef<boolean>} */
const hideChannelCommunity = computed(() => store.getters.getHideChannelCommunity)

/** @type {import('vue').ComputedRef<boolean>} */
const hideWatchedSubs = computed(() => store.getters.getHideWatchedSubs)

/**
* @template T
* @param {T[]} array
Expand Down Expand Up @@ -1048,6 +1051,30 @@ const videoContinuationData = shallowRef(null)
const showVideoSortBy = ref(true)
const videoSortBy = ref('newest')

const filteredVideos = computed(() => {
if (hideWatchedSubs.value) {
return filterWatchedArray(latestVideos.value)
} else {
return latestVideos.value
}
})

const filteredShorts = computed(() => {
if (hideWatchedSubs.value) {
return filterWatchedArray(latestShorts.value)
} else {
return latestShorts.value
}
})

const filteredLive = computed(() => {
if (hideWatchedSubs.value) {
return filterWatchedArray(latestLive.value)
} else {
return latestLive.value
}
})

watch(videoSortBy, () => {
if (!autoRefreshOnSortByChangeEnabled) { return }

Expand Down Expand Up @@ -2315,6 +2342,11 @@ function handleSubscription() {
posts: latestCommunityPosts.value
})
}

function filterWatchedArray(videos) {
const historyCache = store.getters.getHistoryCacheById
return videos.filter(video => !Object.hasOwn(historyCache, video.videoId))
}
</script>

<style scoped src="./Channel.css" />
3 changes: 2 additions & 1 deletion static/locales/en-US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,6 @@ Settings:
Are you sure you want to remove all your playlists?: Are you sure you want to remove all your playlists?
Subscription Settings:
Subscription Settings: Subscription
Hide Videos on Watch: Hide Videos on Watch
Fetch Feeds from RSS: Fetch Feeds from RSS
Fetch Automatically: Fetch Feed Automatically
'Limit the number of videos displayed for each channel': 'Limit the number of videos displayed for each channel'
Expand Down Expand Up @@ -559,6 +558,7 @@ Settings:
Hide Live Streams: Hide Live Streams
Hide Upcoming Premieres: Hide Upcoming Premieres
Hide Sharing Actions: Hide Sharing Actions
Hide Videos on Watch: Hide Videos on Watch
Hide Chapters: Hide Chapters
Hide Channels: Hide Videos From Channels
Hide Channels Disabled Message: Some channels were blocked using ID and weren't processed. Feature is blocked while those IDs are updating
Expand Down Expand Up @@ -1086,6 +1086,7 @@ Tooltips:
The channel ID entered must be a complete match and is case sensitive.
Hide Subscriptions Live: 'This setting is overridden by the app-wide "{appWideSetting}" setting, in the "{subsection}" section of the "{settingsSection}"'
Hide Videos and Playlists Containing Text: Enter a word, word fragment, or phrase (case insensitive) to hide all videos & playlists whose original titles contain it throughout all of FreeTube, excluding only History, Your Playlists, and videos inside of playlists.
Hide Videos on Watch: 'This option hides watched videos, lives and shorts from the Subscriptions and Channels Pages. It does not affect the Home tab on the Channels page"'
Subscription Settings:
Fetch Feeds from RSS: When enabled, FreeTube will use RSS instead of its default
method for grabbing your subscription feed. RSS is faster and prevents IP blocking,
Expand Down