mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-03-22 15:00:07 +00:00
feat: allow clearing background image (#1290)
Co-authored-by: Kyle Mendell <kmendell@ofkm.us> Co-authored-by: Kyle Mendell <ksm@ofkm.us>
This commit is contained in:
@@ -36,6 +36,7 @@ func NewAppImagesController(
|
|||||||
group.PUT("/application-images/favicon", authMiddleware.Add(), controller.updateFaviconHandler)
|
group.PUT("/application-images/favicon", authMiddleware.Add(), controller.updateFaviconHandler)
|
||||||
group.PUT("/application-images/default-profile-picture", authMiddleware.Add(), controller.updateDefaultProfilePicture)
|
group.PUT("/application-images/default-profile-picture", authMiddleware.Add(), controller.updateDefaultProfilePicture)
|
||||||
|
|
||||||
|
group.DELETE("/application-images/background", authMiddleware.Add(), controller.deleteBackgroundImageHandler)
|
||||||
group.DELETE("/application-images/default-profile-picture", authMiddleware.Add(), controller.deleteDefaultProfilePicture)
|
group.DELETE("/application-images/default-profile-picture", authMiddleware.Add(), controller.deleteDefaultProfilePicture)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +195,21 @@ func (c *AppImagesController) updateBackgroundImageHandler(ctx *gin.Context) {
|
|||||||
ctx.Status(http.StatusNoContent)
|
ctx.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deleteBackgroundImageHandler godoc
|
||||||
|
// @Summary Delete background image
|
||||||
|
// @Description Delete the application background image
|
||||||
|
// @Tags Application Images
|
||||||
|
// @Success 204 "No Content"
|
||||||
|
// @Router /api/application-images/background [delete]
|
||||||
|
func (c *AppImagesController) deleteBackgroundImageHandler(ctx *gin.Context) {
|
||||||
|
if err := c.appImagesService.DeleteImage(ctx.Request.Context(), "background"); err != nil {
|
||||||
|
_ = ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
// updateFaviconHandler godoc
|
// updateFaviconHandler godoc
|
||||||
// @Summary Update favicon
|
// @Summary Update favicon
|
||||||
// @Description Update the application favicon
|
// @Description Update the application favicon
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
<script module lang="ts">
|
||||||
|
// Persist the last failing background image URL across route remounts so
|
||||||
|
// login pages without a background do not briefly retry the image and stutter.
|
||||||
|
let persistedMissingBackgroundImageUrl: string | undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { afterNavigate } from '$app/navigation';
|
import { afterNavigate } from '$app/navigation';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
@@ -17,13 +23,32 @@
|
|||||||
showAlternativeSignInMethodButton?: boolean;
|
showAlternativeSignInMethodButton?: boolean;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
|
let missingBackgroundImageUrl = $state<string | undefined>(persistedMissingBackgroundImageUrl);
|
||||||
|
let loadedBackgroundImageUrl = $state<string | undefined>();
|
||||||
let isInitialLoad = $state(false);
|
let isInitialLoad = $state(false);
|
||||||
let animate = $derived(isInitialLoad && !$appConfigStore.disableAnimations);
|
let backgroundImageUrl = $derived(cachedBackgroundImage.getUrl());
|
||||||
|
let imageError = $derived(missingBackgroundImageUrl === backgroundImageUrl);
|
||||||
|
let imageLoaded = $derived(loadedBackgroundImageUrl === backgroundImageUrl);
|
||||||
|
let animate = $derived(isInitialLoad && imageLoaded && !$appConfigStore.disableAnimations);
|
||||||
|
|
||||||
afterNavigate((e) => {
|
afterNavigate((e) => {
|
||||||
isInitialLoad = !e?.from?.url;
|
isInitialLoad = !e?.from?.url;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function onBackgroundImageLoad() {
|
||||||
|
loadedBackgroundImageUrl = backgroundImageUrl;
|
||||||
|
if (persistedMissingBackgroundImageUrl === backgroundImageUrl) {
|
||||||
|
persistedMissingBackgroundImageUrl = undefined;
|
||||||
|
missingBackgroundImageUrl = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBackgroundImageError() {
|
||||||
|
loadedBackgroundImageUrl = undefined;
|
||||||
|
persistedMissingBackgroundImageUrl = backgroundImageUrl;
|
||||||
|
missingBackgroundImageUrl = backgroundImageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
const isDesktop = new MediaQuery('min-width: 1024px');
|
const isDesktop = new MediaQuery('min-width: 1024px');
|
||||||
let alternativeSignInButton = $state({
|
let alternativeSignInButton = $state({
|
||||||
href: '/login/alternative',
|
href: '/login/alternative',
|
||||||
@@ -46,9 +71,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isDesktop.current}
|
{#if isDesktop.current}
|
||||||
<div class="h-screen items-center overflow-hidden text-center">
|
<div class="h-screen items-center overflow-hidden text-center flex justify-center">
|
||||||
<div
|
<div
|
||||||
class="relative z-10 flex h-full w-[650px] 2xl:w-[800px] p-16 {cn(
|
class="flex h-full w-[650px] 2xl:w-[800px] p-16 {cn(
|
||||||
showAlternativeSignInMethodButton && 'pb-0'
|
showAlternativeSignInMethodButton && 'pb-0'
|
||||||
)}"
|
)}"
|
||||||
>
|
>
|
||||||
@@ -69,16 +94,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Background image -->
|
{#if !imageError}
|
||||||
<div class="absolute top-0 right-0 left-500px bottom-0 z-0 overflow-hidden rounded-[40px] m-6">
|
<!-- Background image -->
|
||||||
<img
|
<div class="m-6 flex h-[calc(100vh-3rem)] overflow-hidden rounded-[40px]">
|
||||||
src={cachedBackgroundImage.getUrl()}
|
<img
|
||||||
class="{cn(
|
src={backgroundImageUrl}
|
||||||
animate && 'animate-bg-zoom'
|
class="h-full object-cover {cn(animate && 'animate-bg-zoom')}"
|
||||||
)} h-screen object-cover w-[calc(100vw-650px)] 2xl:w-[calc(100vw-800px)]"
|
alt={m.login_background()}
|
||||||
alt={m.login_background()}
|
onload={onBackgroundImageLoad}
|
||||||
/>
|
onerror={onBackgroundImageError}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ export default class AppConfigService extends APIService {
|
|||||||
cachedBackgroundImage.bustCache();
|
cachedBackgroundImage.bustCache();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
deleteBackgroundImage = async () => {
|
||||||
|
await this.api.delete(`/application-images/background`);
|
||||||
|
cachedBackgroundImage.bustCache();
|
||||||
|
};
|
||||||
|
|
||||||
deleteDefaultProfilePicture = async () => {
|
deleteDefaultProfilePicture = async () => {
|
||||||
await this.api.delete('/application-images/default-profile-picture');
|
await this.api.delete('/application-images/default-profile-picture');
|
||||||
cachedDefaultProfilePicture.bustCache();
|
cachedDefaultProfilePicture.bustCache();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
logoDark: File | undefined,
|
logoDark: File | undefined,
|
||||||
logoEmail: File | undefined,
|
logoEmail: File | undefined,
|
||||||
defaultProfilePicture: File | null | undefined,
|
defaultProfilePicture: File | null | undefined,
|
||||||
backgroundImage: File | undefined,
|
backgroundImage: File | null | undefined,
|
||||||
favicon: File | undefined
|
favicon: File | undefined
|
||||||
) {
|
) {
|
||||||
const faviconPromise = favicon ? appConfigService.updateFavicon(favicon) : Promise.resolve();
|
const faviconPromise = favicon ? appConfigService.updateFavicon(favicon) : Promise.resolve();
|
||||||
@@ -68,9 +68,12 @@
|
|||||||
? appConfigService.updateDefaultProfilePicture(defaultProfilePicture)
|
? appConfigService.updateDefaultProfilePicture(defaultProfilePicture)
|
||||||
: Promise.resolve();
|
: Promise.resolve();
|
||||||
|
|
||||||
const backgroundImagePromise = backgroundImage
|
const backgroundImagePromise =
|
||||||
? appConfigService.updateBackgroundImage(backgroundImage)
|
backgroundImage === null
|
||||||
: Promise.resolve();
|
? appConfigService.deleteBackgroundImage()
|
||||||
|
: backgroundImage
|
||||||
|
? appConfigService.updateBackgroundImage(backgroundImage)
|
||||||
|
: Promise.resolve();
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
lightLogoPromise,
|
lightLogoPromise,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
logoDark: File | undefined,
|
logoDark: File | undefined,
|
||||||
logoEmail: File | undefined,
|
logoEmail: File | undefined,
|
||||||
defaultProfilePicture: File | null | undefined,
|
defaultProfilePicture: File | null | undefined,
|
||||||
backgroundImage: File | undefined,
|
backgroundImage: File | null | undefined,
|
||||||
favicon: File | undefined
|
favicon: File | undefined
|
||||||
) => void;
|
) => void;
|
||||||
} = $props();
|
} = $props();
|
||||||
@@ -26,10 +26,11 @@
|
|||||||
let logoDark = $state<File | undefined>();
|
let logoDark = $state<File | undefined>();
|
||||||
let logoEmail = $state<File | undefined>();
|
let logoEmail = $state<File | undefined>();
|
||||||
let defaultProfilePicture = $state<File | null | undefined>();
|
let defaultProfilePicture = $state<File | null | undefined>();
|
||||||
let backgroundImage = $state<File | undefined>();
|
let backgroundImage = $state<File | null | undefined>();
|
||||||
let favicon = $state<File | undefined>();
|
let favicon = $state<File | undefined>();
|
||||||
|
|
||||||
let defaultProfilePictureSet = $state(true);
|
let defaultProfilePictureSet = $state(true);
|
||||||
|
let backgroundImageSet = $state(true);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-8">
|
<div class="flex flex-col gap-8">
|
||||||
@@ -77,10 +78,12 @@
|
|||||||
/>
|
/>
|
||||||
<ApplicationImage
|
<ApplicationImage
|
||||||
id="background-image"
|
id="background-image"
|
||||||
imageClass="h-[350px] max-w-[500px]"
|
imageClass="max-h-[350px] max-w-[500px]"
|
||||||
label={m.background_image()}
|
label={m.background_image()}
|
||||||
|
isResetable
|
||||||
bind:image={backgroundImage}
|
bind:image={backgroundImage}
|
||||||
imageURL={cachedBackgroundImage.getUrl()}
|
imageURL={cachedBackgroundImage.getUrl()}
|
||||||
|
isImageSet={backgroundImageSet}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
|
|||||||
Reference in New Issue
Block a user