mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-02 18:47:07 -05:00 
			
		
		
		
	feat(web): add current view asset to album (#923)
This commit is contained in:
		
							parent
							
								
									d696ce4e41
								
							
						
					
					
						commit
						5aa06ed3be
					
				
							
								
								
									
										39
									
								
								web/src/lib/components/asset-viewer/album-list-item.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								web/src/lib/components/asset-viewer/album-list-item.svelte
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import { AlbumResponseDto, ThumbnailFormat } from '@api';
 | 
				
			||||||
 | 
						import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const dispatcher = createEventDispatcher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export let album: AlbumResponseDto;
 | 
				
			||||||
 | 
						export let variant: 'simple' | 'full' = 'full';
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<button
 | 
				
			||||||
 | 
						on:click={() => dispatcher('album')}
 | 
				
			||||||
 | 
						class="flex gap-4 px-6 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
						<div class="h-12 w-12">
 | 
				
			||||||
 | 
							<img
 | 
				
			||||||
 | 
								src={`/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`}
 | 
				
			||||||
 | 
								alt={album.albumName}
 | 
				
			||||||
 | 
								class={`object-cover h-full w-full transition-all z-0 rounded-xl duration-300 hover:shadow-lg`}
 | 
				
			||||||
 | 
								data-testid="album-image"
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="h-12 flex flex-col items-start justify-center">
 | 
				
			||||||
 | 
							<span>{album.albumName}</span>
 | 
				
			||||||
 | 
							<span class="flex gap-1 text-sm">
 | 
				
			||||||
 | 
								{#if variant === 'simple'}
 | 
				
			||||||
 | 
									<span
 | 
				
			||||||
 | 
										>{#if album.shared}Shared{/if}
 | 
				
			||||||
 | 
									</span>
 | 
				
			||||||
 | 
								{:else}
 | 
				
			||||||
 | 
									<span>{album.assetCount} items</span>
 | 
				
			||||||
 | 
									<span> · {new Date(album.createdAt).toLocaleDateString()}</span>
 | 
				
			||||||
 | 
									<span
 | 
				
			||||||
 | 
										>{#if album.shared} · Shared{/if}
 | 
				
			||||||
 | 
									</span>
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							</span>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</button>
 | 
				
			||||||
@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import { AlbumResponseDto, api } from '@api';
 | 
				
			||||||
 | 
						import { createEventDispatcher, onMount } from 'svelte';
 | 
				
			||||||
 | 
						import Plus from 'svelte-material-icons/Plus.svelte';
 | 
				
			||||||
 | 
						import BaseModal from '../shared-components/base-modal.svelte';
 | 
				
			||||||
 | 
						import AlbumListItem from './album-list-item.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let albums: AlbumResponseDto[] = [];
 | 
				
			||||||
 | 
						let recentAlbums: AlbumResponseDto[] = [];
 | 
				
			||||||
 | 
						let loading = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const dispatch = createEventDispatcher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export let shared: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						onMount(async () => {
 | 
				
			||||||
 | 
							const { data } = await api.albumApi.getAllAlbums();
 | 
				
			||||||
 | 
							albums = data;
 | 
				
			||||||
 | 
							recentAlbums = albums
 | 
				
			||||||
 | 
								.filter((album) => album.shared === shared)
 | 
				
			||||||
 | 
								.sort((a, b) => (new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1))
 | 
				
			||||||
 | 
								.slice(0, 3);
 | 
				
			||||||
 | 
							loading = false;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleSelect = (album: AlbumResponseDto) => {
 | 
				
			||||||
 | 
							dispatch('album', { album });
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleNew = () => {
 | 
				
			||||||
 | 
							if (shared) {
 | 
				
			||||||
 | 
								dispatch('newAlbum');
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								dispatch('newSharedAlbum');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<BaseModal on:close={() => dispatch('close')}>
 | 
				
			||||||
 | 
						<svelte:fragment slot="title">
 | 
				
			||||||
 | 
							<span class="flex gap-2 place-items-center">
 | 
				
			||||||
 | 
								<p class="font-medium">
 | 
				
			||||||
 | 
									Add to {#if shared}shared {/if}
 | 
				
			||||||
 | 
								</p>
 | 
				
			||||||
 | 
							</span>
 | 
				
			||||||
 | 
						</svelte:fragment>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div class=" max-h-[400px] overflow-y-auto immich-scrollbar">
 | 
				
			||||||
 | 
							<div class="flex flex-col mb-2">
 | 
				
			||||||
 | 
								{#if loading}
 | 
				
			||||||
 | 
									{#each { length: 3 } as _}
 | 
				
			||||||
 | 
										<div class="animate-pulse flex gap-4 px-6 py-2">
 | 
				
			||||||
 | 
											<div class="h-12 w-12 bg-slate-200 rounded-xl" />
 | 
				
			||||||
 | 
											<div class="flex flex-col items-start justify-center gap-2">
 | 
				
			||||||
 | 
												<span class="animate-pulse w-36 h-4 bg-slate-200" />
 | 
				
			||||||
 | 
												<div class="flex animate-pulse gap-1">
 | 
				
			||||||
 | 
													<span class="w-8 h-3 bg-slate-200" />
 | 
				
			||||||
 | 
													<span class="w-20 h-3 bg-slate-200" />
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									{/each}
 | 
				
			||||||
 | 
								{:else}
 | 
				
			||||||
 | 
									<button
 | 
				
			||||||
 | 
										on:click={handleNew}
 | 
				
			||||||
 | 
										class="flex gap-4 px-6 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors items-center"
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										<div class="h-12 w-12 flex justify-center items-center">
 | 
				
			||||||
 | 
											<Plus size="30" />
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<p class="">
 | 
				
			||||||
 | 
											New {#if shared}Shared {/if}Album
 | 
				
			||||||
 | 
										</p>
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
									{#if albums.length > 0}
 | 
				
			||||||
 | 
										<p class="text-sm font-medium px-5 py-1">RECENT</p>
 | 
				
			||||||
 | 
										{#each recentAlbums as album}
 | 
				
			||||||
 | 
											{#key album.id}
 | 
				
			||||||
 | 
												<AlbumListItem variant="simple" {album} on:album={() => handleSelect(album)} />
 | 
				
			||||||
 | 
											{/key}
 | 
				
			||||||
 | 
										{/each}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<p class="text-sm font-medium px-5 py-1">ALL ALBUMS</p>
 | 
				
			||||||
 | 
										{#each albums as album}
 | 
				
			||||||
 | 
											{#key album.id}
 | 
				
			||||||
 | 
												<AlbumListItem {album} on:album={() => handleSelect(album)} />
 | 
				
			||||||
 | 
											{/key}
 | 
				
			||||||
 | 
										{/each}
 | 
				
			||||||
 | 
									{:else}
 | 
				
			||||||
 | 
										<p class="text-sm px-5 py-1">It looks like you do not have any albums yet.</p>
 | 
				
			||||||
 | 
									{/if}
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</BaseModal>
 | 
				
			||||||
@ -4,9 +4,30 @@
 | 
				
			|||||||
	import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
 | 
						import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
 | 
				
			||||||
	import CloudDownloadOutline from 'svelte-material-icons/CloudDownloadOutline.svelte';
 | 
						import CloudDownloadOutline from 'svelte-material-icons/CloudDownloadOutline.svelte';
 | 
				
			||||||
	import InformationOutline from 'svelte-material-icons/InformationOutline.svelte';
 | 
						import InformationOutline from 'svelte-material-icons/InformationOutline.svelte';
 | 
				
			||||||
 | 
						import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 | 
				
			||||||
	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 | 
						import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 | 
				
			||||||
	import CircleIconButton from '../shared-components/circle-icon-button.svelte';
 | 
						import CircleIconButton from '../shared-components/circle-icon-button.svelte';
 | 
				
			||||||
 | 
						import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
 | 
				
			||||||
 | 
						import MenuOption from '../shared-components/context-menu/menu-option.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const dispatch = createEventDispatcher();
 | 
						const dispatch = createEventDispatcher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let contextMenuPosition = { x: 0, y: 0 };
 | 
				
			||||||
 | 
						let isShowAssetOptions = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const showOptionsMenu = (event: CustomEvent) => {
 | 
				
			||||||
 | 
							contextMenuPosition = {
 | 
				
			||||||
 | 
								x: event.detail.mouseEvent.x,
 | 
				
			||||||
 | 
								y: event.detail.mouseEvent.y
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							isShowAssetOptions = !isShowAssetOptions;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const onMenuClick = (eventName: string) => {
 | 
				
			||||||
 | 
							isShowAssetOptions = false;
 | 
				
			||||||
 | 
							dispatch(eventName);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div
 | 
					<div
 | 
				
			||||||
@ -19,5 +40,15 @@
 | 
				
			|||||||
		<CircleIconButton logo={CloudDownloadOutline} on:click={() => dispatch('download')} />
 | 
							<CircleIconButton logo={CloudDownloadOutline} on:click={() => dispatch('download')} />
 | 
				
			||||||
		<CircleIconButton logo={DeleteOutline} on:click={() => dispatch('delete')} />
 | 
							<CircleIconButton logo={DeleteOutline} on:click={() => dispatch('delete')} />
 | 
				
			||||||
		<CircleIconButton logo={InformationOutline} on:click={() => dispatch('showDetail')} />
 | 
							<CircleIconButton logo={InformationOutline} on:click={() => dispatch('showDetail')} />
 | 
				
			||||||
 | 
							<CircleIconButton logo={DotsVertical} on:click={(event) => showOptionsMenu(event)} />
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{#if isShowAssetOptions}
 | 
				
			||||||
 | 
						<ContextMenu {...contextMenuPosition} on:clickoutside={() => (isShowAssetOptions = false)}>
 | 
				
			||||||
 | 
							<div class="flex flex-col rounded-lg ">
 | 
				
			||||||
 | 
								<MenuOption on:click={() => onMenuClick('addToAlbum')} text="Add to Album" />
 | 
				
			||||||
 | 
								<MenuOption on:click={() => onMenuClick('addToSharedAlbum')} text="Add to Shared Album" />
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</ContextMenu>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,9 +6,17 @@
 | 
				
			|||||||
	import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte';
 | 
						import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte';
 | 
				
			||||||
	import PhotoViewer from './photo-viewer.svelte';
 | 
						import PhotoViewer from './photo-viewer.svelte';
 | 
				
			||||||
	import DetailPanel from './detail-panel.svelte';
 | 
						import DetailPanel from './detail-panel.svelte';
 | 
				
			||||||
 | 
						import { goto } from '$app/navigation';
 | 
				
			||||||
	import { downloadAssets } from '$lib/stores/download';
 | 
						import { downloadAssets } from '$lib/stores/download';
 | 
				
			||||||
	import VideoViewer from './video-viewer.svelte';
 | 
						import VideoViewer from './video-viewer.svelte';
 | 
				
			||||||
	import { api, AssetResponseDto, AssetTypeEnum, AlbumResponseDto } from '@api';
 | 
						import AlbumSelectionModal from './album-selection-modal.svelte';
 | 
				
			||||||
 | 
						import {
 | 
				
			||||||
 | 
							api,
 | 
				
			||||||
 | 
							AddAssetsResponseDto,
 | 
				
			||||||
 | 
							AssetResponseDto,
 | 
				
			||||||
 | 
							AssetTypeEnum,
 | 
				
			||||||
 | 
							AlbumResponseDto
 | 
				
			||||||
 | 
						} from '@api';
 | 
				
			||||||
	import {
 | 
						import {
 | 
				
			||||||
		notificationController,
 | 
							notificationController,
 | 
				
			||||||
		NotificationType
 | 
							NotificationType
 | 
				
			||||||
@ -29,6 +37,8 @@
 | 
				
			|||||||
	let halfRightHover = false;
 | 
						let halfRightHover = false;
 | 
				
			||||||
	let isShowDetail = false;
 | 
						let isShowDetail = false;
 | 
				
			||||||
	let appearsInAlbums: AlbumResponseDto[] = [];
 | 
						let appearsInAlbums: AlbumResponseDto[] = [];
 | 
				
			||||||
 | 
						let isShowAlbumPicker = false;
 | 
				
			||||||
 | 
						let addToSharedAlbum = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const onKeyboardPress = (keyInfo: KeyboardEvent) => handleKeyboardPress(keyInfo.key);
 | 
						const onKeyboardPress = (keyInfo: KeyboardEvent) => handleKeyboardPress(keyInfo.key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -167,6 +177,39 @@
 | 
				
			|||||||
			console.error('Error deleteSelectedAssetHandler', e);
 | 
								console.error('Error deleteSelectedAssetHandler', e);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const openAlbumPicker = (shared: boolean) => {
 | 
				
			||||||
 | 
							isShowAlbumPicker = true;
 | 
				
			||||||
 | 
							addToSharedAlbum = shared;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const showAddNotification = (dto: AddAssetsResponseDto) => {
 | 
				
			||||||
 | 
							notificationController.show({
 | 
				
			||||||
 | 
								message: `Added ${dto.successfullyAdded} to ${dto.album?.albumName}`,
 | 
				
			||||||
 | 
								type: NotificationType.Info
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (dto.successfullyAdded === 1 && dto.album) {
 | 
				
			||||||
 | 
								appearsInAlbums = [...appearsInAlbums, dto.album];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleAddToNewAlbum = () => {
 | 
				
			||||||
 | 
							isShowAlbumPicker = false;
 | 
				
			||||||
 | 
							api.albumApi.createAlbum({ albumName: 'Untitled', assetIds: [asset.id] }).then((response) => {
 | 
				
			||||||
 | 
								const album = response.data;
 | 
				
			||||||
 | 
								goto('/albums/' + album.id);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleAddToAlbum = async (event: CustomEvent<{ album: AlbumResponseDto }>) => {
 | 
				
			||||||
 | 
							isShowAlbumPicker = false;
 | 
				
			||||||
 | 
							const album = event.detail.album;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							api.albumApi
 | 
				
			||||||
 | 
								.addAssetsToAlbum(album.id, { assetIds: [asset.id] })
 | 
				
			||||||
 | 
								.then((response) => showAddNotification(response.data));
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section
 | 
					<section
 | 
				
			||||||
@ -179,6 +222,8 @@
 | 
				
			|||||||
			on:showDetail={showDetailInfoHandler}
 | 
								on:showDetail={showDetailInfoHandler}
 | 
				
			||||||
			on:download={downloadFile}
 | 
								on:download={downloadFile}
 | 
				
			||||||
			on:delete={deleteAsset}
 | 
								on:delete={deleteAsset}
 | 
				
			||||||
 | 
								on:addToAlbum={() => openAlbumPicker(false)}
 | 
				
			||||||
 | 
								on:addToSharedAlbum={() => openAlbumPicker(true)}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -246,6 +291,17 @@
 | 
				
			|||||||
			<DetailPanel {asset} albums={appearsInAlbums} on:close={() => (isShowDetail = false)} />
 | 
								<DetailPanel {asset} albums={appearsInAlbums} on:close={() => (isShowDetail = false)} />
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{#if isShowAlbumPicker}
 | 
				
			||||||
 | 
							<AlbumSelectionModal
 | 
				
			||||||
 | 
								shared={addToSharedAlbum}
 | 
				
			||||||
 | 
								on:newAlbum={handleAddToNewAlbum}
 | 
				
			||||||
 | 
								on:newSharedAlbum={handleAddToNewAlbum}
 | 
				
			||||||
 | 
								on:album={handleAddToAlbum}
 | 
				
			||||||
 | 
								on:close={() => (isShowAlbumPicker = false)}
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
							<div class="w-full h-full">Hello</div>
 | 
				
			||||||
 | 
						{/if}
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
 | 
				
			|||||||
@ -38,7 +38,7 @@
 | 
				
			|||||||
		on:out-click={() => dispatch('close')}
 | 
							on:out-click={() => dispatch('close')}
 | 
				
			||||||
		class="bg-immich-bg dark:bg-immich-dark-gray dark:text-immich-dark-fg w-[450px] min-h-[200px] max-h-[500px] rounded-lg shadow-md"
 | 
							class="bg-immich-bg dark:bg-immich-dark-gray dark:text-immich-dark-fg w-[450px] min-h-[200px] max-h-[500px] rounded-lg shadow-md"
 | 
				
			||||||
	>
 | 
						>
 | 
				
			||||||
		<div class="flex justify-between place-items-center p-5">
 | 
							<div class="flex justify-between place-items-center px-5 py-3">
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
				<slot name="title">
 | 
									<slot name="title">
 | 
				
			||||||
					<p>Modal Title</p>
 | 
										<p>Modal Title</p>
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,7 @@
 | 
				
			|||||||
<div
 | 
					<div
 | 
				
			||||||
	transition:slide={{ duration: 200, easing: quintOut }}
 | 
						transition:slide={{ duration: 200, easing: quintOut }}
 | 
				
			||||||
	bind:this={menuEl}
 | 
						bind:this={menuEl}
 | 
				
			||||||
	class="absolute w-[175px] z-[99999] rounded-lg shadow-md"
 | 
						class="absolute w-[200px] z-[99999] rounded-lg overflow-hidden"
 | 
				
			||||||
	style={`top: ${y}px; left: ${x}px;`}
 | 
						style={`top: ${y}px; left: ${x}px;`}
 | 
				
			||||||
	use:clickOutside
 | 
						use:clickOutside
 | 
				
			||||||
	on:out-click={() => dispatch('clickoutside')}
 | 
						on:out-click={() => dispatch('clickoutside')}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@
 | 
				
			|||||||
<button
 | 
					<button
 | 
				
			||||||
	class:disabled={isDisabled}
 | 
						class:disabled={isDisabled}
 | 
				
			||||||
	on:click={handleClick}
 | 
						on:click={handleClick}
 | 
				
			||||||
	class="bg-white hover:bg-gray-300 dark:text-immich-dark-bg transition-all p-4 w-full text-left rounded-lg text-sm"
 | 
						class="bg-white hover:bg-gray-300 dark:text-immich-dark-bg transition-all p-4 w-full text-left text-sm"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{#if text}
 | 
						{#if text}
 | 
				
			||||||
		{text}
 | 
							{text}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user