qr.show.vue
This commit is contained in:
parent
926cd832c6
commit
9c9e6c9ca9
|
@ -12561,6 +12561,14 @@ export interface Locale extends ILocale {
|
|||
* 読み取る
|
||||
*/
|
||||
"readTabTitle": string;
|
||||
/**
|
||||
* {name} {acct}
|
||||
*/
|
||||
"shareTitle": ParameterizedString<"name" | "acct">;
|
||||
/**
|
||||
* Fediverseで私をフォローしてください!
|
||||
*/
|
||||
"shareText": string;
|
||||
};
|
||||
}
|
||||
declare const locales: {
|
||||
|
|
|
@ -3364,3 +3364,5 @@ qr: 二次元コード
|
|||
_qr:
|
||||
showTabTitle: "表示"
|
||||
readTabTitle: "読み取る"
|
||||
shareTitle: "{name} {acct}"
|
||||
shareText: "Fediverseで私をフォローしてください!"
|
||||
|
|
|
@ -17,5 +17,6 @@ const $i = ensureSignin();
|
|||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -5,35 +5,51 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<div :class="$style.root" :style="{ backgroundColor: bgColor }">
|
||||
<div :class="$style.content" class="_spacer" :style="{ '--MI_SPACER-w': '512px' }">
|
||||
<div :class="$style.qrOuter">
|
||||
<div v-flip-on-device-orientation ref="qrCodeEl" :class="$style.qrInner"></div>
|
||||
</div>
|
||||
<div :class="$style.user">
|
||||
<MkAvatar v-flip-on-device-orientation :class="$style.avatar" :user="$i" :indicator="false"/>
|
||||
<div :class="$style.names">
|
||||
<div v-flip-on-device-orientation :class="$style.username"><MkCondensedLine :minScale="2 / 3">@{{ $i.username }}@{{ host }}</MkCondensedLine></div>
|
||||
<div v-flip-on-device-orientation :class="$style.name"><MkCondensedLine :minScale="2 / 3">{{ userName($i) }}</MkCondensedLine></div>
|
||||
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5" />
|
||||
<div :class="$style.fg">
|
||||
<div
|
||||
:class="$style.content" class="_spacer"
|
||||
@click="share"
|
||||
:style="{
|
||||
'--MI_SPACER-w': '512px',
|
||||
'cursor': canShare ? 'pointer' : 'default',
|
||||
}"
|
||||
>
|
||||
<div :class="$style.qrOuter">
|
||||
<div v-flip-on-device-orientation ref="qrCodeEl" :class="$style.qrInner"></div>
|
||||
</div>
|
||||
<div :class="$style.user">
|
||||
<MkAvatar v-flip-on-device-orientation :class="$style.avatar" :user="$i" :indicator="false"/>
|
||||
<div :class="$style.names">
|
||||
<div v-flip-on-device-orientation :class="$style.username"><MkCondensedLine :minScale="2 / 3">{{ acct }}</MkCondensedLine></div>
|
||||
<div v-flip-on-device-orientation :class="$style.name"><MkCondensedLine :minScale="2 / 3">{{ userName($i) }}</MkCondensedLine></div>
|
||||
</div>
|
||||
</div>
|
||||
<img v-flip-on-device-orientation :class="$style.logo" :src="misskeysvg" alt="Misskey Logo"/>
|
||||
</div>
|
||||
<img v-flip-on-device-orientation :class="$style.logo" :src="misskeysvg" alt="Misskey Logo"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash';
|
||||
import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash.js';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import QRCodeStyling from 'qr-code-styling';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { computed, ref, watch, onMounted } from 'vue';
|
||||
import { host } from '@@/js/config.js';
|
||||
import { instance } from '@/instance';
|
||||
import { ensureSignin } from '@/i';
|
||||
import { userPage, userName } from '@/filters/user';
|
||||
import { instance } from '@/instance.js';
|
||||
import { ensureSignin } from '@/i.js';
|
||||
import { userPage, userName } from '@/filters/user.js';
|
||||
import misskeysvg from '/client-assets/misskey.svg';
|
||||
import MkAnimBg from '@/components/MkAnimBg.vue';
|
||||
|
||||
const $i = ensureSignin();
|
||||
|
||||
const acct = computed(() => `@${$i.username}@${host}`);
|
||||
const url = computed(() => userPage($i, undefined, true));
|
||||
|
||||
const canShare = computed(() => navigator.canShare && navigator.canShare({ url: url.value }));
|
||||
|
||||
const qrCodeEl = ref<HTMLDivElement | null>(null);
|
||||
|
||||
const avatarColor = computed(() => tinycolor(
|
||||
|
@ -45,13 +61,21 @@ const avatarColor = computed(() => tinycolor(
|
|||
const avatarHsl = computed(() => avatarColor.value.toHsl());
|
||||
const bgColor = tinycolor(`hsl(${avatarHsl.value.h}, 60, 46)`).toRgbString();
|
||||
|
||||
watch([qrCodeEl, avatarHsl], () => {
|
||||
function share() {
|
||||
return navigator.share?.({
|
||||
title: i18n.tsx._qr.shareTitle({ name: userName($i), acct: acct.value }),
|
||||
text: i18n.tsx._qr.shareText({ name: userName($i), acct: acct.value }),
|
||||
url: url.value,
|
||||
});
|
||||
}
|
||||
|
||||
watch([qrCodeEl, avatarHsl, url], () => {
|
||||
const qrCodeInstance = new QRCodeStyling({
|
||||
width: 512,
|
||||
height: 512,
|
||||
margin: 40,
|
||||
type: 'canvas',
|
||||
data: userPage($i, undefined, true),
|
||||
data: url.value,
|
||||
image: instance.iconUrl ?? '/favicon.ico',
|
||||
qrOptions: {
|
||||
typeNumber: 0,
|
||||
|
@ -91,11 +115,19 @@ watch([qrCodeEl, avatarHsl], () => {
|
|||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
position: relative;
|
||||
margin-top: calc( -1 * var(--MI-stickyTop) );
|
||||
padding-top: var(--MI-stickyTop);
|
||||
height: calc( 100dvh - var(--MI-stickyTop) - var(--MI-stickyBottom) );
|
||||
}
|
||||
|
||||
.fg {
|
||||
position: absolute;
|
||||
top: var(--MI-stickyTop);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -114,8 +146,9 @@ watch([qrCodeEl, avatarHsl], () => {
|
|||
|
||||
> svg,
|
||||
> canvas {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue