summaryrefslogtreecommitdiff
path: root/src/client/components
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-08-17 22:01:46 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-08-17 22:01:46 +0900
commitdf67836c1ad281d2622b52bdf7c767b2dfc0e6a5 (patch)
tree5a2c4e5b681857d846d5fea1f4058b72e80651da /src/client/components
parentMerge branch 'develop' (diff)
parentMerge branch 'develop' of https://github.com/misskey-dev/misskey into develop (diff)
downloadmisskey-df67836c1ad281d2622b52bdf7c767b2dfc0e6a5.tar.gz
misskey-df67836c1ad281d2622b52bdf7c767b2dfc0e6a5.tar.bz2
misskey-df67836c1ad281d2622b52bdf7c767b2dfc0e6a5.zip
Merge branch 'develop'
Diffstat (limited to 'src/client/components')
-rw-r--r--src/client/components/date-separated-list.vue4
-rw-r--r--src/client/components/global/loading.vue32
-rw-r--r--src/client/components/note-detailed.vue40
-rw-r--r--src/client/components/note.vue32
-rw-r--r--src/client/components/notes.vue23
-rw-r--r--src/client/components/post-form.vue4
-rwxr-xr-xsrc/client/components/signin.vue4
-rw-r--r--src/client/components/signup.vue8
-rw-r--r--src/client/components/ui/popup.vue2
-rw-r--r--src/client/components/updated.vue58
10 files changed, 173 insertions, 34 deletions
diff --git a/src/client/components/date-separated-list.vue b/src/client/components/date-separated-list.vue
index 7a4cc5ef98..fa0b6d669c 100644
--- a/src/client/components/date-separated-list.vue
+++ b/src/client/components/date-separated-list.vue
@@ -93,13 +93,13 @@ export default defineComponent({
});
return h(this.$store.state.animation ? TransitionGroup : 'div', this.$store.state.animation ? {
- class: 'sqadhkmv' + (this.noGap ? ' noGap _block' : ''),
+ class: 'sqadhkmv' + (this.noGap ? ' noGap' : ''),
name: 'list',
tag: 'div',
'data-direction': this.direction,
'data-reversed': this.reversed ? 'true' : 'false',
} : {
- class: 'sqadhkmv' + (this.noGap ? ' noGap _block' : ''),
+ class: 'sqadhkmv' + (this.noGap ? ' noGap' : ''),
}, {
default: renderChildren
});
diff --git a/src/client/components/global/loading.vue b/src/client/components/global/loading.vue
index 9b810f0a16..7bde53c12e 100644
--- a/src/client/components/global/loading.vue
+++ b/src/client/components/global/loading.vue
@@ -1,5 +1,5 @@
<template>
-<div class="yxspomdl" :class="{ inline, colored }">
+<div class="yxspomdl" :class="{ inline, colored, mini }">
<div class="ring"></div>
</div>
</template>
@@ -18,7 +18,12 @@ export default defineComponent({
type: Boolean,
required: false,
default: true
- }
+ },
+ mini: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
}
});
</script>
@@ -38,6 +43,8 @@ export default defineComponent({
text-align: center;
cursor: wait;
+ --size: 48px;
+
&.colored {
color: var(--accent);
}
@@ -45,19 +52,12 @@ export default defineComponent({
&.inline {
display: inline;
padding: 0;
+ --size: 32px;
+ }
- > .ring:after {
- width: 32px;
- height: 32px;
- }
-
- > .ring {
- &:before,
- &:after {
- width: 32px;
- height: 32px;
- }
- }
+ &.mini {
+ padding: 16px;
+ --size: 32px;
}
> .ring {
@@ -70,8 +70,8 @@ export default defineComponent({
content: " ";
display: block;
box-sizing: border-box;
- width: 48px;
- height: 48px;
+ width: var(--size);
+ height: var(--size);
border-radius: 50%;
border: solid 4px;
}
diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue
index d601052927..e7f116d1fd 100644
--- a/src/client/components/note-detailed.vue
+++ b/src/client/components/note-detailed.vue
@@ -1,6 +1,6 @@
<template>
<div
- class="note _block"
+ class="lxwezrsl _block"
v-if="!muted"
v-show="!isDeleted"
:tabindex="!isDeleted ? '-1' : null"
@@ -67,6 +67,13 @@
<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
<a class="rp" v-if="appearNote.renote != null">RN:</a>
+ <div class="translation" v-if="translating || translation">
+ <MkLoading v-if="translating" mini/>
+ <div class="translated" v-else>
+ <b>{{ $t('translatedFrom', { x: translation.sourceLang }) }}:</b>
+ {{ translation.text }}
+ </div>
+ </div>
</div>
<div class="files" v-if="appearNote.files.length > 0">
<XMediaList :media-list="appearNote.files"/>
@@ -79,8 +86,8 @@
</div>
<footer class="footer">
<div class="info">
- <span class="mobile" v-if="note.viaMobile"><i class="fas fa-mobile-alt"></i></span>
- <MkTime class="created-at" :time="note.createdAt" mode="detail"/>
+ <span class="mobile" v-if="appearNote.viaMobile"><i class="fas fa-mobile-alt"></i></span>
+ <MkTime class="created-at" :time="appearNote.createdAt" mode="detail"/>
</div>
<XReactionsViewer :note="appearNote" ref="reactionsViewer"/>
<button @click="reply()" class="button _button">
@@ -178,6 +185,8 @@ export default defineComponent({
showContent: false,
isDeleted: false,
muted: false,
+ translation: null,
+ translating: false,
};
},
@@ -619,6 +628,11 @@ export default defineComponent({
text: this.$ts.share,
action: this.share
},
+ this.$instance.translatorAvailable ? {
+ icon: 'fas fa-language',
+ text: this.$ts.translate,
+ action: this.translate
+ } : undefined,
null,
statePromise.then(state => state.isFavorited ? {
icon: 'fas fa-star',
@@ -852,6 +866,17 @@ export default defineComponent({
});
},
+ async translate() {
+ if (this.translation != null) return;
+ this.translating = true;
+ const res = await os.api('notes/translate', {
+ noteId: this.appearNote.id,
+ targetLang: localStorage.getItem('lang') || navigator.language,
+ });
+ this.translating = false;
+ this.translation = res;
+ },
+
focus() {
this.$el.focus();
},
@@ -874,7 +899,7 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
-.note {
+.lxwezrsl {
position: relative;
transition: box-shadow 0.1s ease;
overflow: hidden;
@@ -1050,6 +1075,13 @@ export default defineComponent({
font-style: oblique;
color: var(--renote);
}
+
+ > .translation {
+ border: solid 0.5px var(--divider);
+ border-radius: var(--radius);
+ padding: 12px;
+ margin-top: 8px;
+ }
}
> .url-preview {
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index 873b96030a..38b529dd91 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -51,6 +51,13 @@
<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
<a class="rp" v-if="appearNote.renote != null">RN:</a>
+ <div class="translation" v-if="translating || translation">
+ <MkLoading v-if="translating" mini/>
+ <div class="translated" v-else>
+ <b>{{ $t('translatedFrom', { x: translation.sourceLang }) }}:</b>
+ {{ translation.text }}
+ </div>
+ </div>
</div>
<div class="files" v-if="appearNote.files.length > 0">
<XMediaList :media-list="appearNote.files"/>
@@ -164,6 +171,8 @@ export default defineComponent({
collapsed: false,
isDeleted: false,
muted: false,
+ translation: null,
+ translating: false,
};
},
@@ -594,6 +603,11 @@ export default defineComponent({
text: this.$ts.share,
action: this.share
},
+ this.$instance.translatorAvailable ? {
+ icon: 'fas fa-language',
+ text: this.$ts.translate,
+ action: this.translate
+ } : undefined,
null,
statePromise.then(state => state.isFavorited ? {
icon: 'fas fa-star',
@@ -827,6 +841,17 @@ export default defineComponent({
});
},
+ async translate() {
+ if (this.translation != null) return;
+ this.translating = true;
+ const res = await os.api('notes/translate', {
+ noteId: this.appearNote.id,
+ targetLang: localStorage.getItem('lang') || navigator.language,
+ });
+ this.translating = false;
+ this.translation = res;
+ },
+
focus() {
this.$el.focus();
},
@@ -1053,6 +1078,13 @@ export default defineComponent({
font-style: oblique;
color: var(--renote);
}
+
+ > .translation {
+ border: solid 0.5px var(--divider);
+ border-radius: var(--radius);
+ padding: 12px;
+ margin-top: 8px;
+ }
}
> .url-preview {
diff --git a/src/client/components/notes.vue b/src/client/components/notes.vue
index e90102921a..ba3b7d2b39 100644
--- a/src/client/components/notes.vue
+++ b/src/client/components/notes.vue
@@ -9,7 +9,7 @@
<div>{{ $ts.noNotes }}</div>
</div>
- <div v-else>
+ <div v-else class="giivymft" :class="{ noGap }">
<div v-show="more && reversed" style="margin-bottom: var(--margin);">
<MkButton style="margin: 0 auto;" @click="fetchMoreFeature" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
@@ -17,8 +17,8 @@
</MkButton>
</div>
- <XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed" :no-gap="noGap" :ad="true">
- <XNote :note="note" class="_block" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
+ <XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed" :no-gap="noGap" :ad="true" class="notes">
+ <XNote class="qtqtichx" :note="note" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
</XList>
<div v-show="more && !reversed" style="margin-top: var(--margin);">
@@ -108,4 +108,21 @@ export default defineComponent({
.fade-leave-to {
opacity: 0;
}
+
+.giivymft {
+ &.noGap {
+ > .notes {
+ background: var(--panel);
+ }
+ }
+
+ &:not(.noGap) {
+ > .notes {
+ .qtqtichx {
+ background: var(--panel);
+ border-radius: var(--radius);
+ }
+ }
+ }
+}
</style>
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index f2c625a556..221dc74313 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -17,7 +17,7 @@
<span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span>
<span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span>
</button>
- <button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button>
+ <button class="submit _buttonPrimary" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button>
</div>
</header>
<div class="form" :class="{ fixed }">
@@ -36,7 +36,7 @@
</div>
<MkInfo warn v-if="hasNotSpecifiedMentions" class="hasNotSpecifiedMentions">{{ $ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ $ts.add }}</button></MkInfo>
<input v-show="useCw" ref="cw" class="cw" v-model="cw" :placeholder="$ts.annotation" @keydown="onKeydown">
- <textarea v-model="text" class="text" :class="{ withCw: useCw }" ref="text" :disabled="posting" :placeholder="placeholder" @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd" />
+ <textarea v-model="text" class="text" :class="{ withCw: useCw }" ref="text" :disabled="posting" :placeholder="placeholder" @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd" data-cy-post-form-text/>
<input v-show="withHashtags" ref="hashtags" class="hashtags" v-model="hashtags" :placeholder="$ts.hashtags" list="hashtags">
<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/>
<XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/>
diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue
index f1e5d6afe5..0094038fb6 100755
--- a/src/client/components/signin.vue
+++ b/src/client/components/signin.vue
@@ -3,11 +3,11 @@
<div class="auth _section">
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
<div class="normal-signin" v-if="!totpLogin">
- <MkInput v-model="username" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:modelValue="onUsernameChange">
+ <MkInput v-model="username" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:modelValue="onUsernameChange" data-cy-signin-username>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
- <MkInput v-model="password" :placeholder="$ts.password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
+ <MkInput v-model="password" :placeholder="$ts.password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required data-cy-signin-password>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption><button class="_textButton" @click="resetPassword" type="button">{{ $ts.forgotPassword }}</button></template>
</MkInput>
diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue
index 0cdeb633d8..b0b0c2ad4d 100644
--- a/src/client/components/signup.vue
+++ b/src/client/components/signup.vue
@@ -5,7 +5,7 @@
<template #label>{{ $ts.invitationCode }}</template>
<template #prefix><i class="fas fa-key"></i></template>
</MkInput>
- <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeUsername">
+ <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeUsername" data-cy-signup-username>
<template #label>{{ $ts.username }}</template>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
@@ -19,7 +19,7 @@
<span v-if="usernameState == 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
</template>
</MkInput>
- <MkInput v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword">
+ <MkInput v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword" data-cy-signup-password>
<template #label>{{ $ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
@@ -28,7 +28,7 @@
<span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.strongPassword }}</span>
</template>
</MkInput>
- <MkInput v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePasswordRetype">
+ <MkInput v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePasswordRetype" data-cy-signup-password-retype>
<template #label>{{ $ts.password }} ({{ $ts.retype }})</template>
<template #prefix><i class="fas fa-lock"></i></template>
<template #caption>
@@ -46,7 +46,7 @@
</label>
<captcha v-if="meta.enableHcaptcha" class="captcha" provider="hcaptcha" ref="hcaptcha" v-model:value="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/>
<captcha v-if="meta.enableRecaptcha" class="captcha" provider="recaptcha" ref="recaptcha" v-model:value="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/>
- <MkButton type="submit" :disabled="shouldDisableSubmitting" primary>{{ $ts.start }}</MkButton>
+ <MkButton type="submit" :disabled="shouldDisableSubmitting" primary data-cy-signup-submit>{{ $ts.start }}</MkButton>
</template>
</form>
</template>
diff --git a/src/client/components/ui/popup.vue b/src/client/components/ui/popup.vue
index 9e360411c0..8497eedecb 100644
--- a/src/client/components/ui/popup.vue
+++ b/src/client/components/ui/popup.vue
@@ -1,5 +1,5 @@
<template>
-<transition :name="$store.state.animation ? 'popup-menu' : ''" :duration="$store.state.animation ? 300 : 0" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
+<transition :name="$store.state.animation ? 'popup-menu' : ''" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
<div v-show="manualShowing != null ? manualShowing : showing" class="ccczpooj" :class="{ front, fixed, top: position === 'top' }" ref="content" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<slot :point="point"></slot>
</div>
diff --git a/src/client/components/updated.vue b/src/client/components/updated.vue
new file mode 100644
index 0000000000..5033d866fb
--- /dev/null
+++ b/src/client/components/updated.vue
@@ -0,0 +1,58 @@
+<template>
+<MkModal ref="modal" @click="$refs.modal.close()" @closed="$emit('closed')">
+ <div class="ewlycnyt">
+ <div class="title">{{ $ts.misskeyUpdated }}</div>
+ <div class="version">✨{{ version }}🚀</div>
+ <MkButton full @click="whatIsNew">{{ $ts.whatIsNew }}</MkButton>
+ <MkButton primary full @click="$refs.modal.close()">{{ $ts.gotIt }}</MkButton>
+ </div>
+</MkModal>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import MkModal from '@client/components/ui/modal.vue';
+import MkButton from '@client/components/ui/button.vue';
+import { version } from '@client/config';
+
+export default defineComponent({
+ components: {
+ MkModal,
+ MkButton,
+ },
+
+ data() {
+ return {
+ version: version,
+ };
+ },
+
+ methods: {
+ whatIsNew() {
+ this.$refs.modal.close();
+ this.$router.push('/docs/general/changelog');
+ }
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.ewlycnyt {
+ position: relative;
+ padding: 32px;
+ min-width: 320px;
+ max-width: 480px;
+ box-sizing: border-box;
+ text-align: center;
+ background: var(--panel);
+ border-radius: var(--radius);
+
+ > .title {
+ font-weight: bold;
+ }
+
+ > .version {
+ margin: 1em 0;
+ }
+}
+</style>