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