Merge pull request 'New media list layout + hide controls when player too small' (#10213) from Freeplay/calckey:notes into develop

Reviewed-on: https://codeberg.org/calckey/calckey/pulls/10213
This commit is contained in:
Kainoa Kanter 2023-05-28 21:24:45 +00:00
commit 5475b6ed44
14 changed files with 197 additions and 135 deletions

View File

@ -102,9 +102,9 @@ defineExpose({
} }
&.showLess { &.showLess {
width: 100%; width: 100%;
margin-top: 1em;
position: sticky; position: sticky;
bottom: var(--stickyBottom); bottom: calc(var(--stickyBottom) - 1em);
padding: 20px;
> span { > span {
display: inline-block; display: inline-block;

View File

@ -59,6 +59,7 @@ const props = defineProps<{
transition: all 0.5s ease; transition: all 0.5s ease;
> .img { > .img {
position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;

View File

@ -1,21 +1,22 @@
<template> <template>
<div class="xubzgfgb" :class="{ cover }" :title="title"> <canvas
<canvas v-if="!loaded"
v-if="!loaded" ref="canvas"
ref="canvas" :width="size"
:width="size" :height="size"
:height="size" :title="title"
:title="title" />
/> <img
<img v-if="src"
v-if="src" :src="src"
:src="src" :title="title"
:title="title" :type="type"
:type="type" :alt="alt"
:alt="alt" :class="{ cover }"
@load="onLoad" :style="{ 'object-fit': cover ? 'cover' : null }"
/> loading="lazy"
</div> @load="onLoad"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -64,31 +65,20 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.xubzgfgb { canvas,
position: relative; img {
display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
}
> canvas, canvas {
> img { position: absolute;
display: block; inset: 0;
width: 100%; object-fit: cover;
height: 100%; }
}
> canvas { img {
position: absolute; object-fit: contain;
object-fit: cover;
}
> img {
object-fit: contain;
}
&.cover {
> img {
object-fit: cover;
}
}
} }
</style> </style>

View File

@ -1,7 +1,6 @@
<template> <template>
<button v-if="hide" class="qjewsnkg" @click="hide = false"> <button v-if="hide" class="qjewsnkg" @click="hide = false">
<ImgWithBlurhash <ImgWithBlurhash
class="bg"
:hash="image.blurhash" :hash="image.blurhash"
:title="image.comment" :title="image.comment"
:alt="image.comment" :alt="image.comment"
@ -82,20 +81,17 @@ watch(
all: unset; all: unset;
position: relative; position: relative;
> .bg {
filter: brightness(0.5);
}
> .text { > .text {
position: absolute; position: relative;
left: 0;
top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 1; z-index: 1;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 30px;
box-sizing: border-box;
background: rgba(0,0,0,0.5);
> .wrapper { > .wrapper {
display: table-cell; display: table-cell;
@ -112,7 +108,6 @@ watch(
.gqnyydlz { .gqnyydlz {
position: relative; position: relative;
//box-shadow: 0 0 0 1px var(--divider) inset;
background: var(--bg); background: var(--bg);
> .hide { > .hide {

View File

@ -1,20 +1,18 @@
<template> <template>
<div class="hoawjimk"> <div class="hoawjimk files">
<XBanner <XBanner
v-for="media in mediaList.filter((media) => !previewable(media))" v-for="media in mediaList.filter((media) => !previewable(media))"
:key="media.id" :key="media.id"
:media="media" :media="media"
/> />
<div <div
v-if="mediaList.filter((media) => previewable(media)).length > 0" v-if="previewableCount > 0"
class="gird-container" class="grid-container"
:data-count="previewableCount < 5 ? previewableCount : null"
:class="{ dmWidth: inDm }" :class="{ dmWidth: inDm }"
> >
<div <div
ref="gallery" ref="gallery"
:data-count="
mediaList.filter((media) => previewable(media)).length
"
@click.stop @click.stop
> >
<template <template
@ -191,6 +189,7 @@ const previewable = (file: misskey.entities.DriveFile): boolean => {
FILE_TYPE_BROWSERSAFE.includes(file.type) FILE_TYPE_BROWSERSAFE.includes(file.type)
); );
}; };
const previewableCount = props.mediaList.filter((media) => previewable(media)).length;
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -200,7 +199,7 @@ const previewable = (file: misskey.entities.DriveFile): boolean => {
max-width: 40rem; max-width: 40rem;
} }
> .gird-container { > .grid-container {
position: relative; position: relative;
width: 100%; width: 100%;
margin-top: 4px; margin-top: 4px;
@ -208,71 +207,74 @@ const previewable = (file: misskey.entities.DriveFile): boolean => {
overflow: hidden; overflow: hidden;
pointer-events: none; pointer-events: none;
&:before { &[data-count] {
content: "";
display: block;
padding-top: 56.25%; // 16:9; padding-top: 56.25%; // 16:9;
> div {
position: absolute;
inset: 0;
}
}
&[data-count="1"] > div {
grid-template-rows: 1fr;
}
&[data-count="2"] > div {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
}
&[data-count="3"] > div {
grid-template-columns: 1fr 0.5fr;
grid-template-rows: 1fr 1fr;
> *:nth-child(1) {
grid-row: 1 / 3;
}
> *:nth-child(3) {
grid-column: 2 / 3;
grid-row: 2 / 3;
}
}
&[data-count="4"] > div {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
&:not([data-count]) > div > div {
max-height: 300px;
} }
> div { > div {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: grid; display: grid;
grid-gap: 8px; grid-gap: 8px;
> * { > div, > button {
overflow: hidden; overflow: hidden;
border-radius: 6px; border-radius: 6px;
pointer-events: all; pointer-events: all;
min-height: 50px;
} }
&[data-count="1"] {
grid-template-rows: 1fr;
}
&[data-count="2"] { > :nth-child(1) {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
}
&[data-count="3"] {
grid-template-columns: 1fr 0.5fr;
grid-template-rows: 1fr 1fr;
> *:nth-child(1) {
grid-row: 1 / 3;
}
> *:nth-child(3) {
grid-column: 2 / 3;
grid-row: 2 / 3;
}
}
&[data-count="4"] {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
> *:nth-child(1) {
grid-column: 1 / 2; grid-column: 1 / 2;
grid-row: 1 / 2; grid-row: 1 / 2;
} }
> *:nth-child(2) { > :nth-child(2) {
grid-column: 2 / 3; grid-column: 2 / 3;
grid-row: 1 / 2; grid-row: 1 / 2;
} }
> *:nth-child(3) { > :nth-child(3) {
grid-column: 1 / 2; grid-column: 1 / 2;
grid-row: 2 / 3; grid-row: 2 / 3;
} }
> *:nth-child(4) { > :nth-child(4) {
grid-column: 2 / 3; grid-column: 2 / 3;
grid-row: 2 / 3; grid-row: 2 / 3;
} }

View File

@ -12,8 +12,9 @@
<span>{{ i18n.ts.clickToShow }}</span> <span>{{ i18n.ts.clickToShow }}</span>
</div> </div>
</div> </div>
<div v-else class="kkjnbbplepmiyuadieoenjgutgcmtsvu"> <div v-else class="video" :class="{ mini }">
<VuePlyr <VuePlyr
ref="plyr"
:options="{ :options="{
controls: [ controls: [
'play-large', 'play-large',
@ -40,12 +41,18 @@
<source :src="video.url" :type="video.type" /> <source :src="video.url" :type="video.type" />
</video> </video>
</VuePlyr> </VuePlyr>
<i class="ph-eye-slash ph-bold ph-lg" @click="hide = true"></i> <button
v-tooltip="i18n.ts.hide"
class="_button hide"
@click="hide = true"
>
<i class="ph-eye-slash ph-bold ph-lg"></i>
</button>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { onMounted, ref } from "vue";
import VuePlyr from "vue-plyr"; import VuePlyr from "vue-plyr";
import type * as misskey from "calckey-js"; import type * as misskey from "calckey-js";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -56,31 +63,47 @@ const props = defineProps<{
video: misskey.entities.DriveFile; video: misskey.entities.DriveFile;
}>(); }>();
const plyr = ref();
const mini = ref(false);
const hide = ref( const hide = ref(
defaultStore.state.nsfw === "force" defaultStore.state.nsfw === "force"
? true ? true
: props.video.isSensitive && defaultStore.state.nsfw !== "ignore" : props.video.isSensitive && defaultStore.state.nsfw !== "ignore"
); );
onMounted(() => {
mini.value = plyr.value.player.media.scrollWidth < 300;
if (mini.value) {
plyr.value.player.on('play', () => {
plyr.value.player.fullscreen.enter();
});
}
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.kkjnbbplepmiyuadieoenjgutgcmtsvu { .video {
position: relative; position: relative;
--plyr-color-main: var(--accent); --plyr-color-main: var(--accent);
> i { > .hide {
display: block; display: block;
position: absolute; position: absolute;
border-radius: 6px; border-radius: 6px;
background-color: var(--fg); background-color: var(--accentedBg);
color: var(--accentLighten); -webkit-backdrop-filter: var(--blur, blur(15px));
font-size: 14px; backdrop-filter: var(--blur, blur(15px));
opacity: 0.5; color: var(--accent);
padding: 3px 6px; font-size: 0.8em;
padding: 6px 8px;
text-align: center; text-align: center;
cursor: pointer;
top: 12px; top: 12px;
right: 12px; right: 12px;
> i {
display: block;
}
} }
> video { > video {
@ -95,6 +118,18 @@ const hide = ref(
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
&.mini {
:deep(.plyr:not(:fullscreen)) {
min-width: unset !important;
.plyr__control--overlaid,
.plyr__progress__container,
.plyr__volume,
[data-plyr="fullscreen"] {
display: none;
}
}
}
} }
.icozogqfvdetwohsdglrbswgrejoxbdj { .icozogqfvdetwohsdglrbswgrejoxbdj {

View File

@ -700,6 +700,7 @@ defineExpose({
} }
> .article { > .article {
position: relative;
overflow: clip; overflow: clip;
padding: 4px 32px 10px; padding: 4px 32px 10px;
cursor: pointer; cursor: pointer;

View File

@ -435,6 +435,7 @@ function noteClick(e) {
} }
> .body { > .body {
position: relative;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
margin: 0 -200px; margin: 0 -200px;

View File

@ -41,6 +41,8 @@ defineExpose({
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
padding: 20px;
margin-bottom: -10px;
z-index: 5; z-index: 5;
> span { > span {
display: inline-block; display: inline-block;
@ -58,9 +60,10 @@ defineExpose({
} }
.showLess { .showLess {
width: 100%; width: 100%;
margin-top: 1em;
position: sticky; position: sticky;
bottom: var(--stickyBottom); bottom: calc(var(--stickyBottom) - 1em);
padding: 20px;
z-index: 5;
> span { > span {
display: inline-block; display: inline-block;

View File

@ -34,6 +34,7 @@
:class="{ :class="{
collapsed, collapsed,
isLong, isLong,
manyImages: note.files.length > 4,
showContent: note.cw && !showContent, showContent: note.cw && !showContent,
disableAnim: disableMfm, disableAnim: disableMfm,
}" }"
@ -96,9 +97,7 @@
:to="`/notes/${note.renoteId}`" :to="`/notes/${note.renoteId}`"
>{{ i18n.ts.quoteAttached }}: ...</MkA >{{ i18n.ts.quoteAttached }}: ...</MkA
> >
<div v-if="note.files.length > 0" class="files"> <XMediaList v-if="note.files.length > 0" :media-list="note.files" />
<XMediaList :media-list="note.files" />
</div>
<XPoll v-if="note.poll" :note="note" class="poll" /> <XPoll v-if="note.poll" :note="note" class="poll" />
<template v-if="detailed"> <template v-if="detailed">
<MkUrlPreview <MkUrlPreview
@ -152,6 +151,7 @@
<i class="ph-stop ph-bold"></i> {{ i18n.ts._mfm.stop }} <i class="ph-stop ph-bold"></i> {{ i18n.ts._mfm.stop }}
</template> </template>
</MkButton> </MkButton>
<div v-if="(isLong && !collapsed) || (props.note.cw && showContent)" class="fade"></div>
</div> </div>
</template> </template>
@ -188,11 +188,14 @@ const emit = defineEmits<{
const cwButton = ref<HTMLElement>(); const cwButton = ref<HTMLElement>();
const showMoreButton = ref<HTMLElement>(); const showMoreButton = ref<HTMLElement>();
const isLong = const isLong = !props.detailedView
!props.detailedView && && ( props.note.cw == null
props.note.cw == null && && (props.note.text != null
props.note.text != null && && (props.note.text.split("\n").length > 9 || props.note.text.length > 500)
(props.note.text.split("\n").length > 9 || props.note.text.length > 500); )
|| props.note.files.length > 4
);
const collapsed = $ref(props.note.cw == null && isLong); const collapsed = $ref(props.note.cw == null && isLong);
const urls = props.note.text const urls = props.note.text
@ -285,7 +288,7 @@ function focusFooter(ev) {
background: var(--buttonHoverBg); background: var(--buttonHoverBg);
} }
} }
> .files { > :deep(.files) {
margin-top: 0.4em; margin-top: 0.4em;
margin-bottom: 0.4em; margin-bottom: 0.4em;
} }
@ -332,27 +335,32 @@ function focusFooter(ev) {
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
} }
&.collapsed > .body { }
&.collapsed {
&.manyImages {
max-height: calc(9em + 200px);
}
> .body {
box-sizing: border-box; box-sizing: border-box;
} }
&.showContent { }
> .body { &.showContent {
min-height: 2em; > .body {
max-height: 5em; min-height: 2em;
filter: blur(4px); max-height: 5em;
:deep(span) { filter: blur(4px);
animation: none !important; :deep(span) {
transform: none !important; animation: none !important;
} transform: none !important;
:deep(img) {
filter: blur(12px);
}
} }
:deep(.fade) { :deep(img) {
inset: 0; filter: blur(12px);
top: 40px;
} }
} }
:deep(.fade) {
inset: 0;
top: 40px;
}
} }
&.disableAnim :deep(span) { &.disableAnim :deep(span) {
@ -364,5 +372,26 @@ function focusFooter(ev) {
margin-left: 0; margin-left: 0;
margin-right: 0.4rem; margin-right: 0.4rem;
} }
> .fade {
position: absolute;
inset: 0;
bottom: -400px;
display: flex;
align-items: flex-end;
z-index: 4;
pointer-events: none;
&::before {
content: "";
display: block;
height: 100px;
position: sticky;
bottom: 0;
width: 100%;
background: var(--panel);
mask: linear-gradient(to top, var(--gradient));
-webkit-mask: linear-gradient(to top, var(--gradient));
transition: background .2s;
}
}
} }
</style> </style>

View File

@ -114,6 +114,7 @@ async function del() {
<style lang="scss" scoped> <style lang="scss" scoped>
.yigymqpb { .yigymqpb {
> .img { > .img {
position: relative;
display: block; display: block;
height: 64px; height: 64px;
margin: 0 auto; margin: 0 auto;

View File

@ -98,6 +98,7 @@ onMounted(() => {
grid-gap: 6px; grid-gap: 6px;
> .img { > .img {
position: relative;
height: 128px; height: 128px;
border-radius: 6px; border-radius: 6px;
overflow: clip; overflow: clip;

View File

@ -102,6 +102,7 @@ const zIndex = os.claimZIndex("high");
border-top: none; border-top: none;
} }
.mk-uploader > ol > li > .img { .mk-uploader > ol > li > .img {
position: relative;
display: block; display: block;
background-size: cover; background-size: cover;
background-position: center center; background-position: center center;

View File

@ -114,6 +114,7 @@ defineExpose<WidgetComponentExpose>({
} }
.img { .img {
position: relative;
border: solid 4px transparent; border: solid 4px transparent;
border-radius: 8px; border-radius: 8px;
} }
@ -126,6 +127,7 @@ defineExpose<WidgetComponentExpose>({
padding: 8px; padding: 8px;
.img { .img {
position: relative;
flex: 1 1 33%; flex: 1 1 33%;
width: 33%; width: 33%;
height: 80px; height: 80px;