diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 082590ac..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -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
diff --git a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css
index 13f1c1a1..fc4e51d7 100644
--- a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css
+++ b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css
@@ -1,4 +1,5 @@
.bubblePadding {
+ position: relative;
width: 100px;
height: 115px;
padding: 10px;
@@ -25,6 +26,20 @@
-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 {
font-size: 13px;
height: 60px;
diff --git a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.js b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.js
index c5d16625..d9752676 100644
--- a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.js
+++ b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.js
@@ -7,18 +7,26 @@ export default Vue.extend({
type: String,
required: true
},
- channelId: {
- type: String,
- required: true
- },
channelThumbnail: {
type: String,
required: true
+ },
+ showSelected: {
+ type: Boolean,
+ default: false
+ }
+ },
+ data: function () {
+ return {
+ selected: false
}
},
methods: {
- goToChannel: function () {
- console.log('Go to channel')
+ handleClick: function () {
+ if (this.showSelected) {
+ this.selected = !this.selected
+ }
+ this.$emit('click')
}
}
})
diff --git a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.vue b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.vue
index d845037f..ed60bc77 100644
--- a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.vue
+++ b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.vue
@@ -1,12 +1,21 @@
+
+
+
{{ channelName }}
diff --git a/src/renderer/components/ft-input/ft-input.css b/src/renderer/components/ft-input/ft-input.css
index b8ae1b1e..a87b3605 100644
--- a/src/renderer/components/ft-input/ft-input.css
+++ b/src/renderer/components/ft-input/ft-input.css
@@ -24,7 +24,7 @@
}
.forceTextColor .ft-input {
- color: var(--text-with-main-color);
+ color: #EEEEEE;
background-color: var(--primary-input-color);
}
@@ -36,7 +36,7 @@
}
.forceTextColor ::-webkit-input-placeholder {
- color: var(--text-with-main-color);
+ color: #EEEEEE;
}
.inputAction {
@@ -58,7 +58,7 @@
}
.forceTextColor .inputAction {
- color: var(--text-with-main-color);
+ color: #EEEEEE;
}
.inputAction:hover {
diff --git a/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.css b/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.css
new file mode 100644
index 00000000..c08a84b0
--- /dev/null
+++ b/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.css
@@ -0,0 +1,5 @@
+.card {
+ width: 85%;
+ margin: 0 auto;
+ margin-bottom: 30px;
+}
diff --git a/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.js b/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.js
new file mode 100644
index 00000000..fdd750c7
--- /dev/null
+++ b/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.js
@@ -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'
+ ])
+ }
+})
diff --git a/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.vue b/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.vue
new file mode 100644
index 00000000..4e59fc62
--- /dev/null
+++ b/src/renderer/components/ft-profile-all-channels-list/ft-profile-all-channels-list.vue
@@ -0,0 +1,42 @@
+
+
+
+
+ {{ $t("Profile.Other Channels") }}
+
+
+ {{ selectedText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.css b/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.css
new file mode 100644
index 00000000..ba8f5fc0
--- /dev/null
+++ b/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.css
@@ -0,0 +1,5 @@
+.card {
+ width: 85%;
+ margin: 0 auto;
+ margin-bottom: 15px;
+}
diff --git a/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.js b/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.js
new file mode 100644
index 00000000..7b2e2a54
--- /dev/null
+++ b/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.js
@@ -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'
+ ])
+ }
+})
diff --git a/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.vue b/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.vue
new file mode 100644
index 00000000..d19827b5
--- /dev/null
+++ b/src/renderer/components/ft-profile-channel-list/ft-profile-channel-list.vue
@@ -0,0 +1,49 @@
+
+
+
+
+ {{ $t("Profile.Subscription List") }}
+
+
+ {{ selectedText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/components/ft-profile-edit/ft-profile-edit.css b/src/renderer/components/ft-profile-edit/ft-profile-edit.css
new file mode 100644
index 00000000..c7c669cc
--- /dev/null
+++ b/src/renderer/components/ft-profile-edit/ft-profile-edit.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%;
+ }
+}
diff --git a/src/renderer/components/ft-profile-edit/ft-profile-edit.js b/src/renderer/components/ft-profile-edit/ft-profile-edit.js
new file mode 100644
index 00000000..814f4c8c
--- /dev/null
+++ b/src/renderer/components/ft-profile-edit/ft-profile-edit.js
@@ -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'
+ ])
+ }
+})
diff --git a/src/renderer/components/ft-profile-edit/ft-profile-edit.vue b/src/renderer/components/ft-profile-edit/ft-profile-edit.vue
new file mode 100644
index 00000000..1d893f4a
--- /dev/null
+++ b/src/renderer/components/ft-profile-edit/ft-profile-edit.vue
@@ -0,0 +1,99 @@
+
+
+
+ {{ $t("Profile.Edit Profile") }}
+
+ profileName = e"
+ />
+
+ {{ $t("Profile.Color Picker") }}
+
+
+
+
+
+ {{ $t("Profile.Custom Color") }}
+
+
+
+
+
+
+ {{ $t("Profile.Profile Preview") }}
+
+
+
+ {{ profileInitial }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/components/watch-video-info/watch-video-info.js b/src/renderer/components/watch-video-info/watch-video-info.js
index 68c19ad4..09bd8b61 100644
--- a/src/renderer/components/watch-video-info/watch-video-info.js
+++ b/src/renderer/components/watch-video-info/watch-video-info.js
@@ -62,6 +62,10 @@ export default Vue.extend({
getTimestamp: {
type: Function,
required: true
+ },
+ isUpcoming: {
+ type: Boolean,
+ required: true
}
},
data: function () {
diff --git a/src/renderer/components/watch-video-info/watch-video-info.vue b/src/renderer/components/watch-video-info/watch-video-info.vue
index bdc08f0a..840d898c 100644
--- a/src/renderer/components/watch-video-info/watch-video-info.vue
+++ b/src/renderer/components/watch-video-info/watch-video-info.vue
@@ -67,6 +67,7 @@
@click="$emit('theatreMode')"
/>
{
diff --git a/src/renderer/views/Channel/Channel.vue b/src/renderer/views/Channel/Channel.vue
index b59f0249..e57f66ff 100644
--- a/src/renderer/views/Channel/Channel.vue
+++ b/src/renderer/views/Channel/Channel.vue
@@ -123,6 +123,7 @@
:channel-name="channel.author"
:channel-id="channel.authorId"
:channel-thumbnail="channel.authorThumbnails[channel.authorThumbnails.length - 1].url"
+ @click="goToChannel(channel.authorId)"
/>
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.css b/src/renderer/views/ProfileEdit/ProfileEdit.css
index 07f8f622..e69de29b 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.css
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.css
@@ -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%;
- }
-}
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.js b/src/renderer/views/ProfileEdit/ProfileEdit.js
index aece2bd6..50952dbc 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.js
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.js
@@ -1,62 +1,50 @@
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'
+import FtProfileEdit from '../../components/ft-profile-edit/ft-profile-edit.vue'
+import FtProfileChannelList from '../../components/ft-profile-channel-list/ft-profile-channel-list.vue'
+import FtProfileAllChannelsList from '../../components/ft-profile-all-channels-list/ft-profile-all-channels-list.vue'
export default Vue.extend({
name: 'ProfileEdit',
components: {
'ft-loader': FtLoader,
- 'ft-card': FtCard,
- 'ft-prompt': FtPrompt,
- 'ft-flex-box': FtFlexBox,
- 'ft-input': FtInput,
- 'ft-button': FtButton
+ 'ft-profile-edit': FtProfileEdit,
+ 'ft-profile-channel-list': FtProfileChannelList,
+ 'ft-profile-all-channels-list': FtProfileAllChannelsList
},
data: function () {
return {
isLoading: false,
- showDeletePrompt: false,
- deletePromptLabel: '',
isNew: false,
profileId: '',
- profileName: '',
- profileBgColor: '',
- profileTextColor: '',
- profileSubscriptions: [],
- deletePromptValues: [
- 'yes',
- 'no'
- ]
+ profile: {}
}
},
computed: {
- colorValues: function () {
- return this.$store.getters.getColorValues
+ profileList: function () {
+ return this.$store.getters.getProfileList
},
- 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')
- ]
+ isMainProfile: function () {
+ return this.profileId === 'allChannels'
}
},
watch: {
- profileBgColor: async function (val) {
- this.profileTextColor = await this.calculateColorLuminance(val)
+ profileList: {
+ 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 () {
@@ -67,13 +55,18 @@ export default Vue.extend({
if (profileType === 'newProfile') {
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
} else {
this.isNew = false
this.profileId = this.$route.params.id
- console.log(this.profileId)
- console.log(this.$route.name)
this.grabProfileInfo(this.profileId).then((profile) => {
if (profile === null) {
@@ -84,100 +77,17 @@ export default Vue.extend({
path: '/settings/profile/'
})
}
- this.profileName = profile.name
- this.profileBgColor = profile.bgColor
- this.profileTextColor = profile.textColor
- this.profileSubscriptions = profile.subscriptions
+ this.profile = profile
this.isLoading = false
})
}
},
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',
'grabProfileInfo',
- 'updateProfile',
- 'removeProfile',
- 'updateDefaultProfile',
- 'updateActiveProfile',
- 'calculateColorLuminance',
- 'getRandomColor'
+ 'getRandomColor',
+ 'calculateColorLuminance'
])
}
})
diff --git a/src/renderer/views/ProfileEdit/ProfileEdit.vue b/src/renderer/views/ProfileEdit/ProfileEdit.vue
index 9c0954d2..39262d39 100644
--- a/src/renderer/views/ProfileEdit/ProfileEdit.vue
+++ b/src/renderer/views/ProfileEdit/ProfileEdit.vue
@@ -7,99 +7,20 @@
-
- {{ $t("Profile.Edit Profile") }}
-
- profileName = e"
- />
-
- {{ $t("Profile.Color Picker") }}
-
-
-
-
-
- {{ $t("Profile.Custom Color") }}
-
-
-
-
-
-
- {{ $t("Profile.Profile Preview") }}
-
-
-
- {{ profileInitial }}
-
-
-
-
-
-
-
-
-
-
+
+
+
-
diff --git a/src/renderer/views/Watch/Watch.js b/src/renderer/views/Watch/Watch.js
index 86a61ded..062a87ef 100644
--- a/src/renderer/views/Watch/Watch.js
+++ b/src/renderer/views/Watch/Watch.js
@@ -40,6 +40,8 @@ export default Vue.extend({
showYouTubeNoCookieEmbed: false,
hidePlayer: false,
isLive: false,
+ isUpcoming: false,
+ upcomingTimestamp: null,
activeFormat: 'legacy',
videoId: '',
videoTitle: '',
@@ -215,8 +217,9 @@ export default Vue.extend({
this.videoLikeCount = result.videoDetails.likes
this.videoDislikeCount = result.videoDetails.dislikes
this.isLive = result.player_response.videoDetails.isLiveContent
+ this.isUpcoming = result.player_response.videoDetails.isUpcoming
- if (!this.isLive) {
+ if (!this.isLive && !this.isUpcoming) {
const captionTracks =
result.player_response.captions &&
result.player_response.captions.playerCaptionsTracklistRenderer
@@ -243,7 +246,7 @@ export default Vue.extend({
}
}
- if (this.isLive) {
+ if (this.isLive && !this.isUpcoming) {
this.enableLegacyFormat()
this.videoSourceList = result.formats.filter((format) => {
@@ -279,28 +282,39 @@ export default Vue.extend({
} else {
this.activeSourceList = this.videoSourceList
}
+ } else if (this.isUpcoming) {
+ const upcomingTimestamp = new Date(result.videoDetails.liveBroadcastDetails.startTimestamp)
+ this.upcomingTimestamp = upcomingTimestamp.toLocaleString()
} else {
this.videoLengthSeconds = parseInt(result.videoDetails.lengthSeconds)
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) => {
- return format.mimeType.includes('audio')
- }).map((format) => {
- return {
- url: format.url,
- type: format.mimeType,
- label: 'Audio',
- qualityLabel: format.bitrate
+ if (typeof result.player_response.streamingData.adaptiveFormats !== 'undefined') {
+ this.dashSrc = await this.createLocalDashManifest(result.player_response.streamingData.adaptiveFormats)
+
+ this.audioSourceList = result.player_response.streamingData.adaptiveFormats.filter((format) => {
+ return format.mimeType.includes('audio')
+ }).map((format) => {
+ return {
+ 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 {
this.activeSourceList = this.videoSourceList
+ this.audioSourceList = null
+ this.dashSrc = null
+ this.enableLegacyFormat()
}
if (typeof result.player_response.storyboards !== 'undefined') {
@@ -528,6 +542,13 @@ export default Vue.extend({
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.hidePlayer = true
@@ -555,6 +576,13 @@ export default Vue.extend({
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.activeSourceList = this.audioSourceList
this.hidePlayer = true
diff --git a/src/renderer/views/Watch/Watch.sass b/src/renderer/views/Watch/Watch.sass
index 1b05b898..b444a8f5 100644
--- a/src/renderer/views/Watch/Watch.sass
+++ b/src/renderer/views/Watch/Watch.sass
@@ -35,6 +35,30 @@
grid-column: 1
max-width: calc(80vh * 1.78)
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
margin: 0px 8px 16px
diff --git a/src/renderer/views/Watch/Watch.vue b/src/renderer/views/Watch/Watch.vue
index b40a4a63..8244eb2d 100644
--- a/src/renderer/views/Watch/Watch.vue
+++ b/src/renderer/views/Watch/Watch.vue
@@ -13,7 +13,7 @@
+
+
+
+
+
+ Premieres on:
+
+ {{ upcomingTimestamp }}
+
+
+
@@ -43,6 +67,7 @@
:dislike-count="videoDislikeCount"
:view-count="videoViewCount"
:get-timestamp="getTimestamp"
+ :is-upcoming="isUpcoming"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
@theatreMode="toggleTheatreMode"
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index f0de708b..62c8be96 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -306,6 +306,16 @@ Profile:
Your default profile has been changed to your primary profile: Your default profile
has been changed to your primary profile
$ is now the active profile: $ is now the active profile
+ 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
Channel:
Subscriber: Subscriber
@@ -435,6 +445,8 @@ Change Format:
Use Dash Formats: Use Dash Formats
Use Legacy Formats: Use Legacy 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 Video: Share Video
Include Timestamp: Include Timestamp