This commit is contained in:
ChunkyProgrammer 2024-04-21 11:07:34 +00:00 committed by GitHub
commit a6b9a0851b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 600 additions and 503 deletions

View File

@ -49,7 +49,7 @@ module.exports = {
extends: [
'prettier',
'eslint:recommended',
'plugin:vue/recommended',
'plugin:vue/vue3-recommended',
'standard',
'plugin:jsonc/recommended-with-json',
'plugin:vuejs-accessibility/recommended',

View File

@ -2,7 +2,7 @@ const path = require('path')
const { readFileSync, readdirSync } = require('fs')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const { VueLoaderPlugin } = require('vue-loader')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const ProcessLocalesPlugin = require('./ProcessLocalesPlugin')
@ -50,6 +50,7 @@ const config = {
options: {
compilerOptions: {
whitespace: 'condense',
isCustomElement: (tag) => ['swiper-container', 'swiper-slide'].includes(tag),
}
}
},
@ -117,6 +118,9 @@ const config = {
plugins: [
processLocalesPlugin,
new webpack.DefinePlugin({
'__VUE_PROD_DEVTOOLS__': isDevMode,
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': false,
'process.env.IS_ELECTRON': true,
'process.env.IS_ELECTRON_MAIN': false,
'process.env.SUPPORTS_LOCAL_API': true,
@ -149,7 +153,7 @@ const config = {
],
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
vue$: 'vue/dist/vue.runtime.esm-bundler.js',
'DB_HANDLERS_ELECTRON_RENDERER_OR_WEB$': path.resolve(__dirname, '../src/datastores/handlers/electron.js'),

View File

@ -2,7 +2,7 @@ const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const { VueLoaderPlugin } = require('vue-loader')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const JsonMinimizerPlugin = require('json-minimizer-webpack-plugin')
@ -40,6 +40,7 @@ const config = {
options: {
compilerOptions: {
whitespace: 'condense',
isCustomElement: (tag) => ['swiper-container', 'swiper-slide'].includes(tag),
}
}
},
@ -113,6 +114,9 @@ const config = {
},
plugins: [
new webpack.DefinePlugin({
'__VUE_PROD_DEVTOOLS__': isDevMode,
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': false,
'process.env.IS_ELECTRON': false,
'process.env.IS_ELECTRON_MAIN': false,
'process.env.SUPPORTS_LOCAL_API': false,
@ -158,9 +162,7 @@ const config = {
],
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
'DB_HANDLERS_ELECTRON_RENDERER_OR_WEB$': path.resolve(__dirname, '../src/datastores/handlers/web.js'),
DB_HANDLERS_ELECTRON_RENDERER_OR_WEB$: path.resolve(__dirname, '../src/datastores/handlers/web.js'),
// video.js's mpd-parser uses @xmldom/xmldom so that it can support both node and web browsers
// As FreeTube only runs in electron and web browsers, we can use the native DOMParser class, instead of the "polyfill"

View File

@ -1,6 +1,6 @@
{
"vueCompilerOptions": {
"target": 2.7
"target": 3.3
},
"compilerOptions": {
"strictNullChecks": true,

View File

@ -56,7 +56,7 @@
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/vue-fontawesome": "^2.0.10",
"@fortawesome/vue-fontawesome": "^3.0.6",
"@seald-io/nedb": "^4.0.4",
"@silvermine/videojs-quality-selector": "^1.3.1",
"autolinker": "^4.0.0",
@ -72,11 +72,11 @@
"videojs-mobile-ui": "^0.8.0",
"videojs-overlay": "^3.1.0",
"videojs-vtt-thumbnails-freetube": "0.0.15",
"vue": "^2.7.16",
"vue-i18n": "^8.28.2",
"vue-observe-visibility": "^1.0.0",
"vue-router": "^3.6.5",
"vuex": "^3.6.2",
"vue": "^3.4.23",
"vue-i18n": "^9.13.0",
"vue-observe-visibility": "^2.0.0-alpha.1",
"vue-router": "^4.3.2",
"vuex": "^4.1.0",
"youtubei.js": "^9.3.0"
},
"devDependencies": {
@ -124,7 +124,7 @@
"tree-kill": "1.2.2",
"vue-devtools": "^5.1.4",
"vue-eslint-parser": "^9.4.2",
"vue-loader": "^15.10.0",
"vue-loader": "^17.4.2",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",

View File

@ -46,7 +46,7 @@
transition: opacity .15s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
.fade-enter-from, .fade-leave-to {
opacity: 0;
}

View File

@ -1,6 +1,5 @@
import Vue, { defineComponent } from 'vue'
import { defineComponent } from 'vue'
import { mapActions, mapMutations } from 'vuex'
import { ObserveVisibility } from 'vue-observe-visibility'
import FtFlexBox from './components/ft-flex-box/ft-flex-box.vue'
import TopNav from './components/top-nav/top-nav.vue'
import SideNav from './components/side-nav/side-nav.vue'
@ -19,8 +18,6 @@ import { translateWindowTitle } from './helpers/strings'
let ipcRenderer = null
Vue.directive('observe-visibility', ObserveVisibility)
export default defineComponent({
name: 'App',
components: {
@ -192,11 +189,9 @@ export default defineComponent({
this.$refs.topNav?.navigateHistory()
})
this.$router.onReady(() => {
if (this.$router.currentRoute.path === '/') {
this.$router.replace({ path: this.landingPage })
}
})
if (this.$router.currentRoute.path === '/') {
this.$router.replace({ path: this.landingPage })
}
})
},
methods: {

View File

@ -33,18 +33,20 @@
@click="handleNewBlogBannerClick"
/>
</div>
<transition
v-if="dataReady"
mode="out-in"
name="fade"
<RouterView
ref="router"
v-slot="{ Component }"
class="routerView"
>
<!-- <keep-alive> -->
<RouterView
ref="router"
class="routerView"
/>
<!-- </keep-alive> -->
</transition>
<transition
mode="in-out"
name="fade"
>
<!-- <keep-alive> -->
<component :is="Component" />
<!-- </keep-alive> -->
</transition>
</RouterView>
</ft-flex-box>
<ft-prompt

View File

@ -79,7 +79,7 @@ export default defineComponent({
window.addEventListener('resize', this.handleResize)
}
},
beforeDestroy: function () {
beforeUnmount: function () {
if (this.dropdownModalOnMobile) {
window.removeEventListener('resize', this.handleResize)
}

View File

@ -1,19 +1,7 @@
<template>
<div class="ftIconButton">
<font-awesome-icon
<span
ref="iconButton"
class="iconButton"
:title="title"
:icon="icon"
:class="{
[theme]: true,
shadow: useShadow,
disabled
}"
:style="{
padding: padding + 'px',
fontSize: size + 'px'
}"
tabindex="0"
role="button"
:aria-expanded="dropdownShown"
@ -21,7 +9,22 @@
@mousedown="handleIconMouseDown"
@keydown.enter.prevent="handleIconClick"
@keydown.space.prevent="handleIconClick"
/>
>
<font-awesome-icon
class="iconButton"
:title="title"
:icon="icon"
:class="{
[theme]: true,
shadow: useShadow,
disabled
}"
:style="{
padding: padding + 'px',
fontSize: size + 'px'
}"
/>
</span>
<template
v-if="dropdownShown"
>

View File

@ -42,15 +42,18 @@
<span>{{ (tag.preferredName) ? tag.preferredName : tag.name }}</span>
</template>
<span v-else>{{ tag }}</span>
<font-awesome-icon
<span
v-if="!disabled"
:icon="['fas', 'fa-times']"
class="removeTagButton"
tabindex="0"
role="button"
@click="removeTag(tag)"
@keydown.enter.prevent="removeTag(tag)"
/>
>
<font-awesome-icon
:icon="['fas', 'fa-times']"
class="removeTagButton"
/>
</span>
</li>
</ul>
</div>

View File

@ -121,10 +121,13 @@ export default defineComponent({
},
},
watch: {
dataList(val, oldVal) {
if (val !== oldVal) {
this.updateVisibleDataList()
}
dataList: {
handler(val, oldVal) {
if (val !== oldVal) {
this.updateVisibleDataList()
}
},
deep: true
},
inputData(val, oldVal) {
if (val !== oldVal) {
@ -138,7 +141,7 @@ export default defineComponent({
}
},
mounted: function () {
this.id = this._uid
this.id = this._.uid
this.inputData = this.value
this.updateVisibleDataList()
},

View File

@ -24,20 +24,24 @@
:tooltip="tooltip"
/>
</label>
<font-awesome-icon
<span
v-if="showClearTextButton"
:icon="['fas', 'times-circle']"
class="clearInputTextButton"
:class="{
visible: inputDataPresent
}"
tabindex="0"
role="button"
:title="$t('Search Bar.Clear Input')"
@click="handleClearTextClick"
@keydown.space.prevent="handleClearTextClick"
@keydown.enter.prevent="handleClearTextClick"
/>
>
<font-awesome-icon
:icon="['fas', 'times-circle']"
class="clearInputTextButton"
:class="{
visible: inputDataPresent
}"
tabindex="0"
:title="$t('Search Bar.Clear Input')"
/>
</span>
<span class="inputWrapper">
<input
:id="id"
@ -55,16 +59,21 @@
@blur="handleInputBlur"
@keydown="handleKeyDown"
>
<font-awesome-icon
<span
v-if="showActionButton"
:icon="actionButtonIconName"
class="inputAction"
:class="{
enabled: inputDataPresent,
withLabel: showLabel
}"
tabindex="0"
@click="handleClick"
/>
@keydown.enter.prevent="handleCLick"
>
<font-awesome-icon
:icon="actionButtonIconName"
class="inputAction"
:class="{
enabled: inputDataPresent,
withLabel: showLabel
}"
/>
</span>
</span>
<div class="options">
<ul

View File

@ -1,7 +1,7 @@
<template>
<div
v-if="showResult"
v-observe-visibility="firstScreen ? false : {
v-observe-visibility="firstScreen ? null : {
callback: onVisibilityChanged,
once: true,
}"

View File

@ -1,7 +1,7 @@
<template>
<div
v-show="show"
v-observe-visibility="initialVisibleState || visible ? false : {
v-observe-visibility="initialVisibleState || visible ? null : {
callback: onVisibilityChanged,
once: true,
}"

View File

@ -85,7 +85,7 @@ export default defineComponent({
default: false,
},
},
emits: ['move-video-down', 'move-video-up', 'pause-player', 'remove-from-playlist'],
emits: ['pause-player', 'move-video-up', 'move-video-down', 'remove-from-playlist'],
data: function () {
return {
id: '',

View File

@ -18,14 +18,18 @@
</p>
</slot>
</div>
<font-awesome-icon
class="bannerIcon"
:icon="['fas', 'times']"
<span
role="button"
tabindex="0"
:title="$t('Close Banner')"
@click.stop="handleClose"
@keydown.enter.space.stop.prevent="handleClose"
/>
>
<font-awesome-icon
class="bannerIcon"
:icon="['fas', 'times']"
:title="$t('Close Banner')"
/>
</span>
</div>
</template>

View File

@ -203,7 +203,7 @@ export default defineComponent({
// User might want to search first if they have many playlists
this.$refs.searchBar.focus()
},
beforeDestroy() {
beforeUnmount() {
this.lastActiveElement?.focus()
},
methods: {

View File

@ -26,8 +26,8 @@
ref="profileList"
class="profileList"
tabindex="-1"
@focusout.native="handleProfileListFocusOut"
@keydown.native.esc.stop="handleProfileListEscape"
@focusout="handleProfileListFocusOut"
@keydown.esc.stop="handleProfileListEscape"
>
<h3
id="profileListTitle"

View File

@ -50,7 +50,7 @@ export default defineComponent({
return sanitizeForHtmlId(this.label)
}
},
beforeDestroy: function () {
beforeUnmount: function () {
document.removeEventListener('keydown', this.closeEventFunction, true)
this.lastActiveElement?.focus()
},

View File

@ -34,7 +34,7 @@ export default defineComponent({
}
},
mounted: function () {
this.id = this._uid
this.id = this._.uid
this.selectedValue = this.values[0]
},
methods: {

View File

@ -4,13 +4,13 @@
{{ title }}
</h3>
<!-- eslint-disable vue/no-template-key -->
<template
<div
v-for="(label, index) in labels"
:key="index"
class="radioButtonContainer"
>
<input
:id="values[index] + id"
:key="index"
v-model="selectedValue"
:name="inputName"
:value="values[index]"
@ -21,12 +21,11 @@
@change="change(values[index])"
>
<label
:key="label"
:for="values[index] + id"
>
{{ label }}
</label>
</template>
</div>
</div>
</template>

View File

@ -8,7 +8,7 @@ export default defineComponent({
'ft-flex-box': FtFlexBox,
'ft-radio-button': FtRadioButton
},
emits: ['filterValueUpdated'],
emits: ['filter-value-updated'],
data: function () {
return {
sortByValues: [
@ -100,7 +100,7 @@ export default defineComponent({
updateSortBy: function (value) {
this.$store.commit('setSearchSortBy', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
this.$emit('filter-value-updated', this.filterValueChanged)
},
updateTime: function (value) {
@ -110,7 +110,7 @@ export default defineComponent({
this.$store.commit('setSearchType', 'all')
}
this.$store.commit('setSearchTime', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
this.$emit('filter-value-updated', this.filterValueChanged)
},
updateType: function (value) {
@ -126,7 +126,7 @@ export default defineComponent({
this.$store.commit('setSearchSortBy', this.sortByValues[0])
}
this.$store.commit('setSearchType', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
this.$emit('filter-value-updated', this.filterValueChanged)
},
updateDuration: function (value) {
@ -136,7 +136,7 @@ export default defineComponent({
this.updateType('all')
}
this.$store.commit('setSearchDuration', value)
this.$emit('filterValueUpdated', this.filterValueChanged)
this.$emit('filter-value-updated', this.filterValueChanged)
}
}
})

View File

@ -53,15 +53,18 @@ export default defineComponent({
// e.g. when you change the display language, the locations list gets updated
// as the locations list is sorted alphabetically for the language, the ordering can be different
// so we need to ensure that the correct location is selected after a language change
selectValues: function () {
nextTick(() => {
this.$refs.select.value = this.value
})
selectValues: {
handler() {
nextTick(() => {
this.$refs.select.value = this.value
})
},
deep: true
}
},
methods: {
change: function(value) {
this.$emit('change', value)
}
}
},
})

View File

@ -54,7 +54,7 @@ export default defineComponent({
}
},
mounted: function () {
this.id = this._uid
this.id = this._.uid
this.currentValue = this.defaultValue
},
methods: {

View File

@ -13,7 +13,7 @@ export default defineComponent({
mounted: function () {
FtToastEvents.addEventListener('toast-open', this.open)
},
beforeDestroy: function () {
beforeUnmount: function () {
FtToastEvents.removeEventListener('toast-open', this.open)
},
methods: {

View File

@ -49,7 +49,7 @@ export default defineComponent({
}
},
mounted: function () {
this.id = this._uid
this.id = this._.uid
this.currentValue = this.defaultValue
},
methods: {

View File

@ -408,7 +408,7 @@ export default defineComponent({
window.addEventListener('beforeunload', this.stopPowerSaveBlocker)
},
beforeDestroy: function () {
beforeUnmount: function () {
document.removeEventListener('keydown', this.keyboardShortcutHandler)
if (this.player !== null) {
this.exitFullWindow()

View File

@ -206,7 +206,7 @@ export default defineComponent({
this.setCurrentInvidiousInstanceBounce =
debounce(this.setCurrentInvidiousInstance, 500)
},
beforeDestroy: function () {
beforeUnmount: function () {
if (this.currentInvidiousInstance === '') {
// FIXME: If we call an action from here, there's no guarantee it will finish
// before the component is destroyed, which could bring up some problems

View File

@ -263,7 +263,7 @@ export default defineComponent({
mounted: function () {
document.addEventListener('keydown', this.keyboardShortcutHandler)
},
beforeDestroy: function () {
beforeUnmount: function () {
document.removeEventListener('keydown', this.keyboardShortcutHandler)
},
methods: {

View File

@ -83,7 +83,7 @@ export default defineComponent({
created: function () {
this.debounceEnableProxy = debounce(this.enableProxy, 200)
},
beforeDestroy: function () {
beforeUnmount: function () {
if (this.proxyHostname === '') {
this.updateProxyHostname('127.0.0.1')
}

View File

@ -94,7 +94,7 @@ export default defineComponent({
mounted: function () {
document.addEventListener('keydown', this.keyboardShortcutHandler)
},
beforeDestroy: function () {
beforeUnmount: function () {
document.removeEventListener('keydown', this.keyboardShortcutHandler)
},
methods: {
@ -120,7 +120,7 @@ export default defineComponent({
case 'R':
case 'F5':
if (!this.isLoading && this.activeSubscriptionList.length > 0) {
this.$emit('refresh')
this.refresh()
}
break
}

View File

@ -1,5 +1,6 @@
import { defineComponent } from 'vue'
import { mapActions } from 'vuex'
import FtIconButton from '../ft-icon-button/ft-icon-button.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'
@ -14,6 +15,7 @@ import { invidiousAPICall } from '../../helpers/api/invidious'
export default defineComponent({
name: 'TopNav',
components: {
'ft-icon-button': FtIconButton,
FtInput,
FtSearchFilters,
FtProfileSelector
@ -94,6 +96,10 @@ export default defineComponent({
newWindowText: function () {
return this.$t('Open New Window')
},
usingElectron: function () {
return process.env.IS_ELECTRON
}
},
mounted: function () {

View File

@ -51,7 +51,8 @@
cursor: pointer;
font-size: 20px;
block-size: 1em;
padding: 10px;
padding-block: 0;
padding-inline: 10px;
transition: background 0.2s ease-out;
inline-size: 1em;

View File

@ -5,54 +5,71 @@
role="navigation"
>
<div class="side">
<font-awesome-icon
class="menuIcon navIcon"
:icon="['fas', 'bars']"
<div
role="button"
tabindex="0"
@click="toggleSideNav"
@click.prevent="toggleSideNav"
@keydown.enter.prevent="toggleSideNav"
/>
<font-awesome-icon
:aria-disabled="isArrowBackwardDisabled"
class="navIcon"
:class="{ arrowBackwardDisabled: isArrowBackwardDisabled}"
:icon="['fas', 'arrow-left']"
>
<font-awesome-icon
style="padding-top: 0;padding-bottom: 0;"
class="menuIcon navIcon"
:icon="['fas', 'bars']"
/>
</div>
<div
role="button"
tabindex="0"
:title="backwardText"
@click="historyBack"
:aria-disabled="isArrowBackwardDisabled || null"
@click.prevent="historyBack"
@keydown.enter.prevent="historyBack"
/>
<font-awesome-icon
:aria-disabled="isArrowForwardDisabled"
class="navIcon"
:class="{ arrowForwardDisabled: isArrowForwardDisabled}"
:icon="['fas', 'arrow-right']"
>
<font-awesome-icon
class="navIcon"
:class="{ arrowBackwardDisabled: isArrowBackwardDisabled}"
:icon="['fas', 'arrow-left']"
:title="backwardText"
/>
</div>
<div
role="button"
tabindex="0"
:title="forwardText"
@click="historyForward"
:aria-disabled="isArrowForwardDisabled || null"
@click.prevent="historyForward"
@keydown.enter.prevent="historyForward"
/>
<font-awesome-icon
>
<font-awesome-icon
class="navIcon"
:class="{ arrowForwardDisabled: isArrowForwardDisabled}"
:icon="['fas', 'arrow-right']"
:title="forwardText"
/>
</div>
<div
v-if="!hideSearchBar"
class="navSearchIcon navIcon"
:icon="['fas', 'search']"
role="button"
tabindex="0"
@click="toggleSearchContainer"
@click.prevent="toggleSearchContainer"
@keydown.enter.prevent="toggleSearchContainer"
/>
<font-awesome-icon
class="navNewWindowIcon navIcon"
:icon="['fas', 'clone']"
:title="newWindowText"
>
<font-awesome-icon
class="navSearchIcon navIcon"
:icon="['fas', 'search']"
/>
</div>
<div
v-if="usingElectron"
role="button"
tabindex="0"
@click="createNewWindow"
@click.prevent="createNewWindow"
@keydown.enter.prevent="createNewWindow"
/>
>
<font-awesome-icon
class="navNewWindowIcon navIcon"
:icon="['fas', 'clone']"
:title="newWindowText"
/>
</div>
<div
v-if="!hideHeaderLogo"
class="logo"
@ -90,21 +107,24 @@
@input="getSearchSuggestionsDebounce"
@click="goToSearch"
/>
<font-awesome-icon
class="navFilterIcon navIcon"
:class="{ filterChanged: searchFilterValueChanged }"
:icon="['fas', 'filter']"
<div
role="button"
tabindex="0"
@click="showFilters = !showFilters"
@keydown.enter.prevent="showFilters = !showFilters"
/>
>
<font-awesome-icon
class="navFilterIcon navIcon"
:class="{ filterChanged: searchFilterValueChanged }"
:icon="['fas', 'filter']"
/>
</div>
</div>
<ft-search-filters
v-if="!hideSearchBar"
v-show="showFilters"
class="searchFilters"
@filterValueUpdated="handleSearchFilterValueChanged"
@filter-value-updated="handleSearchFilterValueChanged"
/>
</div>
<ft-profile-selector class="side profiles" />

View File

@ -354,6 +354,7 @@ export default defineComponent({
this.addToQuickBookmarkPlaylist()
}
},
addToQuickBookmarkPlaylist() {
const videoData = {
videoId: this.id,
@ -375,6 +376,7 @@ export default defineComponent({
// TODO: Maybe show playlist name
showToast(this.$t('Video.Video has been saved'))
},
removeFromQuickBookmarkPlaylist() {
this.removeVideo({
_id: this.quickBookmarkPlaylist._id,

View File

@ -88,7 +88,7 @@ export default defineComponent({
return this.watchingCount !== null ? formatNumber(this.watchingCount) : '0'
}
},
beforeDestroy: function () {
beforeUnmount: function () {
this.hasEnded = true
this.liveChatInstance?.stop()
this.liveChatInstance = null

View File

@ -225,7 +225,7 @@ export default defineComponent({
navigator.mediaSession.setActionHandler('nexttrack', this.playNextVideo)
}
},
beforeDestroy: function () {
beforeUnmount: function () {
if ('mediaSession' in navigator) {
navigator.mediaSession.setActionHandler('previoustrack', null)
navigator.mediaSession.setActionHandler('nexttrack', null)

View File

@ -49,74 +49,92 @@
/>
</span>
<p>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: loopEnabled }"
:icon="['fas', 'retweet']"
:title="$t('Video.Loop Playlist')"
<span
role="button"
tabindex="0"
:aria-pressed="loopEnabled"
@click="toggleLoop"
@keydown.enter.prevent="toggleLoop"
@keydown.space.prevent="toggleLoop"
/>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: shuffleEnabled }"
:icon="['fas', 'random']"
:title="$t('Video.Shuffle Playlist')"
>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: loopEnabled }"
:icon="['fas', 'retweet']"
:title="$t('Video.Loop Playlist')"
/>
</span>
<span
role="button"
tabindex="0"
:aria-pressed="shuffleEnabled"
@click="toggleShuffle"
@keydown.enter.prevent="toggleShuffle"
@keydown.space.prevent="toggleShuffle"
/>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: reversePlaylist }"
:icon="['fas', 'exchange-alt']"
:title="$t('Video.Reverse Playlist')"
>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: shuffleEnabled }"
:icon="['fas', 'random']"
:title="$t('Video.Shuffle Playlist')"
/>
</span>
<span
role="button"
tabindex="0"
:aria-pressed="reversePlaylist"
@click="toggleReversePlaylist"
@keydown.enter.prevent="toggleReversePlaylist"
@keydown.space.prevent="toggleReversePlaylist"
/>
<font-awesome-icon
class="playlistIcon"
:icon="['fas', 'step-backward']"
:title="$t('Video.Play Previous Video')"
>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: reversePlaylist }"
:icon="['fas', 'exchange-alt']"
:title="$t('Video.Reverse Playlist')"
/>
</span>
<span
role="button"
tabindex="0"
@click="playPreviousVideo"
@keydown.enter.prevent="playPreviousVideo"
@keydown.space.prevent="playPreviousVideo"
/>
<font-awesome-icon
class="playlistIcon"
:icon="['fas', 'step-forward']"
:title="$t('Video.Play Next Video')"
>
<font-awesome-icon
class="playlistIcon"
:icon="['fas', 'step-backward']"
:title="$t('Video.Play Previous Video')"
/>
</span>
<span
role="button"
tabindex="0"
@click="playNextVideo"
@keydown.enter.prevent="playNextVideo"
@keydown.space.prevent="playNextVideo"
/>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: pauseOnCurrentVideo }"
:icon="['fas', 'pause']"
:title="$t('Video.Pause on Current Video')"
>
<font-awesome-icon
class="playlistIcon"
:icon="['fas', 'step-forward']"
:title="$t('Video.Play Next Video')"
/>
</span>
<span
role="button"
tabindex="0"
:aria-pressed="pauseOnCurrentVideo"
@click="togglePauseOnCurrentVideo"
@keydown.enter.prevent="togglePauseOnCurrentVideo"
@keydown.space.prevent="togglePauseOnCurrentVideo"
/>
>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: pauseOnCurrentVideo }"
:icon="['fas', 'pause']"
:title="$t('Video.Pause on Current Video')"
/>
</span>
</p>
<div
v-if="!isLoading"

View File

@ -42,43 +42,43 @@ export const colors = [
export function getColorTranslations() {
return [
i18n.t('Settings.Theme Settings.Main Color Theme.Red'),
i18n.t('Settings.Theme Settings.Main Color Theme.Pink'),
i18n.t('Settings.Theme Settings.Main Color Theme.Purple'),
i18n.t('Settings.Theme Settings.Main Color Theme.Deep Purple'),
i18n.t('Settings.Theme Settings.Main Color Theme.Indigo'),
i18n.t('Settings.Theme Settings.Main Color Theme.Blue'),
i18n.t('Settings.Theme Settings.Main Color Theme.Light Blue'),
i18n.t('Settings.Theme Settings.Main Color Theme.Cyan'),
i18n.t('Settings.Theme Settings.Main Color Theme.Teal'),
i18n.t('Settings.Theme Settings.Main Color Theme.Green'),
i18n.t('Settings.Theme Settings.Main Color Theme.Light Green'),
i18n.t('Settings.Theme Settings.Main Color Theme.Lime'),
i18n.t('Settings.Theme Settings.Main Color Theme.Yellow'),
i18n.t('Settings.Theme Settings.Main Color Theme.Amber'),
i18n.t('Settings.Theme Settings.Main Color Theme.Orange'),
i18n.t('Settings.Theme Settings.Main Color Theme.Deep Orange'),
i18n.t('Settings.Theme Settings.Main Color Theme.Dracula Cyan'),
i18n.t('Settings.Theme Settings.Main Color Theme.Dracula Green'),
i18n.t('Settings.Theme Settings.Main Color Theme.Dracula Orange'),
i18n.t('Settings.Theme Settings.Main Color Theme.Dracula Pink'),
i18n.t('Settings.Theme Settings.Main Color Theme.Dracula Purple'),
i18n.t('Settings.Theme Settings.Main Color Theme.Dracula Red'),
i18n.t('Settings.Theme Settings.Main Color Theme.Dracula Yellow'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Rosewater'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Flamingo'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Pink'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Mauve'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Red'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Maroon'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Peach'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Yellow'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Green'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Teal'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Sky'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Sapphire'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Blue'),
i18n.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Lavender')
i18n.global.t('Settings.Theme Settings.Main Color Theme.Red'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Pink'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Purple'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Deep Purple'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Indigo'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Blue'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Light Blue'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Cyan'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Teal'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Green'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Light Green'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Lime'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Yellow'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Amber'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Orange'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Deep Orange'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Dracula Cyan'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Dracula Green'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Dracula Orange'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Dracula Pink'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Dracula Purple'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Dracula Red'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Dracula Yellow'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Rosewater'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Flamingo'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Pink'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Mauve'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Red'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Maroon'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Peach'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Yellow'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Green'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Teal'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Sky'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Sapphire'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Blue'),
i18n.global.t('Settings.Theme Settings.Main Color Theme.Catppuccin Mocha Lavender')
]
}

View File

@ -98,6 +98,7 @@ export async function parseYouTubeRSSFeed(rssString, channelId) {
async function parseRSSEntry(entry, channelId, channelName) {
// doesn't need to be asynchronous, but doing it allows us to do the relatively slow DOM querying in parallel
const published = new Date(entry.querySelector('published').textContent)
const viewCount = entry.getElementsByTagName('media:statistics')[0]?.getAttribute('views') || null
return {
authorId: channelId,
@ -106,7 +107,7 @@ async function parseRSSEntry(entry, channelId, channelName) {
videoId: entry.getElementsByTagName('yt:videoId')[0].textContent,
title: entry.querySelector('title').textContent,
published: published.getTime(),
viewCount: entry.getElementsByTagName('media:statistics')[0]?.getAttribute('views') || null,
viewCount: viewCount ? parseInt(viewCount) : null,
type: 'video',
lengthSeconds: '0:00',
isRSS: true

View File

@ -88,10 +88,10 @@ export function setPublishedTimestampsInvidious(videos) {
export function toLocalePublicationString ({ publishText, isLive = false, isUpcoming = false, isRSS = false }) {
if (isLive) {
return i18n.tc('Global.Counts.Watching Count', 0, { count: 0 })
return i18n.global.tc('Global.Counts.Watching Count', 0, { count: 0 })
} else if (isUpcoming || publishText === null) {
// the check for null is currently just an inferring of knowledge, because there is no other possibility left
return `${i18n.t('Video.Published.Upcoming')}: ${publishText}`
return `${i18n.global.t('Video.Published.Upcoming')}: ${publishText}`
} else if (isRSS) {
return publishText
}
@ -103,63 +103,63 @@ export function toLocalePublicationString ({ publishText, isLive = false, isUpco
case 'se':
case 's':
if (singular) {
unit = i18n.t('Video.Published.Second')
unit = i18n.global.t('Video.Published.Second')
} else {
unit = i18n.t('Video.Published.Seconds')
unit = i18n.global.t('Video.Published.Seconds')
}
break
case 'mi':
case 'm':
if (singular) {
unit = i18n.t('Video.Published.Minute')
unit = i18n.global.t('Video.Published.Minute')
} else {
unit = i18n.t('Video.Published.Minutes')
unit = i18n.global.t('Video.Published.Minutes')
}
break
case 'ho':
case 'h':
if (singular) {
unit = i18n.t('Video.Published.Hour')
unit = i18n.global.t('Video.Published.Hour')
} else {
unit = i18n.t('Video.Published.Hours')
unit = i18n.global.t('Video.Published.Hours')
}
break
case 'da':
case 'd':
if (singular) {
unit = i18n.t('Video.Published.Day')
unit = i18n.global.t('Video.Published.Day')
} else {
unit = i18n.t('Video.Published.Days')
unit = i18n.global.t('Video.Published.Days')
}
break
case 'we':
case 'w':
if (singular) {
unit = i18n.t('Video.Published.Week')
unit = i18n.global.t('Video.Published.Week')
} else {
unit = i18n.t('Video.Published.Weeks')
unit = i18n.global.t('Video.Published.Weeks')
}
break
case 'mo':
if (singular) {
unit = i18n.t('Video.Published.Month')
unit = i18n.global.t('Video.Published.Month')
} else {
unit = i18n.t('Video.Published.Months')
unit = i18n.global.t('Video.Published.Months')
}
break
case 'ye':
case 'y':
if (singular) {
unit = i18n.t('Video.Published.Year')
unit = i18n.global.t('Video.Published.Year')
} else {
unit = i18n.t('Video.Published.Years')
unit = i18n.global.t('Video.Published.Years')
}
break
default:
return publishText
}
return i18n.t('Video.Publicationtemplate', { number: match[1], unit })
return i18n.global.t('Video.Publicationtemplate', { number: match[1], unit })
}
export function buildVTTFileLocally(storyboard, videoLengthSeconds) {
@ -284,11 +284,11 @@ export async function copyToClipboard(content, { messageOnSuccess = null, messag
if (messageOnError !== null) {
showToast(`${messageOnError}: ${error}`, 5000)
} else {
showToast(`${i18n.t('Clipboard.Copy failed')}: ${error}`, 5000)
showToast(`${i18n.global.t('Clipboard.Copy failed')}: ${error}`, 5000)
}
}
} else {
showToast(i18n.t('Clipboard.Cannot access clipboard without a secure connection'), 5000)
showToast(i18n.global.t('Clipboard.Cannot access clipboard without a secure connection'), 5000)
}
}
@ -605,7 +605,7 @@ export function extractNumberFromString(str) {
}
export function showExternalPlayerUnsupportedActionToast(externalPlayer, action) {
const message = i18n.t('Video.External Player.UnsupportedActionTemplate', { externalPlayer, action })
const message = i18n.global.t('Video.External Player.UnsupportedActionTemplate', { externalPlayer, action })
showToast(message)
}
@ -707,7 +707,7 @@ export function toDistractionFreeTitle(title, minUpperCase = 3) {
}
export function formatNumber(number, options = undefined) {
return Intl.NumberFormat([i18n.locale.replace('_', '-'), 'en'], options).format(number)
return Intl.NumberFormat([i18n.global.locale.replace('_', '-'), 'en'], options).format(number)
}
export function getTodayDateStrLocalTimezone() {

View File

@ -1,12 +1,9 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import { createI18n } from 'vue-i18n'
import { createWebURL } from '../helpers/utils'
// List of locales approved for use
import activeLocales from '../../../static/locales/activeLocales.json'
Vue.use(VueI18n)
const i18n = new VueI18n({
const i18n = createI18n({
locale: 'en-US',
fallbackLocale: {
// https://kazupon.github.io/vue-i18n/guide/fallback.html#explicit-fallback-with-decision-maps
@ -21,12 +18,15 @@ const i18n = new VueI18n({
'pt-PT': ['pt'],
// any -> en-US
default: ['en-US'],
}
},
globalInjection: true,
legacy: true,
})
export async function loadLocale(locale) {
// don't need to load it if it's already loaded
if (i18n.availableLocales.includes(locale)) {
if (i18n.global.availableLocales.includes(locale) &&
Object.keys(i18n.global.messages[locale]).length !== 0) {
return
}
if (!activeLocales.includes(locale)) {
@ -35,6 +35,7 @@ export async function loadLocale(locale) {
}
// locales are only compressed in our production Electron builds
let data
if (process.env.IS_ELECTRON && process.env.NODE_ENV !== 'development') {
const { promisify } = require('util')
const { brotliDecompress } = require('zlib')
@ -45,8 +46,8 @@ export async function loadLocale(locale) {
const compressed = await (await fetch(url)).arrayBuffer()
const decompressed = await brotliDecompressAsync(compressed)
const data = JSON.parse(decompressed.toString())
i18n.setLocaleMessage(locale, data)
data = JSON.parse(decompressed.toString())
i18n.global.setLocaleMessage(locale, data)
} catch (err) {
console.error(locale, err)
}
@ -54,8 +55,8 @@ export async function loadLocale(locale) {
const url = createWebURL(`/static/locales/${locale}.json`)
const response = await fetch(url)
const data = await response.json()
i18n.setLocaleMessage(locale, data)
data = await response.json()
i18n.global.setLocaleMessage(locale, data)
}
}

View File

@ -1,11 +1,11 @@
// import the styles
import Vue from 'vue'
import { createApp } from 'vue'
import App from './App.vue'
import i18n, { loadLocale } from './i18n/index'
import router from './router/index'
import store from './store/index'
import i18n from './i18n/index'
import { library } from '@fortawesome/fontawesome-svg-core'
import { ObserveVisibility } from 'vue-observe-visibility'
import { register as registerSwiper } from 'swiper/element'
// Please keep the list of constants sorted by name
@ -82,10 +82,6 @@ import {
} from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
Vue.config.devtools = process.env.NODE_ENV === 'development'
Vue.config.performance = process.env.NODE_ENV === 'development'
Vue.config.productionTip = process.env.NODE_ENV === 'development'
// Please keep the list of constants sorted by name
// to avoid code conflict and duplicate entries
library.add(
@ -162,25 +158,29 @@ library.add(
registerSwiper()
Vue.component('FontAwesomeIcon', FontAwesomeIcon)
loadLocale('en-US').then(() => {
const app = createApp(App)
/* eslint-disable-next-line no-new */
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
app.use(router)
app.use(store)
app.use(i18n)
app.config.performance = process.env.NODE_ENV === 'development'
app.component('FontAwesomeIcon', FontAwesomeIcon)
app.directive('observe-visibility', ObserveVisibility)
// to avoid accessing electron api from web app build
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
// handle menu event updates from main script
ipcRenderer.on('change-view', (event, data) => {
if (data.route) {
router.isReady().then(() =>
router.push(data.route)
)
}
})
}
router.isReady().then(() => app.mount('#app'))
})
// to avoid accessing electron api from web app build
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
// handle menu event updates from main script
ipcRenderer.on('change-view', (event, data) => {
if (data.route) {
router.push(data.route)
}
})
}

View File

@ -1,5 +1,4 @@
import Vue from 'vue'
import Router from 'vue-router'
import { createRouter, createWebHashHistory } from 'vue-router'
import Subscriptions from '../views/Subscriptions/Subscriptions.vue'
import SubscribedChannels from '../views/SubscribedChannels/SubscribedChannels.vue'
import ProfileSettings from '../views/ProfileSettings/ProfileSettings.vue'
@ -15,9 +14,8 @@ import Channel from '../views/Channel/Channel.vue'
import Watch from '../views/Watch/Watch.vue'
import Hashtag from '../views/Hashtag/Hashtag.vue'
Vue.use(Router)
const router = new Router({
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
@ -141,7 +139,7 @@ const router = new Router({
if (savedPosition !== null) {
resolve(savedPosition)
} else {
resolve({ x: 0, y: 0 })
resolve({ left: 0, top: 0 })
}
}, 500)
})

View File

@ -1,15 +1,15 @@
import Vue from 'vue'
import Vuex from 'vuex'
import { createStore } from 'vuex'
// import createPersistedState from 'vuex-persistedstate'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
export default createStore({
modules,
strict: process.env.NODE_ENV !== 'production'
strict: process.env.NODE_ENV !== 'production',
// TODO: Enable when deploy
// plugins: [createPersistedState()]
// Debugging vuex:
// import { createLogger } from 'vuex'
// plugins: [createLogger()]
})

View File

@ -1,4 +1,3 @@
import { set as vueSet, del as vueDel } from 'vue'
import { DBHistoryHandlers } from '../../../datastores/handlers/index'
const state = {
@ -104,7 +103,7 @@ const mutations = {
}
state.historyCacheSorted.unshift(record)
vueSet(state.historyCacheById, record.videoId, record)
state.historyCacheById[record.videoId] = record
},
updateRecordWatchProgressInHistoryCache(state, { videoId, watchProgress }) {
@ -115,7 +114,7 @@ const mutations = {
const targetRecord = Object.assign({}, state.historyCacheSorted[i])
targetRecord.watchProgress = watchProgress
state.historyCacheSorted.splice(i, 1, targetRecord)
vueSet(state.historyCacheById, videoId, targetRecord)
state.historyCacheById[videoId] = targetRecord
},
updateRecordLastViewedPlaylistIdInHistoryCache(state, { videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId }) {
@ -128,7 +127,7 @@ const mutations = {
targetRecord.lastViewedPlaylistType = lastViewedPlaylistType
targetRecord.lastViewedPlaylistItemId = lastViewedPlaylistItemId
state.historyCacheSorted.splice(i, 1, targetRecord)
vueSet(state.historyCacheById, videoId, targetRecord)
state.historyCacheById[videoId] = targetRecord
},
removeFromHistoryCacheById(state, videoId) {
@ -139,7 +138,7 @@ const mutations = {
}
}
vueDel(state.historyCacheById, videoId)
delete state.historyCacheById[videoId]
}
}

View File

@ -382,7 +382,8 @@ const stateWithSideEffects = {
await Promise.allSettled(loadPromises)
i18n.locale = targetLocale
i18n.global.locale = targetLocale
await dispatch('getRegionData', {
locale: targetLocale
})

View File

@ -1,7 +1,6 @@
import fs from 'fs/promises'
import path from 'path'
import i18n from '../../i18n/index'
import { set as vueSet } from 'vue'
import { IpcChannels } from '../../../constants'
import { pathExists } from '../../helpers/filesystem'
@ -187,7 +186,7 @@ const actions = {
}
const fileName = `${replaceFilenameForbiddenChars(title)}.${extension}`
const errorMessage = i18n.t('Downloading failed', { videoTitle: title })
const errorMessage = i18n.global.t('Downloading failed', { videoTitle: title })
const askFolderPath = rootState.settings.downloadAskPath
let folderPath = rootState.settings.downloadFolderPath
@ -222,7 +221,7 @@ const actions = {
folderPath = path.join(folderPath, fileName)
}
showToast(i18n.t('Starting download', { videoTitle: title }))
showToast(i18n.global.t('Starting download', { videoTitle: title }))
const response = await fetch(url).catch((error) => {
console.error(error)
@ -258,7 +257,7 @@ const actions = {
try {
await fs.writeFile(folderPath, new DataView(buffer))
showToast(i18n.t('Downloading has completed', { videoTitle: title }))
showToast(i18n.global.t('Downloading has completed', { videoTitle: title }))
} catch (err) {
console.error(err)
showToast(errorMessage)
@ -287,7 +286,7 @@ const actions = {
}
if (parsedString !== replaceFilenameForbiddenChars(parsedString)) {
reject(new Error(i18n.t('Settings.Player Settings.Screenshot.Error.Forbidden Characters')))
reject(new Error(i18n.global.t('Settings.Player Settings.Screenshot.Error.Forbidden Characters')))
}
let filename
@ -299,7 +298,7 @@ const actions = {
}
if (!filename) {
reject(new Error(i18n.t('Settings.Player Settings.Screenshot.Error.Empty File Name')))
reject(new Error(i18n.global.t('Settings.Player Settings.Screenshot.Error.Empty File Name')))
}
resolve(parsedString)
@ -682,7 +681,7 @@ const actions = {
args.push(cmdArgs.startOffset, Math.trunc(payload.watchProgress))
}
} else if (!ignoreWarnings) {
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.t('Video.External Player.Unsupported Actions.starting video at offset'))
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.global.t('Video.External Player.Unsupported Actions.starting video at offset'))
}
}
@ -690,7 +689,7 @@ const actions = {
if (typeof cmdArgs.playbackRate === 'string') {
args.push(`${cmdArgs.playbackRate}${payload.playbackRate}`)
} else if (!ignoreWarnings) {
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.t('Video.External Player.Unsupported Actions.setting a playback rate'))
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.global.t('Video.External Player.Unsupported Actions.setting a playback rate'))
}
}
@ -700,7 +699,7 @@ const actions = {
if (typeof cmdArgs.playlistIndex === 'string') {
args.push(`${cmdArgs.playlistIndex}${payload.playlistIndex}`)
} else if (!ignoreWarnings) {
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.t('Video.External Player.Unsupported Actions.opening specific video in a playlist (falling back to opening the video)'))
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.global.t('Video.External Player.Unsupported Actions.opening specific video in a playlist (falling back to opening the video)'))
}
}
@ -708,7 +707,7 @@ const actions = {
if (typeof cmdArgs.playlistReverse === 'string') {
args.push(cmdArgs.playlistReverse)
} else if (!ignoreWarnings) {
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.t('Video.External Player.Unsupported Actions.reversing playlists'))
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.global.t('Video.External Player.Unsupported Actions.reversing playlists'))
}
}
@ -716,7 +715,7 @@ const actions = {
if (typeof cmdArgs.playlistShuffle === 'string') {
args.push(cmdArgs.playlistShuffle)
} else if (!ignoreWarnings) {
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.t('Video.External Player.Unsupported Actions.shuffling playlists'))
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.global.t('Video.External Player.Unsupported Actions.shuffling playlists'))
}
}
@ -724,7 +723,7 @@ const actions = {
if (typeof cmdArgs.playlistLoop === 'string') {
args.push(cmdArgs.playlistLoop)
} else if (!ignoreWarnings) {
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.t('Video.External Player.Unsupported Actions.looping playlists'))
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.global.t('Video.External Player.Unsupported Actions.looping playlists'))
}
}
@ -736,7 +735,7 @@ const actions = {
}
} else {
if (payload.playlistId != null && payload.playlistId !== '' && !ignoreWarnings) {
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.t('Video.External Player.Unsupported Actions.opening playlists'))
showExternalPlayerUnsupportedActionToast(externalPlayer, i18n.global.t('Video.External Player.Unsupported Actions.opening playlists'))
}
if (payload.videoId != null) {
args.push(`${cmdArgs.videoUrl}https://www.youtube.com/watch?v=${payload.videoId}`)
@ -745,10 +744,10 @@ const actions = {
}
const videoOrPlaylist = payload.playlistId != null && payload.playlistId !== ''
? i18n.t('Video.External Player.playlist')
: i18n.t('Video.External Player.video')
? i18n.global.t('Video.External Player.playlist')
: i18n.global.t('Video.External Player.video')
showToast(i18n.t('Video.External Player.OpeningTemplate', { videoOrPlaylist, externalPlayer }))
showToast(i18n.global.t('Video.External Player.OpeningTemplate', { videoOrPlaylist, externalPlayer }))
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
@ -802,14 +801,12 @@ const mutations = {
const sameVideo = state.deArrowCache[payload.videoId]
if (!sameVideo) {
// setting properties directly doesn't trigger watchers in Vue 2,
// so we need to use Vue's set function
vueSet(state.deArrowCache, payload.videoId, payload)
state.deArrowCache[payload.videoId] = payload
}
},
addThumbnailToDeArrowCache (state, payload) {
vueSet(state.deArrowCache, payload.videoId, payload)
state.deArrowCache[payload.videoId] = payload
},
addToSessionSearchHistory (state, payload) {
@ -871,19 +868,19 @@ const mutations = {
},
updateLastCommunityRefreshTimestampByProfile (state, { profileId, timestamp }) {
vueSet(state.lastCommunityRefreshTimestampByProfile, profileId, timestamp)
state.lastCommunityRefreshTimestampByProfile[profileId] = timestamp
},
updateLastShortRefreshTimestampByProfile (state, { profileId, timestamp }) {
vueSet(state.lastShortRefreshTimestampByProfile, profileId, timestamp)
state.lastShortRefreshTimestampByProfile[profileId] = timestamp
},
updateLastLiveRefreshTimestampByProfile (state, { profileId, timestamp }) {
vueSet(state.lastLiveRefreshTimestampByProfile, profileId, timestamp)
state.lastLiveRefreshTimestampByProfile[profileId] = timestamp
},
updateLastVideoRefreshTimestampByProfile (state, { profileId, timestamp }) {
vueSet(state.lastVideoRefreshTimestampByProfile, profileId, timestamp)
state.lastVideoRefreshTimestampByProfile[profileId] = timestamp
},
clearTrendingCache(state) {

View File

@ -297,7 +297,8 @@ export default defineComponent({
},
},
watch: {
$route() {
$route(to) {
if (!to.fullPath.startsWith('/channel')) { return }
// react to route changes...
this.isLoading = true
@ -345,7 +346,7 @@ export default defineComponent({
if (this.id === '@@@') {
this.showShareMenu = false
this.setErrorMessage(this.$i18n.t('Channel.This channel does not exist'))
this.setErrorMessage(this.$t('Channel.This channel does not exist'))
return
}
@ -446,7 +447,7 @@ export default defineComponent({
if (this.id === '@@@') {
this.showShareMenu = false
this.setErrorMessage(this.$i18n.t('Channel.This channel does not exist'))
this.setErrorMessage(this.$t('Channel.This channel does not exist'))
return
}

View File

@ -43,7 +43,8 @@ export default defineComponent({
},
},
watch: {
$route() {
$route(to) {
if (!to.fullPath.startsWith('/hashtag')) { return }
this.resetData()
this.getHashtag()
}

View File

@ -243,8 +243,10 @@ export default defineComponent({
},
},
watch: {
$route () {
$route (to) {
// react to route changes...
if (!to.fullPath.startsWith('/playlist')) { return }
this.getPlaylistInfoDebounce()
},
userPlaylistsReady () {

View File

@ -40,7 +40,7 @@ export default defineComponent({
this.fetchPopularInfo()
}
},
beforeDestroy: function () {
beforeUnmount: function () {
document.removeEventListener('keydown', this.keyboardShortcutHandler)
},
methods: {

View File

@ -43,7 +43,7 @@ export default defineComponent({
},
watch: {
profileList: {
handler: function () {
handler() {
this.openSettingsProfile = this.getProfileById(this.openSettingsProfileId)
},
deep: true

View File

@ -50,9 +50,9 @@ export default defineComponent({
},
},
watch: {
$route () {
$route (to) {
// react to route changes...
if (!to.fullPath.startsWith('/search')) { return }
const query = this.$route.params.query
const searchSettings = {
sortBy: this.$route.query.sortBy,

View File

@ -79,16 +79,19 @@ export default defineComponent({
sessionStorage.removeItem('Subscriptions/currentTab')
}
},
visibleTabs: {
/**
* @param {string[]} newValue
*/
visibleTabs: function (newValue) {
if (newValue.length === 0) {
this.currentTab = null
} else if (!newValue.includes(this.currentTab)) {
this.currentTab = newValue[0]
}
}
handler(newValue) {
if (newValue.length === 0) {
this.currentTab = null
} else if (!newValue.includes(this.currentTab)) {
this.currentTab = newValue[0]
}
},
deep: true
},
},
created: async function () {
if (this.visibleTabs.length === 0) {

View File

@ -55,7 +55,7 @@ export default defineComponent({
this.getTrendingInfo()
}
},
beforeDestroy: function () {
beforeUnmount: function () {
document.removeEventListener('keydown', this.keyboardShortcutHandler)
},
methods: {

View File

@ -236,7 +236,9 @@ export default defineComponent({
},
},
watch: {
$route() {
$route(to) {
if (!to.fullPath.startsWith('/watch')) { return }
this.handleRouteChange(this.videoId)
// react to route changes...
this.videoId = this.$route.params.id

333
yarn.lock
View File

@ -384,7 +384,7 @@
js-tokens "^4.0.0"
picocolors "^1.0.0"
"@babel/parser@^7.23.5", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4":
"@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4":
version "7.24.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88"
integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==
@ -1279,10 +1279,10 @@
dependencies:
"@fortawesome/fontawesome-common-types" "6.5.2"
"@fortawesome/vue-fontawesome@^2.0.10":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.10.tgz#b10721425d7efdee6d83fba21c64cad86fa51904"
integrity sha512-OTETSXz+3ygD2OK2/vy82cmUBpuJqeOAg4gfnnv+f2Rir1tDIhQg026Q3NQxznq83ZLz8iNqGG9XJm26inpDeg==
"@fortawesome/vue-fontawesome@^3.0.6":
version "3.0.6"
resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.6.tgz#c5e627475c10869a091280610c96411d18206f3a"
integrity sha512-akrL7lTroyNpPkoHtvK2UpsMzJr6jXdHaQ0YdcwqDsB8jdwlpNHZYijpOUd9KJsARr+VB3WXY4EyObepqJ4ytQ==
"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
@ -1303,6 +1303,14 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
"@intlify/core-base@9.13.0":
version "9.13.0"
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.13.0.tgz#b6305c99521a613c4577b2a9e7dc503631ebcd85"
integrity sha512-Lx8+YTrFpom7AtdbbuJHzgmr612/bceHU92v8ZPU9HU9/rczf+TmCs95BxWPIR4K42xh4MVMLsNzLUWiXcNaLg==
dependencies:
"@intlify/message-compiler" "9.13.0"
"@intlify/shared" "9.13.0"
"@intlify/core-base@^9.1.9":
version "9.10.1"
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.10.1.tgz#e61d507d35beb0c69f9c94566313f9520c25a84a"
@ -1340,11 +1348,24 @@
"@intlify/shared" "9.10.1"
source-map-js "^1.0.2"
"@intlify/message-compiler@9.13.0":
version "9.13.0"
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.13.0.tgz#3b523a5331d7ac89dde4aefc2f1952e79b49070b"
integrity sha512-zhESuudiDpFQhUOx/qrSMd7ZYHbmgCc0QzBc27cDUxaaAj3olbYJnsx3osiHPQyYnv/LuC+WTqoNOEBoHP6dqQ==
dependencies:
"@intlify/shared" "9.13.0"
source-map-js "^1.0.2"
"@intlify/shared@9.10.1":
version "9.10.1"
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.10.1.tgz#024ad6dd4ee9581962437570b3dc25516c82f4e9"
integrity sha512-liyH3UMoglHBUn70iCYcy9CQlInx/lp50W2aeSxqqrvmG+LDj/Jj7tBJhBoQL4fECkldGhbmW0g2ommHfL6Wmw==
"@intlify/shared@9.13.0":
version "9.13.0"
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.13.0.tgz#e723296fdab798d280087b89764aab06791bc7a4"
integrity sha512-fUwWcpDz9Wm4dSaz+6XmjoNXWBjZLJtT1Zf1cpLBELbCAOS8WBRscPtgOSfzm6JCqf5KgMI4g917f5TtEeez3A==
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
@ -1443,7 +1464,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/sourcemap-codec@^1.4.14":
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@ -1909,32 +1930,89 @@
global "~4.4.0"
is-function "^1.0.1"
"@vue/compiler-sfc@2.7.16":
version "2.7.16"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz#ff81711a0fac9c68683d8bb00b63f857de77dc83"
integrity sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==
"@vue/compiler-core@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.23.tgz#a08f5998e391ad75e602a66dd7255af9054df2f3"
integrity sha512-HAFmuVEwNqNdmk+w4VCQ2pkLk1Vw4XYiiyxEp3z/xvl14aLTUBw2OfVH3vBcx+FtGsynQLkkhK410Nah1N2yyQ==
dependencies:
"@babel/parser" "^7.23.5"
postcss "^8.4.14"
source-map "^0.6.1"
optionalDependencies:
prettier "^1.18.2 || ^2.0.0"
"@babel/parser" "^7.24.1"
"@vue/shared" "3.4.23"
entities "^4.5.0"
estree-walker "^2.0.2"
source-map-js "^1.2.0"
"@vue/component-compiler-utils@^3.1.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz#f9f5fb53464b0c37b2c8d2f3fbfe44df60f61dc9"
integrity sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==
"@vue/compiler-dom@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.23.tgz#6fa622d1e5c8508551564c5dc5948e9cddf60867"
integrity sha512-t0b9WSTnCRrzsBGrDd1LNR5HGzYTr7LX3z6nNBG+KGvZLqrT0mY6NsMzOqlVMBKKXKVuusbbB5aOOFgTY+senw==
dependencies:
consolidate "^0.15.1"
hash-sum "^1.0.2"
lru-cache "^4.1.2"
merge-source-map "^1.1.0"
postcss "^7.0.36"
postcss-selector-parser "^6.0.2"
source-map "~0.6.1"
vue-template-es2015-compiler "^1.9.0"
optionalDependencies:
prettier "^1.18.2 || ^2.0.0"
"@vue/compiler-core" "3.4.23"
"@vue/shared" "3.4.23"
"@vue/compiler-sfc@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.23.tgz#7041517b9bbd1b304f0db33bfa424e9a899fda8d"
integrity sha512-fSDTKTfzaRX1kNAUiaj8JB4AokikzStWgHooMhaxyjZerw624L+IAP/fvI4ZwMpwIh8f08PVzEnu4rg8/Npssw==
dependencies:
"@babel/parser" "^7.24.1"
"@vue/compiler-core" "3.4.23"
"@vue/compiler-dom" "3.4.23"
"@vue/compiler-ssr" "3.4.23"
"@vue/shared" "3.4.23"
estree-walker "^2.0.2"
magic-string "^0.30.8"
postcss "^8.4.38"
source-map-js "^1.2.0"
"@vue/compiler-ssr@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.23.tgz#1ae4afe962a9e156b1a79eff909c37cd423dd4c2"
integrity sha512-hb6Uj2cYs+tfqz71Wj6h3E5t6OKvb4MVcM2Nl5i/z1nv1gjEhw+zYaNOV+Xwn+SSN/VZM0DgANw5TuJfxfezPg==
dependencies:
"@vue/compiler-dom" "3.4.23"
"@vue/shared" "3.4.23"
"@vue/devtools-api@^6.0.0-beta.11", "@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.5.1":
version "6.6.1"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.1.tgz#7c14346383751d9f6ad4bea0963245b30220ef83"
integrity sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==
"@vue/reactivity@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.23.tgz#f29044a90a24994af075e4368790f31fa29ed747"
integrity sha512-GlXR9PL+23fQ3IqnbSQ8OQKLodjqCyoCrmdLKZk3BP7jN6prWheAfU7a3mrltewTkoBm+N7qMEb372VHIkQRMQ==
dependencies:
"@vue/shared" "3.4.23"
"@vue/runtime-core@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.23.tgz#21f6c7153c33f56081145c2819c2f194cbe3eb22"
integrity sha512-FeQ9MZEXoFzFkFiw9MQQ/FWs3srvrP+SjDKSeRIiQHIhtkzoj0X4rWQlRNHbGuSwLra6pMyjAttwixNMjc/xLw==
dependencies:
"@vue/reactivity" "3.4.23"
"@vue/shared" "3.4.23"
"@vue/runtime-dom@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.23.tgz#d5a9cca88b445de125c57e6b0d73faa2491e853f"
integrity sha512-RXJFwwykZWBkMiTPSLEWU3kgVLNAfActBfWFlZd0y79FTUxexogd0PLG4HH2LfOktjRxV47Nulygh0JFXe5f9A==
dependencies:
"@vue/runtime-core" "3.4.23"
"@vue/shared" "3.4.23"
csstype "^3.1.3"
"@vue/server-renderer@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.23.tgz#e605872e26d995f5ba9382e8758cd8cc7fa2e16d"
integrity sha512-LDwGHtnIzvKFNS8dPJ1SSU5Gvm36p2ck8wCZc52fc3k/IfjKcwCyrWEf0Yag/2wTFUBXrqizfhK9c/mC367dXQ==
dependencies:
"@vue/compiler-ssr" "3.4.23"
"@vue/shared" "3.4.23"
"@vue/shared@3.4.23":
version "3.4.23"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.23.tgz#e536a6dfd2f5f950d08c2e8ebcfe7e5329a851a1"
integrity sha512-wBQ0gvf+SMwsCQOyusNw/GoXPV47WGd1xB5A1Pgzy0sQ3Bi5r5xm3n+92y3gCnB3MWqnRDdvfkRGxhKtbBRNgg==
"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
version "1.12.1"
@ -2459,11 +2537,6 @@ big-integer@^1.6.44:
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
@ -2476,7 +2549,7 @@ bluebird-lst@^1.0.9:
dependencies:
bluebird "^3.5.5"
bluebird@^3.1.1, bluebird@^3.5.5:
bluebird@^3.5.5:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@ -2735,7 +2808,7 @@ chalk@^2.4.1, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2:
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@ -2944,13 +3017,6 @@ connect-history-api-fallback@^2.0.0:
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
consolidate@^0.15.1:
version "0.15.1"
resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7"
integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==
dependencies:
bluebird "^3.1.1"
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
@ -3188,10 +3254,10 @@ csso@^5.0.5:
dependencies:
css-tree "~2.2.0"
csstype@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
csstype@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
dashdash@^1.12.0:
version "1.14.1"
@ -3597,11 +3663,6 @@ emoji-regex@^9.2.2:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
emojis-list@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@ -3632,7 +3693,7 @@ entities@^4.2.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
entities@^4.4.0:
entities@^4.4.0, entities@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
@ -4092,6 +4153,11 @@ estraverse@^5.1.0, estraverse@^5.2.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@ -4832,10 +4898,10 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hash-sum@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=
hash-sum@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a"
integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
hasown@^2.0.0:
version "2.0.0"
@ -5607,7 +5673,7 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^1.0.1, json5@^1.0.2:
json5@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
@ -5796,15 +5862,6 @@ loader-runner@^4.2.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
loader-utils@^1.0.2, loader-utils@^1.1.0:
version "1.4.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
json5 "^1.0.1"
localforage@^1.9.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
@ -5875,14 +5932,6 @@ lowercase-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lru-cache@^4.1.2:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
dependencies:
pseudomap "^1.0.2"
yallist "^2.1.2"
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@ -5911,6 +5960,13 @@ m3u8-parser@4.8.0:
"@videojs/vhs-utils" "^3.0.5"
global "^4.4.0"
magic-string@^0.30.8:
version "0.30.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.9.tgz#8927ae21bfdd856310e07a1bc8dd5e73cb6c251d"
integrity sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
marked@^12.0.1:
version "12.0.1"
resolved "https://registry.yarnpkg.com/marked/-/marked-12.0.1.tgz#8ab1eb15560c7cbe3b011074845d7ca6c4d392b0"
@ -5965,13 +6021,6 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merge-source-map@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646"
integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==
dependencies:
source-map "^0.6.1"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@ -6613,11 +6662,6 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
picocolors@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
@ -6938,15 +6982,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^7.0.36:
version "7.0.39"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309"
integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
dependencies:
picocolors "^0.2.1"
source-map "^0.6.1"
postcss@^8.4.14, postcss@^8.4.33, postcss@^8.4.38:
postcss@^8.4.33, postcss@^8.4.38:
version "8.4.38"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
@ -6967,7 +7003,7 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
"prettier@^1.18.2 || ^2.0.0", prettier@^2.8.8:
prettier@^2.8.8:
version "2.8.8"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
@ -7011,11 +7047,6 @@ proxy-addr@~2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.28:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
@ -7781,7 +7812,7 @@ source-map-support@^0.5.19, source-map-support@~0.5.20:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
source-map@^0.6.0, source-map@~0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@ -8696,64 +8727,55 @@ vue-eslint-parser@^9.0.0, vue-eslint-parser@^9.0.1, vue-eslint-parser@^9.4.2:
lodash "^4.17.21"
semver "^7.3.6"
vue-hot-reload-api@^2.3.0:
version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==
vue-i18n@^8.28.2:
version "8.28.2"
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.28.2.tgz#913558066e274395c0a9f40b2f3393d5c2636840"
integrity sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA==
vue-loader@^15.10.0:
version "15.10.0"
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.10.0.tgz#2a12695c421a2a2cc2138f05a949d04ed086e38b"
integrity sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==
vue-i18n@^9.13.0:
version "9.13.0"
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.13.0.tgz#bedc0b9c5ebdc299c56296db747d8616db5eebc7"
integrity sha512-NlZ+e8rhGSGNk/Vfh4IUvlPRjljPCRslbNYgQmYZY+sLXZgahw8fylQguZU3e8ntJDvitfe40f8p3udOiKMS0A==
dependencies:
"@vue/component-compiler-utils" "^3.1.0"
hash-sum "^1.0.2"
loader-utils "^1.1.0"
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
"@intlify/core-base" "9.13.0"
"@intlify/shared" "9.13.0"
"@vue/devtools-api" "^6.5.0"
vue-observe-visibility@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/vue-observe-visibility/-/vue-observe-visibility-1.0.0.tgz#17cf1b2caf74022f0f3c95371468ddf2b9573152"
integrity sha512-s5TFh3s3h3Mhd3jaz3zGzkVHKHnc/0C/gNr30olO99+yw2hl3WBhK3ng3/f9OF+qkW4+l7GkmwfAzDAcY3lCFg==
vue-router@^3.6.5:
version "3.6.5"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.6.5.tgz#95847d52b9a7e3f1361cb605c8e6441f202afad8"
integrity sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==
vue-style-loader@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35"
integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==
vue-loader@^17.4.2:
version "17.4.2"
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.4.2.tgz#f87f0d8adfcbbe8623de9eba1979d41ba223c6da"
integrity sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==
dependencies:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
chalk "^4.1.0"
hash-sum "^2.0.0"
watchpack "^2.4.0"
vue-template-es2015-compiler@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue-observe-visibility@^2.0.0-alpha.1:
version "2.0.0-alpha.1"
resolved "https://registry.yarnpkg.com/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz#1e4eda7b12562161d58984b7e0dea676d83bdb13"
integrity sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==
vue@^2.7.16:
version "2.7.16"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
vue-router@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.3.2.tgz#08096c7765dacc6832f58e35f7a081a8b34116a7"
integrity sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q==
dependencies:
"@vue/compiler-sfc" "2.7.16"
csstype "^3.1.0"
"@vue/devtools-api" "^6.5.1"
vuex@^3.6.2:
version "3.6.2"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"
integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==
vue@^3.4.23:
version "3.4.23"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.23.tgz#9d5a990a71c5bd5446f80377828e6587cfc488d5"
integrity sha512-X1y6yyGJ28LMUBJ0k/qIeKHstGd+BlWQEOT40x3auJFTmpIhpbKLgN7EFsqalnJXq1Km5ybDEsp6BhuWKciUDg==
dependencies:
"@vue/compiler-dom" "3.4.23"
"@vue/compiler-sfc" "3.4.23"
"@vue/runtime-dom" "3.4.23"
"@vue/server-renderer" "3.4.23"
"@vue/shared" "3.4.23"
watchpack@^2.4.1:
vuex@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.1.0.tgz#aa1b3ea5c7385812b074c86faeeec2217872e36c"
integrity sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==
dependencies:
"@vue/devtools-api" "^6.0.0-beta.11"
watchpack@^2.4.0, watchpack@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff"
integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==
@ -9009,11 +9031,6 @@ y18n@^5.0.5:
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.2:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"