diff options
Diffstat (limited to 'src/client/app/common')
17 files changed, 253 insertions, 23 deletions
diff --git a/src/client/app/common/scripts/collect-page-vars.ts b/src/client/app/common/scripts/collect-page-vars.ts index 4c40d5d88e..a4096fb2c2 100644 --- a/src/client/app/common/scripts/collect-page-vars.ts +++ b/src/client/app/common/scripts/collect-page-vars.ts @@ -32,6 +32,12 @@ export function collectPageVars(content) { type: 'number', value: 0 }); + } else if (x.type === 'radioButton') { + pageVars.push({ + name: x.name, + type: 'string', + value: x.default || '' + }); } else if (x.children) { collect(x.children); } diff --git a/src/client/app/common/scripts/get-face.ts b/src/client/app/common/scripts/get-face.ts index b523948bd3..19f2bdb064 100644 --- a/src/client/app/common/scripts/get-face.ts +++ b/src/client/app/common/scripts/get-face.ts @@ -4,7 +4,8 @@ const faces = [ '🐡( \'-\' 🐡 )フグパンチ!!!!', '✌️(´・_・`)✌️', '(。>﹏<。)', - '(Δ・x・Δ)' + '(Δ・x・Δ)', + '(コ`・ヘ・´ケ)' ]; export default () => faces[Math.floor(Math.random() * faces.length)]; diff --git a/src/client/app/common/views/components/mention.vue b/src/client/app/common/views/components/mention.vue index f212fd3ca5..4e9f9e90d6 100644 --- a/src/client/app/common/views/components/mention.vue +++ b/src/client/app/common/views/components/mention.vue @@ -1,11 +1,17 @@ <template> -<router-link class="ldlomzub" :to="`/${ canonical }`" v-user-preview="canonical"> +<router-link class="ldlomzub" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')"> <span class="me" v-if="isMe">{{ $t('@.you') }}</span> <span class="main"> <span class="username">@{{ username }}</span> <span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span> </span> </router-link> +<a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else> + <span class="main"> + <span class="username">@{{ username }}</span> + <span class="host" :class="{ fade: $store.state.settings.contrastedAcct }">@{{ toUnicode(host) }}</span> + </span> +</a> </template> <script lang="ts"> @@ -32,6 +38,15 @@ export default Vue.extend({ }; }, computed: { + url(): string { + switch (this.host) { + case 'twitter.com': + case 'github.com': + return `https://${this.host}/${this.username}`; + default: + return `/${this.canonical}`; + } + }, canonical(): string { return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`; }, diff --git a/src/client/app/common/views/components/misskey-flavored-markdown.vue b/src/client/app/common/views/components/misskey-flavored-markdown.vue index 64496f9c84..963efd9ab8 100644 --- a/src/client/app/common/views/components/misskey-flavored-markdown.vue +++ b/src/client/app/common/views/components/misskey-flavored-markdown.vue @@ -30,6 +30,7 @@ export default Vue.extend({ border-radius 4px >>> .quote + display block margin 8px padding 6px 0 6px 12px color var(--mfmQuote) diff --git a/src/client/app/common/views/components/page/page.block.vue b/src/client/app/common/views/components/page/page.block.vue index 1c421fc2c0..56d1822013 100644 --- a/src/client/app/common/views/components/page/page.block.vue +++ b/src/client/app/common/views/components/page/page.block.vue @@ -16,10 +16,11 @@ import XIf from './page.if.vue'; import XTextarea from './page.textarea.vue'; import XPost from './page.post.vue'; import XCounter from './page.counter.vue'; +import XRadioButton from './page.radio-button.vue'; export default Vue.extend({ components: { - XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter + XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton }, props: { diff --git a/src/client/app/common/views/components/page/page.radio-button.vue b/src/client/app/common/views/components/page/page.radio-button.vue new file mode 100644 index 0000000000..27c11bebad --- /dev/null +++ b/src/client/app/common/views/components/page/page.radio-button.vue @@ -0,0 +1,37 @@ +<template> +<div> + <div>{{ script.interpolate(value.title) }}</div> + <ui-radio v-for="x in value.values" v-model="v" :value="x" :key="x">{{ x }}</ui-radio> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; + +export default Vue.extend({ + props: { + value: { + required: true + }, + script: { + required: true + } + }, + + data() { + return { + v: this.value.default, + }; + }, + + watch: { + v() { + this.script.aiScript.updatePageVar(this.value.name, this.v); + this.script.eval(); + } + } +}); +</script> + +<style lang="stylus" scoped> +</style> diff --git a/src/client/app/common/views/components/poll-editor.vue b/src/client/app/common/views/components/poll-editor.vue index f7a4d3af8c..49940134c7 100644 --- a/src/client/app/common/views/components/poll-editor.vue +++ b/src/client/app/common/views/components/poll-editor.vue @@ -26,13 +26,19 @@ <option value="after">{{ $t('after') }}</option> </ui-select> <section v-if="expiration === 'at'"> - <ui-input v-model="atDate" type="date">{{ $t('deadline-date') }}</ui-input> - <ui-input v-model="atTime" type="time">{{ $t('deadline-time') }}</ui-input> + <ui-input v-model="atDate" type="date"> + <template #title>{{ $t('deadline-date') }}</template> + </ui-input> + <ui-input v-model="atTime" type="time"> + <template #title>{{ $t('deadline-time') }}</template> + </ui-input> </section> <section v-if="expiration === 'after'"> - <ui-input v-model="after" type="number">{{ $t('interval') }}</ui-input> + <ui-input v-model="after" type="number"> + <template #title>{{ $t('interval') }}</template> + </ui-input> <ui-select v-model="unit"> - <template #label>{{ $t('unit') }}</template> + <template #title>{{ $t('unit') }}</template> <option value="second">{{ $t('second') }}</option> <option value="minute">{{ $t('minute') }}</option> <option value="hour">{{ $t('hour') }}</option> diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue index 970d430069..ff534d37ce 100644 --- a/src/client/app/common/views/components/reaction-picker.vue +++ b/src/client/app/common/views/components/reaction-picker.vue @@ -276,6 +276,7 @@ export default Vue.extend({ font-size 14px color var(--popupFg) border-bottom solid var(--lineWidth) var(--faceDivider) + line-height 20px > .buttons padding 4px 4px 8px 4px diff --git a/src/client/app/common/views/components/settings/app-type.vue b/src/client/app/common/views/components/settings/app-type.vue index 90ff28803b..d163f1e746 100644 --- a/src/client/app/common/views/components/settings/app-type.vue +++ b/src/client/app/common/views/components/settings/app-type.vue @@ -29,8 +29,25 @@ export default Vue.extend({ computed: { appTypeForce: { get() { return this.$store.state.device.appTypeForce; }, - set(value) { this.$store.commit('device/set', { key: 'appTypeForce', value }); } + set(value) { + this.$store.commit('device/set', { key: 'appTypeForce', value }); + this.reload(); + } }, }, + + methods: { + reload() { + this.$root.dialog({ + type: 'warning', + text: this.$t('@.reload-to-apply-the-setting'), + showCancelButton: true + }).then(({ canceled }) => { + if (!canceled) { + location.reload(); + } + }); + }, + } }); </script> diff --git a/src/client/app/common/views/components/settings/profile.vue b/src/client/app/common/views/components/settings/profile.vue index a22fd6df98..edfc5a9edf 100644 --- a/src/client/app/common/views/components/settings/profile.vue +++ b/src/client/app/common/views/components/settings/profile.vue @@ -51,6 +51,26 @@ <template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template> </ui-input> + <div class="fields"> + <header>{{ $t('profile-metadata') }}</header> + <ui-horizon-group> + <ui-input v-model="fieldName0">{{ $t('metadata-label') }}</ui-input> + <ui-input v-model="fieldValue0">{{ $t('metadata-content') }}</ui-input> + </ui-horizon-group> + <ui-horizon-group> + <ui-input v-model="fieldName1">{{ $t('metadata-label') }}</ui-input> + <ui-input v-model="fieldValue1">{{ $t('metadata-content') }}</ui-input> + </ui-horizon-group> + <ui-horizon-group> + <ui-input v-model="fieldName2">{{ $t('metadata-label') }}</ui-input> + <ui-input v-model="fieldValue2">{{ $t('metadata-content') }}</ui-input> + </ui-horizon-group> + <ui-horizon-group> + <ui-input v-model="fieldName3">{{ $t('metadata-label') }}</ui-input> + <ui-input v-model="fieldValue3">{{ $t('metadata-content') }}</ui-input> + </ui-horizon-group> + </div> + <ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> </ui-form> </section> @@ -189,6 +209,17 @@ export default Vue.extend({ this.isLocked = this.$store.state.i.isLocked; this.carefulBot = this.$store.state.i.carefulBot; this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed; + + if (this.$store.state.i.fields) { + this.fieldName0 = this.$store.state.i.fields[0].name; + this.fieldValue0 = this.$store.state.i.fields[0].value; + this.fieldName1 = this.$store.state.i.fields[1].name; + this.fieldValue1 = this.$store.state.i.fields[1].value; + this.fieldName2 = this.$store.state.i.fields[2].name; + this.fieldValue2 = this.$store.state.i.fields[2].value; + this.fieldName3 = this.$store.state.i.fields[3].name; + this.fieldValue3 = this.$store.state.i.fields[3].value; + } }, methods: { @@ -237,6 +268,13 @@ export default Vue.extend({ }, save(notify) { + const fields = [ + { name: this.fieldName0, value: this.fieldValue0 }, + { name: this.fieldName1, value: this.fieldValue1 }, + { name: this.fieldName2, value: this.fieldValue2 }, + { name: this.fieldName3, value: this.fieldValue3 }, + ]; + this.saving = true; this.$root.api('i/update', { @@ -247,6 +285,7 @@ export default Vue.extend({ birthday: this.birthday || null, avatarId: this.avatarId || undefined, bannerId: this.bannerId || undefined, + fields, isCat: !!this.isCat, isBot: !!this.isBot, isLocked: !!this.isLocked, @@ -265,6 +304,29 @@ export default Vue.extend({ text: this.$t('saved') }); } + }).catch(err => { + this.saving = false; + switch(err.id) { + case 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191': + this.$root.dialog({ + type: 'error', + title: this.$t('unable-to-process'), + text: this.$t('avatar-not-an-image') + }); + break; + case '75aedb19-2afd-4e6d-87fc-67941256fa60': + this.$root.dialog({ + type: 'error', + title: this.$t('unable-to-process'), + text: this.$t('banner-not-an-image') + }); + break; + default: + this.$root.dialog({ + type: 'error', + text: this.$t('unable-to-process') + }); + } }); }, @@ -366,4 +428,11 @@ export default Vue.extend({ height 72px margin auto +.fields + > header + padding 8px 0px + font-weight bold + > div + padding-left 16px + </style> diff --git a/src/client/app/common/views/components/settings/settings.vue b/src/client/app/common/views/components/settings/settings.vue index 281524979e..401d9423ae 100644 --- a/src/client/app/common/views/components/settings/settings.vue +++ b/src/client/app/common/views/components/settings/settings.vue @@ -143,13 +143,17 @@ <ui-input v-model="webSearchEngine">{{ $t('@._settings.web-search-engine') }} <template #desc>{{ $t('@._settings.web-search-engine-desc') }}</template> </ui-input> + <ui-button @click="save('webSearchEngine', webSearchEngine)"><fa :icon="faSave"/> {{ $t('@._settings.save') }}</ui-button> </section> <section v-if="!$root.isMobile"> <header>{{ $t('@._settings.paste') }}</header> <ui-input v-model="pastedFileName">{{ $t('@._settings.pasted-file-name') }} - <template #desc>{{ $t('@._settings.pasted-file-name-desc') }}</template> + <template v-if="pastedFileName === this.$store.state.settings.pastedFileName" #desc>{{ $t('@._settings.pasted-file-name-desc') }}</template> + <template v-else #desc>{{ pastedFileNamePreview() }}</template> </ui-input> + <ui-button @click="save('pastedFileName', pastedFileName)"><fa :icon="faSave"/> {{ $t('@._settings.save') }}</ui-button> + <ui-switch v-model="pasteDialog">{{ $t('@._settings.paste-dialog') }} <template #desc>{{ $t('@._settings.paste-dialog-desc') }}</template> </ui-switch> @@ -289,6 +293,8 @@ import XNotification from './notification.vue'; import { url, version } from '../../../../config'; import checkForUpdate from '../../../scripts/check-for-update'; +import { formatTimeString } from '../../../../../../misc/format-time-string'; +import { faSave } from '@fortawesome/free-regular-svg-icons'; export default Vue.extend({ i18n: i18n(), @@ -319,8 +325,11 @@ export default Vue.extend({ return { meta: null, version, + webSearchEngine: this.$store.state.settings.webSearchEngine, + pastedFileName : this.$store.state.settings.pastedFileName, latestVersion: undefined, - checkingForUpdate: false + checkingForUpdate: false, + faSave }; }, computed: { @@ -419,16 +428,6 @@ export default Vue.extend({ set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); } }, - webSearchEngine: { - get() { return this.$store.state.settings.webSearchEngine; }, - set(value) { this.$store.dispatch('settings/set', { key: 'webSearchEngine', value }); } - }, - - pastedFileName: { - get() { return this.$store.state.settings.pastedFileName; }, - set(value) { this.$store.dispatch('settings/set', { key: 'pastedFileName', value }); } - }, - pasteDialog: { get() { return this.$store.state.settings.pasteDialog; }, set(value) { this.$store.dispatch('settings/set', { key: 'pasteDialog', value }); } @@ -565,6 +564,17 @@ export default Vue.extend({ } }); }, + save(key, value) { + this.$store.dispatch('settings/set', { + key, + value + }).then(() => { + this.$root.dialog({ + type: 'success', + text: this.$t('@._settings.saved') + }) + }); + }, customizeHome() { location.href = '/?customize'; }, @@ -600,7 +610,10 @@ export default Vue.extend({ const sound = new Audio(`${url}/assets/message.mp3`); sound.volume = this.$store.state.device.soundVolume; sound.play(); - } + }, + pastedFileNamePreview() { + return `${formatTimeString(new Date(), this.pastedFileName).replace(/{{number}}/g, `1`)}.png` + }, } }); </script> diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue index 421d09a4dd..893f6575fb 100644 --- a/src/client/app/common/views/components/signup.vue +++ b/src/client/app/common/views/components/signup.vue @@ -43,7 +43,7 @@ </i18n> </ui-switch> <div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div> - <ui-button type="submit" :disabled="!(meta.ToSUrl ? ToSAgreement : true) || passwordRetypeState == 'not-match'">{{ $t('create') }}</ui-button> + <ui-button type="submit" :disabled=" submitting || !(meta.ToSUrl ? ToSAgreement : true) || passwordRetypeState == 'not-match'">{{ $t('create') }}</ui-button> </template> </form> </template> @@ -70,6 +70,7 @@ export default Vue.extend({ passwordStrength: '', passwordRetypeState: null, meta: {}, + submitting: false, ToSAgreement: false } }, @@ -145,6 +146,9 @@ export default Vue.extend({ }, onSubmit() { + if (this.submitting) return; + this.submitting = true; + this.$root.api('signup', { username: this.username, password: this.password, @@ -159,6 +163,8 @@ export default Vue.extend({ location.href = '/'; }); }).catch(() => { + this.submitting = false; + this.$root.dialog({ type: 'error', text: this.$t('some-error') diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue index 476c671e77..80aae5999d 100644 --- a/src/client/app/common/views/components/url-preview.vue +++ b/src/client/app/common/views/components/url-preview.vue @@ -66,6 +66,7 @@ export default Vue.extend({ (this.url.substr(local.length) === '/') || this.url.substr(local.length).startsWith('/@') || this.url.substr(local.length).startsWith('/notes/') || + this.url.substr(local.length).startsWith('/tags/') || this.url.substr(local.length).startsWith('/pages/'); return { local, diff --git a/src/client/app/common/views/components/url.vue b/src/client/app/common/views/components/url.vue index b1ca3f285c..3a304ad6e7 100644 --- a/src/client/app/common/views/components/url.vue +++ b/src/client/app/common/views/components/url.vue @@ -28,6 +28,7 @@ export default Vue.extend({ (this.url.substr(local.length) === '/') || this.url.substr(local.length).startsWith('/@') || this.url.substr(local.length).startsWith('/notes/') || + this.url.substr(local.length).startsWith('/tags/') || this.url.substr(local.length).startsWith('/pages/')); return { local, diff --git a/src/client/app/common/views/pages/page-editor/els/page-editor.el.radio-button.vue b/src/client/app/common/views/pages/page-editor/els/page-editor.el.radio-button.vue new file mode 100644 index 0000000000..3401c46f47 --- /dev/null +++ b/src/client/app/common/views/pages/page-editor/els/page-editor.el.radio-button.vue @@ -0,0 +1,53 @@ +<template> +<x-container @remove="() => $emit('remove')" :draggable="true"> + <template #header><fa :icon="faBolt"/> {{ $t('blocks.radioButton') }}</template> + + <section style="padding: 0 16px 16px 16px;"> + <ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._radioButton.name') }}</span></ui-input> + <ui-input v-model="value.title"><span>{{ $t('blocks._radioButton.title') }}</span></ui-input> + <ui-textarea v-model="values"><span>{{ $t('blocks._radioButton.values') }}</span></ui-textarea> + <ui-input v-model="value.default"><span>{{ $t('blocks._radioButton.default') }}</span></ui-input> + </section> +</x-container> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; +import i18n from '../../../../../i18n'; +import XContainer from '../page-editor.container.vue'; + +export default Vue.extend({ + i18n: i18n('pages'), + + components: { + XContainer + }, + + props: { + value: { + required: true + }, + }, + + data() { + return { + values: '', + faBolt, faMagic + }; + }, + + watch: { + values() { + Vue.set(this.value, 'values', this.values.split('\n')); + } + }, + + created() { + if (this.value.name == null) Vue.set(this.value, 'name', ''); + if (this.value.title == null) Vue.set(this.value, 'title', ''); + if (this.value.values == null) Vue.set(this.value, 'values', []); + this.values = this.value.values.join('\n'); + }, +}); +</script> diff --git a/src/client/app/common/views/pages/page-editor/page-editor.blocks.vue b/src/client/app/common/views/pages/page-editor/page-editor.blocks.vue index c5f3419e7b..4d7293231f 100644 --- a/src/client/app/common/views/pages/page-editor/page-editor.blocks.vue +++ b/src/client/app/common/views/pages/page-editor/page-editor.blocks.vue @@ -19,10 +19,11 @@ import XSwitch from './els/page-editor.el.switch.vue'; import XIf from './els/page-editor.el.if.vue'; import XPost from './els/page-editor.el.post.vue'; import XCounter from './els/page-editor.el.counter.vue'; +import XRadioButton from './els/page-editor.el.radio-button.vue'; export default Vue.extend({ components: { - XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter + XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton }, props: { diff --git a/src/client/app/common/views/pages/page-editor/page-editor.vue b/src/client/app/common/views/pages/page-editor/page-editor.vue index ade7d86991..0162915c38 100644 --- a/src/client/app/common/views/pages/page-editor/page-editor.vue +++ b/src/client/app/common/views/pages/page-editor/page-editor.vue @@ -342,6 +342,7 @@ export default Vue.extend({ label: this.$t('input-blocks'), items: [ { value: 'button', text: this.$t('blocks.button') }, + { value: 'radioButton', text: this.$t('blocks.radioButton') }, { value: 'textInput', text: this.$t('blocks.textInput') }, { value: 'textareaInput', text: this.$t('blocks.textareaInput') }, { value: 'numberInput', text: this.$t('blocks.numberInput') }, |