This commit is contained in:
parent
6c67d79f0c
commit
f04d6f7a76
|
@ -64,6 +64,7 @@
|
|||
"photoswipe": "5.4.4",
|
||||
"punycode.js": "2.3.1",
|
||||
"qr-code-styling": "1.9.2",
|
||||
"qr-scanner": "1.4.2",
|
||||
"rollup": "4.48.0",
|
||||
"sanitize-html": "2.17.0",
|
||||
"sass": "1.90.0",
|
||||
|
|
|
@ -4,19 +4,195 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div :class="$style.root">
|
||||
TODO
|
||||
<div ref="rootEl" :class="$style.root" :style="{
|
||||
'--MI-QrReadScrollHeight': scrollContainer ? `${scrollHeight}px` : `calc( 100dvh - var(--MI-minBottomSpacing) )`,
|
||||
'--MI-QrReadViewHeight': 'calc(var(--MI-QrReadScrollHeight) - var(--MI-stickyTop, 0px) - var(--MI-stickyBottom, 0px))',
|
||||
'--MI-QrReadVideoHeight': 'min(calc(var(--MI-QrReadViewHeight) * 0.3), 512px)',
|
||||
}">
|
||||
<video ref="videoEl" :class="$style.video" autoplay muted playsinline></video>
|
||||
<div class="_spacer" :style="{
|
||||
'--MI-stickyTop': 'calc(var(--MI-stickyTop, 0px) + var(--MI-QrReadVideoHeight, 0px))',
|
||||
}">
|
||||
<div :class="$style.listBig">
|
||||
<MkUserInfo v-for="user in users.slice(0, 5)" :key="user.id" :user="user"/>
|
||||
</div>
|
||||
<div :class="$style.listMini">
|
||||
<MkA v-for="user in users.slice(5)" :key="user.id" :to="userPage(user)">
|
||||
<MkUserCardMini :user="user" />
|
||||
</MkA>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ensureSignin } from '@/i';
|
||||
import QrScanner from 'qr-scanner';
|
||||
import { onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as os from '@/os.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkUserInfo from '@/components/MkUserInfo.vue';
|
||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { userPage } from '@/filters/user.js';
|
||||
import { getScrollContainer } from '@@/js/scroll.js';
|
||||
|
||||
const $i = ensureSignin();
|
||||
const scrollContainer = shallowRef<HTMLElement | null>(null);
|
||||
const rootEl = ref<HTMLDivElement | null>(null);
|
||||
const scrollHeight = ref(window.innerHeight);
|
||||
|
||||
const scannerInstance = shallowRef<QrScanner | null>(null);
|
||||
const uris = ref<string[]>([]);
|
||||
const usersSource = new Map<string, misskey.entities.UserDetailed | null>();
|
||||
const users = ref<(misskey.entities.UserDetailed)[]>([]);
|
||||
|
||||
let timer = ref<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
function updateUsers() {
|
||||
users.value = uris.value.map(uri => usersSource.get(uri)).filter(u => u) as misskey.entities.UserDetailed[];
|
||||
}
|
||||
|
||||
watch([uris, timer], () => {
|
||||
if (timer.value) {
|
||||
console.log('Skip updateUsers because of timer');
|
||||
return;
|
||||
}
|
||||
|
||||
updateUsers();
|
||||
|
||||
timer.value = setTimeout(() => {
|
||||
console.log('Update users after 3 seconds');
|
||||
timer.value = null;
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
async function processResult(result: QrScanner.ScanResult) {
|
||||
if (!result) return;
|
||||
const uri = result.data.trim();
|
||||
try {
|
||||
new URL(uri);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (uris.value[0] !== uri) {
|
||||
// 並べ替え
|
||||
uris.value = [uri, ...uris.value.slice(0, 29).filter(u => u !== uri)];
|
||||
}
|
||||
|
||||
if (usersSource.has(uri)) return;
|
||||
// Start fetching user info
|
||||
usersSource.set(uri, null);
|
||||
|
||||
await misskeyApi('ap/show', { uri })
|
||||
.then(data => {
|
||||
if (data.type === 'User') {
|
||||
usersSource.set(uri, data.object);
|
||||
updateUsers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const alertLock = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
const videoEl = document.querySelector('video');
|
||||
|
||||
if (!videoEl) {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: i18n.ts.somethingHappened,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
scannerInstance.value = new QrScanner(
|
||||
videoEl,
|
||||
processResult,
|
||||
{
|
||||
onDecodeError(err) {
|
||||
if (err === 'No QR code found') return;
|
||||
if (alertLock.value) return;
|
||||
alertLock.value = true;
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err.toString(),
|
||||
}).finally(() => {
|
||||
alertLock.value = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
scannerInstance.value.start();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer.value) {
|
||||
clearTimeout(timer.value);
|
||||
timer.value = null;
|
||||
}
|
||||
|
||||
scannerInstance.value?.destroy();
|
||||
});
|
||||
|
||||
//#region scroll height
|
||||
function checkScrollHeight() {
|
||||
scrollHeight.value = scrollContainer.value ? scrollContainer.value.clientHeight : window.innerHeight;
|
||||
}
|
||||
|
||||
let ro: ResizeObserver | undefined;
|
||||
|
||||
onMounted(() => {
|
||||
scrollContainer.value = getScrollContainer(rootEl.value);
|
||||
checkScrollHeight();
|
||||
|
||||
if (scrollContainer.value) {
|
||||
ro = new ResizeObserver(checkScrollHeight);
|
||||
ro.observe(scrollContainer.value);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
ro?.disconnect();
|
||||
});
|
||||
//#endregion
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.video {
|
||||
position: sticky;
|
||||
background: var(--MI_THEME-bg);
|
||||
background-size: 16px 16px;
|
||||
width: 100%;
|
||||
height: var(--MI-QrReadVideoHeight);
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
html[data-color-scheme=dark] .video {
|
||||
--c: rgb(255 255 255 / 2%);
|
||||
background-image: linear-gradient(45deg, var(--c) 16.67%, var(--MI_THEME-bg) 16.67%, var(--MI_THEME-bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--MI_THEME-bg) 66.67%, var(--MI_THEME-bg) 100%);
|
||||
}
|
||||
|
||||
html[data-color-scheme=light] .video {
|
||||
--c: rgb(0 0 0 / 2%);
|
||||
background-image: linear-gradient(45deg, var(--c) 16.67%, var(--MI_THEME-bg) 16.67%, var(--MI_THEME-bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--MI_THEME-bg) 66.67%, var(--MI_THEME-bg) 100%);
|
||||
}
|
||||
|
||||
.listBig {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(405px, 1fr));
|
||||
grid-gap: var(--MI-margin);
|
||||
}
|
||||
|
||||
.listMini {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
||||
grid-gap: 12px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div ref="rootEl" :class="$style.root" :style="{
|
||||
backgroundColor: bgColor,
|
||||
'--MI-QrShowMinHeight': scrollContainer ? `${scrollHeight}px` : `calc( 100dvh - var(--MI-minBottomSpacing) )`,
|
||||
'--MI-QrShowScrollHeight': scrollContainer ? `${scrollHeight}px` : `calc( 100dvh - var(--MI-minBottomSpacing, 0px) )`,
|
||||
}">
|
||||
<MkAnimBg style="position: absolute; top: 0;" :scale="1.5" />
|
||||
<div :class="$style.fg">
|
||||
|
@ -214,7 +214,7 @@ $avatarSize: 58px;
|
|||
margin-top: calc( -1 * var(--MI-stickyTop) );
|
||||
margin-bottom: calc( -1 * var(--MI-stickyBottom) );
|
||||
height: fit-content;
|
||||
min-height: var(--MI-QrShowMinHeight);
|
||||
min-height: var(--MI-QrShowScrollHeight);
|
||||
}
|
||||
|
||||
.fg {
|
||||
|
@ -237,7 +237,7 @@ $avatarSize: 58px;
|
|||
display: flex;
|
||||
width: 100%;
|
||||
padding: 0 0 $s3;
|
||||
max-height: max(256px, calc((var(--MI-QrShowMinHeight) - var(--MI-stickyTop) - var(--MI-stickyBottom)) * 0.55));
|
||||
max-height: max(256px, calc((var(--MI-QrShowScrollHeight) - var(--MI-stickyTop, 0px) - var(--MI-stickyBottom, 0px)) * 0.55));
|
||||
}
|
||||
|
||||
.qrInner {
|
||||
|
|
|
@ -847,6 +847,9 @@ importers:
|
|||
qr-code-styling:
|
||||
specifier: 1.9.2
|
||||
version: 1.9.2
|
||||
qr-scanner:
|
||||
specifier: 1.4.2
|
||||
version: 1.4.2
|
||||
rollup:
|
||||
specifier: 4.48.0
|
||||
version: 4.48.0
|
||||
|
@ -9515,6 +9518,9 @@ packages:
|
|||
resolution: {integrity: sha512-RgJaZJ1/RrXJ6N0j7a+pdw3zMBmzZU4VN2dtAZf8ZggCfRB5stEQ3IoDNGaNhYY3nnZKYlYSLl5YkfWN5dPutg==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
|
||||
qr-scanner@1.4.2:
|
||||
resolution: {integrity: sha512-kV1yQUe2FENvn59tMZW6mOVfpq9mGxGf8l6+EGaXUOd4RBOLg7tRC83OrirM5AtDvZRpdjdlXURsHreAOSPOUw==}
|
||||
|
||||
qrcode-generator@1.5.2:
|
||||
resolution: {integrity: sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw==}
|
||||
|
||||
|
@ -21094,6 +21100,10 @@ snapshots:
|
|||
dependencies:
|
||||
qrcode-generator: 1.5.2
|
||||
|
||||
qr-scanner@1.4.2:
|
||||
dependencies:
|
||||
'@types/offscreencanvas': 2019.7.0
|
||||
|
||||
qrcode-generator@1.5.2: {}
|
||||
|
||||
qrcode@1.5.4:
|
||||
|
|
Loading…
Reference in New Issue