Add subscription manager within profile settings. Add Upcoming video information. Other changes
This commit is contained in:
parent
20b379c269
commit
2a0c062915
20
appveyor.yml
20
appveyor.yml
|
@ -1,20 +0,0 @@
|
||||||
image: Visual Studio 2017
|
|
||||||
|
|
||||||
platform:
|
|
||||||
- x64
|
|
||||||
|
|
||||||
cache:
|
|
||||||
- node_modules
|
|
||||||
- '%USERPROFILE%\.electron'
|
|
||||||
|
|
||||||
init:
|
|
||||||
- git config --global core.autocrlf input
|
|
||||||
|
|
||||||
install:
|
|
||||||
- ps: Install-Product node 12 x64
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- npm run build
|
|
||||||
|
|
||||||
test: off
|
|
|
@ -1,4 +1,5 @@
|
||||||
.bubblePadding {
|
.bubblePadding {
|
||||||
|
position: relative;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 115px;
|
height: 115px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -25,6 +26,20 @@
|
||||||
-webkit-border-radius: 200px 200px 200px 200px;
|
-webkit-border-radius: 200px 200px 200px 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: #EEEEEE;
|
||||||
|
font-size: 25px;
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.channelName {
|
.channelName {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
|
|
@ -7,18 +7,26 @@ export default Vue.extend({
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
channelId: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
channelThumbnail: {
|
channelThumbnail: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
showSelected: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
selected: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goToChannel: function () {
|
handleClick: function () {
|
||||||
console.log('Go to channel')
|
if (this.showSelected) {
|
||||||
|
this.selected = !this.selected
|
||||||
|
}
|
||||||
|
this.$emit('click')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="bubblePadding"
|
class="bubblePadding"
|
||||||
@click="goToChannel(channelId)"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="bubble"
|
class="bubble"
|
||||||
:src="channelThumbnail"
|
:src="channelThumbnail"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="selected"
|
||||||
|
class="bubble selected"
|
||||||
|
>
|
||||||
|
<font-awesome-icon
|
||||||
|
icon="check"
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="channelName">
|
<div class="channelName">
|
||||||
{{ channelName }}
|
{{ channelName }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.forceTextColor .ft-input {
|
.forceTextColor .ft-input {
|
||||||
color: var(--text-with-main-color);
|
color: #EEEEEE;
|
||||||
background-color: var(--primary-input-color);
|
background-color: var(--primary-input-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.forceTextColor ::-webkit-input-placeholder {
|
.forceTextColor ::-webkit-input-placeholder {
|
||||||
color: var(--text-with-main-color);
|
color: #EEEEEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputAction {
|
.inputAction {
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.forceTextColor .inputAction {
|
.forceTextColor .inputAction {
|
||||||
color: var(--text-with-main-color);
|
color: #EEEEEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputAction:hover {
|
.inputAction:hover {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
.card {
|
||||||
|
width: 85%;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { mapActions } from 'vuex'
|
||||||
|
|
||||||
|
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||||
|
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||||
|
import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubble.vue'
|
||||||
|
import FtButton from '../../components/ft-button/ft-button.vue'
|
||||||
|
import FtPrompt from '../../components/ft-prompt/ft-prompt.vue'
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: 'FtProfileAllChannelsList',
|
||||||
|
components: {
|
||||||
|
'ft-card': FtCard,
|
||||||
|
'ft-flex-box': FtFlexBox,
|
||||||
|
'ft-channel-bubble': FtChannelBubble,
|
||||||
|
'ft-button': FtButton,
|
||||||
|
'ft-prompt': FtPrompt
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
profile: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
channels: [],
|
||||||
|
selectedLength: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
profileList: function () {
|
||||||
|
return this.$store.getters.getProfileList
|
||||||
|
},
|
||||||
|
selectedText: function () {
|
||||||
|
const localeText = this.$t('Profile.$ selected')
|
||||||
|
return localeText.replace('$', this.selectedLength)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
profile: function () {
|
||||||
|
this.channels = [].concat(this.profileList[0].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
|
||||||
|
}).filter((channel) => {
|
||||||
|
const index = this.profile.subscriptions.findIndex((sub) => {
|
||||||
|
return sub.id === channel.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return index === -1
|
||||||
|
}).map((channel) => {
|
||||||
|
channel.selected = false
|
||||||
|
return channel
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
if (typeof this.profile.subscriptions !== 'undefined') {
|
||||||
|
this.channels = [].concat(this.profileList[0].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
|
||||||
|
}).filter((channel) => {
|
||||||
|
const index = this.profile.subscriptions.findIndex((sub) => {
|
||||||
|
return sub.id === channel.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return index === -1
|
||||||
|
}).map((channel) => {
|
||||||
|
channel.selected = false
|
||||||
|
return channel
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleChannelClick: function (index) {
|
||||||
|
this.channels[index].selected = !this.channels[index].selected
|
||||||
|
this.selectedLength = this.channels.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
}).length
|
||||||
|
},
|
||||||
|
|
||||||
|
addChannelToProfile: function () {
|
||||||
|
if (this.selectedLength === 0) {
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.No channel(s) have been selected')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const subscriptions = this.channels.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
})
|
||||||
|
|
||||||
|
const profile = JSON.parse(JSON.stringify(this.profile))
|
||||||
|
profile.subscriptions = profile.subscriptions.concat(subscriptions)
|
||||||
|
this.updateProfile(profile)
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.Profile has been updated')
|
||||||
|
})
|
||||||
|
this.selectNone()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectAll: function () {
|
||||||
|
Object.keys(this.$refs).forEach((ref) => {
|
||||||
|
if (typeof this.$refs[ref][0] !== 'undefined') {
|
||||||
|
this.$refs[ref][0].selected = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.channels = this.channels.map((channel) => {
|
||||||
|
channel.selected = true
|
||||||
|
return channel
|
||||||
|
})
|
||||||
|
|
||||||
|
this.selectedLength = this.channels.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
}).length
|
||||||
|
},
|
||||||
|
|
||||||
|
selectNone: function () {
|
||||||
|
Object.keys(this.$refs).forEach((ref) => {
|
||||||
|
if (typeof this.$refs[ref][0] !== 'undefined') {
|
||||||
|
this.$refs[ref][0].selected = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.channels = this.channels.map((channel) => {
|
||||||
|
channel.selected = false
|
||||||
|
return channel
|
||||||
|
})
|
||||||
|
|
||||||
|
this.selectedLength = this.channels.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
}).length
|
||||||
|
},
|
||||||
|
|
||||||
|
...mapActions([
|
||||||
|
'showToast',
|
||||||
|
'updateProfile'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ft-card class="card">
|
||||||
|
<h2>
|
||||||
|
{{ $t("Profile.Other Channels") }}
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
{{ selectedText }}
|
||||||
|
</p>
|
||||||
|
<ft-flex-box>
|
||||||
|
<ft-channel-bubble
|
||||||
|
v-for="(channel, index) in channels"
|
||||||
|
:key="index"
|
||||||
|
:ref="`all-channels-${index}`"
|
||||||
|
:channel-name="channel.name"
|
||||||
|
:channel-thumbnail="channel.thumbnail"
|
||||||
|
:show-selected="true"
|
||||||
|
@click="handleChannelClick(index)"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
<ft-flex-box>
|
||||||
|
<ft-button
|
||||||
|
:label="$t('Profile.Select All')"
|
||||||
|
@click="selectAll"
|
||||||
|
/>
|
||||||
|
<ft-button
|
||||||
|
:label="$t('Profile.Select None')"
|
||||||
|
@click="selectNone"
|
||||||
|
/>
|
||||||
|
<ft-button
|
||||||
|
:label="$t('Profile.Add Selected To Profile')"
|
||||||
|
text-color="var(--text-with-main-color)"
|
||||||
|
background-color="var(--primary-color)"
|
||||||
|
@click="addChannelToProfile"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
</ft-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./ft-profile-all-channels-list.js" />
|
||||||
|
<style scoped src="./ft-profile-all-channels-list.css" />
|
|
@ -0,0 +1,5 @@
|
||||||
|
.card {
|
||||||
|
width: 85%;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { mapActions } from 'vuex'
|
||||||
|
|
||||||
|
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||||
|
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||||
|
import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubble.vue'
|
||||||
|
import FtButton from '../../components/ft-button/ft-button.vue'
|
||||||
|
import FtPrompt from '../../components/ft-prompt/ft-prompt.vue'
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: 'FtProfileChannelList',
|
||||||
|
components: {
|
||||||
|
'ft-card': FtCard,
|
||||||
|
'ft-flex-box': FtFlexBox,
|
||||||
|
'ft-channel-bubble': FtChannelBubble,
|
||||||
|
'ft-button': FtButton,
|
||||||
|
'ft-prompt': FtPrompt
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
profile: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
isMainProfile: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
showDeletePrompt: false,
|
||||||
|
subscriptions: [],
|
||||||
|
selectedLength: 0,
|
||||||
|
componentKey: 0,
|
||||||
|
deletePromptValues: [
|
||||||
|
'yes',
|
||||||
|
'no'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
profileList: function () {
|
||||||
|
return this.$store.getters.getProfileList
|
||||||
|
},
|
||||||
|
selectedText: function () {
|
||||||
|
const localeText = this.$t('Profile.$ selected')
|
||||||
|
return localeText.replace('$', this.selectedLength)
|
||||||
|
},
|
||||||
|
deletePromptMessage: function () {
|
||||||
|
if (this.isMainProfile) {
|
||||||
|
return this.$t('Profile["This is your primary profile. Are you sure you want to delete the selected channels? The same channels will be deleted in any profile they are found in."]')
|
||||||
|
} else {
|
||||||
|
return this.$t('Profile["Are you sure you want to delete the selected channels? This will not delete the channel from any other profile."]')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deletePromptNames: function () {
|
||||||
|
return [
|
||||||
|
this.$t('Yes'),
|
||||||
|
this.$t('No')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
profile: function () {
|
||||||
|
this.subscriptions = [].concat(this.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
|
||||||
|
}).map((channel) => {
|
||||||
|
channel.selected = false
|
||||||
|
return channel
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
if (typeof this.profile.subscriptions !== 'undefined') {
|
||||||
|
this.subscriptions = [].concat(this.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
|
||||||
|
}).map((channel) => {
|
||||||
|
channel.selected = false
|
||||||
|
return channel
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
displayDeletePrompt: function () {
|
||||||
|
if (this.selectedLength === 0) {
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.No channel(s) have been selected')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.showDeletePrompt = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeletePromptClick: function (value) {
|
||||||
|
if (value !== 'no' && value !== null) {
|
||||||
|
if (this.isMainProfile) {
|
||||||
|
const channelsToRemove = this.subscriptions.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
})
|
||||||
|
|
||||||
|
this.subscriptions = this.subscriptions.filter((channel) => {
|
||||||
|
return !channel.selected
|
||||||
|
})
|
||||||
|
|
||||||
|
this.profileList.forEach((x) => {
|
||||||
|
const profile = JSON.parse(JSON.stringify(x))
|
||||||
|
profile.subscriptions = profile.subscriptions.filter((channel) => {
|
||||||
|
const index = channelsToRemove.findIndex((y) => {
|
||||||
|
return y.id === channel.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return index === -1
|
||||||
|
})
|
||||||
|
this.updateProfile(profile)
|
||||||
|
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.Profile has been updated')
|
||||||
|
})
|
||||||
|
this.selectNone()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const profile = JSON.parse(JSON.stringify(this.profile))
|
||||||
|
|
||||||
|
this.subscriptions = this.subscriptions.filter((channel) => {
|
||||||
|
return !channel.selected
|
||||||
|
})
|
||||||
|
|
||||||
|
profile.subscriptions = this.subscriptions
|
||||||
|
this.selectedLength = 0
|
||||||
|
|
||||||
|
this.updateProfile(profile)
|
||||||
|
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.Profile has been updated')
|
||||||
|
})
|
||||||
|
this.selectNone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.showDeletePrompt = false
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChannelClick: function (index) {
|
||||||
|
this.subscriptions[index].selected = !this.subscriptions[index].selected
|
||||||
|
this.selectedLength = this.subscriptions.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
}).length
|
||||||
|
},
|
||||||
|
|
||||||
|
selectAll: function () {
|
||||||
|
Object.keys(this.$refs).forEach((ref) => {
|
||||||
|
if (typeof this.$refs[ref][0] !== 'undefined') {
|
||||||
|
this.$refs[ref][0].selected = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.subscriptions = this.subscriptions.map((channel) => {
|
||||||
|
channel.selected = true
|
||||||
|
return channel
|
||||||
|
})
|
||||||
|
|
||||||
|
this.selectedLength = this.subscriptions.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
}).length
|
||||||
|
},
|
||||||
|
|
||||||
|
selectNone: function () {
|
||||||
|
Object.keys(this.$refs).forEach((ref) => {
|
||||||
|
if (typeof this.$refs[ref][0] !== 'undefined') {
|
||||||
|
this.$refs[ref][0].selected = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.subscriptions = this.subscriptions.map((channel) => {
|
||||||
|
channel.selected = false
|
||||||
|
return channel
|
||||||
|
})
|
||||||
|
|
||||||
|
this.selectedLength = this.subscriptions.filter((channel) => {
|
||||||
|
return channel.selected
|
||||||
|
}).length
|
||||||
|
},
|
||||||
|
|
||||||
|
...mapActions([
|
||||||
|
'showToast',
|
||||||
|
'updateProfile'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ft-card class="card">
|
||||||
|
<h2>
|
||||||
|
{{ $t("Profile.Subscription List") }}
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
{{ selectedText }}
|
||||||
|
</p>
|
||||||
|
<ft-flex-box>
|
||||||
|
<ft-channel-bubble
|
||||||
|
v-for="(channel, index) in subscriptions"
|
||||||
|
:key="index"
|
||||||
|
:ref="`channel-${index}`"
|
||||||
|
:channel-name="channel.name"
|
||||||
|
:channel-thumbnail="channel.thumbnail"
|
||||||
|
:show-selected="true"
|
||||||
|
@click="handleChannelClick(index)"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
<ft-flex-box>
|
||||||
|
<ft-button
|
||||||
|
:label="$t('Profile.Select All')"
|
||||||
|
@click="selectAll"
|
||||||
|
/>
|
||||||
|
<ft-button
|
||||||
|
:label="$t('Profile.Select None')"
|
||||||
|
@click="selectNone"
|
||||||
|
/>
|
||||||
|
<ft-button
|
||||||
|
:label="$t('Profile.Delete Selected')"
|
||||||
|
text-color="var(--text-with-main-color)"
|
||||||
|
background-color="var(--primary-color)"
|
||||||
|
@click="displayDeletePrompt"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
</ft-card>
|
||||||
|
<ft-prompt
|
||||||
|
v-if="showDeletePrompt"
|
||||||
|
:label="deletePromptMessage"
|
||||||
|
:option-names="deletePromptNames"
|
||||||
|
:option-values="deletePromptValues"
|
||||||
|
@click="handleDeletePromptClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./ft-profile-channel-list.js" />
|
||||||
|
<style scoped src="./ft-profile-channel-list.css" />
|
|
@ -0,0 +1,45 @@
|
||||||
|
.card {
|
||||||
|
width: 85%;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
color: var(--tertiary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profileName {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomMargin {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorOptions {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
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%;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { mapActions } from 'vuex'
|
||||||
|
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'
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: 'FtProfileEdit',
|
||||||
|
components: {
|
||||||
|
'ft-card': FtCard,
|
||||||
|
'ft-prompt': FtPrompt,
|
||||||
|
'ft-flex-box': FtFlexBox,
|
||||||
|
'ft-input': FtInput,
|
||||||
|
'ft-button': FtButton
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
profile: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
isNew: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
showDeletePrompt: false,
|
||||||
|
profileId: '',
|
||||||
|
profileName: '',
|
||||||
|
profileBgColor: '',
|
||||||
|
profileTextColor: '',
|
||||||
|
profileSubscriptions: [],
|
||||||
|
deletePromptValues: [
|
||||||
|
'yes',
|
||||||
|
'no'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
colorValues: function () {
|
||||||
|
return this.$store.getters.getColorValues
|
||||||
|
},
|
||||||
|
profileInitial: function () {
|
||||||
|
return this.profileName.slice(0, 1).toUpperCase()
|
||||||
|
},
|
||||||
|
activeProfile: function () {
|
||||||
|
return this.$store.getters.getActiveProfile
|
||||||
|
},
|
||||||
|
defaultProfile: function () {
|
||||||
|
return this.$store.getters.getDefaultProfile
|
||||||
|
},
|
||||||
|
deletePromptLabel: function () {
|
||||||
|
return `${this.$t('Profile.Are you sure you want to delete this profile?')} ${this.$t('Profile["All subscriptions will also be deleted."]')}`
|
||||||
|
},
|
||||||
|
deletePromptNames: function () {
|
||||||
|
return [
|
||||||
|
this.$t('Yes'),
|
||||||
|
this.$t('No')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
profileBgColor: async function (val) {
|
||||||
|
this.profileTextColor = await this.calculateColorLuminance(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: async function () {
|
||||||
|
this.profileId = this.$route.params.id
|
||||||
|
this.profileName = this.profile.name
|
||||||
|
this.profileBgColor = this.profile.bgColor
|
||||||
|
this.profileTextColor = this.profile.textColor
|
||||||
|
this.profileSubscriptions = this.profile.subscriptions
|
||||||
|
},
|
||||||
|
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: this.$t('Profile.Your profile name cannot be empty')
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
if (this.isNew) {
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.Profile has been created')
|
||||||
|
})
|
||||||
|
this.$router.push({
|
||||||
|
path: '/settings/profile/'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.showToast({
|
||||||
|
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: message
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteProfile: function () {
|
||||||
|
this.removeProfile(this.profileId)
|
||||||
|
const message = this.$t('Profile.Removed $ from your profiles').replace('$', this.profileName)
|
||||||
|
this.showToast({
|
||||||
|
message: message
|
||||||
|
})
|
||||||
|
if (this.defaultProfile === this.profileId) {
|
||||||
|
this.updateDefaultProfile('allChannels')
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.Your default profile has been changed to your primary profile')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (this.activeProfile._id === this.profileId) {
|
||||||
|
this.updateActiveProfile('allChannels')
|
||||||
|
}
|
||||||
|
this.$router.push({
|
||||||
|
path: '/settings/profile/'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
...mapActions([
|
||||||
|
'showToast',
|
||||||
|
'updateProfile',
|
||||||
|
'removeProfile',
|
||||||
|
'updateDefaultProfile',
|
||||||
|
'updateActiveProfile',
|
||||||
|
'calculateColorLuminance'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,99 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ft-card class="card">
|
||||||
|
<h2>{{ $t("Profile.Edit Profile") }}</h2>
|
||||||
|
<ft-flex-box>
|
||||||
|
<ft-input
|
||||||
|
class="profileName"
|
||||||
|
placeholder="Profile Name"
|
||||||
|
:value="profileName"
|
||||||
|
:show-arrow="false"
|
||||||
|
@input="e => profileName = e"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
<h3>{{ $t("Profile.Color Picker") }}</h3>
|
||||||
|
<ft-flex-box
|
||||||
|
class="bottomMargin colorOptions"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(color, index) in colorValues"
|
||||||
|
:key="index"
|
||||||
|
class="colorOption"
|
||||||
|
:style="{ background: color }"
|
||||||
|
@click="profileBgColor = color"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
<ft-flex-box
|
||||||
|
class="bottomMargin"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<label for="colorPicker">{{ $t("Profile.Custom Color") }}</label>
|
||||||
|
<input
|
||||||
|
id="colorPicker"
|
||||||
|
v-model="profileBgColor"
|
||||||
|
type="color"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</ft-flex-box>
|
||||||
|
<ft-flex-box>
|
||||||
|
<ft-input
|
||||||
|
class="profileName"
|
||||||
|
placeholder=""
|
||||||
|
:value="profileBgColor"
|
||||||
|
:show-arrow="false"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
<h3>{{ $t("Profile.Profile Preview") }}</h3>
|
||||||
|
<ft-flex-box
|
||||||
|
class="bottomMargin"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="colorOption"
|
||||||
|
:style="{ background: profileBgColor, color: profileTextColor }"
|
||||||
|
style="cursor: default"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="initial"
|
||||||
|
>
|
||||||
|
{{ profileInitial }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</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="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="openDeletePrompt"
|
||||||
|
/>
|
||||||
|
</ft-flex-box>
|
||||||
|
</ft-card>
|
||||||
|
<ft-prompt
|
||||||
|
v-if="showDeletePrompt"
|
||||||
|
:label="deletePromptLabel"
|
||||||
|
:option-names="deletePromptNames"
|
||||||
|
:option-values="deletePromptValues"
|
||||||
|
@click="handleDeletePrompt"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./ft-profile-edit.js" />
|
||||||
|
<style scoped src="./ft-profile-edit.css" />
|
|
@ -62,6 +62,10 @@ export default Vue.extend({
|
||||||
getTimestamp: {
|
getTimestamp: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isUpcoming: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
@click="$emit('theatreMode')"
|
@click="$emit('theatreMode')"
|
||||||
/>
|
/>
|
||||||
<ft-icon-button
|
<ft-icon-button
|
||||||
|
v-if="!isUpcoming"
|
||||||
:title="$t('Change Format.Change Video Formats')"
|
:title="$t('Change Format.Change Video Formats')"
|
||||||
class="option"
|
class="option"
|
||||||
theme="secondary"
|
theme="secondary"
|
||||||
|
|
|
@ -152,6 +152,7 @@ export default Vue.extend({
|
||||||
$route() {
|
$route() {
|
||||||
// react to route changes...
|
// react to route changes...
|
||||||
this.id = this.$route.params.id
|
this.id = this.$route.params.id
|
||||||
|
this.currentTab = 'videos'
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
|
|
||||||
if (!this.usingElectron) {
|
if (!this.usingElectron) {
|
||||||
|
@ -224,6 +225,10 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
goToChannel: function (id) {
|
||||||
|
this.$router.push({ path: `/channel/${id}` })
|
||||||
|
},
|
||||||
|
|
||||||
getChannelInfoLocal: function () {
|
getChannelInfoLocal: function () {
|
||||||
this.apiUsed = 'local'
|
this.apiUsed = 'local'
|
||||||
ytch.getChannelInfo(this.id).then((response) => {
|
ytch.getChannelInfo(this.id).then((response) => {
|
||||||
|
|
|
@ -123,6 +123,7 @@
|
||||||
:channel-name="channel.author"
|
:channel-name="channel.author"
|
||||||
:channel-id="channel.authorId"
|
:channel-id="channel.authorId"
|
||||||
:channel-thumbnail="channel.authorThumbnails[channel.authorThumbnails.length - 1].url"
|
:channel-thumbnail="channel.authorThumbnails[channel.authorThumbnails.length - 1].url"
|
||||||
|
@click="goToChannel(channel.authorId)"
|
||||||
/>
|
/>
|
||||||
</ft-flex-box>
|
</ft-flex-box>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
.card {
|
|
||||||
width: 85%;
|
|
||||||
margin: 0 auto;
|
|
||||||
margin-bottom: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
color: var(--tertiary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.profileName {
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottomMargin {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.colorOptions {
|
|
||||||
max-width: 1000px;
|
|
||||||
margin: 0 auto;
|
|
||||||
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%;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +1,50 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { mapActions } from 'vuex'
|
import { mapActions } from 'vuex'
|
||||||
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
||||||
import FtCard from '../../components/ft-card/ft-card.vue'
|
import FtProfileEdit from '../../components/ft-profile-edit/ft-profile-edit.vue'
|
||||||
import FtPrompt from '../../components/ft-prompt/ft-prompt.vue'
|
import FtProfileChannelList from '../../components/ft-profile-channel-list/ft-profile-channel-list.vue'
|
||||||
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
import FtProfileAllChannelsList from '../../components/ft-profile-all-channels-list/ft-profile-all-channels-list.vue'
|
||||||
import FtInput from '../../components/ft-input/ft-input.vue'
|
|
||||||
import FtButton from '../../components/ft-button/ft-button.vue'
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'ProfileEdit',
|
name: 'ProfileEdit',
|
||||||
components: {
|
components: {
|
||||||
'ft-loader': FtLoader,
|
'ft-loader': FtLoader,
|
||||||
'ft-card': FtCard,
|
'ft-profile-edit': FtProfileEdit,
|
||||||
'ft-prompt': FtPrompt,
|
'ft-profile-channel-list': FtProfileChannelList,
|
||||||
'ft-flex-box': FtFlexBox,
|
'ft-profile-all-channels-list': FtProfileAllChannelsList
|
||||||
'ft-input': FtInput,
|
|
||||||
'ft-button': FtButton
|
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
showDeletePrompt: false,
|
|
||||||
deletePromptLabel: '',
|
|
||||||
isNew: false,
|
isNew: false,
|
||||||
profileId: '',
|
profileId: '',
|
||||||
profileName: '',
|
profile: {}
|
||||||
profileBgColor: '',
|
|
||||||
profileTextColor: '',
|
|
||||||
profileSubscriptions: [],
|
|
||||||
deletePromptValues: [
|
|
||||||
'yes',
|
|
||||||
'no'
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
colorValues: function () {
|
profileList: function () {
|
||||||
return this.$store.getters.getColorValues
|
return this.$store.getters.getProfileList
|
||||||
},
|
},
|
||||||
profileInitial: function () {
|
isMainProfile: function () {
|
||||||
return this.profileName.slice(0, 1).toUpperCase()
|
return this.profileId === 'allChannels'
|
||||||
},
|
|
||||||
activeProfile: function () {
|
|
||||||
return this.$store.getters.getActiveProfile
|
|
||||||
},
|
|
||||||
defaultProfile: function () {
|
|
||||||
return this.$store.getters.getDefaultProfile
|
|
||||||
},
|
|
||||||
deletePromptNames: function () {
|
|
||||||
return [
|
|
||||||
this.$t('Yes'),
|
|
||||||
this.$t('No')
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
profileBgColor: async function (val) {
|
profileList: {
|
||||||
this.profileTextColor = await this.calculateColorLuminance(val)
|
handler: function () {
|
||||||
|
this.grabProfileInfo(this.profileId).then((profile) => {
|
||||||
|
if (profile === null) {
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Profile.Profile could not be found')
|
||||||
|
})
|
||||||
|
this.$router.push({
|
||||||
|
path: '/settings/profile/'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.profile = profile
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: async function () {
|
mounted: async function () {
|
||||||
|
@ -67,13 +55,18 @@ export default Vue.extend({
|
||||||
|
|
||||||
if (profileType === 'newProfile') {
|
if (profileType === 'newProfile') {
|
||||||
this.isNew = true
|
this.isNew = true
|
||||||
this.profileBgColor = await this.getRandomColor()
|
const bgColor = await this.getRandomColor()
|
||||||
|
const textColor = await this.calculateColorLuminance(bgColor)
|
||||||
|
this.profile = {
|
||||||
|
name: '',
|
||||||
|
bgColor: bgColor,
|
||||||
|
textColor: textColor,
|
||||||
|
subscriptions: []
|
||||||
|
}
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
} else {
|
} else {
|
||||||
this.isNew = false
|
this.isNew = false
|
||||||
this.profileId = this.$route.params.id
|
this.profileId = this.$route.params.id
|
||||||
console.log(this.profileId)
|
|
||||||
console.log(this.$route.name)
|
|
||||||
|
|
||||||
this.grabProfileInfo(this.profileId).then((profile) => {
|
this.grabProfileInfo(this.profileId).then((profile) => {
|
||||||
if (profile === null) {
|
if (profile === null) {
|
||||||
|
@ -84,100 +77,17 @@ export default Vue.extend({
|
||||||
path: '/settings/profile/'
|
path: '/settings/profile/'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.profileName = profile.name
|
this.profile = profile
|
||||||
this.profileBgColor = profile.bgColor
|
|
||||||
this.profileTextColor = profile.textColor
|
|
||||||
this.profileSubscriptions = profile.subscriptions
|
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
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: this.$t('Profile.Your profile name cannot be empty')
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
|
|
||||||
if (this.isNew) {
|
|
||||||
this.showToast({
|
|
||||||
message: this.$t('Profile.Profile has been created')
|
|
||||||
})
|
|
||||||
this.$router.push({
|
|
||||||
path: '/settings/profile/'
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.showToast({
|
|
||||||
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: message
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteProfile: function () {
|
|
||||||
this.removeProfile(this.profileId)
|
|
||||||
const message = this.$t('Profile.Removed $ from your profiles').replace('$', this.profileName)
|
|
||||||
this.showToast({
|
|
||||||
message: message
|
|
||||||
})
|
|
||||||
if (this.defaultProfile === this.profileId) {
|
|
||||||
this.updateDefaultProfile('allChannels')
|
|
||||||
this.showToast({
|
|
||||||
message: this.$t('Profile.Your default profile has been changed to your primary profile')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (this.activeProfile._id === this.profileId) {
|
|
||||||
this.updateActiveProfile('allChannels')
|
|
||||||
}
|
|
||||||
this.$router.push({
|
|
||||||
path: '/settings/profile/'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
...mapActions([
|
...mapActions([
|
||||||
'showToast',
|
'showToast',
|
||||||
'grabProfileInfo',
|
'grabProfileInfo',
|
||||||
'updateProfile',
|
'getRandomColor',
|
||||||
'removeProfile',
|
'calculateColorLuminance'
|
||||||
'updateDefaultProfile',
|
|
||||||
'updateActiveProfile',
|
|
||||||
'calculateColorLuminance',
|
|
||||||
'getRandomColor'
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,99 +7,20 @@
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
>
|
>
|
||||||
<ft-card class="card">
|
<ft-profile-edit
|
||||||
<h2>{{ $t("Profile.Edit Profile") }}</h2>
|
:profile="profile"
|
||||||
<ft-flex-box>
|
:is-new="isNew"
|
||||||
<ft-input
|
/>
|
||||||
class="profileName"
|
<ft-profile-channel-list
|
||||||
placeholder="Profile Name"
|
v-if="!isNew"
|
||||||
:value="profileName"
|
:profile="profile"
|
||||||
:show-arrow="false"
|
:is-main-profile="isMainProfile"
|
||||||
@input="e => profileName = e"
|
/>
|
||||||
/>
|
<ft-profile-all-channels-list
|
||||||
</ft-flex-box>
|
v-if="!isNew && !isMainProfile"
|
||||||
<h3>{{ $t("Profile.Color Picker") }}</h3>
|
:profile="profile"
|
||||||
<ft-flex-box
|
/>
|
||||||
class="bottomMargin colorOptions"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-for="(color, index) in colorValues"
|
|
||||||
:key="index"
|
|
||||||
class="colorOption"
|
|
||||||
:style="{ background: color }"
|
|
||||||
@click="profileBgColor = color"
|
|
||||||
/>
|
|
||||||
</ft-flex-box>
|
|
||||||
<ft-flex-box
|
|
||||||
class="bottomMargin"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<label for="colorPicker">{{ $t("Profile.Custom Color") }}</label>
|
|
||||||
<input
|
|
||||||
id="colorPicker"
|
|
||||||
v-model="profileBgColor"
|
|
||||||
type="color"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</ft-flex-box>
|
|
||||||
<ft-flex-box>
|
|
||||||
<ft-input
|
|
||||||
class="profileName"
|
|
||||||
placeholder=""
|
|
||||||
:value="profileBgColor"
|
|
||||||
:show-arrow="false"
|
|
||||||
:disabled="true"
|
|
||||||
/>
|
|
||||||
</ft-flex-box>
|
|
||||||
<h3>{{ $t("Profile.Profile Preview") }}</h3>
|
|
||||||
<ft-flex-box
|
|
||||||
class="bottomMargin"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="colorOption"
|
|
||||||
:style="{ background: profileBgColor, color: profileTextColor }"
|
|
||||||
style="cursor: default"
|
|
||||||
>
|
|
||||||
<p
|
|
||||||
class="initial"
|
|
||||||
>
|
|
||||||
{{ profileInitial }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</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="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="openDeletePrompt"
|
|
||||||
/>
|
|
||||||
</ft-flex-box>
|
|
||||||
</ft-card>
|
|
||||||
</div>
|
</div>
|
||||||
<ft-prompt
|
|
||||||
v-if="showDeletePrompt"
|
|
||||||
:label="deletePromptLabel"
|
|
||||||
:option-names="deletePromptNames"
|
|
||||||
:option-values="deletePromptValues"
|
|
||||||
@click="handleDeletePrompt"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ export default Vue.extend({
|
||||||
showYouTubeNoCookieEmbed: false,
|
showYouTubeNoCookieEmbed: false,
|
||||||
hidePlayer: false,
|
hidePlayer: false,
|
||||||
isLive: false,
|
isLive: false,
|
||||||
|
isUpcoming: false,
|
||||||
|
upcomingTimestamp: null,
|
||||||
activeFormat: 'legacy',
|
activeFormat: 'legacy',
|
||||||
videoId: '',
|
videoId: '',
|
||||||
videoTitle: '',
|
videoTitle: '',
|
||||||
|
@ -215,8 +217,9 @@ export default Vue.extend({
|
||||||
this.videoLikeCount = result.videoDetails.likes
|
this.videoLikeCount = result.videoDetails.likes
|
||||||
this.videoDislikeCount = result.videoDetails.dislikes
|
this.videoDislikeCount = result.videoDetails.dislikes
|
||||||
this.isLive = result.player_response.videoDetails.isLiveContent
|
this.isLive = result.player_response.videoDetails.isLiveContent
|
||||||
|
this.isUpcoming = result.player_response.videoDetails.isUpcoming
|
||||||
|
|
||||||
if (!this.isLive) {
|
if (!this.isLive && !this.isUpcoming) {
|
||||||
const captionTracks =
|
const captionTracks =
|
||||||
result.player_response.captions &&
|
result.player_response.captions &&
|
||||||
result.player_response.captions.playerCaptionsTracklistRenderer
|
result.player_response.captions.playerCaptionsTracklistRenderer
|
||||||
|
@ -243,7 +246,7 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isLive) {
|
if (this.isLive && !this.isUpcoming) {
|
||||||
this.enableLegacyFormat()
|
this.enableLegacyFormat()
|
||||||
|
|
||||||
this.videoSourceList = result.formats.filter((format) => {
|
this.videoSourceList = result.formats.filter((format) => {
|
||||||
|
@ -279,28 +282,39 @@ export default Vue.extend({
|
||||||
} else {
|
} else {
|
||||||
this.activeSourceList = this.videoSourceList
|
this.activeSourceList = this.videoSourceList
|
||||||
}
|
}
|
||||||
|
} else if (this.isUpcoming) {
|
||||||
|
const upcomingTimestamp = new Date(result.videoDetails.liveBroadcastDetails.startTimestamp)
|
||||||
|
this.upcomingTimestamp = upcomingTimestamp.toLocaleString()
|
||||||
} else {
|
} else {
|
||||||
this.videoLengthSeconds = parseInt(result.videoDetails.lengthSeconds)
|
this.videoLengthSeconds = parseInt(result.videoDetails.lengthSeconds)
|
||||||
this.videoSourceList = result.player_response.streamingData.formats
|
this.videoSourceList = result.player_response.streamingData.formats
|
||||||
this.dashSrc = await this.createLocalDashManifest(result.player_response.streamingData.adaptiveFormats)
|
|
||||||
|
|
||||||
this.audioSourceList = result.player_response.streamingData.adaptiveFormats.filter((format) => {
|
if (typeof result.player_response.streamingData.adaptiveFormats !== 'undefined') {
|
||||||
return format.mimeType.includes('audio')
|
this.dashSrc = await this.createLocalDashManifest(result.player_response.streamingData.adaptiveFormats)
|
||||||
}).map((format) => {
|
|
||||||
return {
|
this.audioSourceList = result.player_response.streamingData.adaptiveFormats.filter((format) => {
|
||||||
url: format.url,
|
return format.mimeType.includes('audio')
|
||||||
type: format.mimeType,
|
}).map((format) => {
|
||||||
label: 'Audio',
|
return {
|
||||||
qualityLabel: format.bitrate
|
url: format.url,
|
||||||
|
type: format.mimeType,
|
||||||
|
label: 'Audio',
|
||||||
|
qualityLabel: format.bitrate
|
||||||
|
}
|
||||||
|
}).sort((a, b) => {
|
||||||
|
return a.qualityLabel - b.qualityLabel
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.activeFormat === 'audio') {
|
||||||
|
this.activeSourceList = this.audioSourceList
|
||||||
|
} else {
|
||||||
|
this.activeSourceList = this.videoSourceList
|
||||||
}
|
}
|
||||||
}).sort((a, b) => {
|
|
||||||
return a.qualityLabel - b.qualityLabel
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.activeFormat === 'audio') {
|
|
||||||
this.activeSourceList = this.audioSourceList
|
|
||||||
} else {
|
} else {
|
||||||
this.activeSourceList = this.videoSourceList
|
this.activeSourceList = this.videoSourceList
|
||||||
|
this.audioSourceList = null
|
||||||
|
this.dashSrc = null
|
||||||
|
this.enableLegacyFormat()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof result.player_response.storyboards !== 'undefined') {
|
if (typeof result.player_response.storyboards !== 'undefined') {
|
||||||
|
@ -528,6 +542,13 @@ export default Vue.extend({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.dashSrc === null) {
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Change Format.Dash formats are not available for this video')
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.activeFormat = 'dash'
|
this.activeFormat = 'dash'
|
||||||
this.hidePlayer = true
|
this.hidePlayer = true
|
||||||
|
|
||||||
|
@ -555,6 +576,13 @@ export default Vue.extend({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.audioSourceList === null) {
|
||||||
|
this.showToast({
|
||||||
|
message: this.$t('Change Format.Audio formats are not available for this video')
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.activeFormat = 'audio'
|
this.activeFormat = 'audio'
|
||||||
this.activeSourceList = this.audioSourceList
|
this.activeSourceList = this.audioSourceList
|
||||||
this.hidePlayer = true
|
this.hidePlayer = true
|
||||||
|
|
|
@ -35,6 +35,30 @@
|
||||||
grid-column: 1
|
grid-column: 1
|
||||||
max-width: calc(80vh * 1.78)
|
max-width: calc(80vh * 1.78)
|
||||||
margin: 0 auto
|
margin: 0 auto
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.upcomingThumbnail
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.premiereDate
|
||||||
|
color: #FFFFFF
|
||||||
|
background-color: rgba(0, 0, 0, 0.7)
|
||||||
|
width: 400px
|
||||||
|
height: 60px
|
||||||
|
border-radius: 5%
|
||||||
|
position: absolute
|
||||||
|
bottom: 5px
|
||||||
|
|
||||||
|
.premiereIcon
|
||||||
|
float: left
|
||||||
|
font-size: 25px
|
||||||
|
margin-top: 12px
|
||||||
|
margin-left: 8px
|
||||||
|
padding: 5px
|
||||||
|
|
||||||
|
.premiereText
|
||||||
|
margin-left: 50px
|
||||||
|
margin-top: 10px
|
||||||
|
|
||||||
.watchVideo
|
.watchVideo
|
||||||
margin: 0px 8px 16px
|
margin: 0px 8px 16px
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<div class="videoArea">
|
<div class="videoArea">
|
||||||
<div class="videoAreaMargin">
|
<div class="videoAreaMargin">
|
||||||
<ft-video-player
|
<ft-video-player
|
||||||
v-if="!isLoading && !hidePlayer"
|
v-if="!isLoading && !hidePlayer && !isUpcoming"
|
||||||
ref="videoPlayer"
|
ref="videoPlayer"
|
||||||
:dash-src="dashSrc"
|
:dash-src="dashSrc"
|
||||||
:source-list="activeSourceList"
|
:source-list="activeSourceList"
|
||||||
|
@ -27,6 +27,30 @@
|
||||||
@ended="handleVideoEnded"
|
@ended="handleVideoEnded"
|
||||||
@error="handleVideoError"
|
@error="handleVideoError"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-if="!isLoading && isUpcoming"
|
||||||
|
class="videoPlayer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="thumbnail"
|
||||||
|
class="upcomingThumbnail"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class='premiereDate'
|
||||||
|
>
|
||||||
|
<font-awesome-icon
|
||||||
|
icon="satellite-dish"
|
||||||
|
class="premiereIcon"
|
||||||
|
/>
|
||||||
|
<p
|
||||||
|
class="premiereText"
|
||||||
|
>
|
||||||
|
Premieres on:
|
||||||
|
<br />
|
||||||
|
{{ upcomingTimestamp }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="infoArea">
|
<div class="infoArea">
|
||||||
|
@ -43,6 +67,7 @@
|
||||||
:dislike-count="videoDislikeCount"
|
:dislike-count="videoDislikeCount"
|
||||||
:view-count="videoViewCount"
|
:view-count="videoViewCount"
|
||||||
:get-timestamp="getTimestamp"
|
:get-timestamp="getTimestamp"
|
||||||
|
:is-upcoming="isUpcoming"
|
||||||
class="watchVideo"
|
class="watchVideo"
|
||||||
:class="{ theatreWatchVideo: useTheatreMode }"
|
:class="{ theatreWatchVideo: useTheatreMode }"
|
||||||
@theatreMode="toggleTheatreMode"
|
@theatreMode="toggleTheatreMode"
|
||||||
|
|
|
@ -306,6 +306,16 @@ Profile:
|
||||||
Your default profile has been changed to your primary profile: Your default profile
|
Your default profile has been changed to your primary profile: Your default profile
|
||||||
has been changed to your primary profile
|
has been changed to your primary profile
|
||||||
$ is now the active profile: $ is now the active profile
|
$ is now the active profile: $ is now the active profile
|
||||||
|
Subscription List: Subscription List
|
||||||
|
Other Channels: Other Channels
|
||||||
|
$ selected: $ selected
|
||||||
|
Select All: Select All
|
||||||
|
Select None: Select None
|
||||||
|
Delete Selected: Delete Selected
|
||||||
|
Add Selected To Profile: Add Selected To Profile
|
||||||
|
No channel(s) have been selected: No channel(s) have been selected
|
||||||
|
This is your primary profile. Are you sure you want to delete the selected channels? The same channels will be deleted in any profile they are found in.: This is your primary profile. Are you sure you want to delete the selected channels? The same channels will be deleted in any profile they are found in.
|
||||||
|
Are you sure you want to delete the selected channels? This will not delete the channel from any other profile.: Are you sure you want to delete the selected channels? This will not delete the channel from any other profile.
|
||||||
#On Channel Page
|
#On Channel Page
|
||||||
Channel:
|
Channel:
|
||||||
Subscriber: Subscriber
|
Subscriber: Subscriber
|
||||||
|
@ -435,6 +445,8 @@ Change Format:
|
||||||
Use Dash Formats: Use Dash Formats
|
Use Dash Formats: Use Dash Formats
|
||||||
Use Legacy Formats: Use Legacy Formats
|
Use Legacy Formats: Use Legacy Formats
|
||||||
Use Audio Formats: Use Audio Formats
|
Use Audio Formats: Use Audio Formats
|
||||||
|
Dash formats are not available for this video: Dash formats are not available for this video
|
||||||
|
Audio formats are not available for this video: Audio formats are not available for this video
|
||||||
Share:
|
Share:
|
||||||
Share Video: Share Video
|
Share Video: Share Video
|
||||||
Include Timestamp: Include Timestamp
|
Include Timestamp: Include Timestamp
|
||||||
|
|
Loading…
Reference in New Issue