Browse Source

Add navbar button to copy image (#961)

* Add navbar button to copy image

* Use global event for copy image

* merge upstream

* Fixed missing required props

* feat(web): Show notification after copying image to clipboard

* chore(web): Fix typescript error

* chore(web): Formatting

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
bo0tzz 2 years ago
parent
commit
70cd313082

+ 8 - 0
web/src/app.d.ts

@@ -9,3 +9,11 @@ declare namespace App {
 
 	// interface Platform {}
 }
+
+// Source: https://stackoverflow.com/questions/63814432/typescript-typing-of-non-standard-window-event-in-svelte
+// To fix the <svelte:window... in components/asset-viewer/photo-viewer.svelte
+declare namespace svelte.JSX {
+	interface HTMLAttributes<T> {
+		oncopyImage?: () => void;
+	}
+}

+ 12 - 0
web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte

@@ -11,10 +11,13 @@
 	import MenuOption from '../shared-components/context-menu/menu-option.svelte';
 	import Star from 'svelte-material-icons/Star.svelte';
 	import StarOutline from 'svelte-material-icons/StarOutline.svelte';
+	import ContentCopy from 'svelte-material-icons/ContentCopy.svelte';
+
 	import { page } from '$app/stores';
 	import { AssetResponseDto } from '../../../api';
 
 	export let asset: AssetResponseDto;
+	export let showCopyButton: boolean;
 
 	const isOwner = asset.ownerId === $page.data.user.id;
 
@@ -45,6 +48,15 @@
 		<CircleIconButton logo={ArrowLeft} on:click={() => dispatch('goBack')} />
 	</div>
 	<div class="text-white flex gap-2">
+		{#if showCopyButton}
+			<CircleIconButton
+				logo={ContentCopy}
+				on:click={() => {
+					const copyEvent = new CustomEvent('copyImage');
+					window.dispatchEvent(copyEvent);
+				}}
+			/>
+		{/if}
 		<CircleIconButton logo={CloudDownloadOutline} on:click={() => dispatch('download')} />
 		<CircleIconButton logo={InformationOutline} on:click={() => dispatch('showDetail')} />
 		{#if isOwner}

+ 3 - 2
web/src/lib/components/asset-viewer/asset-viewer.svelte

@@ -1,7 +1,7 @@
 <script lang="ts">
 	import { createEventDispatcher, onMount, onDestroy } from 'svelte';
 	import { fly } from 'svelte/transition';
-	import AsserViewerNavBar from './asset-viewer-nav-bar.svelte';
+	import AssetViewerNavBar from './asset-viewer-nav-bar.svelte';
 	import ChevronRight from 'svelte-material-icons/ChevronRight.svelte';
 	import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte';
 	import PhotoViewer from './photo-viewer.svelte';
@@ -219,11 +219,12 @@
 	class="fixed h-screen w-screen top-0 overflow-y-hidden bg-black z-[999] grid grid-rows-[64px_1fr] grid-cols-4"
 >
 	<div class="col-start-1 col-span-4 row-start-1 row-span-1 z-[1000] transition-transform">
-		<AsserViewerNavBar
+		<AssetViewerNavBar
 			{asset}
 			on:goBack={closeViewer}
 			on:showDetail={showDetailInfoHandler}
 			on:download={downloadFile}
+			showCopyButton={asset.type === AssetTypeEnum.Image}
 			on:delete={deleteAsset}
 			on:favorite={toggleFavorite}
 			on:addToAlbum={() => openAlbumPicker(false)}

+ 18 - 3
web/src/lib/components/asset-viewer/photo-viewer.svelte

@@ -5,6 +5,10 @@
 	import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 	import { api, AssetResponseDto } from '@api';
 	import Keydown from 'svelte-keydown';
+	import {
+		notificationController,
+		NotificationType
+	} from '../shared-components/notification/notification';
 
 	export let assetId: string;
 
@@ -39,14 +43,25 @@
 		}
 	};
 
-	const handleCopy = async (keyEvent: CustomEvent<string>) => {
+	const handleKeypress = async (keyEvent: CustomEvent<string>) => {
 		if (keyEvent.detail == 'Control-c' || keyEvent.detail == 'Meta-c') {
-			await copyImageToClipboard(assetData);
+			await doCopy();
 		}
 	};
+
+	export const doCopy = async () => {
+		await copyImageToClipboard(assetData);
+		notificationController.show({
+			type: NotificationType.Info,
+			message: 'Copied image to clipboard.',
+			timeout: 3000
+		});
+	};
 </script>
 
-<Keydown on:combo={handleCopy} />
+<Keydown on:combo={handleKeypress} />
+
+<svelte:window on:copyImage={async () => await doCopy()} />
 
 <div
 	transition:fade={{ duration: 150 }}