summaryrefslogtreecommitdiff
path: root/src/client/components
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-09-22 22:53:41 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-09-22 22:53:41 +0900
commit338793d891d1657f158cd4dc83f998e124bd7e45 (patch)
treed47080ad4fcff61ad5eafdb8eb1e3ca997739115 /src/client/components
parentMerge branch 'develop' (diff)
parent12.91.0 (diff)
downloadmisskey-338793d891d1657f158cd4dc83f998e124bd7e45.tar.gz
misskey-338793d891d1657f158cd4dc83f998e124bd7e45.tar.bz2
misskey-338793d891d1657f158cd4dc83f998e124bd7e45.zip
Merge branch 'develop'
Diffstat (limited to 'src/client/components')
-rw-r--r--src/client/components/global/avatar.vue26
-rw-r--r--src/client/components/mfm.ts14
-rw-r--r--src/client/components/note-header.vue12
-rw-r--r--src/client/components/page-window.vue2
-rwxr-xr-xsrc/client/components/signin.vue41
-rw-r--r--src/client/components/sparkle.vue180
-rw-r--r--src/client/components/ui/folder.vue3
-rw-r--r--src/client/components/ui/input.vue2
-rw-r--r--src/client/components/ui/menu.vue33
-rw-r--r--src/client/components/ui/textarea.vue2
10 files changed, 280 insertions, 35 deletions
diff --git a/src/client/components/global/avatar.vue b/src/client/components/global/avatar.vue
index eea970ec9a..395ed5d8ce 100644
--- a/src/client/components/global/avatar.vue
+++ b/src/client/components/global/avatar.vue
@@ -73,6 +73,22 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
+@keyframes earwiggleleft {
+ from { transform: rotate(37.6deg) skew(30deg); }
+ 25% { transform: rotate(10deg) skew(30deg); }
+ 50% { transform: rotate(20deg) skew(30deg); }
+ 75% { transform: rotate(0deg) skew(30deg); }
+ to { transform: rotate(37.6deg) skew(30deg); }
+}
+
+@keyframes earwiggleright {
+ from { transform: rotate(-37.6deg) skew(-30deg); }
+ 30% { transform: rotate(-10deg) skew(-30deg); }
+ 55% { transform: rotate(-20deg) skew(-30deg); }
+ 75% { transform: rotate(0deg) skew(-30deg); }
+ to { transform: rotate(-37.6deg) skew(-30deg); }
+}
+
.eiwwqkts {
position: relative;
display: inline-block;
@@ -132,6 +148,16 @@ export default defineComponent({
border-radius: 75% 0 75% 75%;
transform: rotate(-37.5deg) skew(-30deg);
}
+
+ &:hover {
+ &:before {
+ animation: earwiggleleft 1s infinite;
+ }
+
+ &:after {
+ animation: earwiggleright 1s infinite;
+ }
+ }
}
}
</style>
diff --git a/src/client/components/mfm.ts b/src/client/components/mfm.ts
index c248f934df..a228ca4b8d 100644
--- a/src/client/components/mfm.ts
+++ b/src/client/components/mfm.ts
@@ -8,6 +8,7 @@ import { concat } from '@client/../prelude/array';
import MkFormula from '@client/components/formula.vue';
import MkCode from '@client/components/code.vue';
import MkGoogle from '@client/components/google.vue';
+import MkSparkle from '@client/components/sparkle.vue';
import MkA from '@client/components/global/a.vue';
import { host } from '@client/config';
@@ -169,6 +170,19 @@ export default defineComponent({
style = this.$store.state.animatedMfm ? 'animation: mfm-rainbow 1s linear infinite;' : '';
break;
}
+ case 'sparkle': {
+ if (!this.$store.state.animatedMfm) {
+ return genEl(token.children);
+ }
+ let count = token.props.args.count ? parseInt(token.props.args.count) : 10;
+ if (count > 100) {
+ count = 100;
+ }
+ const speed = token.props.args.speed ? parseFloat(token.props.args.speed) : 1;
+ return h(MkSparkle, {
+ count, speed,
+ }, genEl(token.children));
+ }
}
if (style == null) {
return h('span', {}, ['[', token.props.name, ...genEl(token.children), ']']);
diff --git a/src/client/components/note-header.vue b/src/client/components/note-header.vue
index 7758dea3ae..80bfea9b07 100644
--- a/src/client/components/note-header.vue
+++ b/src/client/components/note-header.vue
@@ -3,10 +3,10 @@
<MkA class="name" :to="userPage(note.user)" v-user-preview="note.user.id">
<MkUserName :user="note.user"/>
</MkA>
- <span class="is-bot" v-if="note.user.isBot">bot</span>
- <span class="username"><MkAcct :user="note.user"/></span>
- <span class="admin" v-if="note.user.isAdmin"><i class="fas fa-bookmark"></i></span>
- <span class="moderator" v-if="!note.user.isAdmin && note.user.isModerator"><i class="far fa-bookmark"></i></span>
+ <div class="is-bot" v-if="note.user.isBot">bot</div>
+ <div class="username"><MkAcct :user="note.user"/></div>
+ <div class="admin" v-if="note.user.isAdmin"><i class="fas fa-bookmark"></i></div>
+ <div class="moderator" v-if="!note.user.isAdmin && note.user.isModerator"><i class="far fa-bookmark"></i></div>
<div class="info">
<span class="mobile" v-if="note.viaMobile"><i class="fas fa-mobile-alt"></i></span>
<MkA class="created-at" :to="notePage(note)">
@@ -55,6 +55,7 @@ export default defineComponent({
white-space: nowrap;
> .name {
+ flex-shrink: 1;
display: block;
margin: 0 .5em 0 0;
padding: 0;
@@ -81,17 +82,20 @@ export default defineComponent({
> .admin,
> .moderator {
+ flex-shrink: 0;
margin-right: 0.5em;
color: var(--badge);
}
> .username {
+ flex-shrink: 9999999;
margin: 0 .5em 0 0;
overflow: hidden;
text-overflow: ellipsis;
}
> .info {
+ flex-shrink: 0;
margin-left: auto;
font-size: 0.9em;
diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue
index c83b040dd8..fbc9f0b7fd 100644
--- a/src/client/components/page-window.vue
+++ b/src/client/components/page-window.vue
@@ -8,7 +8,7 @@
@closed="$emit('closed')"
>
<template #header>
- <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()"/>
+ <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()" :title-only="true"/>
</template>
<div class="yrolvcoq _flat_">
<component :is="component" v-bind="props" :ref="changePage"/>
diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue
index c051288d0a..69f527b7d6 100755
--- a/src/client/components/signin.vue
+++ b/src/client/components/signin.vue
@@ -54,6 +54,7 @@ import { apiUrl, host } from '@client/config';
import { byteify, hexify } from '@client/scripts/2fa';
import * as os from '@client/os';
import { login } from '@client/account';
+import { showSuspendedDialog } from '../scripts/show-suspended-dialog';
export default defineComponent({
components: {
@@ -169,15 +170,7 @@ export default defineComponent({
this.signing = false;
this.challengeData = res;
return this.queryKey();
- }).catch(() => {
- os.dialog({
- type: 'error',
- text: this.$ts.signinFailed
- });
- this.challengeData = null;
- this.totpLogin = false;
- this.signing = false;
- });
+ }).catch(this.loginFailed);
} else {
this.totpLogin = true;
this.signing = false;
@@ -190,14 +183,36 @@ export default defineComponent({
}).then(res => {
this.$emit('login', res);
this.onLogin(res);
- }).catch(() => {
+ }).catch(this.loginFailed);
+ }
+ },
+
+ loginFailed(err) {
+ switch (err.id) {
+ case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
os.dialog({
type: 'error',
- text: this.$ts.loginFailed
+ title: this.$ts.loginFailed,
+ text: this.$ts.noSuchUser
});
- this.signing = false;
- });
+ break;
+ }
+ case 'e03a5f46-d309-4865-9b69-56282d94e1eb': {
+ showSuspendedDialog();
+ break;
+ }
+ default: {
+ os.dialog({
+ type: 'error',
+ title: this.$ts.loginFailed,
+ text: JSON.stringify(err)
+ });
+ }
}
+
+ this.challengeData = null;
+ this.totpLogin = false;
+ this.signing = false;
},
resetPassword() {
diff --git a/src/client/components/sparkle.vue b/src/client/components/sparkle.vue
new file mode 100644
index 0000000000..942412b445
--- /dev/null
+++ b/src/client/components/sparkle.vue
@@ -0,0 +1,180 @@
+<template>
+<span class="mk-sparkle">
+ <span ref="content">
+ <slot></slot>
+ </span>
+ <canvas ref="canvas"></canvas>
+</span>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import * as os from '@client/os';
+
+const sprite = new Image();
+sprite.src = "/static-assets/client/sparkle-spritesheet.png";
+
+
+export default defineComponent({
+ props: {
+ count: {
+ type: Number,
+ required: true,
+ },
+ speed: {
+ type: Number,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ sprites: [0,6,13,20],
+ particles: [],
+ anim: null,
+ ctx: null,
+ };
+ },
+ methods: {
+ createSparkles(w, h, count) {
+ var holder = [];
+
+ for (var i = 0; i < count; i++) {
+
+ const color = '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6);
+
+ holder[i] = {
+ position: {
+ x: Math.floor(Math.random() * w),
+ y: Math.floor(Math.random() * h)
+ },
+ style: this.sprites[ Math.floor(Math.random() * 4) ],
+ delta: {
+ x: Math.floor(Math.random() * 1000) - 500,
+ y: Math.floor(Math.random() * 1000) - 500
+ },
+ color: color,
+ opacity: Math.random(),
+ };
+
+ }
+
+ return holder;
+ },
+ draw(time) {
+ this.ctx.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
+ this.ctx.beginPath();
+
+ const particleSize = Math.floor(this.fontSize / 2);
+ this.particles.forEach((particle) => {
+ var modulus = Math.floor(Math.random()*7);
+
+ if (Math.floor(time) % modulus === 0) {
+ particle.style = this.sprites[ Math.floor(Math.random()*4) ];
+ }
+
+ this.ctx.save();
+ this.ctx.globalAlpha = particle.opacity;
+ this.ctx.drawImage(sprite, particle.style, 0, 7, 7, particle.position.x, particle.position.y, particleSize, particleSize);
+
+ this.ctx.globalCompositeOperation = "source-atop";
+ this.ctx.globalAlpha = 0.5;
+ this.ctx.fillStyle = particle.color;
+ this.ctx.fillRect(particle.position.x, particle.position.y, particleSize, particleSize);
+
+ this.ctx.restore();
+ });
+ this.ctx.stroke();
+ },
+ tick() {
+ this.anim = window.requestAnimationFrame((time) => {
+ if (!this.$refs.canvas) {
+ return;
+ }
+ this.particles.forEach((particle) => {
+ if (!particle) {
+ return;
+ }
+ var randX = Math.random() > Math.random() * 2;
+ var randY = Math.random() > Math.random() * 3;
+
+ if (randX) {
+ particle.position.x += (particle.delta.x * this.speed) / 1500;
+ }
+
+ if (!randY) {
+ particle.position.y -= (particle.delta.y * this.speed) / 800;
+ }
+
+ if( particle.position.x > this.$refs.canvas.width ) {
+ particle.position.x = -7;
+ } else if (particle.position.x < -7) {
+ particle.position.x = this.$refs.canvas.width;
+ }
+
+ if (particle.position.y > this.$refs.canvas.height) {
+ particle.position.y = -7;
+ particle.position.x = Math.floor(Math.random() * this.$refs.canvas.width);
+ } else if (particle.position.y < -7) {
+ particle.position.y = this.$refs.canvas.height;
+ particle.position.x = Math.floor(Math.random() * this.$refs.canvas.width);
+ }
+
+ particle.opacity -= 0.005;
+
+ if (particle.opacity <= 0) {
+ particle.opacity = 1;
+ }
+ });
+
+ this.draw(time);
+
+ this.tick();
+ });
+ },
+ resize() {
+ if (this.$refs.content) {
+ const contentRect = this.$refs.content.getBoundingClientRect();
+ this.fontSize = parseFloat(getComputedStyle(this.$refs.content).fontSize);
+ const padding = this.fontSize * 0.2;
+
+ this.$refs.canvas.width = parseInt(contentRect.width + padding);
+ this.$refs.canvas.height = parseInt(contentRect.height + padding);
+
+ this.particles = this.createSparkles(this.$refs.canvas.width, this.$refs.canvas.height, this.count);
+ }
+ },
+ },
+ mounted() {
+ this.ctx = this.$refs.canvas.getContext('2d');
+
+ new ResizeObserver(this.resize).observe(this.$refs.content);
+
+ this.resize();
+ this.tick();
+ },
+ updated() {
+ this.resize();
+ },
+ destroyed() {
+ window.cancelAnimationFrame(this.anim);
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.mk-sparkle {
+ position: relative;
+ display: inline-block;
+
+ > span {
+ display: inline-block;
+ }
+
+ > canvas {
+ position: absolute;
+ top: -0.1em;
+ left: -0.1em;
+ pointer-events: none;
+ }
+}
+</style>
diff --git a/src/client/components/ui/folder.vue b/src/client/components/ui/folder.vue
index 1f3593a74a..eecf1d8be1 100644
--- a/src/client/components/ui/folder.vue
+++ b/src/client/components/ui/folder.vue
@@ -99,7 +99,8 @@ export default defineComponent({
z-index: 10;
position: sticky;
top: var(--stickyTop, 0px);
- background: var(--panel);
+ padding: var(--x-padding);
+ background: var(--x-header, var(--panel));
/* TODO panelの半透明バージョンをプログラマティックに作りたい
background: var(--X17);
-webkit-backdrop-filter: var(--blur, blur(8px));
diff --git a/src/client/components/ui/input.vue b/src/client/components/ui/input.vue
index 05ce5d3e15..a916a0b035 100644
--- a/src/client/components/ui/input.vue
+++ b/src/client/components/ui/input.vue
@@ -245,7 +245,7 @@ export default defineComponent({
font-size: 1em;
color: var(--fg);
background: var(--panel);
- border: solid 1px var(--inputBorder);
+ border: solid 0.5px var(--inputBorder);
border-radius: 6px;
outline: none;
box-shadow: none;
diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue
index d652d9b84f..26b4b04b11 100644
--- a/src/client/components/ui/menu.vue
+++ b/src/client/components/ui/menu.vue
@@ -41,7 +41,7 @@
</template>
<script lang="ts">
-import { defineComponent, ref } from 'vue';
+import { defineComponent, ref, unref } from 'vue';
import { focusPrev, focusNext } from '@client/scripts/focus';
import contains from '@client/scripts/contains';
@@ -79,21 +79,26 @@ export default defineComponent({
};
},
},
- created() {
- const items = ref(this.items.filter(item => item !== undefined));
+ watch: {
+ items: {
+ handler() {
+ const items = ref(unref(this.items).filter(item => item !== undefined));
- for (let i = 0; i < items.value.length; i++) {
- const item = items.value[i];
-
- if (item && item.then) { // if item is Promise
- items.value[i] = { type: 'pending' };
- item.then(actualItem => {
- items.value[i] = actualItem;
- });
- }
- }
+ for (let i = 0; i < items.value.length; i++) {
+ const item = items.value[i];
+
+ if (item && item.then) { // if item is Promise
+ items.value[i] = { type: 'pending' };
+ item.then(actualItem => {
+ items.value[i] = actualItem;
+ });
+ }
+ }
- this._items = items;
+ this._items = items;
+ },
+ immediate: true
+ }
},
mounted() {
if (this.viaKeyboard) {
diff --git a/src/client/components/ui/textarea.vue b/src/client/components/ui/textarea.vue
index 53a141f011..08ac3182a9 100644
--- a/src/client/components/ui/textarea.vue
+++ b/src/client/components/ui/textarea.vue
@@ -212,7 +212,7 @@ export default defineComponent({
font-size: 1em;
color: var(--fg);
background: var(--panel);
- border: solid 1px var(--inputBorder);
+ border: solid 0.5px var(--inputBorder);
border-radius: 6px;
outline: none;
box-shadow: none;