diff --git a/src/i18n.js b/src/i18n.js index 9460c8dc..c71e7a1e 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -205,7 +205,7 @@ export class TranslationManager extends Module { if ( locale === 'en' ) return {}; - if ( locale === 'de' ) + /*if ( locale === 'de' ) return { site: { menu_button: 'FrankerFaceZ Leitstelle' @@ -272,8 +272,8 @@ export class TranslationManager extends Module { _: 'Erweiterung' }, - 'inherited-from': 'Vererbt von: %{title}', - 'overridden-by': 'Überschrieben von: %{title}' + 'inherited-from': 'Vererbt von: {title}', + 'overridden-by': 'Überschrieben von: {title}' }, 'main-menu': { @@ -306,7 +306,7 @@ export class TranslationManager extends Module { 'main-menu': { search: '検索設定', - version: 'バージョン%{version}', + version: 'バージョン{version}', about: { _: '約', @@ -314,7 +314,7 @@ export class TranslationManager extends Module { support: '対応' } } - } + }*/ const resp = await fetch(`${SERVER}/script/i18n/${locale}.json`); if ( ! resp.ok ) { diff --git a/src/main.js b/src/main.js index 0b76d4f5..7ad8c93a 100644 --- a/src/main.js +++ b/src/main.js @@ -149,7 +149,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}` FrankerFaceZ.Logger = Logger; const VER = FrankerFaceZ.version_info = { - major: 4, minor: 0, revision: 0, extra: '-rc19', + major: 4, minor: 0, revision: 0, extra: '-rc19.1', commit: __git_commit__, build: __webpack_hash__, toString: () => diff --git a/src/modules/chat/actions/index.jsx b/src/modules/chat/actions/index.jsx index cb903bf5..93e63aba 100644 --- a/src/modules/chat/actions/index.jsx +++ b/src/modules/chat/actions/index.jsx @@ -39,6 +39,16 @@ export default class Actions extends Module { type: 'array_merge', always_inherit: true, + // Clean up after Vue being stupid. + process(ctx, val) { + if ( Array.isArray(val) ) + for(const entry of val) + if ( entry.i18n && typeof entry.i18n !== 'string' ) + delete entry.i18n; + + return val; + }, + ui: { path: 'Chat > Actions > Reasons >> Custom Reasons', component: 'chat-reasons', diff --git a/src/modules/chat/actions/types.jsx b/src/modules/chat/actions/types.jsx index 742ab2cd..1b80e3fb 100644 --- a/src/modules/chat/actions/types.jsx +++ b/src/modules/chat/actions/types.jsx @@ -21,7 +21,7 @@ export const open_url = { editor: () => import(/* webpackChunkName: 'main-menu' */ './components/edit-url.vue'), title: 'Open URL', - description: '%{options.url}', + description: '{options.url}', tooltip(data) { const url = this.replaceVariables(data.options.url, data); @@ -67,7 +67,7 @@ export const chat = { }, title: 'Chat Command', - description: '%{options.command}', + description: '{options.command}', editor: () => import(/* webpackChunkName: 'main-menu' */ './components/edit-chat.vue'), @@ -183,7 +183,7 @@ export const timeout = { editor: () => import(/* webpackChunkName: 'main-menu' */ './components/edit-timeout.vue'), title: 'Timeout User', - description: '{options.duration,number} second%{options.duration,en_plural}', + description: '{options.duration,number} second{options.duration,en_plural}', reason_text(data) { return this.i18n.t('chat.actions.timeout-reason', diff --git a/src/modules/chat/rich_providers.js b/src/modules/chat/rich_providers.js index 193e1408..2104f598 100644 --- a/src/modules/chat/rich_providers.js +++ b/src/modules/chat/rich_providers.js @@ -175,7 +175,7 @@ export const Videos = { }); else if ( game ) - desc_1 = this.i18n.t('clip.desc.1.playing', '{user} playing %{game}', { + desc_1 = this.i18n.t('clip.desc.1.playing', '{user} playing {game}', { user, game: game_display }); diff --git a/src/modules/main_menu/components/main-menu.vue b/src/modules/main_menu/components/main-menu.vue index ba248bbf..902e39cf 100644 --- a/src/modules/main_menu/components/main-menu.vue +++ b/src/modules/main_menu/components/main-menu.vue @@ -65,6 +65,8 @@ :modal="nav" :filter="filter" @change-item="changeItem" + @mark-seen="markSeen" + @mark-expanded="markExpanded" @navigate="navigate" /> @@ -97,6 +99,7 @@ :item="currentItem" :filter="filter" @change-item="changeItem" + @mark-seen="markSeen" @navigate="navigate" /> @@ -171,6 +174,8 @@ export default { return; } + this.markSeen(item); + this.currentItem = item; let current = item; while(current = current.parent) // eslint-disable-line no-cond-assign diff --git a/src/modules/main_menu/components/menu-page.vue b/src/modules/main_menu/components/menu-page.vue index 37856d59..88879544 100644 --- a/src/modules/main_menu/components/menu-page.vue +++ b/src/modules/main_menu/components/menu-page.vue @@ -46,6 +46,7 @@ > {{ t(i.i18n_key, i.title, i) }} + {{ i.unseen }} @@ -62,6 +63,7 @@ :item="i" :filter="filter" @change-item="changeItem" + @mark-seen="markSeen" @navigate="navigate" /> @@ -94,6 +96,10 @@ export default { return item.search_terms.includes(this.filter); }, + markSeen(item) { + this.$emit('mark-seen', item); + }, + changeItem(item) { this.$emit('change-item', item); }, diff --git a/src/modules/main_menu/components/menu-tree.vue b/src/modules/main_menu/components/menu-tree.vue index b71e7268..9c6c4929 100644 --- a/src/modules/main_menu/components/menu-tree.vue +++ b/src/modules/main_menu/components/menu-tree.vue @@ -38,6 +38,9 @@ {{ item.pill_i18n_key ? t(item.pill_i18n_key, item.pill, item) : item.pill }} + + {{ item.unseen }} + @@ -75,11 +80,16 @@ function findNextVisible(node, modal) { } -function recursiveExpand(node) { - node.expanded = true; +function recursiveExpand(node, vue) { + if ( ! node.expanded ) { + node.expanded = true; + if ( vue ) + vue.$emit('mark-expanded', node); + } + if ( node.items ) for(const item of node.items) - recursiveExpand(item); + recursiveExpand(item, vue); } @@ -121,12 +131,13 @@ export default { else if ( this.currentItem === item ) item.expanded = false; + this.$emit('mark-expanded', item); this.$emit('change-item', item); }, expandAll() { for(const item of this.modal) - recursiveExpand(item); + recursiveExpand(item, this); }, prevItem() { @@ -164,9 +175,10 @@ export default { const i = this.currentItem; - if ( i.expanded && i.items ) + if ( i.expanded && i.items ) { i.expanded = false; - else if ( i.parent ) + this.$emit('mark-expanded', i); + } else if ( i.parent ) this.$emit('change-item', i.parent); }, @@ -176,11 +188,13 @@ export default { const i = this.currentItem; if ( i.expanded && i.items ) this.$emit('change-item', i.items[0]); - else + else { i.expanded = true; + this.$emit('mark-expanded', i); + } if ( event.ctrlKey ) - recursiveExpand(this.currentItem); + recursiveExpand(this.currentItem, this); } } } diff --git a/src/modules/main_menu/components/setting-check-box.vue b/src/modules/main_menu/components/setting-check-box.vue index 3fde3319..cde0614e 100644 --- a/src/modules/main_menu/components/setting-check-box.vue +++ b/src/modules/main_menu/components/setting-check-box.vue @@ -15,6 +15,7 @@ -
+
{{ key }}
diff --git a/src/modules/viewer_cards/card.vue b/src/modules/viewer_cards/card.vue index 0aa95e5f..140f0340 100644 --- a/src/modules/viewer_cards/card.vue +++ b/src/modules/viewer_cards/card.vue @@ -48,7 +48,7 @@ {{ t('viewer-card.age', '{age,humantime}', {age: userAge}) }} diff --git a/src/sites/twitch-twilight/modules/featured-follow.vue b/src/sites/twitch-twilight/modules/featured-follow.vue index 38da3eb0..aac902d5 100644 --- a/src/sites/twitch-twilight/modules/featured-follow.vue +++ b/src/sites/twitch-twilight/modules/featured-follow.vue @@ -37,7 +37,7 @@ v-if="user.following" :disabled="user.loading" :class="{'tw-button--disabled': user.loading}" - :data-title="user.loading ? null : t('featured-follow.button.unfollow', 'Unfollow %{user}', {user: user.displayName})" + :data-title="user.loading ? null : t('featured-follow.button.unfollow', 'Unfollow {user}', {user: user.displayName})" data-tooltip-type="html" class="tw-button tw-button--status tw-button--success ffz-tooltip ffz--featured-button-unfollow" @click="clickWithTip($event, unfollowUser, user.id)" diff --git a/src/sites/twitch-twilight/modules/menu_button.jsx b/src/sites/twitch-twilight/modules/menu_button.jsx index cc12b8ca..363e1774 100644 --- a/src/sites/twitch-twilight/modules/menu_button.jsx +++ b/src/sites/twitch-twilight/modules/menu_button.jsx @@ -19,6 +19,7 @@ export default class MenuButton extends SiteModule { this._pill_content = null; this._has_update = false; this._important_update = false; + this._new_settings = 0; this.NavBar = this.fine.define( 'nav-bar', @@ -26,6 +27,22 @@ export default class MenuButton extends SiteModule { ); } + get new_settings() { + return this._new_settings; + } + + set new_settings(val) { + if ( val === this._new_settings ) + return; + + this._new_settings = val; + this.update(); + } + + get has_new() { + return this._new_settings > 0 + } + get important_update() { return this._important_update; } @@ -115,6 +132,11 @@ export default class MenuButton extends SiteModule { {this.i18n.t('site.menu_button.dev', 'dev')} )} + {this.has_new && ! pill && (
+
+ {this.i18n.formatNumber(this.new_settings)} +
+
)} {pill && (
@@ -127,6 +149,9 @@ export default class MenuButton extends SiteModule { {this.has_update && (
{this.i18n.t('site.menu_button.update-desc', 'There is an update available. Please refresh your page.')}
)} + {this.has_new && (
+ {this.i18n.t('site.menu_button.new-desc', 'There {count,plural,one {is one new setting} other {are # new settings}}.', {count: this._new_settings})} +
)} {DEBUG && (
{this.i18n.t('site.menu_button.dev-desc', 'You are running a developer build of FrankerFaceZ.')}
)} diff --git a/src/std-components/tab-container.vue b/src/std-components/tab-container.vue index 372566a3..60929779 100644 --- a/src/std-components/tab-container.vue +++ b/src/std-components/tab-container.vue @@ -27,9 +27,10 @@ :class="{'active': selected === idx, 'ffz-unmatched-item': showing && ! shouldShow(i)}" role="tab" class="tab tw-pd-y-05 tw-pd-x-1" - @click="selected = idx" + @click="select(idx)" > {{ t(i.i18n_key, i.title, i) }} + {{ i.unseen }}
0 ) + if ( this.selected > 0 ) { this.selected--; + this.markSeen(); + } + }, + + select(idx) { + this.selected = idx; + this.markSeen(); }, nextTab() { - if ( this.selected + 1 < this.item.tabs.length ) + if ( this.selected + 1 < this.item.tabs.length ) { this.selected++; + this.markSeen(); + } }, shouldShow(item) { diff --git a/src/utilities/translation-core.js b/src/utilities/translation-core.js index 0626e68f..8b09bc23 100644 --- a/src/utilities/translation-core.js +++ b/src/utilities/translation-core.js @@ -8,6 +8,7 @@ import dayjs from 'dayjs'; import Parser from '@ffz/icu-msgparser'; import {get} from 'utilities/object'; +import {duration_to_string} from 'utilities/time'; // ============================================================================ @@ -58,11 +59,11 @@ export const DEFAULT_TYPES = { return this.formatDateTime(val, node.f); }, - duration(val, node) { - + duration(val) { + return duration_to_string(val); }, - localestring(val, node) { + localestring(val) { return this.toLocaleString(val); },