* [client] show drive file icon * 🎨 * exchange icon * fix * fuck crlf * 背景差し戻し * fix selected color * 🎨 * fix * pointer-events none * fix bug * ✌️ * Clean up * ✌️ * Clean up * Fix
This commit is contained in:
		
							parent
							
								
									763ae8f1a6
								
							
						
					
					
						commit
						8ca1fe3f0a
					
				
					 6 changed files with 209 additions and 44 deletions
				
			
		
							
								
								
									
										174
									
								
								src/client/app/common/views/components/drive-file-thumbnail.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/client/app/common/views/components/drive-file-thumbnail.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,174 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="zdjebgpv" :class="{ detail }" ref="thumbnail" :style="`background-color: ${ background }`">
 | 
			
		||||
	<img
 | 
			
		||||
		:src="file.url"
 | 
			
		||||
		:alt="file.name"
 | 
			
		||||
		:title="file.name"
 | 
			
		||||
		v-if="detail && is === 'image'"/>
 | 
			
		||||
	<video
 | 
			
		||||
		:src="file.url"
 | 
			
		||||
		ref="volumectrl"
 | 
			
		||||
		preload="metadata"
 | 
			
		||||
		controls
 | 
			
		||||
		v-else-if="detail && is === 'video'"/>
 | 
			
		||||
	<img :src="file.thumbnailUrl" alt="" @load="onThumbnailLoaded" :style="`object-fit: ${ fit }`" v-else-if="isThumbnailAvailable"/>
 | 
			
		||||
	<fa :icon="faFileImage" class="icon" v-else-if="is === 'image'"/>
 | 
			
		||||
	<fa :icon="faFileVideo" class="icon" v-else-if="is === 'video'"/>
 | 
			
		||||
 | 
			
		||||
	<audio
 | 
			
		||||
		:src="file.url"
 | 
			
		||||
		ref="volumectrl"
 | 
			
		||||
		preload="metadata"
 | 
			
		||||
		controls
 | 
			
		||||
		v-else-if="detail && is === 'audio'"/>
 | 
			
		||||
	<fa :icon="faMusic" class="icon" v-else-if="is === 'audio' || is === 'midi'"/>
 | 
			
		||||
 | 
			
		||||
	<fa :icon="faFileCsv" class="icon" v-else-if="is === 'csv'"/>
 | 
			
		||||
	<fa :icon="faFilePdf" class="icon" v-else-if="is === 'pdf'"/>
 | 
			
		||||
	<fa :icon="faFileAlt" class="icon" v-else-if="is === 'textfile'"/>
 | 
			
		||||
	<fa :icon="faFileArchive" class="icon" v-else-if="is === 'archive'"/>
 | 
			
		||||
	<fa :icon="faFile" class="icon" v-else/>
 | 
			
		||||
 | 
			
		||||
	<fa :icon="faFilm" class="icon-sub" v-if="!detail && isThumbnailAvailable && is === 'video'"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import anime from 'animejs';
 | 
			
		||||
import i18n from '../../../i18n';
 | 
			
		||||
import {
 | 
			
		||||
	faFile,
 | 
			
		||||
	faFileAlt,
 | 
			
		||||
	faFileImage,
 | 
			
		||||
	faMusic,
 | 
			
		||||
	faFileVideo,
 | 
			
		||||
	faFileCsv,
 | 
			
		||||
	faFilePdf,
 | 
			
		||||
	faFileArchive,
 | 
			
		||||
	faFilm
 | 
			
		||||
	} from '@fortawesome/free-solid-svg-icons';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		file: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		fit: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		detail: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			isContextmenuShowing: false,
 | 
			
		||||
			isDragging: false,
 | 
			
		||||
 | 
			
		||||
			faFile,
 | 
			
		||||
			faFileAlt,
 | 
			
		||||
			faFileImage,
 | 
			
		||||
			faMusic,
 | 
			
		||||
			faFileVideo,
 | 
			
		||||
			faFileCsv,
 | 
			
		||||
			faFilePdf,
 | 
			
		||||
			faFileArchive,
 | 
			
		||||
			faFilm
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		is(): 'image' | 'video' | 'midi' | 'audio' | 'csv' | 'pdf' | 'textfile' | 'archive' | 'unknown' {
 | 
			
		||||
			if (this.file.type.startsWith('image/')) return 'image';
 | 
			
		||||
			if (this.file.type.startsWith('video/')) return 'video';
 | 
			
		||||
			if (this.file.type === 'audio/midi') return 'midi';
 | 
			
		||||
			if (this.file.type.startsWith('audio/')) return 'audio';
 | 
			
		||||
			if (this.file.type.endsWith('/csv')) return 'csv';
 | 
			
		||||
			if (this.file.type.endsWith('/pdf')) return 'pdf';
 | 
			
		||||
			if (this.file.type.startsWith('text/')) return 'textfile';
 | 
			
		||||
			if ([
 | 
			
		||||
					"application/zip",
 | 
			
		||||
					"application/x-cpio",
 | 
			
		||||
					"application/x-bzip",
 | 
			
		||||
					"application/x-bzip2",
 | 
			
		||||
					"application/java-archive",
 | 
			
		||||
					"application/x-rar-compressed",
 | 
			
		||||
					"application/x-tar",
 | 
			
		||||
					"application/gzip",
 | 
			
		||||
					"application/x-7z-compressed"
 | 
			
		||||
				].some(e => e === this.file.type)) return 'archive';
 | 
			
		||||
			return 'unknown';
 | 
			
		||||
		},
 | 
			
		||||
		isThumbnailAvailable(): boolean {
 | 
			
		||||
			return this.file.thumbnailUrl.endsWith('?thumbnail') ? (this.is === 'image' || this.is === 'video') : true;
 | 
			
		||||
		},
 | 
			
		||||
		background(): string {
 | 
			
		||||
			return this.file.properties.avgColor && this.file.properties.avgColor.length == 3
 | 
			
		||||
				? `rgb(${this.file.properties.avgColor.join(',')})`
 | 
			
		||||
				: 'transparent';
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		const audioTag = this.$refs.volumectrl as HTMLAudioElement;
 | 
			
		||||
		if (audioTag) audioTag.volume = this.$store.state.device.mediaVolume;
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onThumbnailLoaded() {
 | 
			
		||||
			if (this.file.properties.avgColor && this.file.properties.avgColor.length == 3) {
 | 
			
		||||
				anime({
 | 
			
		||||
					targets: this.$refs.thumbnail,
 | 
			
		||||
					backgroundColor: `rgba(${this.file.properties.avgColor.join(',')}, 0)`,
 | 
			
		||||
					duration: 100,
 | 
			
		||||
					easing: 'linear'
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		volumechange() {
 | 
			
		||||
			const audioTag = this.$refs.volumectrl as HTMLAudioElement;
 | 
			
		||||
			this.$store.commit('device/set', { key: 'mediaVolume', value: audioTag.volume });
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.zdjebgpv
 | 
			
		||||
	display flex
 | 
			
		||||
 | 
			
		||||
	> img,
 | 
			
		||||
	> .icon
 | 
			
		||||
		pointer-events none
 | 
			
		||||
 | 
			
		||||
	> img
 | 
			
		||||
		height 100%
 | 
			
		||||
		width 100%
 | 
			
		||||
		margin auto
 | 
			
		||||
		object-fit cover
 | 
			
		||||
 | 
			
		||||
	> .icon
 | 
			
		||||
		height 65%
 | 
			
		||||
		width 65%
 | 
			
		||||
		margin auto
 | 
			
		||||
 | 
			
		||||
	> video,
 | 
			
		||||
	> audio
 | 
			
		||||
		width 100%
 | 
			
		||||
 | 
			
		||||
	> .icon-sub
 | 
			
		||||
		position absolute
 | 
			
		||||
		width 30%
 | 
			
		||||
		height auto
 | 
			
		||||
		margin 0
 | 
			
		||||
		right 4%
 | 
			
		||||
		bottom 4%
 | 
			
		||||
 | 
			
		||||
	&.detail
 | 
			
		||||
		> .icon
 | 
			
		||||
			height 100px
 | 
			
		||||
			margin 16px auto
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -21,9 +21,9 @@
 | 
			
		|||
		<img src="/assets/label-red.svg"/>
 | 
			
		||||
		<p>{{ $t('nsfw') }}</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="thumbnail" ref="thumbnail" :style="`background-color: ${ background }`">
 | 
			
		||||
		<img :src="file.thumbnailUrl" alt="" @load="onThumbnailLoaded"/>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<x-file-thumbnail class="thumbnail" :file="file" fit="contain"/>
 | 
			
		||||
 | 
			
		||||
	<p class="name">
 | 
			
		||||
		<span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
 | 
			
		||||
		<span class="ext" v-if="file.name.lastIndexOf('.') != -1">{{ file.name.substr(file.name.lastIndexOf('.')) }}</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -34,15 +34,18 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import i18n from '../../../i18n';
 | 
			
		||||
import anime from 'animejs';
 | 
			
		||||
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
 | 
			
		||||
import updateAvatar from '../../api/update-avatar';
 | 
			
		||||
import updateBanner from '../../api/update-banner';
 | 
			
		||||
import { appendQuery } from '../../../../../prelude/url';
 | 
			
		||||
import XFileThumbnail from '../../../common/views/components/drive-file-thumbnail.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	i18n: i18n('desktop/views/components/drive.file.vue'),
 | 
			
		||||
	props: ['file'],
 | 
			
		||||
	components: {
 | 
			
		||||
		XFileThumbnail
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			isContextmenuShowing: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -58,11 +61,6 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
		title(): string {
 | 
			
		||||
			return `${this.file.name}\n${this.file.type} ${Vue.filter('bytes')(this.file.datasize)}`;
 | 
			
		||||
		},
 | 
			
		||||
		background(): string {
 | 
			
		||||
			return this.file.properties.avgColor && this.file.properties.avgColor.length == 3
 | 
			
		||||
				? `rgb(${this.file.properties.avgColor.join(',')})`
 | 
			
		||||
				: 'transparent';
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			@ -256,6 +254,9 @@ export default Vue.extend({
 | 
			
		|||
		> .name
 | 
			
		||||
			color var(--primaryForeground)
 | 
			
		||||
 | 
			
		||||
		> .thumbnail
 | 
			
		||||
			color var(--primaryForeground)
 | 
			
		||||
 | 
			
		||||
	&[data-is-contextmenu-showing]
 | 
			
		||||
		&:after
 | 
			
		||||
			content ""
 | 
			
		||||
| 
						 | 
				
			
			@ -321,18 +322,7 @@ export default Vue.extend({
 | 
			
		|||
		width 128px
 | 
			
		||||
		height 128px
 | 
			
		||||
		margin auto
 | 
			
		||||
 | 
			
		||||
		> img
 | 
			
		||||
			display block
 | 
			
		||||
			position absolute
 | 
			
		||||
			top 0
 | 
			
		||||
			left 0
 | 
			
		||||
			right 0
 | 
			
		||||
			bottom 0
 | 
			
		||||
			margin auto
 | 
			
		||||
			max-width 128px
 | 
			
		||||
			max-height 128px
 | 
			
		||||
			pointer-events none
 | 
			
		||||
		color var(--driveFileIcon)
 | 
			
		||||
 | 
			
		||||
	> .name
 | 
			
		||||
		display block
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,7 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="pyvicwrksnfyhpfgkjwqknuururpaztw">
 | 
			
		||||
	<div class="preview">
 | 
			
		||||
		<img v-if="kind == 'image'" ref="img"
 | 
			
		||||
			:src="file.url"
 | 
			
		||||
			:alt="file.name"
 | 
			
		||||
			:title="file.name"
 | 
			
		||||
			:style="style">
 | 
			
		||||
		<x-file-thumbnail class="preview" :file="file" fit="cover" :detail="true"/>
 | 
			
		||||
		<template v-if="kind != 'image'"><fa icon="file"/></template>
 | 
			
		||||
		<footer v-if="kind == 'image' && file.properties && file.properties.width && file.properties.height">
 | 
			
		||||
			<span class="size">
 | 
			
		||||
| 
						 | 
				
			
			@ -62,11 +58,16 @@ import Vue from 'vue';
 | 
			
		|||
import i18n from '../../../i18n';
 | 
			
		||||
import { gcd } from '../../../../../prelude/math';
 | 
			
		||||
import { appendQuery } from '../../../../../prelude/url';
 | 
			
		||||
import XFileThumbnail from '../../../common/views/components/drive-file-thumbnail.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	i18n: i18n('mobile/views/components/drive.file-detail.vue'),
 | 
			
		||||
	props: ['file'],
 | 
			
		||||
 | 
			
		||||
	components: {
 | 
			
		||||
		XFileThumbnail
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			gcd,
 | 
			
		||||
| 
						 | 
				
			
			@ -147,8 +148,7 @@ export default Vue.extend({
 | 
			
		|||
		padding 8px
 | 
			
		||||
		background var(--bg)
 | 
			
		||||
 | 
			
		||||
		> img
 | 
			
		||||
			display block
 | 
			
		||||
		> .preview
 | 
			
		||||
			max-width 100%
 | 
			
		||||
			max-height 300px
 | 
			
		||||
			margin 0 auto
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
<template>
 | 
			
		||||
<a class="vupkuhvjnjyqaqhsiogfbywvjxynrgsm" @click.prevent="onClick" :href="`/i/drive/file/${ file.id }`" :data-is-selected="isSelected">
 | 
			
		||||
	<div class="container">
 | 
			
		||||
		<div class="thumbnail" :style="thumbnail"></div>
 | 
			
		||||
		<x-file-thumbnail class="thumbnail" :file="file" fit="cover"/>
 | 
			
		||||
		<div class="body">
 | 
			
		||||
			<p class="name">
 | 
			
		||||
				<span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -26,9 +26,14 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import i18n from '../../../i18n';
 | 
			
		||||
import XFileThumbnail from '../../../common/views/components/drive-file-thumbnail.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	i18n: i18n('mobile/views/components/drive.file.vue'),
 | 
			
		||||
	props: ['file'],
 | 
			
		||||
	components: {
 | 
			
		||||
		XFileThumbnail
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			isSelected: false
 | 
			
		||||
| 
						 | 
				
			
			@ -37,12 +42,6 @@ export default Vue.extend({
 | 
			
		|||
	computed: {
 | 
			
		||||
		browser(): any {
 | 
			
		||||
			return this.$parent;
 | 
			
		||||
		},
 | 
			
		||||
		thumbnail(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'background-color': this.file.properties.avgColor && this.file.properties.avgColor.length == 3 ? `rgb(${this.file.properties.avgColor.join(',')})` : 'transparent',
 | 
			
		||||
				'background-image': `url(${this.file.thumbnailUrl})`
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
| 
						 | 
				
			
			@ -74,9 +73,12 @@ export default Vue.extend({
 | 
			
		|||
		pointer-events none
 | 
			
		||||
 | 
			
		||||
	> .container
 | 
			
		||||
		display grid
 | 
			
		||||
		max-width 500px
 | 
			
		||||
		margin 0 auto
 | 
			
		||||
		padding 16px
 | 
			
		||||
		grid-template-columns 64px 1fr
 | 
			
		||||
		grid-column-gap 10px
 | 
			
		||||
 | 
			
		||||
		&:after
 | 
			
		||||
			content ""
 | 
			
		||||
| 
						 | 
				
			
			@ -84,18 +86,13 @@ export default Vue.extend({
 | 
			
		|||
			clear both
 | 
			
		||||
 | 
			
		||||
		> .thumbnail
 | 
			
		||||
			display block
 | 
			
		||||
			float left
 | 
			
		||||
			width 64px
 | 
			
		||||
			height 64px
 | 
			
		||||
			background-size cover
 | 
			
		||||
			background-position center center
 | 
			
		||||
			color var(--driveFileIcon)
 | 
			
		||||
 | 
			
		||||
		> .body
 | 
			
		||||
			display block
 | 
			
		||||
			float left
 | 
			
		||||
			width calc(100% - 74px)
 | 
			
		||||
			margin-left 10px
 | 
			
		||||
			word-break break-all
 | 
			
		||||
 | 
			
		||||
			> .name
 | 
			
		||||
				display block
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +151,6 @@ export default Vue.extend({
 | 
			
		|||
		background var(--primary)
 | 
			
		||||
 | 
			
		||||
		&, *
 | 
			
		||||
			color #fff !important
 | 
			
		||||
			color var(--primaryForeground) !important
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,6 +153,8 @@
 | 
			
		|||
		messagingRoomMessageBg: '$secondary',
 | 
			
		||||
		messagingRoomMessageFg: '#fff',
 | 
			
		||||
 | 
			
		||||
		driveFileIcon: '$text',
 | 
			
		||||
 | 
			
		||||
		formButtonBorder: 'rgba(255, 255, 255, 0.1)',
 | 
			
		||||
		formButtonHoverBg: ':alpha<0.2<$primary',
 | 
			
		||||
		formButtonHoverBorder: ':alpha<0.5<$primary',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,6 +153,8 @@
 | 
			
		|||
		messagingRoomMessageBg: '#eee',
 | 
			
		||||
		messagingRoomMessageFg: '#333',
 | 
			
		||||
 | 
			
		||||
		driveFileIcon: '$text',
 | 
			
		||||
 | 
			
		||||
		formButtonBorder: 'rgba(0, 0, 0, 0.1)',
 | 
			
		||||
		formButtonHoverBg: ':alpha<0.12<$primary',
 | 
			
		||||
		formButtonHoverBorder: ':alpha<0.3<$primary',
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +181,7 @@
 | 
			
		|||
		desktopTimelineSrcHover: ':darken<7<$text',
 | 
			
		||||
		desktopWindowTitle: '$text',
 | 
			
		||||
		desktopWindowShadow: 'rgba(0, 0, 0, 0.2)',
 | 
			
		||||
		desktopDriveBg: '#fff',
 | 
			
		||||
		desktopDriveBg: '@bg',
 | 
			
		||||
		desktopDriveFolderBg: ':lighten<31<$primary',
 | 
			
		||||
		desktopDriveFolderHoverBg: ':lighten<27<$primary',
 | 
			
		||||
		desktopDriveFolderActiveBg: ':lighten<25<$primary',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue