1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-04 02:46:47 +01:00

fix(web): user management responsive design (#5698)

* fix: user management tailwind

* use top instead of inset-y-0

* add types to createEventDispatcher
This commit is contained in:
martin 2023-12-14 17:55:15 +01:00 committed by GitHub
parent 8e39d389b5
commit f2270ad757
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 257 additions and 135 deletions

View file

@ -22,6 +22,7 @@
import SettingAccordion from '../setting-accordion.svelte'; import SettingAccordion from '../setting-accordion.svelte';
import { mdiHelpCircleOutline } from '@mdi/js'; import { mdiHelpCircleOutline } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte'; import Icon from '$lib/components/elements/icon.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let ffmpegConfig: SystemConfigFFmpegDto; // this is the config that is being edited export let ffmpegConfig: SystemConfigFFmpegDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -63,6 +64,14 @@
} }
} }
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function reset() { async function reset() {
const { data: resetConfig } = await api.systemConfigApi.getConfig(); const { data: resetConfig } = await api.systemConfigApi.getConfig();
@ -354,9 +363,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -9,6 +9,7 @@
import { handleError } from '../../../../utils/handle-error'; import { handleError } from '../../../../utils/handle-error';
import SettingButtonsRow from '../setting-buttons-row.svelte'; import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let jobConfig: SystemConfigJobDto; // this is the config that is being edited export let jobConfig: SystemConfigJobDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -29,6 +30,14 @@
JobName.Migration, JobName.Migration,
]; ];
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function getConfigs() { async function getConfigs() {
[savedConfig, defaultConfig] = await Promise.all([ [savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.job), api.systemConfigApi.getConfig().then((res) => res.data.job),
@ -101,9 +110,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -11,6 +11,7 @@
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { handleError } from '../../../../utils/handle-error'; import { handleError } from '../../../../utils/handle-error';
import SettingAccordion from '../setting-accordion.svelte'; import SettingAccordion from '../setting-accordion.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let libraryConfig: SystemConfigLibraryDto; // this is the config that is being edited export let libraryConfig: SystemConfigLibraryDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -25,6 +26,14 @@
let savedConfig: SystemConfigLibraryDto; let savedConfig: SystemConfigLibraryDto;
let defaultConfig: SystemConfigLibraryDto; let defaultConfig: SystemConfigLibraryDto;
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function getConfigs() { async function getConfigs() {
[savedConfig, defaultConfig] = await Promise.all([ [savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.library), api.systemConfigApi.getConfig().then((res) => res.data.library),
@ -131,9 +140,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -12,6 +12,7 @@
import SettingSwitch from '../setting-switch.svelte'; import SettingSwitch from '../setting-switch.svelte';
import SettingAccordion from '../setting-accordion.svelte'; import SettingAccordion from '../setting-accordion.svelte';
import SettingSelect from '../setting-select.svelte'; import SettingSelect from '../setting-select.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let machineLearningConfig: SystemConfigMachineLearningDto; // this is the config that is being edited export let machineLearningConfig: SystemConfigMachineLearningDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -33,6 +34,14 @@
notificationController.show({ message: 'Reset to the last saved settings', type: NotificationType.Info }); notificationController.show({ message: 'Reset to the last saved settings', type: NotificationType.Info });
} }
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function saveSetting() { async function saveSetting() {
try { try {
const { data: current } = await api.systemConfigApi.getConfig(); const { data: current } = await api.systemConfigApi.getConfig();
@ -212,9 +221,8 @@
</SettingAccordion> </SettingAccordion>
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -11,6 +11,7 @@
import SettingButtonsRow from '../setting-buttons-row.svelte'; import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingSwitch from '../setting-switch.svelte'; import SettingSwitch from '../setting-switch.svelte';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let config: SystemConfigDto; // this is the config that is being edited export let config: SystemConfigDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -18,6 +19,14 @@
let savedConfig: SystemConfigDto; let savedConfig: SystemConfigDto;
let defaultConfig: SystemConfigDto; let defaultConfig: SystemConfigDto;
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function refreshConfig() { async function refreshConfig() {
[savedConfig, defaultConfig] = await Promise.all([ [savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data), api.systemConfigApi.getConfig().then((res) => res.data),
@ -133,9 +142,8 @@
> >
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual( showResetToDefault={!isEqual(
{ ...savedConfig.map, ...savedConfig.reverseGeocoding }, { ...savedConfig.map, ...savedConfig.reverseGeocoding },
{ ...defaultConfig.map, ...defaultConfig.reverseGeocoding }, { ...defaultConfig.map, ...defaultConfig.reverseGeocoding },

View file

@ -9,6 +9,7 @@
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import SettingButtonsRow from '../setting-buttons-row.svelte'; import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingSwitch from '../setting-switch.svelte'; import SettingSwitch from '../setting-switch.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let newVersionCheckConfig: SystemConfigNewVersionCheckDto; // this is the config that is being edited export let newVersionCheckConfig: SystemConfigNewVersionCheckDto; // this is the config that is being edited
@ -22,6 +23,14 @@
]); ]);
} }
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function saveSetting() { async function saveSetting() {
try { try {
const { data: configs } = await api.systemConfigApi.getConfig(); const { data: configs } = await api.systemConfigApi.getConfig();
@ -79,9 +88,8 @@
bind:checked={newVersionCheckConfig.enabled} bind:checked={newVersionCheckConfig.enabled}
/> />
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
/> />
</div> </div>

View file

@ -11,6 +11,7 @@
import SettingButtonsRow from '../setting-buttons-row.svelte'; import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import SettingSwitch from '../setting-switch.svelte'; import SettingSwitch from '../setting-switch.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let oauthConfig: SystemConfigOAuthDto; export let oauthConfig: SystemConfigOAuthDto;
export let disabled = false; export let disabled = false;
@ -18,6 +19,14 @@
let savedConfig: SystemConfigOAuthDto; let savedConfig: SystemConfigOAuthDto;
let defaultConfig: SystemConfigOAuthDto; let defaultConfig: SystemConfigOAuthDto;
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
const handleToggleOverride = () => { const handleToggleOverride = () => {
// click runs before bind // click runs before bind
const previouslyEnabled = oauthConfig.mobileOverrideEnabled; const previouslyEnabled = oauthConfig.mobileOverrideEnabled;
@ -209,9 +218,8 @@
{/if} {/if}
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -10,6 +10,7 @@
import ConfirmDisableLogin from '../confirm-disable-login.svelte'; import ConfirmDisableLogin from '../confirm-disable-login.svelte';
import SettingButtonsRow from '../setting-buttons-row.svelte'; import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingSwitch from '../setting-switch.svelte'; import SettingSwitch from '../setting-switch.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let passwordLoginConfig: SystemConfigPasswordLoginDto; // this is the config that is being edited export let passwordLoginConfig: SystemConfigPasswordLoginDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -17,6 +18,14 @@
let savedConfig: SystemConfigPasswordLoginDto; let savedConfig: SystemConfigPasswordLoginDto;
let defaultConfig: SystemConfigPasswordLoginDto; let defaultConfig: SystemConfigPasswordLoginDto;
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function getConfigs() { async function getConfigs() {
[savedConfig, defaultConfig] = await Promise.all([ [savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.passwordLogin), api.systemConfigApi.getConfig().then((res) => res.data.passwordLogin),
@ -107,9 +116,8 @@
/> />
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -1,8 +1,12 @@
<script lang="ts"> <script lang="ts">
import Button from '$lib/components/elements/buttons/button.svelte'; import Button from '$lib/components/elements/buttons/button.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
reset: ResetOptions;
save: void;
}>();
export let showResetToDefault = true; export let showResetToDefault = true;
export let disabled = false; export let disabled = false;
@ -12,7 +16,7 @@
<div class="left"> <div class="left">
{#if showResetToDefault} {#if showResetToDefault}
<button <button
on:click={() => dispatch('reset-to-default')} on:click={() => dispatch('reset', { default: true })}
class="bg-none text-sm font-medium text-immich-primary hover:text-immich-primary/75 dark:text-immich-dark-primary hover:dark:text-immich-dark-primary/75" class="bg-none text-sm font-medium text-immich-primary hover:text-immich-primary/75 dark:text-immich-dark-primary hover:dark:text-immich-dark-primary/75"
> >
Reset to default Reset to default
@ -21,7 +25,7 @@
</div> </div>
<div class="right"> <div class="right">
<Button {disabled} size="sm" color="gray" on:click={() => dispatch('reset')}>Reset</Button> <Button {disabled} size="sm" color="gray" on:click={() => dispatch('reset', { default: false })}>Reset</Button>
<Button {disabled} size="sm" on:click={() => dispatch('save')}>Save</Button> <Button {disabled} size="sm" on:click={() => dispatch('save')}>Save</Button>
</div> </div>
</div> </div>

View file

@ -14,6 +14,7 @@
} from '$lib/components/shared-components/notification/notification'; } from '$lib/components/shared-components/notification/notification';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import { user } from '$lib/stores/user.store'; import { user } from '$lib/stores/user.store';
import type { ResetOptions } from '$lib/utils/dipatch';
export let storageConfig: SystemConfigStorageTemplateDto; export let storageConfig: SystemConfigStorageTemplateDto;
export let disabled = false; export let disabled = false;
@ -23,6 +24,14 @@
let templateOptions: SystemConfigTemplateStorageOptionDto; let templateOptions: SystemConfigTemplateStorageOptionDto;
let selectedPreset = ''; let selectedPreset = '';
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function getConfigs() { async function getConfigs() {
[savedConfig, defaultConfig, templateOptions] = await Promise.all([ [savedConfig, defaultConfig, templateOptions] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.storageTemplate), api.systemConfigApi.getConfig().then((res) => res.data.storageTemplate),
@ -232,9 +241,8 @@
</div> </div>
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -9,6 +9,7 @@
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import SettingButtonsRow from '../setting-buttons-row.svelte'; import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingTextarea from '../setting-textarea.svelte'; import SettingTextarea from '../setting-textarea.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let themeConfig: SystemConfigThemeDto; // this is the config that is being edited export let themeConfig: SystemConfigThemeDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -16,6 +17,14 @@
let savedConfig: SystemConfigThemeDto; let savedConfig: SystemConfigThemeDto;
let defaultConfig: SystemConfigThemeDto; let defaultConfig: SystemConfigThemeDto;
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function getConfigs() { async function getConfigs() {
[savedConfig, defaultConfig] = await Promise.all([ [savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.theme), api.systemConfigApi.getConfig().then((res) => res.data.theme),
@ -84,9 +93,8 @@
/> />
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -10,6 +10,7 @@
} from '$lib/components/shared-components/notification/notification'; } from '$lib/components/shared-components/notification/notification';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import SettingSwitch from '../setting-switch.svelte'; import SettingSwitch from '../setting-switch.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let thumbnailConfig: SystemConfigThumbnailDto; // this is the config that is being edited export let thumbnailConfig: SystemConfigThumbnailDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -17,6 +18,14 @@
let savedConfig: SystemConfigThumbnailDto; let savedConfig: SystemConfigThumbnailDto;
let defaultConfig: SystemConfigThumbnailDto; let defaultConfig: SystemConfigThumbnailDto;
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function getConfigs() { async function getConfigs() {
[savedConfig, defaultConfig] = await Promise.all([ [savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.thumbnail), api.systemConfigApi.getConfig().then((res) => res.data.thumbnail),
@ -133,9 +142,8 @@
<div class="ml-4"> <div class="ml-4">
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -10,6 +10,7 @@
import SettingButtonsRow from '../setting-buttons-row.svelte'; import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingSwitch from '../setting-switch.svelte'; import SettingSwitch from '../setting-switch.svelte';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import type { ResetOptions } from '$lib/utils/dipatch';
export let trashConfig: SystemConfigTrashDto; // this is the config that is being edited export let trashConfig: SystemConfigTrashDto; // this is the config that is being edited
export let disabled = false; export let disabled = false;
@ -17,6 +18,14 @@
let savedConfig: SystemConfigTrashDto; let savedConfig: SystemConfigTrashDto;
let defaultConfig: SystemConfigTrashDto; let defaultConfig: SystemConfigTrashDto;
const handleReset = (detail: ResetOptions) => {
if (detail.default) {
resetToDefault();
} else {
reset();
}
};
async function getConfigs() { async function getConfigs() {
[savedConfig, defaultConfig] = await Promise.all([ [savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.trash), api.systemConfigApi.getConfig().then((res) => res.data.trash),
@ -90,9 +99,8 @@
/> />
<SettingButtonsRow <SettingButtonsRow
on:reset={reset} on:reset={({ detail }) => handleReset(detail)}
on:save={saveSetting} on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)} showResetToDefault={!isEqual(savedConfig, defaultConfig)}
{disabled} {disabled}
/> />

View file

@ -50,7 +50,12 @@
let message = ''; let message = '';
let isSendingMessage = false; let isSendingMessage = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
deleteComment: void;
deleteLike: void;
addComment: void;
close: void;
}>();
$: showDeleteReaction = Array(reactions.length).fill(false); $: showDeleteReaction = Array(reactions.length).fill(false);
$: { $: {

View file

@ -725,9 +725,9 @@
albumId={album?.id} albumId={album?.id}
albums={appearsInAlbums} albums={appearsInAlbums}
on:close={() => ($isShowDetail = false)} on:close={() => ($isShowDetail = false)}
on:close-viewer={handleCloseViewer} on:closeViewer={handleCloseViewer}
on:description-focus-in={disableKeyDownEvent} on:descriptionFocusIn={disableKeyDownEvent}
on:description-focus-out={enableKeyDownEvent} on:descriptionFocusOut={enableKeyDownEvent}
/> />
</div> </div>
{/if} {/if}

View file

@ -87,7 +87,13 @@
unsubscribe(); unsubscribe();
}); });
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
close: void;
descriptionFocusIn: void;
descriptionFocusOut: void;
click: AlbumResponseDto;
closeViewer: void;
}>();
const getMegapixel = (width: number, height: number): number | undefined => { const getMegapixel = (width: number, height: number): number | undefined => {
const megapixel = Math.round((height * width) / 1_000_000); const megapixel = Math.round((height * width) / 1_000_000);
@ -114,11 +120,11 @@
}; };
const handleFocusIn = () => { const handleFocusIn = () => {
dispatch('description-focus-in'); dispatch('descriptionFocusIn');
}; };
const handleFocusOut = async () => { const handleFocusOut = async () => {
dispatch('description-focus-out'); dispatch('descriptionFocusOut');
try { try {
await api.assetApi.updateAsset({ await api.assetApi.updateAsset({
id: asset.id, id: asset.id,
@ -241,7 +247,7 @@
href="{AppRoute.PEOPLE}/{person.id}?previousRoute={albumId href="{AppRoute.PEOPLE}/{person.id}?previousRoute={albumId
? `${AppRoute.ALBUMS}/${albumId}` ? `${AppRoute.ALBUMS}/${albumId}`
: AppRoute.PHOTOS}" : AppRoute.PHOTOS}"
on:click={() => dispatch('close-viewer')} on:click={() => dispatch('closeViewer')}
> >
<div class="relative"> <div class="relative">
<ImageThumbnail <ImageThumbnail

View file

@ -11,7 +11,13 @@
let intersecting = false; let intersecting = false;
let container: HTMLDivElement; let container: HTMLDivElement;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
hidden: HTMLDivElement;
intersected: {
container: HTMLDivElement;
position: BucketPosition;
};
}>();
onMount(() => { onMount(() => {
if (typeof IntersectionObserver !== 'undefined') { if (typeof IntersectionObserver !== 'undefined') {

View file

@ -8,7 +8,10 @@
export let album: AlbumResponseDto; export let album: AlbumResponseDto;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
editSuccess: void;
cancel: void;
}>();
const editUser = async () => { const editUser = async () => {
try { try {
@ -21,7 +24,7 @@
}); });
if (status === 200) { if (status === 200) {
dispatch('edit-success'); dispatch('editSuccess');
} }
} catch (error) { } catch (error) {
handleError(error, 'Unable to update user'); handleError(error, 'Unable to update user');

View file

@ -6,8 +6,9 @@
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
import { handleError } from '../../utils/handle-error'; import { handleError } from '../../utils/handle-error';
import Icon from '$lib/components/elements/icon.svelte'; import Icon from '$lib/components/elements/icon.svelte';
import { mdiAccountEditOutline } from '@mdi/js'; import { mdiAccountEditOutline, mdiClose } from '@mdi/js';
import { AppRoute } from '$lib/constants'; import { AppRoute } from '$lib/constants';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
export let user: UserResponseDto; export let user: UserResponseDto;
export let canResetPassword = true; export let canResetPassword = true;
@ -17,7 +18,11 @@
let isShowResetPasswordConfirmation = false; let isShowResetPasswordConfirmation = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
close: void;
resetPasswordSuccess: void;
editSuccess: void;
}>();
const editUser = async () => { const editUser = async () => {
try { try {
@ -33,7 +38,7 @@
}); });
if (status === 200) { if (status === 200) {
dispatch('edit-success'); dispatch('editSuccess');
} }
} catch (error) { } catch (error) {
handleError(error, 'Unable to update user'); handleError(error, 'Unable to update user');
@ -53,7 +58,7 @@
}); });
if (status == 200) { if (status == 200) {
dispatch('reset-password-success'); dispatch('resetPasswordSuccess');
} }
} catch (e) { } catch (e) {
console.error('Error reseting user password', e); console.error('Error reseting user password', e);
@ -68,8 +73,12 @@
</script> </script>
<div <div
class="max-h-screen w-[500px] max-w-[95vw] overflow-y-auto rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg" class="relative max-h-screen w-[500px] max-w-[95vw] overflow-y-auto rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
> >
<div class="absolute top-0 right-0 px-2 py-2 h-fit">
<CircleIconButton icon={mdiClose} on:click={() => dispatch('close')} />
</div>
<div <div
class="flex flex-col place-content-center place-items-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary" class="flex flex-col place-content-center place-items-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
> >

View file

@ -16,7 +16,10 @@
let loading = false; let loading = false;
let oauthLoading = true; let oauthLoading = true;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
success: void;
firstLogin: void;
}>();
onMount(async () => { onMount(async () => {
if (!$featureFlags.oauth) { if (!$featureFlags.oauth) {
@ -62,7 +65,7 @@
}); });
if (!data.isAdmin && data.shouldChangePassword) { if (!data.isAdmin && data.shouldChangePassword) {
dispatch('first-login'); dispatch('firstLogin');
return; return;
} }

View file

@ -6,7 +6,9 @@
import { getAssetControlContext } from '../asset-select-control-bar.svelte'; import { getAssetControlContext } from '../asset-select-control-bar.svelte';
let showModal = false; let showModal = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
escape: void;
}>();
const { getAssets } = getAssetControlContext(); const { getAssets } = getAssetControlContext();
const escape = () => { const escape = () => {
dispatch('escape'); dispatch('escape');

View file

@ -19,7 +19,9 @@
const { clearSelect, getOwnedAssets } = getAssetControlContext(); const { clearSelect, getOwnedAssets } = getAssetControlContext();
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
escape: void;
}>();
let isShowConfirmation = false; let isShowConfirmation = false;
let loading = false; let loading = false;

View file

@ -12,7 +12,18 @@
let loading = true; let loading = true;
let search = ''; let search = '';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
newAlbum: {
albumName: string;
};
album: {
album: AlbumResponseDto;
};
newSharedAlbum: {
albumName: string;
};
close: void;
}>();
export let shared: boolean; export let shared: boolean;

View file

@ -7,7 +7,10 @@
import { clickOutside } from '$lib/utils/click-outside'; import { clickOutside } from '$lib/utils/click-outside';
import { mdiClose } from '@mdi/js'; import { mdiClose } from '@mdi/js';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
escape: void;
close: void;
}>();
export let zIndex = 9999; export let zIndex = 9999;
export let ignoreClickOutside = false; export let ignoreClickOutside = false;

View file

@ -29,7 +29,10 @@
let canCopyImagesToClipboard = true; let canCopyImagesToClipboard = true;
let enablePassword = false; let enablePassword = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
close: void;
escape: void;
}>();
const expiredDateOption: ImmichDropDownOption = { const expiredDateOption: ImmichDropDownOption = {
default: 'Never', default: 'Never',

View file

@ -11,7 +11,9 @@
let viewWidth: number; let viewWidth: number;
$: thumbnailSize = getThumbnailSize(assets.length, viewWidth); $: thumbnailSize = getThumbnailSize(assets.length, viewWidth);
let dispatch = createEventDispatcher(); let dispatch = createEventDispatcher<{
select: { asset: AssetResponseDto; selectedAssets: Set<AssetResponseDto> };
}>();
const selectAssetHandler = (event: CustomEvent) => { const selectAssetHandler = (event: CustomEvent) => {
const { asset }: { asset: AssetResponseDto } = event.detail; const { asset }: { asset: AssetResponseDto } = event.detail;

View file

@ -16,7 +16,10 @@
let isShowSelectAvatar = false; let isShowSelectAvatar = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
logout: void;
close: void;
}>();
const handleSaveProfile = async (color: UserAvatarColor) => { const handleSaveProfile = async (color: UserAvatarColor) => {
try { try {

View file

@ -8,7 +8,10 @@
export let user: UserResponseDto; export let user: UserResponseDto;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
close: void;
choose: UserAvatarColor;
}>();
const colors: UserAvatarColor[] = Object.values(UserAvatarColor); const colors: UserAvatarColor[] = Object.values(UserAvatarColor);
</script> </script>

View file

@ -23,7 +23,9 @@
let shouldShowAccountInfo = false; let shouldShowAccountInfo = false;
let shouldShowAccountInfoPanel = false; let shouldShowAccountInfoPanel = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
uploadClicked: void;
}>();
const logOut = async () => { const logOut = async () => {
const { data } = await api.authenticationApi.logout(); const { data } = await api.authenticationApi.logout();

View file

@ -10,7 +10,9 @@
export let asset: AssetResponseDto; export let asset: AssetResponseDto;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
close: void;
}>();
let imgElement: HTMLDivElement; let imgElement: HTMLDivElement;
onMount(() => { onMount(() => {

View file

@ -19,7 +19,9 @@
{ key: ['Del'], action: 'Delete Asset' }, { key: ['Del'], action: 'Delete Asset' },
], ],
}; };
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
close: void;
}>();
</script> </script>
<FullScreenModal on:clickOutside={() => dispatch('close')} on:escape={() => dispatch('close')}> <FullScreenModal on:clickOutside={() => dispatch('close')} on:escape={() => dispatch('close')}>

View file

@ -11,7 +11,9 @@
let showMoreInformation = false; let showMoreInformation = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher<{
selected: void;
}>();
const onButtonClicked = () => dispatch('selected'); const onButtonClicked = () => dispatch('selected');
</script> </script>

View file

@ -17,7 +17,9 @@
export let device: AuthDeviceResponseDto; export let device: AuthDeviceResponseDto;
const dispatcher = createEventDispatcher(); const dispatcher = createEventDispatcher<{
delete: void;
}>();
const options: ToRelativeCalendarOptions = { const options: ToRelativeCalendarOptions = {
unit: 'days', unit: 'days',

View file

@ -0,0 +1,3 @@
export interface ResetOptions {
default?: boolean;
}

View file

@ -239,7 +239,7 @@
<FullScreenModal on:clickOutside={() => (shouldShowEditUserForm = false)}> <FullScreenModal on:clickOutside={() => (shouldShowEditUserForm = false)}>
<EditAlbumForm <EditAlbumForm
album={selectedAlbum} album={selectedAlbum}
on:edit-success={() => successModifyAlbum()} on:editSuccess={() => successModifyAlbum()}
on:cancel={() => (shouldShowEditUserForm = false)} on:cancel={() => (shouldShowEditUserForm = false)}
/> />
</FullScreenModal> </FullScreenModal>

View file

@ -107,7 +107,7 @@
<UserPageLayout title={data.meta.title} admin> <UserPageLayout title={data.meta.title} admin>
<section id="setting-content" class="flex place-content-center sm:mx-4"> <section id="setting-content" class="flex place-content-center sm:mx-4">
<section class="w-full pb-28 sm:w-5/6 md:w-[850px]"> <section class="w-full pb-28 lg:w-[850px]">
{#if shouldShowCreateUserForm} {#if shouldShowCreateUserForm}
<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}> <FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>
<CreateUserForm on:user-created={onUserCreated} on:cancel={() => (shouldShowCreateUserForm = false)} /> <CreateUserForm on:user-created={onUserCreated} on:cancel={() => (shouldShowCreateUserForm = false)} />
@ -119,8 +119,9 @@
<EditUserForm <EditUserForm
user={selectedUser} user={selectedUser}
canResetPassword={selectedUser?.id !== $user.id} canResetPassword={selectedUser?.id !== $user.id}
on:edit-success={onEditUserSuccess} on:editSuccess={onEditUserSuccess}
on:reset-password-success={onEditPasswordSuccess} on:reset-password-success={onEditPasswordSuccess}
on:close={() => (shouldShowEditUserForm = false)}
/> />
</FullScreenModal> </FullScreenModal>
{/if} {/if}
@ -163,32 +164,34 @@
</FullScreenModal> </FullScreenModal>
{/if} {/if}
<table class="my-5 hidden w-full text-left sm:block"> <table class="my-5 w-full text-left">
<thead <thead
class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary" class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary"
> >
<tr class="flex w-full place-items-center"> <tr class="flex w-full place-items-center">
<th class="w-4/12 text-center text-sm font-medium">Email</th> <th class="w-8/12 sm:w-5/12 lg:w-6/12 xl:w-4/12 2xl:w-5/12 text-center text-sm font-medium">Email</th>
<th class="w-2/12 text-center text-sm font-medium">Name</th> <th class="hidden sm:block w-3/12 text-center text-sm font-medium">Name</th>
<th class="w-2/12 text-center text-sm font-medium">Can import</th> <th class="hidden xl:block w-3/12 2xl:w-2/12 text-center text-sm font-medium">Can import</th>
<th class="w-2/12 text-center text-sm font-medium">Action</th> <th class="w-4/12 lg:w-3/12 xl:w-2/12 text-center text-sm font-medium">Action</th>
</tr> </tr>
</thead> </thead>
<tbody class="block max-h-[320px] w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray"> <tbody class="block max-h-[320px] w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
{#if allUsers} {#if allUsers}
{#each allUsers as immichUser, i} {#each allUsers as immichUser, i}
<tr <tr
class={`flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg ${ class="flex h-[80px] overflow-hidden w-full place-items-center text-center dark:text-immich-dark-fg {isDeleted(
isDeleted(immichUser) immichUser,
? 'bg-red-300 dark:bg-red-900' )
: i % 2 == 0 ? 'bg-red-300 dark:bg-red-900'
? 'bg-immich-gray dark:bg-immich-dark-gray/75' : i % 2 == 0
: 'bg-immich-bg dark:bg-immich-dark-gray/50' ? 'bg-immich-gray dark:bg-immich-dark-gray/75'
}`} : 'bg-immich-bg dark:bg-immich-dark-gray/50'}"
> >
<td class="w-4/12 text-ellipsis break-all px-2 text-sm">{immichUser.email}</td> <td class="w-8/12 sm:w-5/12 lg:w-6/12 xl:w-4/12 2xl:w-5/12 text-ellipsis break-all px-2 text-sm"
<td class="w-2/12 text-ellipsis break-all px-2 text-sm">{immichUser.name}</td> >{immichUser.email}</td
<td class="w-2/12 text-ellipsis break-all px-2 text-sm"> >
<td class="hidden sm:block w-3/12 text-ellipsis break-all px-2 text-sm">{immichUser.name}</td>
<td class="hidden xl:block w-3/12 2xl:w-2/12 text-ellipsis break-all px-2 text-sm">
<div class="container mx-auto flex flex-wrap justify-center"> <div class="container mx-auto flex flex-wrap justify-center">
{#if immichUser.externalPath} {#if immichUser.externalPath}
<Icon path={mdiCheck} size="16" /> <Icon path={mdiCheck} size="16" />
@ -197,18 +200,18 @@
{/if} {/if}
</div> </div>
</td> </td>
<td class="w-2/12 text-ellipsis break-all px-4 text-sm"> <td class="w-4/12 lg:w-3/12 xl:w-2/12 text-ellipsis break-all px-4 text-sm">
{#if !isDeleted(immichUser)} {#if !isDeleted(immichUser)}
<button <button
on:click={() => editUserHandler(immichUser)} on:click={() => editUserHandler(immichUser)}
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700" class="rounded-full bg-immich-primary p-2 sm:p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700 max-sm:mb-1"
> >
<Icon path={mdiPencilOutline} size="16" /> <Icon path={mdiPencilOutline} size="16" />
</button> </button>
{#if immichUser.id !== $user.id} {#if immichUser.id !== $user.id}
<button <button
on:click={() => deleteUserHandler(immichUser)} on:click={() => deleteUserHandler(immichUser)}
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700" class="rounded-full bg-immich-primary p-2 sm:p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
> >
<Icon path={mdiTrashCanOutline} size="16" /> <Icon path={mdiTrashCanOutline} size="16" />
</button> </button>
@ -218,62 +221,7 @@
<button <button
on:click={() => restoreUserHandler(immichUser)} on:click={() => restoreUserHandler(immichUser)}
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700" class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
title={`scheduled removal on ${getDeleteDate(immichUser)}`} title="scheduled removal on {getDeleteDate(immichUser)}"
>
<Icon path={mdiDeleteRestore} size="16" />
</button>
{/if}
</td>
</tr>
{/each}
{/if}
</tbody>
</table>
<table class="my-5 block w-full text-left sm:hidden">
<thead
class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary"
>
<tr class="flex w-full place-items-center">
<th class="w-1/4 text-center text-sm font-medium">Name</th>
<th class="w-1/2 text-center text-sm font-medium">Email</th>
<th class="w-1/4 text-center text-sm font-medium">Action</th>
</tr>
</thead>
<tbody class="block max-h-[320px] w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
{#if allUsers}
{#each allUsers as user, i}
<tr
class={`flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg ${
isDeleted(user)
? 'bg-red-300 dark:bg-red-900'
: i % 2 == 0
? 'bg-immich-gray dark:bg-immich-dark-gray/75'
: 'bg-immich-bg dark:bg-immich-dark-gray/50'
}`}
>
<td class="w-1/4 text-ellipsis break-words px-2 text-sm">{user.name}</td>
<td class="w-1/2 text-ellipsis break-all px-2 text-sm">{user.email}</td>
<td class="w-1/4 text-ellipsis px-2 text-sm">
{#if !isDeleted(user)}
<button
on:click={() => editUserHandler(user)}
class="rounded-full bg-immich-primary p-2 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700 max-sm:mb-1 sm:p-3"
>
<Icon path={mdiPencilOutline} size="16" />
</button>
<button
on:click={() => deleteUserHandler(user)}
class="rounded-full bg-immich-primary p-2 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700 sm:p-3"
>
<Icon path={mdiTrashCanOutline} size="16" />
</button>
{/if}
{#if isDeleted(user)}
<button
on:click={() => restoreUserHandler(user)}
class="rounded-full bg-immich-primary p-2 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700 sm:p-3"
title={`scheduled removal on ${getDeleteDate(user)}`}
> >
<Icon path={mdiDeleteRestore} size="16" /> <Icon path={mdiDeleteRestore} size="16" />
</button> </button>