Merge branch 'develop' into pizzax-indexeddb

This commit is contained in:
tamaina
2021-12-30 00:46:35 +09:00
39 changed files with 560 additions and 615 deletions

View File

@@ -77,7 +77,7 @@
import { defineComponent, markRaw } from 'vue';
import { emojilist } from '@/scripts/emojilist';
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import Particle from '@/components/particle.vue';
import Ripple from '@/components/ripple.vue';
import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch';
import { isMobile } from '@/scripts/is-mobile';
@@ -296,9 +296,9 @@ export default defineComponent({
if (ev) {
const el = ev.currentTarget || ev.target;
const rect = el.getBoundingClientRect();
const x = rect.left + (el.clientWidth / 2);
const y = rect.top + (el.clientHeight / 2);
os.popup(Particle, { x, y }, {}, 'end');
const x = rect.left + (el.offsetWidth / 2);
const y = rect.top + (el.offsetHeight / 2);
os.popup(Ripple, { x, y }, {}, 'end');
}
const key = this.getKey(emoji);

View File

@@ -9,7 +9,7 @@
:disabled="disabled"
@keydown.enter="toggle"
>
<span v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
<span ref="button" v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
<i class="check fas fa-check"></i>
</span>
<span class="label">
@@ -20,7 +20,9 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, ref, toRefs } from 'vue';
import * as os from '@/os';
import Ripple from '@/components/ripple.vue';
export default defineComponent({
props: {
@@ -33,17 +35,28 @@ export default defineComponent({
default: false
}
},
computed: {
checked(): boolean {
return this.modelValue;
}
setup(props, context) {
const button = ref<HTMLElement>();
const checked = toRefs(props).modelValue;
const toggle = () => {
if (props.disabled) return;
context.emit('update:modelValue', !checked.value);
if (!checked.value) {
const rect = button.value.getBoundingClientRect();
const x = rect.left + (button.value.offsetWidth / 2);
const y = rect.top + (button.value.offsetHeight / 2);
os.popup(Ripple, { x, y, particle: false }, {}, 'end');
}
};
return {
button,
checked,
toggle,
};
},
methods: {
toggle() {
if (this.disabled) return;
this.$emit('update:modelValue', !this.checked);
}
}
});
</script>
@@ -51,7 +64,7 @@ export default defineComponent({
.ziffeoms {
position: relative;
display: flex;
transition: all 0.2s;
transition: all 0.2s ease;
> * {
user-select: none;
@@ -85,6 +98,8 @@ export default defineComponent({
opacity: 0;
color: var(--fgOnAccent);
font-size: 13px;
transform: scale(0.5);
transition: all 0.2s ease;
}
}
@@ -131,6 +146,7 @@ export default defineComponent({
> .check {
opacity: 1;
transform: scale(1);
}
}
}

View File

@@ -175,14 +175,7 @@ export default defineComponent({
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));
return h(MkSparkle, {}, genEl(token.children));
}
case 'rotate': {
const degrees = parseInt(token.props.args.deg) || '90';

View File

@@ -2,7 +2,7 @@
<button
v-if="count > 0"
ref="buttonRef"
v-particle="canToggle"
v-ripple="canToggle"
class="hkzvhatu _button"
:class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction()"

View File

@@ -1,5 +1,5 @@
<template>
<div class="vswabwbm" :style="{ top: `${y - 64}px`, left: `${x - 64}px` }" :class="{ active }">
<div class="vswabwbm" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }" :class="{ active }">
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<circle fill="none" cx="64" cy="64">
<animate attributeName="r"
@@ -52,7 +52,8 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, onMounted } from 'vue';
import * as os from '@/os';
export default defineComponent({
props: {
@@ -63,37 +64,46 @@ export default defineComponent({
y: {
type: Number,
required: true
},
particle: {
type: Boolean,
required: false,
default: true,
}
},
emits: ['end'],
data() {
setup(props, context) {
const particles = [];
const origin = 64;
const colors = ['#FF1493', '#00FFFF', '#FFE202'];
for (let i = 0; i < 12; i++) {
const angle = Math.random() * (Math.PI * 2);
const pos = Math.random() * 16;
const velocity = 16 + (Math.random() * 48);
particles.push({
size: 4 + (Math.random() * 8),
xA: origin + (Math.sin(angle) * pos),
yA: origin + (Math.cos(angle) * pos),
xB: origin + (Math.sin(angle) * (pos + velocity)),
yB: origin + (Math.cos(angle) * (pos + velocity)),
color: colors[Math.floor(Math.random() * colors.length)]
});
if (props.particle) {
for (let i = 0; i < 12; i++) {
const angle = Math.random() * (Math.PI * 2);
const pos = Math.random() * 16;
const velocity = 16 + (Math.random() * 48);
particles.push({
size: 4 + (Math.random() * 8),
xA: origin + (Math.sin(angle) * pos),
yA: origin + (Math.cos(angle) * pos),
xB: origin + (Math.sin(angle) * (pos + velocity)),
yB: origin + (Math.cos(angle) * (pos + velocity)),
color: colors[Math.floor(Math.random() * colors.length)]
});
}
}
onMounted(() => {
setTimeout(() => {
context.emit('end');
}, 1100);
});
return {
particles
particles,
zIndex: os.claimZIndex('high'),
};
},
mounted() {
setTimeout(() => {
this.$emit('end');
}, 1100);
}
});
</script>
@@ -101,7 +111,6 @@ export default defineComponent({
.vswabwbm {
pointer-events: none;
position: fixed;
z-index: 1000000;
width: 128px;
height: 128px;

View File

@@ -1,161 +1,121 @@
<template>
<span class="mk-sparkle">
<span ref="content">
<span ref="el">
<slot></slot>
</span>
<canvas ref="canvas"></canvas>
<!-- なぜか path に対する key が機能しないため
<svg :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg">
<path v-for="particle in particles" :key="particle.id" style="transform-origin: center; transform-box: fill-box;"
:transform="`translate(${particle.x} ${particle.y})`"
:fill="particle.color"
d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z"
>
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 0 0"
to="360 0 0"
:dur="`${particle.dur}ms`"
repeatCount="indefinite"
additive="sum"
/>
<animateTransform
attributeName="transform"
attributeType="XML"
type="scale"
:values="`0; ${particle.size}; 0`"
:dur="`${particle.dur}ms`"
repeatCount="indefinite"
additive="sum"
/>
</path>
</svg>
-->
<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg">
<path style="transform-origin: center; transform-box: fill-box;"
:transform="`translate(${particle.x} ${particle.y})`"
:fill="particle.color"
d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z"
>
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 0 0"
to="360 0 0"
:dur="`${particle.dur}ms`"
repeatCount="1"
additive="sum"
/>
<animateTransform
attributeName="transform"
attributeType="XML"
type="scale"
:values="`0; ${particle.size}; 0`"
:dur="`${particle.dur}ms`"
repeatCount="1"
additive="sum"
/>
</path>
</svg>
</span>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
import * as os from '@/os';
const sprite = new Image();
sprite.src = '/client-assets/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,
};
},
unmounted() {
window.cancelAnimationFrame(this.anim);
},
mounted() {
this.ctx = this.$refs.canvas.getContext('2d');
setup() {
const particles = ref([]);
const el = ref<HTMLElement>();
const width = ref(0);
const height = ref(0);
const colors = ['#FF1493', '#00FFFF', '#FFE202', '#FFE202', '#FFE202'];
new ResizeObserver(this.resize).observe(this.$refs.content);
this.resize();
this.tick();
},
updated() {
this.resize();
},
methods: {
createSparkles(w, h, count) {
const holder = [];
for (let 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(),
onMounted(() => {
const ro = new ResizeObserver((entries, observer) => {
width.value = el.value?.offsetWidth + 64;
height.value = el.value?.offsetHeight + 64;
});
ro.observe(el.value);
let stop = false;
const add = () => {
if (stop) return;
const x = (Math.random() * (width.value - 64));
const y = (Math.random() * (height.value - 64));
const sizeFactor = Math.random();
const particle = {
id: Math.random().toString(),
x,
y,
size: 0.2 + ((sizeFactor / 10) * 3),
dur: 1000 + (sizeFactor * 1000),
color: colors[Math.floor(Math.random() * colors.length)],
};
particles.value.push(particle);
window.setTimeout(() => {
particles.value = particles.value.filter(x => x.id !== particle.id);
}, particle.dur - 100);
}
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();
window.setTimeout(() => {
add();
}, 500 + (Math.random() * 500));
};
add();
onUnmounted(() => {
ro.disconnect();
stop = true;
});
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);
}
},
return {
el,
width,
height,
particles,
};
},
});
</script>
@@ -169,10 +129,10 @@ export default defineComponent({
display: inline-block;
}
> canvas {
> svg {
position: absolute;
top: -0.1em;
left: -0.1em;
top: -32px;
left: -32px;
pointer-events: none;
}
}

View File

@@ -1,7 +1,7 @@
<template>
<MkModal ref="modal" :z-priority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="ewlycnyt">
<div class="title">{{ $ts.misskeyUpdated }}</div>
<div class="title"><MkSparkle>{{ $ts.misskeyUpdated }}</MkSparkle></div>
<div class="version">{{ version }}🚀</div>
<MkButton full @click="whatIsNew">{{ $ts.whatIsNew }}</MkButton>
<MkButton class="gotIt" primary full @click="$refs.modal.close()">{{ $ts.gotIt }}</MkButton>
@@ -9,31 +9,19 @@
</MkModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
<script lang="ts" setup>
import { ref } from 'vue';
import MkModal from '@/components/ui/modal.vue';
import MkButton from '@/components/ui/button.vue';
import MkSparkle from '@/components/sparkle.vue';
import { version } from '@/config';
export default defineComponent({
components: {
MkModal,
MkButton,
},
const modal = ref();
data() {
return {
version: version,
};
},
methods: {
whatIsNew() {
this.$refs.modal.close();
window.open(`https://misskey-hub.net/docs/releases.html#_${version.replace(/\./g, '-')}`, '_blank');
}
}
});
const whatIsNew = () => {
modal.value.close();
window.open(`https://misskey-hub.net/docs/releases.html#_${version.replace(/\./g, '-')}`, '_blank');
};
</script>
<style lang="scss" scoped>

View File

@@ -3,7 +3,7 @@ import { App } from 'vue';
import userPreview from './user-preview';
import size from './size';
import getSize from './get-size';
import particle from './particle';
import ripple from './ripple';
import tooltip from './tooltip';
import hotkey from './hotkey';
import appear from './appear';
@@ -18,7 +18,7 @@ export default function(app: App) {
app.directive('user-preview', userPreview);
app.directive('size', size);
app.directive('get-size', getSize);
app.directive('particle', particle);
app.directive('ripple', ripple);
app.directive('tooltip', tooltip);
app.directive('hotkey', hotkey);
app.directive('appear', appear);

View File

@@ -1,4 +1,4 @@
import Particle from '@/components/particle.vue';
import Ripple from '@/components/ripple.vue';
import { popup } from '@/os';
export default {
@@ -9,10 +9,10 @@ export default {
el.addEventListener('click', () => {
const rect = el.getBoundingClientRect();
const x = rect.left + (el.clientWidth / 2);
const y = rect.top + (el.clientHeight / 2);
const x = rect.left + (el.offsetWidth / 2);
const y = rect.top + (el.offsetHeight / 2);
popup(Particle, { x, y }, {}, 'end');
popup(Ripple, { x, y }, {}, 'end');
});
}
};

View File

@@ -41,6 +41,8 @@ export class Storage<T extends StateDef> {
return promise;
}
private connection = stream.useChannel('main');
constructor(key: string, def: T) {
this.key = key;
this.deviceStateKeyName = `pizzax::${key}`;

View File

@@ -285,7 +285,7 @@ export default defineComponent({
}
&:before {
width: auto;
width: min-content;
height: 100%;
aspect-ratio: 1/1;
border-radius: 8px;
@@ -301,7 +301,7 @@ export default defineComponent({
&.post:before {
width: calc(100% - 28px);
height: auto;
height: min-content;
aspect-ratio: 1/1;
border-radius: 100%;
}

View File

@@ -5,7 +5,8 @@
<span class="year">{{ $t('yearX', { year }) }}</span>
<span class="month">{{ $t('monthX', { month }) }}</span>
</p>
<p class="day">{{ $t('dayX', { day }) }}</p>
<p v-if="month === 1 && day === 1" class="day">🎉{{ $t('dayX', { day }) }}<span style="display: inline-block; transform: scaleX(-1);">🎉</span></p>
<p v-else class="day">{{ $t('dayX', { day }) }}</p>
<p class="week-day">{{ weekDay }}</p>
</div>
<div class="info">
@@ -34,7 +35,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import define from './define';
import * as os from '@/os';
const widget = define({
name: 'calendar',
@@ -127,12 +127,12 @@ export default defineComponent({
}
}
> p {
> .month-and-year, > .week-day {
margin: 0;
line-height: 18px;
font-size: 0.9em;
> span {
> .year, > .month {
margin: 0 4px;
}
}