This commit is contained in:
CzBiX
2020-04-01 14:34:24 +08:00
parent 055aacf0e1
commit 20893bc991
6 changed files with 207 additions and 179 deletions

View File

@@ -8,32 +8,9 @@
width="300"
>
<drawer v-model="drawerOptions" />
<template v-if="phoneLayout">
<v-spacer />
<v-divider />
<v-expansion-panels
class="drawer-footer"
>
<v-expansion-panel
lazy
@input="drawerFooterOpen"
>
<v-expansion-panel-header>
<div class="d-flex align-center">
<v-icon class="footer-icon shrink">
mdi-information-outline
</v-icon>
<span class="footer-title">
Status info
</span>
</div>
</v-expansion-panel-header>
<v-expansion-panel-content>
<app-footer phone-layout />
</v-expansion-panel-content>
</v-expansion-panel>
<div ref="end" />
</v-expansion-panels>
<template #append>
<DrawerFooter />
</template>
</v-navigation-drawer>
<main-toolbar v-model="drawer" />
@@ -87,9 +64,9 @@ import Torrents from './components/Torrents.vue';
import AppFooter from './components/Footer.vue';
import LogsDialog from './components/dialogs/LogsDialog.vue';
import RssDialog from './components/dialogs/RssDialog.vue';
import DrawerFooter from './components/drawer/DrawerFooter.vue';
import api from './Api';
import { timeout } from './utils';
import Component from 'vue-class-component';
import { Watch } from 'vue-property-decorator';
import { MainData } from './types';
@@ -108,6 +85,7 @@ let appWrapEl: HTMLElement;
GlobalDialog,
GlobalSnackBar,
RssDialog,
DrawerFooter,
},
computed: {
...mapState([
@@ -203,18 +181,6 @@ export default class App extends Vue {
this.task = setTimeout(this.getMainData, this.config.updateInterval);
}
async drawerFooterOpen(v: boolean) {
if (!v) {
return;
}
await timeout(3000);
(this.$refs.end as HTMLElement).scrollIntoView({
behavior: 'smooth',
});
}
onPaste(e: ClipboardEvent) {
if ((e.target as HTMLElement).tagName === 'INPUT') {
return;
@@ -238,26 +204,6 @@ export default class App extends Vue {
</script>
<style lang="scss" scoped>
.phone-layout ::v-deep .v-navigation-drawer__content {
display: flex;
flex-direction: column;
.drawer-footer .v-expansion-panel-header {
padding: 12px 16px 12px 16px;
.footer-icon {
font-size: 22px;
margin-left: 10px;
margin-right: 34px;
}
.footer-title {
font-size: 13px;
font-weight: 500;
}
}
}
.v-footer {
min-height: 36px;
}

View File

@@ -1,11 +1,12 @@
<template>
<div>
<div class="add-form">
<v-btn
fab
bottom
color="primary"
fixed
right
small
@click="dialog = !dialog"
class="btn-add"
:class="{'with-footer': $vuetify.breakpoint.smAndUp}"

View File

@@ -35,22 +35,6 @@
:value="searchQuery"
/>
<v-spacer v-if="!phoneLayout" />
<v-btn
icon
@click="toggleDarkMode"
>
<v-icon v-text="darkModeIcon" />
</v-btn>
<v-select
v-show="!searchBarExpanded"
class="locales"
:items="locales"
prepend-inner-icon="mdi-translate"
v-model="currentLocale"
hide-details
solo
flat
/>
</v-app-bar>
</template>
@@ -59,16 +43,13 @@ import { throttle } from 'lodash';
import Vue from 'vue';
import { mapMutations } from 'vuex';
import i18n, { tr, translations, defaultLocale } from '@/locale';
import { DialogType, DialogConfig, SnackBarConfig, ConfigPayload } from '@/store/types';
import Component from 'vue-class-component';
import { Prop, Emit, Watch } from 'vue-property-decorator';
import { Prop, Emit } from 'vue-property-decorator';
import { ConfigPayload } from '@/store/types';
@Component({
methods: {
...mapMutations([
'showDialog',
'showSnackBar',
'updateConfig',
]),
},
@@ -77,19 +58,10 @@ export default class MainToolbar extends Vue {
@Prop(Boolean)
readonly value!: boolean
showDialog!: (_: DialogConfig) => void
showSnackBar!: (_: SnackBarConfig) => void
updateConfig!: (_: ConfigPayload) => void
locales = this.buildLocales()
currentLocale = i18n.locale()
oldLocale = this.currentLocale
focusedSearch = false
get darkModeIcon() {
return this.$vuetify.theme.dark ? 'mdi-brightness-4' : 'mdi-brightness-6';
}
get searchQuery() {
return this.$store.getters.config.filter.query;
}
@@ -102,30 +74,13 @@ export default class MainToolbar extends Vue {
return this.phoneLayout && (this.focusedSearch || !!this.searchQuery);
}
buildLocales() {
const locales: {}[] = Object.entries(translations).map(([lang, translation]) => {
return {
text: translation.lang,
value: lang,
};
});
return [
{
text: tr('auto'),
value: null,
},
...locales
]
}
@Emit('input')
toggle() {
return !this.value;
}
onSearch = throttle(async (v: string) => {
// avoid hang input
// avoid input lag
await this.$nextTick();
this.updateConfig({
key: 'filter',
@@ -134,54 +89,6 @@ export default class MainToolbar extends Vue {
},
});
}, 400)
async switchLocale(locale: keyof typeof translations | null) {
if (locale === this.oldLocale) {
return;
}
const confirm = await new Promise((resolve) => {
const localeKey = !locale ? defaultLocale : locale
this.showDialog({
content: {
text: tr('dialog.switch_locale.msg', { lang: translations[localeKey].lang }),
type: DialogType.OkCancel,
callback: resolve,
},
});
});
if (!confirm) {
this.currentLocale = this.oldLocale;
return;
}
this.$store.commit('updateConfig', {
key: 'locale',
value: locale,
});
this.showSnackBar({
text: tr('label.reloading'),
})
location.reload();
}
toggleDarkMode() {
const { theme } = this.$vuetify;
theme.dark = !theme.dark;
this.updateConfig({
key: 'darkMode',
value: theme.dark,
});
}
@Watch('currentLocale')
onCurrentLocaleChanged(v: keyof typeof translations) {
this.switchLocale(v)
}
}
</script>
@@ -207,26 +114,6 @@ export default class MainToolbar extends Vue {
flex: 1;
margin: 0 0.5em 0 1em;
}
.locales ::v-deep .v-select__slot {
display: none;
}
}
}
// Fix width
// see: https://github.com/vuetifyjs/vuetify/issues/6275
.locales {
flex-grow: 0;
&::v-deep .v-select__selections {
.v-select__selection {
max-width: none;
}
input {
width: 0;
}
}
}
</style>

View File

@@ -610,7 +610,19 @@ export default class Torrents extends Vue {
}
::v-deep .v-data-footer {
margin-right: 6em;
margin-right: 4em;
.phone-layout & {
justify-content: flex-start;
.v-data-footer__select {
display: none;
}
.v-data-footer__pagination {
margin-left: 0;
}
}
}
}
}

View File

@@ -0,0 +1,180 @@
<template>
<div class="drawer-footer">
<v-expand-transition v-if="showInfo">
<div>
<v-divider />
<AppFooter
phone-layout
/>
</div>
</v-expand-transition>
<v-divider />
<div class="button-bar">
<template v-if="phoneLayout">
<v-btn
icon
@click="showInfo = !showInfo"
>
<v-icon>mdi-information</v-icon>
</v-btn>
</template>
<v-spacer />
<v-menu>
<template #activator="{ on }">
<v-btn
icon
v-on="on"
>
<v-icon>mdi-translate</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item-group
v-model="currentLocale"
color="primary"
>
<v-list-item
v-for="item in locales"
:key="item.value"
:value="item.value"
>
<v-list-item-title>{{ item.text }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-list>
</v-menu>
<v-btn
icon
@click="toggleDarkMode"
>
<v-icon v-text="darkModeIcon" />
</v-btn>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component';
import { mapMutations } from 'vuex';
import { Watch } from 'vue-property-decorator';
import i18n, { tr, translations, defaultLocale, LocaleKey } from '@/locale';
import { DialogType, DialogConfig, SnackBarConfig, ConfigPayload } from '@/store/types';
import AppFooter from '@/components/Footer.vue';
@Component({
components: {
AppFooter,
},
methods: {
...mapMutations([
'showDialog',
'showSnackBar',
'updateConfig',
]),
},
})
export default class DrawerFooter extends Vue {
locales = this.buildLocales()
currentLocale = i18n.locale()
oldLocale = this.currentLocale
showInfo = false
showDialog!: (_: DialogConfig) => void
showSnackBar!: (_: SnackBarConfig) => void
updateConfig!: (_: ConfigPayload) => void
get darkModeIcon() {
return this.$vuetify.theme.dark ? 'mdi-brightness-4' : 'mdi-brightness-7';
}
get phoneLayout() {
return this.$vuetify.breakpoint.xsOnly;
}
buildLocales() {
const locales: {}[] = Object.entries(translations).map(([lang, translation]) => {
return {
text: translation.lang,
value: lang,
};
});
return [
{
text: tr('auto'),
value: null,
},
...locales
]
}
async switchLocale(locale: LocaleKey) {
if (locale === this.oldLocale) {
return;
}
const confirm = await new Promise((resolve) => {
const localeKey = !locale ? defaultLocale : locale
this.showDialog({
content: {
text: tr('dialog.switch_locale.msg', { lang: translations[localeKey].lang }),
type: DialogType.OkCancel,
callback: resolve,
},
});
});
if (!confirm) {
this.currentLocale = this.oldLocale;
return;
}
this.updateConfig({
key: 'locale',
value: locale,
});
this.showSnackBar({
text: tr('label.reloading'),
})
location.reload();
}
toggleDarkMode() {
const { theme } = this.$vuetify;
theme.dark = !theme.dark;
this.updateConfig({
key: 'darkMode',
value: theme.dark,
});
}
@Watch('currentLocale')
onCurrentLocaleChanged(v: LocaleKey) {
this.switchLocale(v)
}
}
</script>
<style lang="scss" scoped>
.button-bar {
display: flex;
padding: 0.5em;
}
.footer {
padding: 1em;
}
</style>

View File

@@ -9,6 +9,8 @@ export const translations = {
'zh-CN': zhCn,
}
export type LocaleKey = keyof typeof translations | null;
const polyglot = new Polyglot({
phrases: translations.en
});
@@ -18,7 +20,7 @@ function matchLocale() {
for (const code of languages) {
if (code in translations) {
return code as keyof typeof translations;
return (code as LocaleKey)!;
}
}
@@ -28,7 +30,7 @@ function matchLocale() {
export const defaultLocale = matchLocale()
function updateLocale() {
let locale: keyof typeof translations | undefined | null = loadConfig()['locale'];
let locale: LocaleKey | undefined = loadConfig()['locale'];
if (!locale) {
locale = defaultLocale;