From cb2fd51dc5a35f5562cc843cb5bc34bddcc5c40b Mon Sep 17 00:00:00 2001
From: Preston
Date: Fri, 21 Aug 2020 08:51:20 -0400
Subject: [PATCH 1/6] Start progress on subscriptions
---
src/renderer/components/side-nav/side-nav.vue | 2 +-
src/renderer/router/index.js | 26 +++++++++++++++++++
.../views/ProfileEdit/ProfileEdit.css | 15 +++++++++++
src/renderer/views/ProfileEdit/ProfileEdit.js | 15 +++++++++++
.../views/ProfileEdit/ProfileEdit.vue | 15 +++++++++++
.../views/ProfileSettings/ProfileSettings.css | 15 +++++++++++
.../views/ProfileSettings/ProfileSettings.js | 15 +++++++++++
.../views/ProfileSettings/ProfileSettings.vue | 15 +++++++++++
8 files changed, 117 insertions(+), 1 deletion(-)
create mode 100644 src/renderer/views/ProfileEdit/ProfileEdit.css
create mode 100644 src/renderer/views/ProfileEdit/ProfileEdit.js
create mode 100644 src/renderer/views/ProfileEdit/ProfileEdit.vue
create mode 100644 src/renderer/views/ProfileSettings/ProfileSettings.css
create mode 100644 src/renderer/views/ProfileSettings/ProfileSettings.js
create mode 100644 src/renderer/views/ProfileSettings/ProfileSettings.vue
diff --git a/src/renderer/components/side-nav/side-nav.vue b/src/renderer/components/side-nav/side-nav.vue
index 33f5d8d9..f2e911a0 100644
--- a/src/renderer/components/side-nav/side-nav.vue
+++ b/src/renderer/components/side-nav/side-nav.vue
@@ -87,7 +87,7 @@
+
+
+ {{ $t("Subscriptions.Subscriptions") }}
+
+
+ {{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }}
+
+
+
+
+
+
+
+
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.css b/src/renderer/views/ProfileSettings/ProfileSettings.css
new file mode 100644
index 00000000..b6db9095
--- /dev/null
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.css
@@ -0,0 +1,15 @@
+.card {
+ width: 85%;
+ margin: 0 auto;
+ margin-bottom: 60px;
+}
+
+.message {
+ color: var(--tertiary-text-color);
+}
+
+@media only screen and (max-width: 680px) {
+ .card {
+ width: 90%;
+ }
+}
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.js b/src/renderer/views/ProfileSettings/ProfileSettings.js
new file mode 100644
index 00000000..8ba65cbd
--- /dev/null
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.js
@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import FtCard from '../../components/ft-card/ft-card.vue'
+import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
+import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
+
+export default Vue.extend({
+ name: 'ProfileSettings',
+ components: {
+ 'ft-card': FtCard,
+ 'ft-flex-box': FtFlexBox,
+ 'ft-element-list': FtElementList
+ },
+ mounted: function () {
+ }
+})
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.vue b/src/renderer/views/ProfileSettings/ProfileSettings.vue
new file mode 100644
index 00000000..0e840e9e
--- /dev/null
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.vue
@@ -0,0 +1,15 @@
+
+
+
+ {{ $t("Subscriptions.Subscriptions") }}
+
+
+ {{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }}
+
+
+
+
+
+
+
+
From b291cbf37baded5ea9e965df226a3dc4a15bb01b Mon Sep 17 00:00:00 2001
From: Preston
Date: Sun, 23 Aug 2020 15:07:29 -0400
Subject: [PATCH 2/6] Start progress on adding profiles
---
package-lock.json | 5 +
package.json | 1 +
src/renderer/App.js | 1 +
src/renderer/store/modules/profile.js | 106 ++++++++++++++++++
src/renderer/store/modules/utils.js | 38 +++++++
.../views/ProfileSettings/ProfileSettings.vue | 2 +-
static/locales/en-US.yaml | 3 +
7 files changed, 155 insertions(+), 1 deletion(-)
create mode 100644 src/renderer/store/modules/profile.js
diff --git a/package-lock.json b/package-lock.json
index 4adb0e19..344251cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13318,6 +13318,11 @@
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=",
"dev": true
},
+ "lodash.uniqwith": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz",
+ "integrity": "sha1-egy/ZfQ7WShiWp1NDcVLGMrcfvM="
+ },
"log-symbols": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
diff --git a/package.json b/package.json
index a0801978..752f94ad 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"js-yaml": "^3.14.0",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
+ "lodash.uniqwith": "^4.5.0",
"material-design-icons": "^3.0.1",
"mediaelement": "^4.2.16",
"nedb": "^1.8.0",
diff --git a/src/renderer/App.js b/src/renderer/App.js
index 609c375a..0e4e2922 100644
--- a/src/renderer/App.js
+++ b/src/renderer/App.js
@@ -33,6 +33,7 @@ export default Vue.extend({
mounted: function () {
this.$store.dispatch('grabUserSettings')
this.$store.dispatch('grabHistory')
+ this.$store.dispatch('grabAllProfiles', this.$t('Profile.All Channels'))
this.$store.commit('setUsingElectron', useElectron)
this.checkThemeSettings()
this.checkLocale()
diff --git a/src/renderer/store/modules/profile.js b/src/renderer/store/modules/profile.js
new file mode 100644
index 00000000..dc4bfd7a
--- /dev/null
+++ b/src/renderer/store/modules/profile.js
@@ -0,0 +1,106 @@
+import Datastore from 'nedb'
+
+let dbLocation
+
+if (window && window.process && window.process.type === 'renderer') {
+ // Electron is being used
+ /* let dbLocation = localStorage.getItem('dbLocation')
+
+ if (dbLocation === null) {
+ const electron = require('electron')
+ dbLocation = electron.remote.app.getPath('userData')
+ } */
+
+ const electron = require('electron')
+ dbLocation = electron.remote.app.getPath('userData')
+
+ dbLocation = dbLocation + '/profiles.db'
+} else {
+ dbLocation = 'profiles.db'
+}
+
+const profileDb = new Datastore({
+ filename: dbLocation,
+ autoload: true
+})
+
+const state = {
+ profileList: [],
+ activeProfile: 'allChannels'
+}
+
+const getters = {
+ getProfileList: () => {
+ return state.historyCache
+ }
+}
+
+const actions = {
+ grabAllProfiles ({ dispatch, commit }, defaultName = null) {
+ profileDb.find({}, (err, results) => {
+ if (!err) {
+ if (results.length === 0) {
+ dispatch('createDefaultProfile', defaultName)
+ } else {
+ commit('setProfileList', results)
+ }
+ }
+ })
+ },
+
+ async createDefaultProfile ({ dispatch }, defaultName) {
+ const randomColor = await dispatch('getRandomColor')
+ const textColor = await dispatch('calculateColorLuminance', randomColor)
+ const defaultProfile = {
+ _id: 'allChannels',
+ name: defaultName,
+ bgColor: randomColor,
+ textColor: textColor,
+ subscriptions: []
+ }
+ console.log(defaultProfile)
+ return
+ profileDb.update({ _id: 'allChannels' }, defaultProfile, { upsert: true }, (err, numReplaced) => {
+ if (!err) {
+ dispatch('grabAllProfiles')
+ }
+ })
+ },
+
+ updateProfile ({ dispatch }, profile) {
+ profileDb.update({ name: profile.name }, profile, { upsert: true }, (err, numReplaced) => {
+ if (!err) {
+ dispatch('grabAllProfiles')
+ }
+ })
+ },
+
+ removeFromHistory ({ dispatch }, videoId) {
+ historyDb.remove({ videoId: videoId }, (err, numReplaced) => {
+ if (!err) {
+ dispatch('grabHistory')
+ }
+ })
+ },
+
+ updateWatchProgress ({ dispatch }, videoData) {
+ historyDb.update({ videoId: videoData.videoId }, { $set: { watchProgress: videoData.watchProgress } }, { upsert: true }, (err, numReplaced) => {
+ if (!err) {
+ dispatch('grabHistory')
+ }
+ })
+ }
+}
+
+const mutations = {
+ setHistoryCache (state, historyCache) {
+ state.historyCache = historyCache
+ }
+}
+
+export default {
+ state,
+ getters,
+ actions,
+ mutations
+}
diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js
index 788c081d..2e888be0 100644
--- a/src/renderer/store/modules/utils.js
+++ b/src/renderer/store/modules/utils.js
@@ -28,6 +28,24 @@ const state = {
'mainAmber',
'mainOrange',
'mainDeepOrange'
+ ],
+ colorValues: [
+ '#d50000',
+ '#C51162',
+ '#AA00FF',
+ '#6200EA',
+ '#304FFE',
+ '#2962FF',
+ '#0091EA',
+ '#00B8D4',
+ '#00BFA5',
+ '#00C853',
+ '#64DD17',
+ '#AEEA00',
+ '#FFD600',
+ '#FFAB00',
+ '#FF6D00',
+ '#DD2C00'
]
}
@@ -63,6 +81,26 @@ const actions = {
return state.colorClasses[randomInt]
},
+ getRandomColor () {
+ const randomInt = Math.floor(Math.random() * state.colorValues.length)
+ return state.colorValues[randomInt]
+ },
+
+ calculateColorLuminance (_, colorValue) {
+ const cutHex = colorValue.substring(1, 7)
+ const colorValueR = parseInt(cutHex.substring(0, 2), 16)
+ const colorValueG = parseInt(cutHex.substring(2, 4), 16)
+ const colorValueB = parseInt(cutHex.substring(4, 6), 16)
+
+ const luminance = (0.299 * colorValueR + 0.587 * colorValueG + 0.114 * colorValueB) / 255
+
+ if (luminance > 0.5) {
+ return '#000000'
+ } else {
+ return '#FFFFFF'
+ }
+ },
+
getVideoIdFromUrl (_, url) {
/** @type {URL} */
let urlObject
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.vue b/src/renderer/views/ProfileSettings/ProfileSettings.vue
index 0e840e9e..0bbb9867 100644
--- a/src/renderer/views/ProfileSettings/ProfileSettings.vue
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.vue
@@ -1,7 +1,7 @@
- {{ $t("Subscriptions.Subscriptions") }}
+ {{ $t("Profile.Profile Manager") }}
{{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }}
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index f1e7f93a..339c3e84 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -246,6 +246,9 @@ About:
Latest FreeTube News: Latest FreeTube News
+Profile:
+ All Channels: All Channels
+ Profile Manager: Profile Manager
#On Channel Page
Channel:
Subscriber: Subscriber
From 0a612ae0e7b2736bac6075a2bbc81c910c69cdb4 Mon Sep 17 00:00:00 2001
From: Preston
Date: Sun, 23 Aug 2020 22:56:33 -0400
Subject: [PATCH 3/6] Create Profile Edit Page
---
src/renderer/components/ft-input/ft-input.js | 9 ++
src/renderer/components/ft-input/ft-input.vue | 1 +
.../ft-profile-bubble/ft-profile-bubble.css | 41 ++++++++
.../ft-profile-bubble/ft-profile-bubble.js | 35 +++++++
.../ft-profile-bubble/ft-profile-bubble.vue | 21 +++++
.../watch-video-info/watch-video-info.vue | 1 +
src/renderer/router/index.js | 2 +
src/renderer/store/modules/profile.js | 31 +++++--
src/renderer/store/modules/settings.js | 26 ++++--
src/renderer/store/modules/utils.js | 4 +
.../views/ProfileEdit/ProfileEdit.css | 24 +++++
src/renderer/views/ProfileEdit/ProfileEdit.js | 81 +++++++++++++++-
.../views/ProfileEdit/ProfileEdit.vue | 93 +++++++++++++++++--
.../views/ProfileSettings/ProfileSettings.js | 10 +-
.../views/ProfileSettings/ProfileSettings.vue | 11 ++-
static/locales/en-US.yaml | 11 +++
16 files changed, 370 insertions(+), 31 deletions(-)
create mode 100644 src/renderer/components/ft-profile-bubble/ft-profile-bubble.css
create mode 100644 src/renderer/components/ft-profile-bubble/ft-profile-bubble.js
create mode 100644 src/renderer/components/ft-profile-bubble/ft-profile-bubble.vue
diff --git a/src/renderer/components/ft-input/ft-input.js b/src/renderer/components/ft-input/ft-input.js
index 11007b3d..9d246ac3 100644
--- a/src/renderer/components/ft-input/ft-input.js
+++ b/src/renderer/components/ft-input/ft-input.js
@@ -23,6 +23,10 @@ export default Vue.extend({
type: Boolean,
default: false
},
+ disabled: {
+ type: Boolean,
+ default: false
+ },
dataList: {
type: Array,
default: () => { return [] }
@@ -47,6 +51,11 @@ export default Vue.extend({
return `${this.id}_datalist`
}
},
+ watch: {
+ value: function (val) {
+ this.inputData = val
+ }
+ },
mounted: function () {
this.id = this._uid
this.inputData = this.value
diff --git a/src/renderer/components/ft-input/ft-input.vue b/src/renderer/components/ft-input/ft-input.vue
index 1250301d..3707889e 100644
--- a/src/renderer/components/ft-input/ft-input.vue
+++ b/src/renderer/components/ft-input/ft-input.vue
@@ -20,6 +20,7 @@
type="text"
:placeholder="placeholder"
@input="e => handleInput(e.target.value)"
+ :disabled="disabled"
>
+
+
+
+ {{ profileInitial }}
+
+
+
+ {{ profileName }}
+
+
+
+
+
+
diff --git a/src/renderer/components/watch-video-info/watch-video-info.vue b/src/renderer/components/watch-video-info/watch-video-info.vue
index 965b4988..8c88e138 100644
--- a/src/renderer/components/watch-video-info/watch-video-info.vue
+++ b/src/renderer/components/watch-video-info/watch-video-info.vue
@@ -30,6 +30,7 @@
:label="subscribedText"
class="subscribeButton"
background-color="var(--primary-color)"
+ text-color="var(--text-with-main-color)"
@click="handleSubscription"
/>
diff --git a/src/renderer/router/index.js b/src/renderer/router/index.js
index ba5ed0ee..50196b04 100644
--- a/src/renderer/router/index.js
+++ b/src/renderer/router/index.js
@@ -44,6 +44,7 @@ const router = new Router({
},
{
path: '/settings/profile/new',
+ name: 'newProfile',
meta: {
title: 'New Profile',
icon: 'fa-home'
@@ -52,6 +53,7 @@ const router = new Router({
},
{
path: '/settings/profile/edit/:id',
+ name: 'editProfile',
meta: {
title: 'Edit Profile',
icon: 'fa-home'
diff --git a/src/renderer/store/modules/profile.js b/src/renderer/store/modules/profile.js
index dc4bfd7a..562b27b2 100644
--- a/src/renderer/store/modules/profile.js
+++ b/src/renderer/store/modules/profile.js
@@ -31,7 +31,7 @@ const state = {
const getters = {
getProfileList: () => {
- return state.historyCache
+ return state.profileList
}
}
@@ -39,6 +39,7 @@ const actions = {
grabAllProfiles ({ dispatch, commit }, defaultName = null) {
profileDb.find({}, (err, results) => {
if (!err) {
+ console.log(results)
if (results.length === 0) {
dispatch('createDefaultProfile', defaultName)
} else {
@@ -48,6 +49,17 @@ const actions = {
})
},
+ grabProfileInfo (_, profileId) {
+ return new Promise((resolve, reject) => {
+ console.log(profileId)
+ profileDb.findOne({ _id: profileId }, (err, results) => {
+ if (!err) {
+ resolve(results)
+ }
+ })
+ })
+ },
+
async createDefaultProfile ({ dispatch }, defaultName) {
const randomColor = await dispatch('getRandomColor')
const textColor = await dispatch('calculateColorLuminance', randomColor)
@@ -59,7 +71,6 @@ const actions = {
subscriptions: []
}
console.log(defaultProfile)
- return
profileDb.update({ _id: 'allChannels' }, defaultProfile, { upsert: true }, (err, numReplaced) => {
if (!err) {
dispatch('grabAllProfiles')
@@ -68,23 +79,23 @@ const actions = {
},
updateProfile ({ dispatch }, profile) {
- profileDb.update({ name: profile.name }, profile, { upsert: true }, (err, numReplaced) => {
+ profileDb.update({ _id: profile._id }, profile, { upsert: true }, (err, numReplaced) => {
if (!err) {
dispatch('grabAllProfiles')
}
})
},
- removeFromHistory ({ dispatch }, videoId) {
- historyDb.remove({ videoId: videoId }, (err, numReplaced) => {
+ insertProfile ({ dispatch }, profile) {
+ profileDb.insert(profile, (err, newDocs) => {
if (!err) {
- dispatch('grabHistory')
+ dispatch('grabAllProfiles')
}
})
},
- updateWatchProgress ({ dispatch }, videoData) {
- historyDb.update({ videoId: videoData.videoId }, { $set: { watchProgress: videoData.watchProgress } }, { upsert: true }, (err, numReplaced) => {
+ removeProfile ({ dispatch }, videoId) {
+ profileDb.remove({ videoId: videoId }, (err, numReplaced) => {
if (!err) {
dispatch('grabHistory')
}
@@ -93,8 +104,8 @@ const actions = {
}
const mutations = {
- setHistoryCache (state, historyCache) {
- state.historyCache = historyCache
+ setProfileList (state, profileList) {
+ state.profileList = profileList
}
}
diff --git a/src/renderer/store/modules/settings.js b/src/renderer/store/modules/settings.js
index a2670de3..ab3acbc4 100644
--- a/src/renderer/store/modules/settings.js
+++ b/src/renderer/store/modules/settings.js
@@ -36,6 +36,7 @@ const state = {
listType: 'grid',
thumbnailPreference: '',
invidiousInstance: 'https://invidio.us',
+ defaultProfile: 'allChannels',
barColor: false,
enableSearchSuggestions: true,
rememberHistory: true,
@@ -56,9 +57,7 @@ const state = {
debugMode: false,
disctractionFreeMode: false,
hideWatchedSubs: false,
- usingElectron: true,
- profileList: [{ name: 'All Channels', color: '#304FFE' }],
- defaultProfile: 'All Channels'
+ usingElectron: true
}
const getters = {
@@ -102,6 +101,10 @@ const getters = {
return state.invidiousInstance
},
+ getDefaultProfile: () => {
+ return state.defaultProfile
+ },
+
getRememberHistory: () => {
return state.rememberHistory
},
@@ -176,6 +179,9 @@ const actions = {
case 'backendFallback':
commit('setBackendFallback', result.value)
break
+ case 'defaultProfile':
+ commit('setDefaultProfile', result.value)
+ break
case 'checkForUpdates':
commit('setCheckForUpdates', result.value)
break
@@ -255,6 +261,14 @@ const actions = {
})
},
+ updateDefaultProfile ({ commit }, defaultProfile) {
+ settingsDb.update({ _id: 'defaultProfile' }, { _id: 'defaultProfile', value: defaultProfile }, { upsert: true }, (err, numReplaced) => {
+ if (!err) {
+ commit('setDefaultProfile', defaultProfile)
+ }
+ })
+ },
+
updateBackendFallback ({ commit }, backendFallback) {
settingsDb.update({ _id: 'backendFallback' }, { _id: 'backendFallback', value: backendFallback }, { upsert: true }, (err, numReplaced) => {
if (!err) {
@@ -448,6 +462,9 @@ const mutations = {
setCurrentTheme (state, currentTheme) {
state.barColor = currentTheme
},
+ setDefaultProfile (state, defaultProfile) {
+ state.defaultProfile = defaultProfile
+ },
setBackendFallback (state, backendFallback) {
state.backendFallback = backendFallback
},
@@ -537,9 +554,6 @@ const mutations = {
},
setProfileList (state, profileList) {
state.profileList = profileList
- },
- setDefaultProfile (state, defaultProfile) {
- state.defaultProfile = defaultProfile
}
}
diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js
index 2e888be0..8729d665 100644
--- a/src/renderer/store/modules/utils.js
+++ b/src/renderer/store/modules/utils.js
@@ -72,6 +72,10 @@ const getters = {
getSearchSettings () {
return state.searchSettings
+ },
+
+ getColorValues () {
+ return state.colorValues
}
}
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.css b/src/renderer/views/ProfileEdit/ProfileEdit.css
index b6db9095..e2935899 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.css
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.css
@@ -8,6 +8,30 @@
color: var(--tertiary-text-color);
}
+.profileName {
+ width: 400px;
+}
+
+.bottomMargin {
+ margin-bottom: 30px;
+}
+
+.colorOption {
+ width: 100px;
+ height: 100px;
+ margin: 10px;
+ cursor: pointer;
+ border-radius: 200px 200px 200px 200px;
+ -webkit-border-radius: 200px 200px 200px 200px;
+}
+
+.initial {
+ font-size: 50px;
+ text-align: center;
+ position: relative;
+ bottom: 27px;
+}
+
@media only screen and (max-width: 680px) {
.card {
width: 90%;
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.js b/src/renderer/views/ProfileEdit/ProfileEdit.js
index e2f2f7c4..a142dfb2 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.js
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.js
@@ -1,15 +1,92 @@
import Vue from 'vue'
+import { mapActions } from 'vuex'
+import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
-import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
+import FtInput from '../../components/ft-input/ft-input.vue'
+import FtButton from '../../components/ft-button/ft-button.vue'
export default Vue.extend({
name: 'ProfileEdit',
components: {
+ 'ft-loader': FtLoader,
'ft-card': FtCard,
'ft-flex-box': FtFlexBox,
- 'ft-element-list': FtElementList
+ 'ft-input': FtInput,
+ 'ft-button': FtButton
+ },
+ data: function () {
+ return {
+ isLoading: false,
+ isNew: false,
+ profileId: '',
+ profileName: '',
+ profileBgColor: '',
+ profileTextColor: '',
+ profileSubscriptions: []
+ }
+ },
+ computed: {
+ colorValues: function () {
+ return this.$store.getters.getColorValues
+ },
+ profileInitial: function () {
+ return this.profileName.slice(0, 1).toUpperCase()
+ }
+ },
+ watch: {
+ profileBgColor: async function (val) {
+ this.profileTextColor = await this.calculateColorLuminance(val)
+ }
},
mounted: function () {
+ this.isLoading = true
+ const profileType = this.$route.name
+
+ if (profileType === 'newProfile') {
+ this.isNew = true
+ } else {
+ this.isNew = false
+ this.profileId = this.$route.params.id
+ console.log(this.profileId)
+ console.log(this.$route.name)
+
+ this.grabProfileInfo(this.profileId).then((profile) => {
+ console.log(profile)
+ this.profileName = profile.name
+ this.profileBgColor = profile.bgColor
+ this.profileTextColor = profile.textColor
+ this.profileSubscriptions = profile.subscriptions
+ this.isLoading = false
+ })
+ }
+ },
+ methods: {
+ saveProfile: function () {
+ const profile = {
+ name: this.profileName,
+ bgColor: this.profileBgColor,
+ textColor: this.profileTextColor,
+ subscriptions: this.profileSubscriptions
+ }
+
+ if (!this.isNew) {
+ profile._id = this.profileId
+ }
+
+ console.log(profile)
+
+ this.updateProfile(profile)
+ this.showToast({
+ message: 'Profile has been updated'
+ })
+ },
+
+ ...mapActions([
+ 'showToast',
+ 'grabProfileInfo',
+ 'updateProfile',
+ 'calculateColorLuminance'
+ ])
}
})
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.vue b/src/renderer/views/ProfileEdit/ProfileEdit.vue
index 40ee95fc..f001b403 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.vue
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.vue
@@ -1,13 +1,90 @@
-
- {{ $t("Subscriptions.Subscriptions") }}
-
-
- {{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }}
-
-
-
+
+
+
+ {{ $t("Profile.Edit Profile") }}
+
+ profileName = e"
+ />
+
+ {{ $t("Profile.Color Picker") }}
+
+
+
+
+
+ {{ $t("Profile.Custom Color") }}
+
+
+
+
+
+
+ {{ $t("Profile.Profile Preview") }}
+
+
+
+ {{ profileInitial }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.js b/src/renderer/views/ProfileSettings/ProfileSettings.js
index 8ba65cbd..3dd645a0 100644
--- a/src/renderer/views/ProfileSettings/ProfileSettings.js
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.js
@@ -1,15 +1,21 @@
import Vue from 'vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
-import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
+import FtProfileBubble from '../../components/ft-profile-bubble/ft-profile-bubble.vue'
export default Vue.extend({
name: 'ProfileSettings',
components: {
'ft-card': FtCard,
'ft-flex-box': FtFlexBox,
- 'ft-element-list': FtElementList
+ 'ft-profile-bubble': FtProfileBubble
+ },
+ computed: {
+ profileList: function () {
+ return this.$store.getters.getProfileList
+ }
},
mounted: function () {
+ console.log(this.profileList)
}
})
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.vue b/src/renderer/views/ProfileSettings/ProfileSettings.vue
index 0bbb9867..5a85991f 100644
--- a/src/renderer/views/ProfileSettings/ProfileSettings.vue
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.vue
@@ -3,9 +3,14 @@
{{ $t("Profile.Profile Manager") }}
-
- {{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }}
-
+
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index 339c3e84..3b53813f 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -249,6 +249,17 @@ About:
Profile:
All Channels: All Channels
Profile Manager: Profile Manager
+ Create New Profile: Create New Profile
+ Edit Profile: Edit Profile
+ Color Picker: Color Picker
+ Custom Color: Custom Color
+ Profile Preview: Profile Preview
+ Create Profile: Create Profile
+ Update Profile: Update Profile
+ Make Default Profile: Make Default Profile
+ Delete Profile: Delete Profile
+ Are you sure you want to delete this profile?: Are you sure you want to delete this profile?
+ All subscriptions will also be deleted.: All subscriptions will also be deleted.
#On Channel Page
Channel:
Subscriber: Subscriber
From 0680e6c5b6223a8fc4aaacd06e088a047f6d966c Mon Sep 17 00:00:00 2001
From: Preston
Date: Tue, 25 Aug 2020 10:14:58 -0400
Subject: [PATCH 4/6] Fix issues with playlist and leaving watch page from
invalid id
---
src/renderer/views/Playlist/Playlist.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/renderer/views/Playlist/Playlist.js b/src/renderer/views/Playlist/Playlist.js
index 54c9983d..bccec28b 100644
--- a/src/renderer/views/Playlist/Playlist.js
+++ b/src/renderer/views/Playlist/Playlist.js
@@ -107,6 +107,9 @@ export default Vue.extend({
return video
})
+ return video
+ })
+
this.isLoading = false
}).catch((err) => {
console.log(err)
From 1e035105d1462fba19570ec197a1c0b204822edf Mon Sep 17 00:00:00 2001
From: Preston
Date: Mon, 31 Aug 2020 17:35:22 -0400
Subject: [PATCH 5/6] Finish Profile Logic and working subscriptions
---
src/renderer/App.js | 7 +-
src/renderer/App.vue | 3 +
.../ft-profile-selector.css | 65 ++++++++
.../ft-profile-selector.js | 79 ++++++++++
.../ft-profile-selector.vue | 57 +++++++
.../ft-progress-bar/ft-progress-bar.css | 9 ++
.../ft-progress-bar/ft-progress-bar.js | 10 ++
.../ft-progress-bar/ft-progress-bar.vue | 9 ++
src/renderer/components/side-nav/side-nav.css | 44 +++++-
src/renderer/components/side-nav/side-nav.js | 24 +++
src/renderer/components/side-nav/side-nav.vue | 28 +++-
src/renderer/components/top-nav/top-nav.js | 4 +-
src/renderer/components/top-nav/top-nav.sass | 3 +
src/renderer/components/top-nav/top-nav.vue | 2 +-
.../watch-video-info/watch-video-info.js | 100 +++++++++++-
src/renderer/store/modules/profile.js | 43 ++++-
src/renderer/store/modules/subscriptions.js | 71 +++------
src/renderer/store/modules/utils.js | 49 ++++++
src/renderer/views/Channel/Channel.js | 100 +++++++++++-
src/renderer/views/Channel/Channel.vue | 2 +-
src/renderer/views/Playlist/Playlist.js | 3 -
.../views/ProfileEdit/ProfileEdit.css | 6 +
src/renderer/views/ProfileEdit/ProfileEdit.js | 99 +++++++++++-
.../views/ProfileEdit/ProfileEdit.vue | 21 ++-
.../views/ProfileSettings/ProfileSettings.css | 6 +
.../views/ProfileSettings/ProfileSettings.js | 11 +-
.../views/ProfileSettings/ProfileSettings.vue | 10 +-
.../views/Subscriptions/Subscriptions.js | 149 ++++++++++++++++++
.../views/Subscriptions/Subscriptions.vue | 29 +++-
static/locales/en-US.yaml | 2 +
30 files changed, 957 insertions(+), 88 deletions(-)
create mode 100644 src/renderer/components/ft-profile-selector/ft-profile-selector.css
create mode 100644 src/renderer/components/ft-profile-selector/ft-profile-selector.js
create mode 100644 src/renderer/components/ft-profile-selector/ft-profile-selector.vue
create mode 100644 src/renderer/components/ft-progress-bar/ft-progress-bar.css
create mode 100644 src/renderer/components/ft-progress-bar/ft-progress-bar.js
create mode 100644 src/renderer/components/ft-progress-bar/ft-progress-bar.vue
diff --git a/src/renderer/App.js b/src/renderer/App.js
index 0e4e2922..efe9cc85 100644
--- a/src/renderer/App.js
+++ b/src/renderer/App.js
@@ -3,6 +3,7 @@ import { ObserveVisibility } from 'vue-observe-visibility'
import TopNav from './components/top-nav/top-nav.vue'
import SideNav from './components/side-nav/side-nav.vue'
import FtToast from './components/ft-toast/ft-toast.vue'
+import FtProgressBar from './components/ft-progress-bar/ft-progress-bar.vue'
import $ from 'jquery'
let useElectron
@@ -23,11 +24,15 @@ export default Vue.extend({
components: {
TopNav,
SideNav,
- FtToast
+ FtToast,
+ FtProgressBar
},
computed: {
isOpen: function () {
return this.$store.getters.getIsSideNavOpen
+ },
+ showProgressBar: function () {
+ return this.$store.getters.getShowProgressBar
}
},
mounted: function () {
diff --git a/src/renderer/App.vue b/src/renderer/App.vue
index 2bb16c04..67ab34f4 100644
--- a/src/renderer/App.vue
+++ b/src/renderer/App.vue
@@ -15,6 +15,9 @@
+
diff --git a/src/renderer/components/ft-profile-selector/ft-profile-selector.css b/src/renderer/components/ft-profile-selector/ft-profile-selector.css
new file mode 100644
index 00000000..f630dc0c
--- /dev/null
+++ b/src/renderer/components/ft-profile-selector/ft-profile-selector.css
@@ -0,0 +1,65 @@
+.colorOption {
+ width: 50px;
+ height: 50px;
+ margin: 10px;
+ cursor: pointer;
+ border-radius: 200px 200px 200px 200px;
+ -webkit-border-radius: 200px 200px 200px 200px;
+}
+
+.initial {
+ font-size: 25px;
+ text-align: center;
+ position: relative;
+ bottom: 27px;
+}
+
+#profileList {
+ display: none;
+ position: absolute;
+ top: 60px;
+ right: 10px;
+ min-width: 250px;
+ height: 300px;
+ padding: 5px;
+ background-color: var(--card-bg-color);
+ box-shadow: 0 1px 2px rgba(0,0,0,.1);
+}
+
+#profileList:focus {
+ display: inline;
+ outline: none;
+}
+
+.profileWrapper {
+ margin-top: 60px;
+ height: 240px;
+ overflow-y: auto;
+}
+
+.profile {
+ cursor: pointer;
+}
+
+.profile:hover {
+ background-color: var(--side-nav-hover-color);
+}
+
+.profile .colorOption {
+ float: left;
+ position: relative;
+ bottom: 5px;
+}
+
+.profileListTitle {
+ position: absolute;
+ top: -15px;
+ left: 10px;
+}
+
+.profileSettings {
+ float: right;
+ position: absolute;
+ top: 10px;
+ right: 5px;
+}
diff --git a/src/renderer/components/ft-profile-selector/ft-profile-selector.js b/src/renderer/components/ft-profile-selector/ft-profile-selector.js
new file mode 100644
index 00000000..f63d5aa0
--- /dev/null
+++ b/src/renderer/components/ft-profile-selector/ft-profile-selector.js
@@ -0,0 +1,79 @@
+import Vue from 'vue'
+import { mapActions } from 'vuex'
+import $ from 'jquery'
+
+import FtCard from '../../components/ft-card/ft-card.vue'
+import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
+
+export default Vue.extend({
+ name: 'FtProfileSelector',
+ components: {
+ 'ft-card': FtCard,
+ 'ft-icon-button': FtIconButton
+ },
+ data: function () {
+ return {
+ showProfileList: false
+ }
+ },
+ computed: {
+ profileList: function () {
+ return this.$store.getters.getProfileList
+ },
+ activeProfile: function () {
+ return this.$store.getters.getActiveProfile
+ },
+ defaultProfile: function () {
+ return this.$store.getters.getDefaultProfile
+ },
+ activeProfileInitial: function () {
+ return this.activeProfile.name.slice(0, 1).toUpperCase()
+ },
+ profileInitials: function () {
+ return this.profileList.map((profile) => {
+ return profile.name.slice(0, 1).toUpperCase()
+ })
+ }
+ },
+ mounted: function () {
+ $('#profileList').focusout(() => {
+ $('#profileList')[0].style.display = 'none'
+ })
+ },
+ methods: {
+ toggleProfileList: function () {
+ $('#profileList')[0].style.display = 'inline'
+ $('#profileList').focus()
+ },
+
+ openProfileSettings: function () {
+ this.$router.push({
+ path: '/settings/profile/'
+ })
+ $('#profileList').focusout()
+ },
+
+ setActiveProfile: function (profile) {
+ if (this.profileList[this.activeProfile]._id === profile._id) {
+ return
+ }
+ const index = this.profileList.findIndex((x) => {
+ return x._id === profile._id
+ })
+
+ if (index === -1) {
+ return
+ }
+ this.updateActiveProfile(index)
+ this.showToast({
+ message: `${profile.name} is now the active profile`
+ })
+ $('#profileList').focusout()
+ },
+
+ ...mapActions([
+ 'showToast',
+ 'updateActiveProfile'
+ ])
+ }
+})
diff --git a/src/renderer/components/ft-profile-selector/ft-profile-selector.vue b/src/renderer/components/ft-profile-selector/ft-profile-selector.vue
new file mode 100644
index 00000000..e8c864d3
--- /dev/null
+++ b/src/renderer/components/ft-profile-selector/ft-profile-selector.vue
@@ -0,0 +1,57 @@
+
+
+
+
+ {{ profileInitials[activeProfile] }}
+
+
+
+
+ Profile Select
+
+
+
+
+
+
+ {{ profileInitials[index] }}
+
+
+
+ {{ profile.name }}
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/components/ft-progress-bar/ft-progress-bar.css b/src/renderer/components/ft-progress-bar/ft-progress-bar.css
new file mode 100644
index 00000000..43816bd2
--- /dev/null
+++ b/src/renderer/components/ft-progress-bar/ft-progress-bar.css
@@ -0,0 +1,9 @@
+.progressBar {
+ position: fixed;
+ height: 3px;
+ bottom: 0px;
+ left: 0px;
+ background-color: var(--primary-color);
+ z-index: 1;
+ transition: width 0.5s;
+}
diff --git a/src/renderer/components/ft-progress-bar/ft-progress-bar.js b/src/renderer/components/ft-progress-bar/ft-progress-bar.js
new file mode 100644
index 00000000..c5e7fcbc
--- /dev/null
+++ b/src/renderer/components/ft-progress-bar/ft-progress-bar.js
@@ -0,0 +1,10 @@
+import Vue from 'vue'
+
+export default Vue.extend({
+ name: 'FtProgressBar',
+ computed: {
+ progressBarPercentage: function () {
+ return this.$store.getters.getProgressBarPercentage
+ }
+ }
+})
diff --git a/src/renderer/components/ft-progress-bar/ft-progress-bar.vue b/src/renderer/components/ft-progress-bar/ft-progress-bar.vue
new file mode 100644
index 00000000..dbbe9a3f
--- /dev/null
+++ b/src/renderer/components/ft-progress-bar/ft-progress-bar.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/src/renderer/components/side-nav/side-nav.css b/src/renderer/components/side-nav/side-nav.css
index c5e748ad..fda641e5 100644
--- a/src/renderer/components/side-nav/side-nav.css
+++ b/src/renderer/components/side-nav/side-nav.css
@@ -30,7 +30,7 @@
margin-top: 10px;
}
-.navOption {
+.navOption, .navChannel {
position: relative;
padding: 5px;
cursor: pointer;
@@ -40,14 +40,14 @@
display: none;
}
-.navOption:hover {
+.navOption:hover, .navChannel:hover {
background-color: var(--side-nav-hover-color);
-moz-transition: background 0.2s ease-in;
-o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
-.navOption:active {
+.navOption:active, .navChannel:active {
background-color: var(--side-nav-active-color);
-moz-transition: background 0.2s ease-in;
-o-transition: background 0.2s ease-in;
@@ -63,6 +63,26 @@
display: inline-block;
}
+.navChannel .navLabel {
+ font-size: 11px;
+ width: 150px;
+ margin-left: 40px;
+}
+
+.thumbnailContainer {
+ width: 35px;
+ margin: 0;
+ position: absolute;
+ top: 50%;
+ -ms-transform: translateY(-50%);
+ transform: translateY(-50%);
+}
+
+.channelThumbnail {
+ border-radius: 50%;
+ width: 35px;
+}
+
.closed {
width: 80px;
}
@@ -107,6 +127,24 @@
font-size: 11px;
}
+.closed .navChannel {
+ width: 100%;
+ padding: 0px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.closed .thumbnailContainer {
+ position: static;
+ display: block;
+ float: none;
+ margin-left: 0px;
+ margin: 0 auto;
+ top: 0px;
+ -ms-transform: none;
+ transform: none;
+}
+
@media only screen and (max-width: 680px) {
.inner {
display: contents; /* sunglasses emoji */
diff --git a/src/renderer/components/side-nav/side-nav.js b/src/renderer/components/side-nav/side-nav.js
index 8c969441..41ec34d0 100644
--- a/src/renderer/components/side-nav/side-nav.js
+++ b/src/renderer/components/side-nav/side-nav.js
@@ -12,11 +12,35 @@ export default Vue.extend({
computed: {
isOpen: function () {
return this.$store.getters.getIsSideNavOpen
+ },
+ profileList: function () {
+ return this.$store.getters.getProfileList
+ },
+ activeProfile: function () {
+ return this.$store.getters.getActiveProfile
+ },
+ activeSubscriptions: function () {
+ const profile = JSON.parse(JSON.stringify(this.profileList[this.activeProfile]))
+ return profile.subscriptions.sort((a, b) => {
+ const nameA = a.name.toLowerCase()
+ const nameB = b.name.toLowerCase()
+ if (nameA < nameB) {
+ return -1
+ }
+ if (nameA > nameB) {
+ return 1
+ }
+ return 0
+ })
}
},
methods: {
navigate: function (route) {
router.push('/' + route)
+ },
+
+ goToChannel: function (id) {
+ this.$router.push({ path: `/channel/${id}` })
}
}
})
diff --git a/src/renderer/components/side-nav/side-nav.vue b/src/renderer/components/side-nav/side-nav.vue
index f2e911a0..248e71b1 100644
--- a/src/renderer/components/side-nav/side-nav.vue
+++ b/src/renderer/components/side-nav/side-nav.vue
@@ -16,10 +16,6 @@
{{ $t("Subscriptions.Subscriptions") }}
-
+
+
+
+
+
+ {{ channel.name }}
+
+
diff --git a/src/renderer/components/top-nav/top-nav.js b/src/renderer/components/top-nav/top-nav.js
index e95cd834..3eb03949 100644
--- a/src/renderer/components/top-nav/top-nav.js
+++ b/src/renderer/components/top-nav/top-nav.js
@@ -1,6 +1,7 @@
import Vue from 'vue'
import FtInput from '../ft-input/ft-input.vue'
import FtSearchFilters from '../ft-search-filters/ft-search-filters.vue'
+import FtProfileSelector from '../ft-profile-selector/ft-profile-selector.vue'
import $ from 'jquery'
import router from '../../router/index.js'
import debounce from 'lodash.debounce'
@@ -10,7 +11,8 @@ export default Vue.extend({
name: 'TopNav',
components: {
FtInput,
- FtSearchFilters
+ FtSearchFilters,
+ FtProfileSelector
},
data: () => {
return {
diff --git a/src/renderer/components/top-nav/top-nav.sass b/src/renderer/components/top-nav/top-nav.sass
index 4040bcb3..3197cbce 100644
--- a/src/renderer/components/top-nav/top-nav.sass
+++ b/src/renderer/components/top-nav/top-nav.sass
@@ -59,6 +59,9 @@
display: flex
align-items: center
+ &.profiles
+ justify-content: flex-end
+
.navSearchIcon
@media only screen and (min-width: 681px)
display: none
diff --git a/src/renderer/components/top-nav/top-nav.vue b/src/renderer/components/top-nav/top-nav.vue
index e04cf8c4..9a4ed144 100644
--- a/src/renderer/components/top-nav/top-nav.vue
+++ b/src/renderer/components/top-nav/top-nav.vue
@@ -55,7 +55,7 @@
:class="{ expand: !isSideNavOpen }"
/>
-
+
diff --git a/src/renderer/components/watch-video-info/watch-video-info.js b/src/renderer/components/watch-video-info/watch-video-info.js
index 2ccb2e43..2a9d1d4a 100644
--- a/src/renderer/components/watch-video-info/watch-video-info.js
+++ b/src/renderer/components/watch-video-info/watch-video-info.js
@@ -79,6 +79,14 @@ export default Vue.extend({
return this.$store.getters.getUsingElectron
},
+ profileList: function () {
+ return this.$store.getters.getProfileList
+ },
+
+ activeProfile: function () {
+ return this.$store.getters.getActiveProfile
+ },
+
formatTypeNames: function () {
return [
this.$t('Change Format.Use Dash Formats').toUpperCase(),
@@ -99,8 +107,24 @@ export default Vue.extend({
return this.viewCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ` ${this.$t('Video.Views').toLowerCase()}`
},
+ isSubscribed: function () {
+ const subIndex = this.profileList[this.activeProfile].subscriptions.findIndex((channel) => {
+ return channel.id === this.channelId
+ })
+
+ if (subIndex === -1) {
+ return false
+ } else {
+ return true
+ }
+ },
+
subscribedText: function () {
- return `${this.$t('Channel.Subscribe').toUpperCase()} ${this.subscriptionCountText}`
+ if (this.isSubscribed) {
+ return `${this.$t('Channel.Unsubscribe').toUpperCase()} ${this.subscriptionCountText}`
+ } else {
+ return `${this.$t('Channel.Subscribe').toUpperCase()} ${this.subscriptionCountText}`
+ }
},
dateString() {
@@ -116,9 +140,74 @@ export default Vue.extend({
},
handleSubscription: function () {
- this.showToast({
- message: 'Subscriptions have not yet been implemented'
- })
+ const currentProfile = JSON.parse(JSON.stringify(this.profileList[this.activeProfile]))
+ const primaryProfile = JSON.parse(JSON.stringify(this.profileList[0]))
+
+ if (this.isSubscribed) {
+ currentProfile.subscriptions = currentProfile.subscriptions.filter((channel) => {
+ return channel.id !== this.channelId
+ })
+
+ this.updateProfile(currentProfile)
+ this.showToast({
+ message: 'Channel has been removed from your subscriptions'
+ })
+
+ if (this.activeProfile === 0) {
+ // Check if a subscription exists in a different profile.
+ // Remove from there as well.
+ let duplicateSubscriptions = 0
+
+ this.profileList.forEach((profile) => {
+ if (profile._id === 'allChannels') {
+ return
+ }
+ const parsedProfile = JSON.parse(JSON.stringify(profile))
+ const index = parsedProfile.subscriptions.findIndex((channel) => {
+ return channel.id === this.channelId
+ })
+
+ if (index !== -1) {
+ duplicateSubscriptions++
+
+ parsedProfile.subscriptions = parsedProfile.subscriptions.filter((x) => {
+ return x.id !== this.channelId
+ })
+
+ this.updateProfile(parsedProfile)
+ }
+ })
+
+ if (duplicateSubscriptions > 0) {
+ this.showToast({
+ message: `Removed subscription from ${duplicateSubscriptions} other channel(s)`
+ })
+ }
+ }
+ } else {
+ const subscription = {
+ id: this.channelId,
+ name: this.channelName,
+ thumbnail: this.channelThumbnail
+ }
+ currentProfile.subscriptions.push(subscription)
+
+ this.updateProfile(currentProfile)
+ this.showToast({
+ message: 'Added channel to your subscriptions'
+ })
+
+ if (this.activeProfile !== 0) {
+ const index = primaryProfile.subscriptions.findIndex((channel) => {
+ return channel.id === this.channelId
+ })
+
+ if (index === -1) {
+ primaryProfile.subscriptions.push(subscription)
+ this.updateProfile(primaryProfile)
+ }
+ }
+ }
},
handleFormatChange: function (format) {
@@ -136,7 +225,8 @@ export default Vue.extend({
},
...mapActions([
- 'showToast'
+ 'showToast',
+ 'updateProfile'
])
}
})
diff --git a/src/renderer/store/modules/profile.js b/src/renderer/store/modules/profile.js
index 562b27b2..984ab5ec 100644
--- a/src/renderer/store/modules/profile.js
+++ b/src/renderer/store/modules/profile.js
@@ -25,13 +25,23 @@ const profileDb = new Datastore({
})
const state = {
- profileList: [],
- activeProfile: 'allChannels'
+ profileList: [{
+ _id: 'allChannels',
+ name: 'All Channels',
+ bgColor: '#000000',
+ textColor: '#FFFFFF',
+ subscriptions: []
+ }],
+ activeProfile: 0
}
const getters = {
getProfileList: () => {
return state.profileList
+ },
+
+ getActiveProfile: () => {
+ return state.activeProfile
}
}
@@ -39,11 +49,23 @@ const actions = {
grabAllProfiles ({ dispatch, commit }, defaultName = null) {
profileDb.find({}, (err, results) => {
if (!err) {
- console.log(results)
if (results.length === 0) {
dispatch('createDefaultProfile', defaultName)
} else {
- commit('setProfileList', results)
+ // We want the primary profile to always be first
+ // So sort with that then sort alphabetically by profile name
+ const profiles = results.sort((a, b) => {
+ if (a._id === 'allChannels') {
+ return -1
+ }
+
+ if (b._id === 'allChannels') {
+ return 1
+ }
+
+ return b.name - a.name
+ })
+ commit('setProfileList', profiles)
}
}
})
@@ -94,18 +116,25 @@ const actions = {
})
},
- removeProfile ({ dispatch }, videoId) {
- profileDb.remove({ videoId: videoId }, (err, numReplaced) => {
+ removeProfile ({ dispatch }, profileId) {
+ profileDb.remove({ _id: profileId }, (err, numReplaced) => {
if (!err) {
- dispatch('grabHistory')
+ dispatch('grabAllProfiles')
}
})
+ },
+
+ updateActiveProfile ({ commit }, index) {
+ commit('setActiveProfile', index)
}
}
const mutations = {
setProfileList (state, profileList) {
state.profileList = profileList
+ },
+ setActiveProfile (state, activeProfile) {
+ state.activeProfile = activeProfile
}
}
diff --git a/src/renderer/store/modules/subscriptions.js b/src/renderer/store/modules/subscriptions.js
index 2d48dbfc..84e36201 100644
--- a/src/renderer/store/modules/subscriptions.js
+++ b/src/renderer/store/modules/subscriptions.js
@@ -1,61 +1,40 @@
-import Datastore from 'nedb'
-
-let dbLocation
-
-if (window && window.process && window.process.type === 'renderer') {
- // Electron is being used
- let dbLocation = localStorage.getItem('dbLocation')
-
- if (dbLocation === null) {
- const electron = require('electron')
- dbLocation = electron.remote.app.getPath('userData')
- }
-
- dbLocation += '/subscriptions.db'
-} else {
- dbLocation = 'subscriptions.db'
-}
-
-const subDb = new Datastore({
- filename: dbLocation,
- autoload: true
-})
+import ytch from 'yt-channel-info'
const state = {
- subscriptions: []
+ subscriptions: [],
+ profileSubscriptions: {
+ activeProfile: 0,
+ videoList: []
+ }
}
-const mutations = {
- addSubscription (state, payload) {
- state.subscriptions.push(payload)
+const getters = {
+ getSubscriptions: () => {
+ return state.subscriptions
},
- setSubscriptions (state, payload) {
- state.subscriptions = payload
+ getProfileSubscriptions: () => {
+ return state.profileSubscriptions
}
}
const actions = {
- addSubscriptions ({ commit }, payload) {
- subDb.insert(payload, (err, payload) => {
- if (!err) {
- commit('addSubscription', payload)
- }
- })
+ updateSubscriptions ({ commit }, subscriptions) {
+ commit('setSubscriptions', subscriptions)
},
- getSubscriptions ({ commit }, payload) {
- subDb.find({}, (err, payload) => {
- if (!err) {
- commit('setSubscriptions', payload)
- }
- })
- },
- removeSubscription ({ commit }, channelId) {
- subDb.remove({ channelId: channelId }, {}, () => {
- commit('setSubscriptions', this.state.subscriptions.filter(sub => sub.channelId !== channelId))
- })
+ updateProfileSubscriptions ({ commit }, subscriptions) {
+ commit('setProfileSubscriptions', subscriptions)
}
}
-const getters = {}
+
+const mutations = {
+ setSubscriptions (state, subscriptions) {
+ state.subscriptions = subscriptions
+ },
+ setProfileSubscriptions (state, profileSubscriptions) {
+ state.profileSubscriptions = profileSubscriptions
+ }
+}
+
export default {
state,
getters,
diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js
index 8729d665..2ecd99d4 100644
--- a/src/renderer/store/modules/utils.js
+++ b/src/renderer/store/modules/utils.js
@@ -5,6 +5,8 @@ const state = {
sessionSearchHistory: [],
popularCache: null,
trendingCache: null,
+ showProgressBar: false,
+ progressBarPercentage: 0,
searchSettings: {
sortBy: 'relevance',
time: '',
@@ -76,10 +78,22 @@ const getters = {
getColorValues () {
return state.colorValues
+ },
+
+ getShowProgressBar () {
+ return state.showProgressBar
+ },
+
+ getProgressBarPercentage () {
+ return state.progressBarPercentage
}
}
const actions = {
+ updateShowProgressBar ({ commit }, value) {
+ commit('setShowProgressBar', value)
+ },
+
getRandomColorClass () {
const randomInt = Math.floor(Math.random() * state.colorClasses.length)
return state.colorClasses[randomInt]
@@ -105,6 +119,33 @@ const actions = {
}
},
+ calculatePublishedDate(_, publishedText) {
+ const date = new Date()
+
+ const textSplit = publishedText.split(' ')
+ const timeFrame = textSplit[1]
+ const timeAmount = parseInt(textSplit[0])
+ let timeSpan = null
+
+ if (timeFrame.indexOf('second') > -1) {
+ timeSpan = timeAmount * 1000
+ } else if (timeFrame.indexOf('minute') > -1) {
+ timeSpan = timeAmount * 60000
+ } else if (timeFrame.indexOf('hour') > -1) {
+ timeSpan = timeAmount * 3600000
+ } else if (timeFrame.indexOf('day') > -1) {
+ timeSpan = timeAmount * 86400000
+ } else if (timeFrame.indexOf('week') > -1) {
+ timeSpan = timeAmount * 604800000
+ } else if (timeFrame.indexOf('month') > -1) {
+ timeSpan = timeAmount * 2592000000
+ } else if (timeFrame.indexOf('year') > -1) {
+ timeSpan = timeAmount * 31556952000
+ }
+
+ return date.getTime() - timeSpan
+ },
+
getVideoIdFromUrl (_, url) {
/** @type {URL} */
let urlObject
@@ -296,6 +337,14 @@ const mutations = {
state.isSideNavOpen = !state.isSideNavOpen
},
+ setShowProgressBar (state, value) {
+ state.showProgressBar = value
+ },
+
+ setProgressBarPercentage (state, value) {
+ state.progressBarPercentage = value
+ },
+
setSessionSearchHistory (state, history) {
state.sessionSearchHistory = history
},
diff --git a/src/renderer/views/Channel/Channel.js b/src/renderer/views/Channel/Channel.js
index b44326ae..e0754ecd 100644
--- a/src/renderer/views/Channel/Channel.js
+++ b/src/renderer/views/Channel/Channel.js
@@ -78,6 +78,34 @@ export default Vue.extend({
return this.$store.getters.getSessionSearchHistory
},
+ profileList: function () {
+ return this.$store.getters.getProfileList
+ },
+
+ activeProfile: function () {
+ return this.$store.getters.getActiveProfile
+ },
+
+ isSubscribed: function () {
+ const subIndex = this.profileList[this.activeProfile].subscriptions.findIndex((channel) => {
+ return channel.id === this.id
+ })
+
+ if (subIndex === -1) {
+ return false
+ } else {
+ return true
+ }
+ },
+
+ subscribedText: function () {
+ if (this.isSubscribed) {
+ return this.$t('Channel.Unsubscribe').toUpperCase()
+ } else {
+ return this.$t('Channel.Subscribe').toUpperCase()
+ }
+ },
+
videoSelectNames: function () {
return [
this.$t('Channel.Videos.Sort Types.Newest'),
@@ -408,7 +436,74 @@ export default Vue.extend({
},
handleSubscription: function () {
- console.log('TODO: Channel handleSubscription')
+ const currentProfile = JSON.parse(JSON.stringify(this.profileList[this.activeProfile]))
+ const primaryProfile = JSON.parse(JSON.stringify(this.profileList[0]))
+
+ if (this.isSubscribed) {
+ currentProfile.subscriptions = currentProfile.subscriptions.filter((channel) => {
+ return channel.id !== this.id
+ })
+
+ this.updateProfile(currentProfile)
+ this.showToast({
+ message: 'Channel has been removed from your subscriptions'
+ })
+
+ if (this.activeProfile === 0) {
+ // Check if a subscription exists in a different profile.
+ // Remove from there as well.
+ let duplicateSubscriptions = 0
+
+ this.profileList.forEach((profile) => {
+ if (profile._id === 'allChannels') {
+ return
+ }
+ const parsedProfile = JSON.parse(JSON.stringify(profile))
+ const index = parsedProfile.subscriptions.findIndex((channel) => {
+ return channel.id === this.id
+ })
+
+ if (index !== -1) {
+ duplicateSubscriptions++
+
+ parsedProfile.subscriptions = parsedProfile.subscriptions.filter((x) => {
+ return x.id !== this.channelId
+ })
+
+ this.updateProfile(parsedProfile)
+ }
+ })
+
+ if (duplicateSubscriptions > 0) {
+ this.showToast({
+ message: `Removed subscription from ${duplicateSubscriptions} other channel(s)`
+ })
+ }
+ }
+ } else {
+ const subscription = {
+ id: this.id,
+ name: this.channelName,
+ thumbnail: this.thumbnailUrl
+ }
+ currentProfile.subscriptions.push(subscription)
+
+ this.updateProfile(currentProfile)
+ this.showToast({
+ message: 'Added channel to your subscriptions'
+ })
+
+ if (this.activeProfile !== 0) {
+ const index = primaryProfile.subscriptions.findIndex((channel) => {
+ return channel.id === this.id
+ })
+
+ if (index === -1) {
+ primaryProfile.subscriptions.push(subscription)
+ this.updateProfile(primaryProfile)
+ }
+ }
+ }
},
handleFetchMore: function () {
@@ -549,7 +644,8 @@ export default Vue.extend({
},
...mapActions([
- 'showToast'
+ 'showToast',
+ 'updateProfile'
])
}
})
diff --git a/src/renderer/views/Channel/Channel.vue b/src/renderer/views/Channel/Channel.vue
index 5781b469..b59f0249 100644
--- a/src/renderer/views/Channel/Channel.vue
+++ b/src/renderer/views/Channel/Channel.vue
@@ -40,7 +40,7 @@
{
console.log(err)
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.css b/src/renderer/views/ProfileEdit/ProfileEdit.css
index e2935899..07f8f622 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.css
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.css
@@ -16,6 +16,12 @@
margin-bottom: 30px;
}
+.colorOptions {
+ max-width: 1000px;
+ margin: 0 auto;
+ margin-bottom: 30px;
+}
+
.colorOption {
width: 100px;
height: 100px;
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.js b/src/renderer/views/ProfileEdit/ProfileEdit.js
index a142dfb2..28fdbf05 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.js
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.js
@@ -2,6 +2,7 @@ import Vue from 'vue'
import { mapActions } from 'vuex'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtCard from '../../components/ft-card/ft-card.vue'
+import FtPrompt from '../../components/ft-prompt/ft-prompt.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtInput from '../../components/ft-input/ft-input.vue'
import FtButton from '../../components/ft-button/ft-button.vue'
@@ -11,6 +12,7 @@ export default Vue.extend({
components: {
'ft-loader': FtLoader,
'ft-card': FtCard,
+ 'ft-prompt': FtPrompt,
'ft-flex-box': FtFlexBox,
'ft-input': FtInput,
'ft-button': FtButton
@@ -18,12 +20,18 @@ export default Vue.extend({
data: function () {
return {
isLoading: false,
+ showDeletePrompt: false,
+ deletePromptLabel: '',
isNew: false,
profileId: '',
profileName: '',
profileBgColor: '',
profileTextColor: '',
- profileSubscriptions: []
+ profileSubscriptions: [],
+ deletePromptValues: [
+ 'yes',
+ 'no'
+ ]
}
},
computed: {
@@ -32,6 +40,18 @@ export default Vue.extend({
},
profileInitial: function () {
return this.profileName.slice(0, 1).toUpperCase()
+ },
+ activeProfile: function () {
+ return this.$store.getters.getActiveProfile
+ },
+ defaultProfile: function () {
+ return this.$store.getters.getDefaultProfile
+ },
+ deletePromptNames: function () {
+ return [
+ this.$t('Yes'),
+ this.$t('No')
+ ]
}
},
watch: {
@@ -39,12 +59,16 @@ export default Vue.extend({
this.profileTextColor = await this.calculateColorLuminance(val)
}
},
- mounted: function () {
+ mounted: async function () {
this.isLoading = true
const profileType = this.$route.name
+ this.deletePromptLabel = 'Are you sure you want to delete this profile? All subscriptions in this profile will also be deleted.'
+
if (profileType === 'newProfile') {
this.isNew = true
+ this.profileBgColor = await this.getRandomColor()
+ this.isLoading = false
} else {
this.isNew = false
this.profileId = this.$route.params.id
@@ -52,7 +76,14 @@ export default Vue.extend({
console.log(this.$route.name)
this.grabProfileInfo(this.profileId).then((profile) => {
- console.log(profile)
+ if (profile === null) {
+ this.showToast({
+ message: 'Profile could not be found'
+ })
+ this.$router.push({
+ path: '/settings/profile/'
+ })
+ }
this.profileName = profile.name
this.profileBgColor = profile.bgColor
this.profileTextColor = profile.textColor
@@ -62,7 +93,25 @@ export default Vue.extend({
}
},
methods: {
+ openDeletePrompt: function () {
+ this.showDeletePrompt = true
+ },
+
+ handleDeletePrompt: function (response) {
+ if (response === 'yes') {
+ this.deleteProfile()
+ } else {
+ this.showDeletePrompt = false
+ }
+ },
+
saveProfile: function () {
+ if (this.profileName === '') {
+ this.showToast({
+ message: 'Your profile name cannot be empty'
+ })
+ return
+ }
const profile = {
name: this.profileName,
bgColor: this.profileBgColor,
@@ -77,8 +126,44 @@ export default Vue.extend({
console.log(profile)
this.updateProfile(profile)
+
+ if (this.isNew) {
+ this.showToast({
+ message: 'Profile has been created'
+ })
+ this.$router.push({
+ path: '/settings/profile/'
+ })
+ } else {
+ this.showToast({
+ message: 'Profile has been updated'
+ })
+ }
+ },
+
+ setDefaultProfile: function () {
+ this.updateDefaultProfile(this.profileId)
this.showToast({
- message: 'Profile has been updated'
+ message: `Your default profile has been set to ${this.profileName}`
+ })
+ },
+
+ deleteProfile: function () {
+ this.removeProfile(this.profileId)
+ this.showToast({
+ message: `Removed ${this.profileName} from your profiles`
+ })
+ if (this.defaultProfile === this.profileId) {
+ this.updateDefaultProfile('allChannels')
+ this.showToast({
+ message: 'Your default profile has been set your Primary profile'
+ })
+ }
+ if (this.activeProfile._id === this.profileId) {
+ this.updateActiveProfile('allChannels')
+ }
+ this.$router.push({
+ path: '/settings/profile/'
})
},
@@ -86,7 +171,11 @@ export default Vue.extend({
'showToast',
'grabProfileInfo',
'updateProfile',
- 'calculateColorLuminance'
+ 'removeProfile',
+ 'updateDefaultProfile',
+ 'updateActiveProfile',
+ 'calculateColorLuminance',
+ 'getRandomColor'
])
}
})
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.vue b/src/renderer/views/ProfileEdit/ProfileEdit.vue
index f001b403..a51f6648 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.vue
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.vue
@@ -20,7 +20,7 @@
{{ $t("Profile.Color Picker") }}
+
+
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.css b/src/renderer/views/ProfileSettings/ProfileSettings.css
index b6db9095..670ae2a9 100644
--- a/src/renderer/views/ProfileSettings/ProfileSettings.css
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.css
@@ -8,6 +8,12 @@
color: var(--tertiary-text-color);
}
+.profileList {
+ max-width: 1000px;
+ margin: 0 auto;
+ margin-bottom: 10px;
+}
+
@media only screen and (max-width: 680px) {
.card {
width: 90%;
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.js b/src/renderer/views/ProfileSettings/ProfileSettings.js
index 3dd645a0..ba90e5e5 100644
--- a/src/renderer/views/ProfileSettings/ProfileSettings.js
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.js
@@ -2,13 +2,15 @@ import Vue from 'vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtProfileBubble from '../../components/ft-profile-bubble/ft-profile-bubble.vue'
+import FtButton from '../../components/ft-button/ft-button.vue'
export default Vue.extend({
name: 'ProfileSettings',
components: {
'ft-card': FtCard,
'ft-flex-box': FtFlexBox,
- 'ft-profile-bubble': FtProfileBubble
+ 'ft-profile-bubble': FtProfileBubble,
+ 'ft-button': FtButton
},
computed: {
profileList: function () {
@@ -17,5 +19,12 @@ export default Vue.extend({
},
mounted: function () {
console.log(this.profileList)
+ },
+ methods: {
+ newProfile: function () {
+ this.$router.push({
+ path: `/settings/profile/new/`
+ })
+ }
}
})
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.vue b/src/renderer/views/ProfileSettings/ProfileSettings.vue
index 5a85991f..b03cd6c4 100644
--- a/src/renderer/views/ProfileSettings/ProfileSettings.vue
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.vue
@@ -2,7 +2,9 @@
{{ $t("Profile.Profile Manager") }}
-
+
+
+
+
diff --git a/src/renderer/views/Subscriptions/Subscriptions.js b/src/renderer/views/Subscriptions/Subscriptions.js
index a76e0a78..278a1d10 100644
--- a/src/renderer/views/Subscriptions/Subscriptions.js
+++ b/src/renderer/views/Subscriptions/Subscriptions.js
@@ -1,15 +1,164 @@
import Vue from 'vue'
+import { mapActions, mapMutations } from 'vuex'
+import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
+import ytch from 'yt-channel-info'
+
export default Vue.extend({
name: 'Subscriptions',
components: {
+ 'ft-loader': FtLoader,
'ft-card': FtCard,
'ft-flex-box': FtFlexBox,
'ft-element-list': FtElementList
},
+ data: function () {
+ return {
+ isLoading: false,
+ dataLimit: 100,
+ videoList: []
+ }
+ },
+ computed: {
+ backendPreference: function () {
+ return this.$store.getters.getBackendPreference
+ },
+
+ backendFallback: function () {
+ return this.$store.getters.getBackendFallback
+ },
+
+ profileList: function () {
+ return this.$store.getters.getProfileList
+ },
+
+ activeProfile: function () {
+ return this.$store.getters.getActiveProfile
+ },
+
+ profileSubscriptions: function () {
+ return this.$store.getters.getProfileSubscriptions
+ },
+
+ activeSubscriptionList: function () {
+ return this.profileList[this.activeProfile].subscriptions
+ },
+
+ allSubscriptionsList: function () {
+ return this.profileList[0].subscriptions
+ },
+
+ sortedVideoList: function () {
+ const profileSubscriptions = JSON.parse(JSON.stringify(this.profileSubscriptions))
+ return profileSubscriptions.videoList.sort((a, b) => {
+ if (a.title.toLowerCase() > b.title.toLowerCase()) {
+ return -1
+ }
+
+ if (a.title.toLowerCase() < b.title.toLowerCase()) {
+ return 1
+ }
+
+ console.log(a.title)
+
+ return 0
+ })
+ }
+ },
mounted: function () {
+ setTimeout(() => {
+ this.fetchActiveSubscriptionsLocal()
+ }, 1000)
+ },
+ methods: {
+ fetchActiveSubscriptionsLocal: function () {
+ if (this.activeSubscriptionList.length === 0) {
+ return
+ }
+ this.isLoading = true
+ this.updateShowProgressBar(true)
+
+ let videoList = []
+ let channelCount = 0
+
+ this.activeSubscriptionList.forEach(async (channel) => {
+ const videos = await this.getChannelVideosLocalScraper(channel.id)
+ console.log(videos)
+
+ videoList = videoList.concat(videos)
+ channelCount++
+ const percentageComplete = (channelCount / this.activeSubscriptionList.length) * 100
+ this.setProgressBarPercentage(percentageComplete)
+
+ if (channelCount === this.activeSubscriptionList.length) {
+ videoList = await Promise.all(videoList.sort((a, b) => {
+ return b.publishedDate - a.publishedDate
+ }))
+
+ const profileSubscriptions = {
+ activeProfile: this.activeProfile,
+ videoList: videoList
+ }
+
+ this.updateProfileSubscriptions(profileSubscriptions)
+ this.isLoading = false
+ this.updateShowProgressBar(false)
+ }
+ })
+ },
+
+ getChannelVideosLocalScraper: function (channelId) {
+ return new Promise((resolve, reject) => {
+ ytch.getChannelVideos(channelId, 'latest').then(async (response) => {
+ const videos = await Promise.all(response.items.map(async (video) => {
+ video.publishedDate = await this.calculatePublishedDate(video.publishedText)
+ return video
+ }))
+
+ resolve(videos)
+ }).catch((err) => {
+ console.log(err)
+ const errorMessage = this.$t('Local API Error (Click to copy)')
+ this.showToast({
+ message: `${errorMessage}: ${err}`,
+ time: 10000,
+ action: () => {
+ navigator.clipboard.writeText(err)
+ }
+ })
+ resolve([])
+ })
+ })
+ },
+
+ getChannelVideosLocalRSS: function (channelId) {
+ console.log('TODO')
+ },
+
+ fetchActiveSubscriptionsInvidious: function () {
+ console.log('TODO')
+ },
+
+ getChannelVideosInvidiousScraper: function (channelId) {
+ console.log('TODO')
+ },
+
+ getChannelVideosInvidiousRSS: function (channelId) {
+ console.log('TODO')
+ },
+
+ ...mapActions([
+ 'showToast',
+ 'updateShowProgressBar',
+ 'updateProfileSubscriptions',
+ 'calculatePublishedDate'
+ ]),
+
+ ...mapMutations([
+ 'setProgressBarPercentage'
+ ])
}
})
diff --git a/src/renderer/views/Subscriptions/Subscriptions.vue b/src/renderer/views/Subscriptions/Subscriptions.vue
index ad990d1f..4f31f6ce 100644
--- a/src/renderer/views/Subscriptions/Subscriptions.vue
+++ b/src/renderer/views/Subscriptions/Subscriptions.vue
@@ -1,12 +1,35 @@
-
+
+
{{ $t("Subscriptions.Subscriptions") }}
-
+
- {{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }}
+ {{ $t("History['Your history list is currently empty.']") }}
+
+
+
+
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index 3b53813f..0769a239 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -335,6 +335,8 @@ Video:
Dec: Dec
Second: Second
Seconds: Seconds
+ Minute: Minute
+ Minutes: Minutes
Hour: Hour
Hours: Hours
Day: Day
From 8f35f95a5b8c24624ae294efcd5f51fa728a86ba Mon Sep 17 00:00:00 2001
From: Preston
Date: Tue, 1 Sep 2020 23:20:21 -0400
Subject: [PATCH 6/6] Finish Core Subscriptions logic and add more locales to
profiles page
---
package-lock.json | 52 +++++
package.json | 1 +
.../components/ft-list-video/ft-list-video.js | 3 +-
.../ft-profile-selector.css | 20 +-
.../ft-profile-selector.js | 13 +-
.../ft-profile-selector.vue | 4 +-
.../subscription-settings.js | 16 +-
.../subscription-settings.vue | 13 +-
src/renderer/store/modules/settings.js | 36 ++-
src/renderer/store/modules/subscriptions.js | 16 +-
src/renderer/store/modules/utils.js | 2 +
src/renderer/views/ProfileEdit/ProfileEdit.js | 18 +-
.../views/ProfileSettings/ProfileSettings.vue | 2 +-
src/renderer/views/Settings/Settings.vue | 1 +
.../views/Subscriptions/Subscriptions.css | 6 +
.../views/Subscriptions/Subscriptions.js | 211 +++++++++++++++---
.../views/Subscriptions/Subscriptions.vue | 17 +-
static/locales/en-US.yaml | 12 +-
18 files changed, 373 insertions(+), 70 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 344251cc..8fdf9ba5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11136,6 +11136,14 @@
}
}
},
+ "javascript-time-ago": {
+ "version": "2.0.13",
+ "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.0.13.tgz",
+ "integrity": "sha512-zH+obXUQ4vlc9UlERFe637rNJQaVYLizwODUfGzYN/cNW/owkk5wzb327gAfEXFpI4yhFcStEaoqoJtMGAmrAg==",
+ "requires": {
+ "relative-time-format": "^0.1.3"
+ }
+ },
"jest": {
"version": "26.4.2",
"resolved": "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz",
@@ -16240,6 +16248,11 @@
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
"dev": true
},
+ "relative-time-format": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-0.1.3.tgz",
+ "integrity": "sha512-0O6i4fKjsx8qhz57zorG+LrIDnF9pSvP5s7H9R1Nb5nSqih5dvRyKzNKs6MxhL3bv4iwsz4DuDwAyw+c47QFIA=="
+ },
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -16522,6 +16535,31 @@
"sprintf-js": "^1.1.2"
}
},
+ "rss-parser": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.9.0.tgz",
+ "integrity": "sha512-wlRSfGrotOXuWo19Dtl2KmQt7o9i5zzCExUrxpechE0O54BAx7JD+xhWyGumPPqiJj771ndflV3sE3bTHen0HQ==",
+ "requires": {
+ "entities": "^2.0.3",
+ "xml2js": "^0.4.19"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
+ "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
+ }
+ }
+ },
+ "rss-to-json": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/rss-to-json/-/rss-to-json-1.1.1.tgz",
+ "integrity": "sha512-d+TwrFI5wAHbZ/fTd3Pvty14tadBjKHAjfMcUam9FWoWrC9g5rHJN9Slw10OZwk6Mey+hqdXwdmymO7d8ebVmw==",
+ "requires": {
+ "axios": "^0.19.2",
+ "xml2json": "^0.12.0"
+ }
+ },
"rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -20249,6 +20287,15 @@
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
"dev": true
},
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
"xml2json": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/xml2json/-/xml2json-0.12.0.tgz",
@@ -20259,6 +20306,11 @@
"node-expat": "^2.3.18"
}
},
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
+ },
"xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
diff --git a/package.json b/package.json
index 752f94ad..24fc7771 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"mediaelement": "^4.2.16",
"nedb": "^1.8.0",
"opml-to-json": "0.0.3",
+ "rss-parser": "^3.9.0",
"video.js": "7.6.6",
"videojs-abloop": "^1.1.2",
"videojs-contrib-quality-levels": "^2.0.9",
diff --git a/src/renderer/components/ft-list-video/ft-list-video.js b/src/renderer/components/ft-list-video/ft-list-video.js
index d1117955..5ab2216d 100644
--- a/src/renderer/components/ft-list-video/ft-list-video.js
+++ b/src/renderer/components/ft-list-video/ft-list-video.js
@@ -255,7 +255,8 @@ export default Vue.extend({
liveStreamString: this.$t('Video.Watching'),
upcomingString: this.$t('Video.Published.Upcoming'),
isLive: this.isLive,
- isUpcoming: this.data.isUpcoming
+ isUpcoming: this.data.isUpcoming,
+ isRSS: this.data.isRSS
}).then((data) => {
this.uploadedTime = data
}).catch((error) => {
diff --git a/src/renderer/components/ft-profile-selector/ft-profile-selector.css b/src/renderer/components/ft-profile-selector/ft-profile-selector.css
index f630dc0c..1d4559f5 100644
--- a/src/renderer/components/ft-profile-selector/ft-profile-selector.css
+++ b/src/renderer/components/ft-profile-selector/ft-profile-selector.css
@@ -1,6 +1,6 @@
.colorOption {
- width: 50px;
- height: 50px;
+ width: 40px;
+ height: 40px;
margin: 10px;
cursor: pointer;
border-radius: 200px 200px 200px 200px;
@@ -8,10 +8,10 @@
}
.initial {
- font-size: 25px;
+ font-size: 20px;
text-align: center;
position: relative;
- bottom: 27px;
+ bottom: 30px;
}
#profileList {
@@ -39,10 +39,18 @@
.profile {
cursor: pointer;
+ height: 50px;
+ -webkit-transition: background 0.2s ease-out;
+ -moz-transition: background 0.2s ease-out;
+ -o-transition: background 0.2s ease-out;
+ transition: background 0.2s ease-out;
}
.profile:hover {
background-color: var(--side-nav-hover-color);
+ -moz-transition: background 0.2s ease-in;
+ -o-transition: background 0.2s ease-in;
+ transition: background 0.2s ease-in;
}
.profile .colorOption {
@@ -51,6 +59,10 @@
bottom: 5px;
}
+.profileName {
+ line-height: 50px;
+}
+
.profileListTitle {
position: absolute;
top: -15px;
diff --git a/src/renderer/components/ft-profile-selector/ft-profile-selector.js b/src/renderer/components/ft-profile-selector/ft-profile-selector.js
index f63d5aa0..218e7434 100644
--- a/src/renderer/components/ft-profile-selector/ft-profile-selector.js
+++ b/src/renderer/components/ft-profile-selector/ft-profile-selector.js
@@ -36,6 +36,16 @@ export default Vue.extend({
}
},
mounted: function () {
+ setTimeout(() => {
+ const profileIndex = this.profileList.findIndex((profile) => {
+ return profile._id === this.defaultProfile
+ })
+
+ if (profileIndex !== -1) {
+ this.updateActiveProfile(profileIndex)
+ }
+ }, 100)
+
$('#profileList').focusout(() => {
$('#profileList')[0].style.display = 'none'
})
@@ -65,8 +75,9 @@ export default Vue.extend({
return
}
this.updateActiveProfile(index)
+ const message = this.$t('Profile.$ is now the active profile').replace('$', profile.name)
this.showToast({
- message: `${profile.name} is now the active profile`
+ message: message
})
$('#profileList').focusout()
},
diff --git a/src/renderer/components/ft-profile-selector/ft-profile-selector.vue b/src/renderer/components/ft-profile-selector/ft-profile-selector.vue
index e8c864d3..8f865446 100644
--- a/src/renderer/components/ft-profile-selector/ft-profile-selector.vue
+++ b/src/renderer/components/ft-profile-selector/ft-profile-selector.vue
@@ -44,7 +44,9 @@
{{ profileInitials[index] }}
-
+
{{ profile.name }}
diff --git a/src/renderer/components/subscription-settings/subscription-settings.js b/src/renderer/components/subscription-settings/subscription-settings.js
index 7d5d2ef7..5c8fe9e0 100644
--- a/src/renderer/components/subscription-settings/subscription-settings.js
+++ b/src/renderer/components/subscription-settings/subscription-settings.js
@@ -1,4 +1,5 @@
import Vue from 'vue'
+import { mapActions } from 'vuex'
import FtCard from '../ft-card/ft-card.vue'
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import FtButton from '../ft-button/ft-button.vue'
@@ -27,9 +28,18 @@ export default Vue.extend({
]
}
},
- methods: {
- goToChannel: function () {
- console.log('TODO: Handle goToChannel')
+ computed: {
+ hideWatchedSubs: function () {
+ return this.$store.getters.getHideWatchedSubs
+ },
+ useRssFeeds: function () {
+ return this.$store.getters.getUseRssFeeds
}
+ },
+ methods: {
+ ...mapActions([
+ 'updateHideWatchedSubs',
+ 'updateUseRssFeeds'
+ ])
}
})
diff --git a/src/renderer/components/subscription-settings/subscription-settings.vue b/src/renderer/components/subscription-settings/subscription-settings.vue
index cdc82dc3..aa1c2e79 100644
--- a/src/renderer/components/subscription-settings/subscription-settings.vue
+++ b/src/renderer/components/subscription-settings/subscription-settings.vue
@@ -5,16 +5,24 @@
- {{ title }}
+ {{ $t("Settings.Subscription Settings.Subscription Settings") }}
+
diff --git a/src/renderer/store/modules/settings.js b/src/renderer/store/modules/settings.js
index ab3acbc4..f51346f1 100644
--- a/src/renderer/store/modules/settings.js
+++ b/src/renderer/store/modules/settings.js
@@ -57,6 +57,7 @@ const state = {
debugMode: false,
disctractionFreeMode: false,
hideWatchedSubs: false,
+ useRssFeeds: false,
usingElectron: true
}
@@ -157,13 +158,21 @@ const getters = {
return state.defaultQuality
},
+ getHideWatchedSubs: () => {
+ return state.hideWatchedSubs
+ },
+
+ getUseRssFeeds: () => {
+ return state.useRssFeeds
+ },
+
getUsingElectron: () => {
return state.usingElectron
}
}
const actions = {
- grabUserSettings ({ dispatch, commit }) {
+ grabUserSettings ({ dispatch, commit, rootState }) {
settingsDb.find({}, (err, results) => {
if (!err) {
console.log(results)
@@ -206,6 +215,12 @@ const actions = {
case 'barColor':
commit('setBarColor', result.value)
break
+ case 'hideWatchedSubs':
+ commit('setHideWatchedSubs', result.value)
+ break
+ case 'useRssFeeds':
+ commit('setUseRssFeeds', result.value)
+ break
case 'rememberHistory':
commit('setRememberHistory', result.value)
break
@@ -341,6 +356,22 @@ const actions = {
})
},
+ updateHideWatchedSubs ({ commit }, hideWatchedSubs) {
+ settingsDb.update({ _id: 'hideWatchedSubs' }, { _id: 'hideWatchedSubs', value: hideWatchedSubs }, { upsert: true }, (err, numReplaced) => {
+ if (!err) {
+ commit('setHideWatchedSubs', hideWatchedSubs)
+ }
+ })
+ },
+
+ updateUseRssFeeds ({ commit }, useRssFeeds) {
+ settingsDb.update({ _id: 'useRssFeeds' }, { _id: 'useRssFeeds', value: useRssFeeds }, { upsert: true }, (err, numReplaced) => {
+ if (!err) {
+ commit('setUseRssFeeds', useRssFeeds)
+ }
+ })
+ },
+
updateRememberHistory ({ commit }, history) {
settingsDb.update({ _id: 'rememberHistory' }, { _id: 'rememberHistory', value: history }, { upsert: true }, (err, numReplaced) => {
if (!err) {
@@ -546,6 +577,9 @@ const mutations = {
setHideWatchedSubs (state, hideWatchedSubs) {
state.hideWatchedSubs = hideWatchedSubs
},
+ setUseRssFeeds (state, useRssFeeds) {
+ state.useRssFeeds = useRssFeeds
+ },
setUsingElectron (state, usingElectron) {
state.usingElectron = usingElectron
},
diff --git a/src/renderer/store/modules/subscriptions.js b/src/renderer/store/modules/subscriptions.js
index 84e36201..6bbf944c 100644
--- a/src/renderer/store/modules/subscriptions.js
+++ b/src/renderer/store/modules/subscriptions.js
@@ -1,7 +1,5 @@
-import ytch from 'yt-channel-info'
-
const state = {
- subscriptions: [],
+ allSubscriptionsList: [],
profileSubscriptions: {
activeProfile: 0,
videoList: []
@@ -9,8 +7,8 @@ const state = {
}
const getters = {
- getSubscriptions: () => {
- return state.subscriptions
+ getAllSubscriptionsList: () => {
+ return state.allSubscriptionsList
},
getProfileSubscriptions: () => {
return state.profileSubscriptions
@@ -18,8 +16,8 @@ const getters = {
}
const actions = {
- updateSubscriptions ({ commit }, subscriptions) {
- commit('setSubscriptions', subscriptions)
+ updateAllSubscriptionsList ({ commit }, subscriptions) {
+ commit('setAllSubscriptionsList', subscriptions)
},
updateProfileSubscriptions ({ commit }, subscriptions) {
commit('setProfileSubscriptions', subscriptions)
@@ -27,8 +25,8 @@ const actions = {
}
const mutations = {
- setSubscriptions (state, subscriptions) {
- state.subscriptions = subscriptions
+ setAllSubscriptionsList (state, allSubscriptionsList) {
+ state.allSubscriptionsList = allSubscriptionsList
},
setProfileSubscriptions (state, profileSubscriptions) {
state.profileSubscriptions = profileSubscriptions
diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js
index 2ecd99d4..bfa780fc 100644
--- a/src/renderer/store/modules/utils.js
+++ b/src/renderer/store/modules/utils.js
@@ -265,6 +265,8 @@ const actions = {
} else if (payload.isUpcoming || payload.publishText === null) {
// the check for null is currently just an inferring of knowledge, because there is no other possibility left
return payload.upcomingString
+ } else if (payload.isRSS) {
+ return payload.publishText
}
const strings = payload.publishText.split(' ')
const singular = (strings[0] === '1')
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.js b/src/renderer/views/ProfileEdit/ProfileEdit.js
index 28fdbf05..aece2bd6 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.js
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.js
@@ -63,7 +63,7 @@ export default Vue.extend({
this.isLoading = true
const profileType = this.$route.name
- this.deletePromptLabel = 'Are you sure you want to delete this profile? All subscriptions in this profile will also be deleted.'
+ this.deletePromptLabel = `${this.$t('Profile.Are you sure you want to delete this profile?')} ${this.$t('Profile["All subscriptions will also be deleted."]')}`
if (profileType === 'newProfile') {
this.isNew = true
@@ -78,7 +78,7 @@ export default Vue.extend({
this.grabProfileInfo(this.profileId).then((profile) => {
if (profile === null) {
this.showToast({
- message: 'Profile could not be found'
+ message: this.$t('Profile.Profile could not be found')
})
this.$router.push({
path: '/settings/profile/'
@@ -108,7 +108,7 @@ export default Vue.extend({
saveProfile: function () {
if (this.profileName === '') {
this.showToast({
- message: 'Your profile name cannot be empty'
+ message: this.$t('Profile.Your profile name cannot be empty')
})
return
}
@@ -129,34 +129,36 @@ export default Vue.extend({
if (this.isNew) {
this.showToast({
- message: 'Profile has been created'
+ message: this.$t('Profile.Profile has been created')
})
this.$router.push({
path: '/settings/profile/'
})
} else {
this.showToast({
- message: 'Profile has been updated'
+ message: this.$t('Profile.Profile has been updated')
})
}
},
setDefaultProfile: function () {
this.updateDefaultProfile(this.profileId)
+ const message = this.$t('Profile.Your default profile has been set to $').replace('$', this.profileName)
this.showToast({
- message: `Your default profile has been set to ${this.profileName}`
+ message: message
})
},
deleteProfile: function () {
this.removeProfile(this.profileId)
+ const message = this.$t('Profile.Removed $ from your profiles').replace('$', this.profileName)
this.showToast({
- message: `Removed ${this.profileName} from your profiles`
+ message: message
})
if (this.defaultProfile === this.profileId) {
this.updateDefaultProfile('allChannels')
this.showToast({
- message: 'Your default profile has been set your Primary profile'
+ message: this.$t('Profile.Your default profile has been changed to your primary profile')
})
}
if (this.activeProfile._id === this.profileId) {
diff --git a/src/renderer/views/ProfileSettings/ProfileSettings.vue b/src/renderer/views/ProfileSettings/ProfileSettings.vue
index b03cd6c4..b1e0b605 100644
--- a/src/renderer/views/ProfileSettings/ProfileSettings.vue
+++ b/src/renderer/views/ProfileSettings/ProfileSettings.vue
@@ -16,7 +16,7 @@
diff --git a/src/renderer/views/Settings/Settings.vue b/src/renderer/views/Settings/Settings.vue
index 665ad161..16f22727 100644
--- a/src/renderer/views/Settings/Settings.vue
+++ b/src/renderer/views/Settings/Settings.vue
@@ -3,6 +3,7 @@
+
diff --git a/src/renderer/views/Subscriptions/Subscriptions.css b/src/renderer/views/Subscriptions/Subscriptions.css
index b6db9095..76a3d760 100644
--- a/src/renderer/views/Subscriptions/Subscriptions.css
+++ b/src/renderer/views/Subscriptions/Subscriptions.css
@@ -8,6 +8,12 @@
color: var(--tertiary-text-color);
}
+.floatingTopButton {
+ position: absolute;
+ top: 70px;
+ right: 10px;
+}
+
@media only screen and (max-width: 680px) {
.card {
width: 90%;
diff --git a/src/renderer/views/Subscriptions/Subscriptions.js b/src/renderer/views/Subscriptions/Subscriptions.js
index 278a1d10..fc187453 100644
--- a/src/renderer/views/Subscriptions/Subscriptions.js
+++ b/src/renderer/views/Subscriptions/Subscriptions.js
@@ -2,16 +2,21 @@ import Vue from 'vue'
import { mapActions, mapMutations } from 'vuex'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtCard from '../../components/ft-card/ft-card.vue'
+import FtButton from '../../components/ft-button/ft-button.vue'
+import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
import ytch from 'yt-channel-info'
+import Parser from 'rss-parser'
export default Vue.extend({
name: 'Subscriptions',
components: {
'ft-loader': FtLoader,
'ft-card': FtCard,
+ 'ft-button': FtButton,
+ 'ft-icon-button': FtIconButton,
'ft-flex-box': FtFlexBox,
'ft-element-list': FtElementList
},
@@ -23,6 +28,10 @@ export default Vue.extend({
}
},
computed: {
+ usingElectron: function () {
+ return this.$store.getters.getUsingElectron
+ },
+
backendPreference: function () {
return this.$store.getters.getBackendPreference
},
@@ -31,10 +40,30 @@ export default Vue.extend({
return this.$store.getters.getBackendFallback
},
+ invidiousInstance: function () {
+ return this.$store.getters.getInvidiousInstance
+ },
+
+ hideWatchedSubs: function () {
+ return this.$store.getters.getHideWatchedSubs
+ },
+
+ useRssFeeds: function () {
+ return this.$store.getters.getUseRssFeeds
+ },
+
profileList: function () {
return this.$store.getters.getProfileList
},
+ activeVideoList: function () {
+ if (this.videoList.length < this.dataLimit) {
+ return this.videoList
+ } else {
+ return this.videoList.slice(0, this.dataLimit)
+ }
+ },
+
activeProfile: function () {
return this.$store.getters.getActiveProfile
},
@@ -43,50 +72,99 @@ export default Vue.extend({
return this.$store.getters.getProfileSubscriptions
},
+ allSubscriptionsList: function () {
+ return this.$store.getters.getAllSubscriptionsList
+ },
+
+ historyCache: function () {
+ return this.$store.getters.getHistoryCache
+ },
+
activeSubscriptionList: function () {
return this.profileList[this.activeProfile].subscriptions
- },
-
- allSubscriptionsList: function () {
- return this.profileList[0].subscriptions
- },
-
- sortedVideoList: function () {
- const profileSubscriptions = JSON.parse(JSON.stringify(this.profileSubscriptions))
- return profileSubscriptions.videoList.sort((a, b) => {
- if (a.title.toLowerCase() > b.title.toLowerCase()) {
- return -1
- }
-
- if (a.title.toLowerCase() < b.title.toLowerCase()) {
- return 1
- }
-
- console.log(a.title)
-
- return 0
- })
}
},
- mounted: function () {
- setTimeout(() => {
- this.fetchActiveSubscriptionsLocal()
- }, 1000)
+ watch: {
+ activeProfile: async function (val) {
+ if (this.allSubscriptionsList.length !== 0) {
+ this.isLoading = true
+ this.videoList = await Promise.all(this.allSubscriptionsList.filter((video) => {
+ const channelIndex = this.activeSubscriptionList.findIndex((x) => {
+ return x.id === video.authorId
+ })
+
+ const historyIndex = this.historyCache.findIndex((x) => {
+ return x.videoId === video.videoId
+ })
+
+ if (this.hideWatchedSubs) {
+ return channelIndex !== -1 && historyIndex === -1
+ } else {
+ return channelIndex !== -1
+ }
+ }))
+ this.isLoading = false
+ } else {
+ this.getSubscriptions()
+ }
+ }
+ },
+ mounted: async function () {
+ this.isLoading = true
+ const dataLimit = sessionStorage.getItem('subscriptionLimit')
+ if (dataLimit !== null) {
+ this.dataLimit = dataLimit
+ }
+ setTimeout(async () => {
+ if (this.profileSubscriptions.videoList.length === 0) {
+ this.getSubscriptions()
+ } else {
+ const subscriptionList = JSON.parse(JSON.stringify(this.profileSubscriptions))
+ if (this.hideWatchedSubs) {
+ this.videoList = await Promise.all(subscriptionList.videoList.filter((video) => {
+ const historyIndex = this.historyCache.findIndex((x) => {
+ return x.videoId === video.videoId
+ })
+
+ return historyIndex === -1
+ }))
+ } else {
+ this.videoList = subscriptionList.videoList
+ }
+ this.isLoading = false
+ }
+ }, 200)
},
methods: {
- fetchActiveSubscriptionsLocal: function () {
+ getSubscriptions: function () {
if (this.activeSubscriptionList.length === 0) {
+ this.isLoading = false
+ this.videoList = []
return
}
this.isLoading = true
this.updateShowProgressBar(true)
+ this.setProgressBarPercentage(0)
let videoList = []
let channelCount = 0
this.activeSubscriptionList.forEach(async (channel) => {
- const videos = await this.getChannelVideosLocalScraper(channel.id)
- console.log(videos)
+ let videos = []
+
+ if (!this.usingElectron || this.backendPreference === 'invidious') {
+ if (this.useRssFeeds) {
+ videos = await this.getChannelVideosInvidiousRSS(channel.id)
+ } else {
+ videos = await this.getChannelVideosInvidiousScraper(channel.id)
+ }
+ } else {
+ if (this.useRssFeeds) {
+ videos = await this.getChannelVideosLocalRSS(channel.id)
+ } else {
+ videos = await this.getChannelVideosLocalScraper(channel.id)
+ }
+ }
videoList = videoList.concat(videos)
channelCount++
@@ -103,9 +181,24 @@ export default Vue.extend({
videoList: videoList
}
+ this.videoList = await Promise.all(videoList.filter((video) => {
+ if (this.hideWatchedSubs) {
+ const historyIndex = this.historyCache.findIndex((x) => {
+ return x.videoId === video.videoId
+ })
+
+ return historyIndex === -1
+ } else {
+ return true
+ }
+ }))
this.updateProfileSubscriptions(profileSubscriptions)
this.isLoading = false
this.updateShowProgressBar(false)
+
+ if (this.activeProfile === 0) {
+ this.updateAllSubscriptionsList(profileSubscriptions.videoList)
+ }
}
})
},
@@ -135,25 +228,75 @@ export default Vue.extend({
},
getChannelVideosLocalRSS: function (channelId) {
- console.log('TODO')
- },
+ return new Promise((resolve, reject) => {
+ const parser = new Parser()
+ const feedUrl = `https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`
- fetchActiveSubscriptionsInvidious: function () {
- console.log('TODO')
+ parser.parseURL(feedUrl).then(async (feed) => {
+ resolve(await Promise.all(feed.items.map((video) => {
+ video.authorId = channelId
+ video.videoId = video.id.replace('yt:video:', '')
+ video.type = 'video'
+ video.publishedDate = new Date(video.pubDate)
+ video.publishedText = video.publishedDate.toLocaleString()
+ video.lengthSeconds = '0:00'
+ video.isRSS = true
+
+ return video
+ })))
+ }).catch((err) => {
+ console.log(err)
+ resolve([])
+ })
+ })
},
getChannelVideosInvidiousScraper: function (channelId) {
- console.log('TODO')
+ return new Promise((resolve, reject) => {
+ const subscriptionsPayload = {
+ resource: 'channels/latest',
+ id: channelId,
+ params: {}
+ }
+
+ this.invidiousAPICall(subscriptionsPayload).then((result) => {
+ resolve(result)
+ })
+ })
},
getChannelVideosInvidiousRSS: function (channelId) {
- console.log('TODO')
+ return new Promise((resolve, reject) => {
+ const parser = new Parser()
+ const feedUrl = `${this.invidiousInstance}/feed/channel/${channelId}`
+
+ parser.parseURL(feedUrl).then(async (feed) => {
+ resolve(await Promise.all(feed.items.map((video) => {
+ video.authorId = channelId
+ video.videoId = video.id.replace('yt:video:', '')
+ video.type = 'video'
+ video.publishedDate = new Date(video.pubDate)
+ video.publishedText = video.publishedDate.toLocaleString()
+ video.lengthSeconds = '0:00'
+ video.isRSS = true
+
+ return video
+ })))
+ })
+ })
+ },
+
+ increaseLimit: function () {
+ this.dataLimit += 100
+ sessionStorage.setItem('subscriptionLimit', this.dataLimit)
},
...mapActions([
'showToast',
+ 'invidiousAPICall',
'updateShowProgressBar',
'updateProfileSubscriptions',
+ 'updateAllSubscriptionsList',
'calculatePublishedDate'
]),
diff --git a/src/renderer/views/Subscriptions/Subscriptions.vue b/src/renderer/views/Subscriptions/Subscriptions.vue
index 4f31f6ce..85137098 100644
--- a/src/renderer/views/Subscriptions/Subscriptions.vue
+++ b/src/renderer/views/Subscriptions/Subscriptions.vue
@@ -10,20 +10,20 @@
>
{{ $t("Subscriptions.Subscriptions") }}
- {{ $t("History['Your history list is currently empty.']") }}
+ {{ $t("Subscriptions['Your Subscription list is currently empty. Start adding subscriptions to see them here.']") }}
+
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index 0769a239..99394fd8 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -70,7 +70,8 @@ Subscriptions:
Latest Subscriptions: Latest Subscriptions
'Your Subscription list is currently empty. Start adding subscriptions to see them here.': Your
Subscription list is currently empty. Start adding subscriptions to see them here.
- 'Getting Subscriptions. Please wait.': Getting Subscriptions. Please wait.
+ 'Getting Subscriptions. Please wait.': Getting Subscriptions. Please wait.
+ Refresh Subscriptions: Refresh Subscriptions
Trending: Trending
Most Popular: Most Popular
Playlists: Playlists
@@ -180,6 +181,7 @@ Settings:
Subscription Settings:
Subscription Settings: Subscription Settings
Hide Videos on Watch: Hide Videos on Watch
+ Fetch Feeds from RSS: Fetch Feeds from RSS
Subscriptions Export Format:
Subscriptions Export Format: Subscriptions Export Format
#& Freetube
@@ -260,6 +262,14 @@ Profile:
Delete Profile: Delete Profile
Are you sure you want to delete this profile?: Are you sure you want to delete this profile?
All subscriptions will also be deleted.: All subscriptions will also be deleted.
+ Profile could not be found: Profile could not be found
+ Your profile name cannot be empty: Your profile name cannot be empty
+ Profile has been created: Profile has been created
+ Profile has been updated: Profile has been updated
+ Your default profile has been set to $: Your default profile has been set to $
+ Removed $ from your profiles: Removed $ from your profiles
+ Your default profile has been changed to your primary profile: Your default profile has been changed to your primary profile
+ $ is now the active profile: $ is now the active profile
#On Channel Page
Channel:
Subscriber: Subscriber