Finish Profile Logic and working subscriptions
This commit is contained in:
parent
0680e6c5b6
commit
1e035105d1
|
@ -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 () {
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
<!-- </keep-alive> -->
|
||||
</Transition>
|
||||
<ft-toast />
|
||||
<ft-progress-bar
|
||||
v-if="showProgressBar"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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'
|
||||
])
|
||||
}
|
||||
})
|
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="colorOption"
|
||||
:style="{ background: profileList[activeProfile].bgColor, color: profileList[activeProfile].textColor }"
|
||||
@click="toggleProfileList"
|
||||
>
|
||||
<p
|
||||
class="initial"
|
||||
>
|
||||
{{ profileInitials[activeProfile] }}
|
||||
</p>
|
||||
</div>
|
||||
<ft-card
|
||||
id="profileList"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
class="profileListTitle"
|
||||
>
|
||||
Profile Select
|
||||
</h3>
|
||||
<ft-icon-button
|
||||
class="profileSettings"
|
||||
icon="sliders-h"
|
||||
@click="openProfileSettings"
|
||||
/>
|
||||
<div
|
||||
class="profileWrapper"
|
||||
>
|
||||
<div
|
||||
class="profile"
|
||||
v-for="(profile, index) in profileList"
|
||||
:key="index"
|
||||
@click="setActiveProfile(profile)"
|
||||
>
|
||||
<div
|
||||
class="colorOption"
|
||||
:style="{ background: profile.bgColor, color: profile.textColor }"
|
||||
>
|
||||
<p
|
||||
class="initial"
|
||||
>
|
||||
{{ profileInitials[index] }}
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
{{ profile.name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ft-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./ft-profile-selector.js" />
|
||||
<style scoped src="./ft-profile-selector.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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'FtProgressBar',
|
||||
computed: {
|
||||
progressBarPercentage: function () {
|
||||
return this.$store.getters.getProgressBarPercentage
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<div
|
||||
class="progressBar"
|
||||
:style="{ width: progressBarPercentage + '%' }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./ft-progress-bar.js" />
|
||||
<style scoped src="./ft-progress-bar.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 */
|
||||
|
|
|
@ -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}` })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
<p class="navLabel">
|
||||
{{ $t("Subscriptions.Subscriptions") }}
|
||||
</p>
|
||||
<font-awesome-icon
|
||||
class="refreshIcon"
|
||||
icon="sync"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="navOption mobileHidden"
|
||||
|
@ -87,7 +83,7 @@
|
|||
</div>
|
||||
<div
|
||||
class="navOption mobileHidden"
|
||||
@click="navigate('settings/profile')"
|
||||
@click="navigate('about')"
|
||||
>
|
||||
<font-awesome-icon
|
||||
icon="info-circle"
|
||||
|
@ -98,6 +94,28 @@
|
|||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div
|
||||
v-for="(channel, index) in activeSubscriptions"
|
||||
:key="index"
|
||||
class="navChannel mobileHidden"
|
||||
:title="channel.name"
|
||||
@click="goToChannel(channel.id)"
|
||||
>
|
||||
<div
|
||||
class="thumbnailContainer"
|
||||
>
|
||||
<img
|
||||
class="channelThumbnail"
|
||||
:src="channel.thumbnail"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
class="navLabel"
|
||||
v-if="isOpen"
|
||||
>
|
||||
{{ channel.name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ft-flex-box>
|
||||
</template>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -59,6 +59,9 @@
|
|||
display: flex
|
||||
align-items: center
|
||||
|
||||
&.profiles
|
||||
justify-content: flex-end
|
||||
|
||||
.navSearchIcon
|
||||
@media only screen and (min-width: 681px)
|
||||
display: none
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
:class="{ expand: !isSideNavOpen }"
|
||||
/>
|
||||
</div>
|
||||
<div class="side" />
|
||||
<ft-profile-selector class="side profiles" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -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'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<ft-button
|
||||
:label="$t('Channel.Subscribe')"
|
||||
:label="subscribedText"
|
||||
background-color="var(--primary-color)"
|
||||
text-color="var(--text-with-main-color)"
|
||||
class="subscribeButton"
|
||||
|
|
|
@ -107,9 +107,6 @@ export default Vue.extend({
|
|||
return video
|
||||
})
|
||||
|
||||
return video
|
||||
})
|
||||
|
||||
this.isLoading = false
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.colorOptions {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.colorOption {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
|
|
|
@ -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'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</ft-flex-box>
|
||||
<h3>{{ $t("Profile.Color Picker") }}</h3>
|
||||
<ft-flex-box
|
||||
class="bottomMargin"
|
||||
class="bottomMargin colorOptions"
|
||||
>
|
||||
<div
|
||||
v-for="(color, index) in colorValues"
|
||||
|
@ -69,22 +69,37 @@
|
|||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-button
|
||||
v-if="isNew"
|
||||
:label="$t('Profile.Create Profile')"
|
||||
@click="saveProfile"
|
||||
/>
|
||||
<ft-button
|
||||
v-if="!isNew"
|
||||
:label="$t('Profile.Update Profile')"
|
||||
@click="saveProfile"
|
||||
/>
|
||||
<ft-button
|
||||
v-if="!isNew"
|
||||
:label="$t('Profile.Make Default Profile')"
|
||||
@click="saveProfile"
|
||||
@click="setDefaultProfile"
|
||||
/>
|
||||
<ft-button
|
||||
v-if="profileId !== 'allChannels' && !isNew"
|
||||
:label="$t('Profile.Delete Profile')"
|
||||
text-color="var(--text-with-main-color)"
|
||||
background-color="var(--primary-color)"
|
||||
@click="saveProfile"
|
||||
@click="openDeletePrompt"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</ft-card>
|
||||
</div>
|
||||
<ft-prompt
|
||||
v-if="showDeletePrompt"
|
||||
:label="deletePromptLabel"
|
||||
:option-names="deletePromptNames"
|
||||
:option-values="deletePromptValues"
|
||||
@click="handleDeletePrompt"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -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%;
|
||||
|
|
|
@ -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/`
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
<div>
|
||||
<ft-card class="card">
|
||||
<h3>{{ $t("Profile.Profile Manager") }}</h3>
|
||||
<ft-flex-box>
|
||||
<ft-flex-box
|
||||
class="profileList"
|
||||
>
|
||||
<ft-profile-bubble
|
||||
v-for="(profile, index) in profileList"
|
||||
:key="index"
|
||||
|
@ -12,6 +14,12 @@
|
|||
:text-color="profile.textColor"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-button
|
||||
label="Create New Profile"
|
||||
@click="newProfile"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</ft-card>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -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'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,12 +1,35 @@
|
|||
<template>
|
||||
<div>
|
||||
<ft-card class="card">
|
||||
<ft-loader
|
||||
v-if="isLoading"
|
||||
:fullscreen="true"
|
||||
/>
|
||||
<ft-card
|
||||
v-else
|
||||
class="card"
|
||||
>
|
||||
<h3>{{ $t("Subscriptions.Subscriptions") }}</h3>
|
||||
<ft-flex-box>
|
||||
<ft-flex-box
|
||||
v-if="profileSubscriptions.videoList.length === 0"
|
||||
>
|
||||
<p class="message">
|
||||
{{ $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.']") }}
|
||||
</p>
|
||||
</ft-flex-box>
|
||||
<ft-element-list
|
||||
v-else
|
||||
:data="profileSubscriptions.videoList"
|
||||
/>
|
||||
<ft-flex-box
|
||||
v-if="false"
|
||||
>
|
||||
<ft-button
|
||||
label="Load More"
|
||||
background-color="var(--primary-color)"
|
||||
text-color="var(--text-with-main-color)"
|
||||
@click="increaseLimit"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</ft-card>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -335,6 +335,8 @@ Video:
|
|||
Dec: Dec
|
||||
Second: Second
|
||||
Seconds: Seconds
|
||||
Minute: Minute
|
||||
Minutes: Minutes
|
||||
Hour: Hour
|
||||
Hours: Hours
|
||||
Day: Day
|
||||
|
|
Loading…
Reference in New Issue