mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
chore(web): change license wording and other things (#11309)
This commit is contained in:
parent
bc20710c6d
commit
ef7a6bb246
40 changed files with 1045 additions and 518 deletions
|
|
@ -1,18 +0,0 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { mdiPartyPopper } from '@mdi/js';
|
||||
|
||||
export let onDone: () => void;
|
||||
</script>
|
||||
|
||||
<div class="m-auto w-3/4 text-center flex flex-col place-content-center place-items-center mb-6 dark:text-white">
|
||||
<Icon path={mdiPartyPopper} class="text-immich-primary dark:text-immich-dark-primary" size="96" />
|
||||
<p class="text-4xl mt-8 font-bold">{$t('license_activated_title')}</p>
|
||||
<p class="text-lg mt-6">{$t('license_activated_subtitle')}</p>
|
||||
|
||||
<div class="mt-10 w-full">
|
||||
<Button fullwidth on:click={onDone}>OK</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import ServerLicenseCard from './server-license-card.svelte';
|
||||
import UserLicenseCard from './user-license-card.svelte';
|
||||
import { activateLicense, getActivationKey } from '$lib/utils/license-utils';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { licenseStore } from '$lib/stores/license.store';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let onActivate: () => void;
|
||||
|
||||
let licenseKey = '';
|
||||
let isLoading = false;
|
||||
|
||||
const activate = async () => {
|
||||
try {
|
||||
licenseKey = licenseKey.trim();
|
||||
isLoading = true;
|
||||
|
||||
const activationKey = await getActivationKey(licenseKey);
|
||||
await activateLicense(licenseKey, activationKey);
|
||||
|
||||
onActivate();
|
||||
licenseStore.setLicenseStatus(true);
|
||||
} catch (error) {
|
||||
handleError(error, $t('license_failed_activation'));
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="p-4">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold text-immich-primary dark:text-immich-dark-primary tracking-wider">
|
||||
{$t('license_license_title')}
|
||||
</h1>
|
||||
<p class="text-lg mt-2 dark:text-immich-gray">{$t('license_license_subtitle')}</p>
|
||||
</div>
|
||||
<div class="flex gap-6 mt-4 justify-between">
|
||||
{#if $user.isAdmin}
|
||||
<ServerLicenseCard />
|
||||
{/if}
|
||||
<UserLicenseCard />
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<p class="dark:text-immich-gray">{$t('license_input_suggestion')}</p>
|
||||
<form class="mt-2 flex gap-2" on:submit={activate}>
|
||||
<input
|
||||
class="immich-form-input w-full"
|
||||
id="licensekey"
|
||||
type="text"
|
||||
bind:value={licenseKey}
|
||||
required
|
||||
placeholder="IMCL-0KEY-0CAN-00BE-FOUD-FROM-YOUR-EMAIL-INBX"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<Button type="submit" rounded="lg"
|
||||
>{#if isLoading}
|
||||
<LoadingSpinner />
|
||||
{:else}
|
||||
{$t('license_button_activate')}
|
||||
{/if}</Button
|
||||
>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<script lang="ts">
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import LicenseActivationSuccess from '$lib/components/shared-components/license/license-activation-success.svelte';
|
||||
import LicenseContent from '$lib/components/shared-components/license/license-content.svelte';
|
||||
|
||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||
|
||||
export let onClose: () => void;
|
||||
|
||||
let showLicenseActivated = false;
|
||||
</script>
|
||||
|
||||
<Portal>
|
||||
<FullScreenModal showLogo title={''} {onClose} width="wide">
|
||||
{#if showLicenseActivated}
|
||||
<LicenseActivationSuccess onDone={onClose} />
|
||||
{:else}
|
||||
<LicenseContent
|
||||
onActivate={() => {
|
||||
showLicenseActivated = true;
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</FullScreenModal>
|
||||
</Portal>
|
||||
|
|
@ -1,39 +1,44 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { ImmichLicense } from '$lib/constants';
|
||||
import { getLicenseLink } from '$lib/utils/license-utils';
|
||||
import { ImmichProduct } from '$lib/constants';
|
||||
import { getLicenseLink as getProductLink } from '$lib/utils/license-utils';
|
||||
import { mdiAccount, mdiCheckCircleOutline } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
</script>
|
||||
|
||||
<!-- USER LICENSE -->
|
||||
<!-- Inidvidual Purchase Option -->
|
||||
<div class="border border-gray-300 dark:border-gray-800 w-[375px] p-8 rounded-3xl bg-gray-100 dark:bg-gray-900">
|
||||
<div class="text-immich-primary dark:text-immich-dark-primary">
|
||||
<Icon path={mdiAccount} size="56" />
|
||||
<p class="font-semibold text-lg mt-1">{$t('license_individual_title')}</p>
|
||||
<p class="font-semibold text-lg mt-1">{$t('purchase_individual_title')}</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 dark:text-immich-gray">
|
||||
<p class="text-6xl font-bold">$24<span class="text-2xl font-medium">.99</span></p>
|
||||
<p>{$t('license_per_user')}</p>
|
||||
<p class="text-6xl font-bold">$25</p>
|
||||
<p>{$t('purchase_per_user')}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-between h-[200px] dark:text-immich-gray">
|
||||
<div class="mt-6 flex flex-col gap-1">
|
||||
<div class="grid grid-cols-[36px_auto]">
|
||||
<Icon path={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />
|
||||
<p class="self-center">{$t('license_individual_description_1')}</p>
|
||||
<p class="self-center">{$t('purchase_individual_description_1')}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-[36px_auto]">
|
||||
<Icon path={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />
|
||||
<p class="self-center">{$t('license_lifetime_description')}</p>
|
||||
<p class="self-center">{$t('purchase_lifetime_description')}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-[36px_auto]">
|
||||
<Icon path={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />
|
||||
<p class="self-center">{$t('purchase_individual_description_2')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href={getLicenseLink(ImmichLicense.Client)}>
|
||||
<Button fullwidth>{$t('license_button_select')}</Button>
|
||||
<a href={getProductLink(ImmichProduct.Client)}>
|
||||
<Button fullwidth>{$t('purchase_button_select')}</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { mdiPartyPopper } from '@mdi/js';
|
||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { setSupportBadgeVisibility } from '$lib/utils/purchase-utils';
|
||||
|
||||
export let onDone: () => void;
|
||||
</script>
|
||||
|
||||
<div class="m-auto w-3/4 text-center flex flex-col place-content-center place-items-center dark:text-white my-6">
|
||||
<Icon path={mdiPartyPopper} class="text-immich-primary dark:text-immich-dark-primary" size="96" />
|
||||
<p class="text-4xl mt-8 font-bold">{$t('purchase_activated_title')}</p>
|
||||
<p class="text-lg mt-6">{$t('purchase_activated_subtitle')}</p>
|
||||
|
||||
<div class="mb-4 w-full mt-6 border rounded-xl p-4 bg-gray-50 dark:bg-gray-900 dark:border-gray-600">
|
||||
<SettingSwitch
|
||||
title={$t('show_supporter_badge')}
|
||||
subtitle={$t('show_supporter_badge_description')}
|
||||
bind:checked={$preferences.purchase.showSupportBadge}
|
||||
on:toggle={({ detail }) => setSupportBadgeVisibility(detail)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 w-full">
|
||||
<Button fullwidth on:click={onDone}>OK</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<script lang="ts">
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import ServerPurchaseOptionCard from './server-purchase-option-card.svelte';
|
||||
import UserPurchaseOptionCard from './individual-purchase-option-card.svelte';
|
||||
import { activateProduct, getActivationKey } from '$lib/utils/license-utils';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { purchaseStore } from '$lib/stores/purchase.store';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let onActivate: () => void;
|
||||
|
||||
export let showTitle = true;
|
||||
export let showMessage = true;
|
||||
let productKey = '';
|
||||
let isLoading = false;
|
||||
|
||||
const activate = async () => {
|
||||
try {
|
||||
productKey = productKey.trim();
|
||||
isLoading = true;
|
||||
|
||||
const activationKey = await getActivationKey(productKey);
|
||||
await activateProduct(productKey, activationKey);
|
||||
|
||||
onActivate();
|
||||
purchaseStore.setPurchaseStatus(true);
|
||||
} catch (error) {
|
||||
handleError(error, $t('purchase_failed_activation'));
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="p-4">
|
||||
<div>
|
||||
{#if showTitle}
|
||||
<h1 class="text-4xl font-bold text-immich-primary dark:text-immich-dark-primary tracking-wider">
|
||||
{$t('purchase_option_title')}
|
||||
</h1>
|
||||
{/if}
|
||||
|
||||
{#if showMessage}
|
||||
<div class="mt-2 dark:text-immich-gray">
|
||||
<p>
|
||||
{$t('purchase_panel_info_1')}
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
{$t('purchase_panel_info_2')}
|
||||
</p>
|
||||
<div />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex gap-6 mt-4 justify-between">
|
||||
<ServerPurchaseOptionCard />
|
||||
<UserPurchaseOptionCard />
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<p class="dark:text-immich-gray">{$t('purchase_input_suggestion')}</p>
|
||||
<form class="mt-2 flex gap-2" on:submit={activate}>
|
||||
<input
|
||||
class="immich-form-input w-full"
|
||||
id="purchaseKey"
|
||||
type="text"
|
||||
bind:value={productKey}
|
||||
required
|
||||
placeholder="IMCL-0KEY-0CAN-00BE-FOUD-FROM-YOUR-EMAIL-INBX"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<Button type="submit" rounded="lg"
|
||||
>{#if isLoading}
|
||||
<LoadingSpinner />
|
||||
{:else}
|
||||
{$t('purchase_button_activate')}
|
||||
{/if}</Button
|
||||
>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<script lang="ts">
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import PurchaseActivationSuccess from '$lib/components/shared-components/purchasing/purchase-activation-success.svelte';
|
||||
import PurchaseContent from '$lib/components/shared-components/purchasing/purchase-content.svelte';
|
||||
|
||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||
|
||||
export let onClose: () => void;
|
||||
|
||||
let showProductActivated = false;
|
||||
</script>
|
||||
|
||||
<Portal>
|
||||
<FullScreenModal showLogo title={''} {onClose} width="wide">
|
||||
{#if showProductActivated}
|
||||
<PurchaseActivationSuccess onDone={onClose} />
|
||||
{:else}
|
||||
<PurchaseContent
|
||||
onActivate={() => {
|
||||
showProductActivated = true;
|
||||
}}
|
||||
showMessage={false}
|
||||
/>
|
||||
{/if}
|
||||
</FullScreenModal>
|
||||
</Portal>
|
||||
|
|
@ -1,44 +1,44 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { ImmichLicense } from '$lib/constants';
|
||||
import { ImmichProduct } from '$lib/constants';
|
||||
import { getLicenseLink } from '$lib/utils/license-utils';
|
||||
import { mdiCheckCircleOutline, mdiServer } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
</script>
|
||||
|
||||
<!-- SERVER LICENSE -->
|
||||
<!-- SERVER Purchase Options -->
|
||||
<div class="border border-gray-300 dark:border-gray-800 w-[375px] p-8 rounded-3xl bg-gray-100 dark:bg-gray-900">
|
||||
<div class="text-immich-primary dark:text-immich-dark-primary">
|
||||
<Icon path={mdiServer} size="56" />
|
||||
<p class="font-semibold text-lg mt-1">{$t('license_server_title')}</p>
|
||||
<p class="font-semibold text-lg mt-1">{$t('purchase_server_title')}</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 dark:text-immich-gray">
|
||||
<p class="text-6xl font-bold">$99<span class="text-2xl font-medium">.99</span></p>
|
||||
<p>{$t('license_per_server')}</p>
|
||||
<p class="text-6xl font-bold">$100</p>
|
||||
<p>{$t('purchase_per_server')}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-between h-[200px] dark:text-immich-gray">
|
||||
<div class="mt-6 flex flex-col gap-1">
|
||||
<div class="grid grid-cols-[36px_auto]">
|
||||
<Icon path={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />
|
||||
<p class="self-center">{$t('license_server_description_1')}</p>
|
||||
<p class="self-center">{$t('purchase_server_description_1')}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-[36px_auto]">
|
||||
<Icon path={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />
|
||||
<p class="self-center">{$t('license_lifetime_description')}</p>
|
||||
<p class="self-center">{$t('purchase_lifetime_description')}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-[36px_auto]">
|
||||
<Icon path={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />
|
||||
<p class="self-center">{$t('license_server_description_2')}</p>
|
||||
<p class="self-center">{$t('purchase_server_description_2')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href={getLicenseLink(ImmichLicense.Server)}>
|
||||
<Button fullwidth>{$t('license_button_select')}</Button>
|
||||
<a href={getLicenseLink(ImmichProduct.Server)}>
|
||||
<Button fullwidth>{$t('purchase_button_select')}</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -10,11 +10,20 @@
|
|||
export let key: string;
|
||||
export let isOpen = $accordionState.has(key);
|
||||
|
||||
let accordionElement: HTMLDivElement;
|
||||
|
||||
$: setIsOpen(isOpen);
|
||||
|
||||
const setIsOpen = (isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
$accordionState = $accordionState.add(key);
|
||||
|
||||
setTimeout(() => {
|
||||
accordionElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start',
|
||||
});
|
||||
}, 200);
|
||||
} else {
|
||||
$accordionState.delete(key);
|
||||
$accordionState = $accordionState;
|
||||
|
|
@ -26,7 +35,7 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<div class="border-b-[1px] border-gray-200 py-4 dark:border-gray-700">
|
||||
<div class="border-b-[1px] border-gray-200 py-4 dark:border-gray-700" bind:this={accordionElement}>
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded={isOpen}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import LicenseInfo from './license-info.svelte';
|
||||
import PurchaseInfo from './purchase-info.svelte';
|
||||
import ServerStatus from './server-status.svelte';
|
||||
import StorageSpace from './storage-space.svelte';
|
||||
</script>
|
||||
|
|
@ -8,10 +8,8 @@
|
|||
<StorageSpace />
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<LicenseInfo />
|
||||
</div>
|
||||
<PurchaseInfo />
|
||||
|
||||
<div class="mb-6">
|
||||
<div class="mb-6 mt-2">
|
||||
<ServerStatus />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
<script lang="ts">
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { mdiClose, mdiInformationOutline, mdiLicense } from '@mdi/js';
|
||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import LicenseModal from '$lib/components/shared-components/license/license-modal.svelte';
|
||||
import { licenseStore } from '$lib/stores/license.store';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { goto } from '$app/navigation';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { getAccountAge } from '$lib/utils/auth';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
let showMessage = false;
|
||||
let isOpen = false;
|
||||
let hoverMessage = false;
|
||||
let hoverButton = false;
|
||||
const { isLicenseActivated } = licenseStore;
|
||||
|
||||
const openLicenseModal = () => {
|
||||
isOpen = true;
|
||||
showMessage = false;
|
||||
};
|
||||
|
||||
const onButtonHover = () => {
|
||||
showMessage = true;
|
||||
hoverButton = true;
|
||||
};
|
||||
|
||||
$: if (showMessage && !hoverMessage && !hoverButton) {
|
||||
setTimeout(() => {
|
||||
if (!hoverMessage && !hoverButton) {
|
||||
showMessage = false;
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if isOpen}
|
||||
<LicenseModal onClose={() => (isOpen = false)} />
|
||||
{/if}
|
||||
|
||||
<div class="hidden md:block license-status pl-4 text-sm">
|
||||
{#if $isLicenseActivated}
|
||||
<button
|
||||
on:click={() => goto(`${AppRoute.USER_SETTINGS}?isOpen=user-license-settings`)}
|
||||
class="w-full"
|
||||
type="button"
|
||||
>
|
||||
<div class="flex gap-1 mt-2 place-items-center dark:bg-immich-dark-primary/10 bg-gray-100 py-3 px-2 rounded-lg">
|
||||
<Icon path={mdiLicense} size="18" class="text-immich-primary dark:text-immich-dark-primary" />
|
||||
<p class="dark:text-gray-100">{$t('license_info_licensed')}</p>
|
||||
</div>
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
on:click={openLicenseModal}
|
||||
on:mouseover={onButtonHover}
|
||||
on:mouseleave={() => (hoverButton = false)}
|
||||
on:focus={onButtonHover}
|
||||
on:blur={() => (hoverButton = false)}
|
||||
class="py-3 px-2 flex justify-between place-items-center place-content-center border border-gray-300 dark:border-immich-dark-primary/50 mt-2 rounded-lg shadow-sm dark:bg-immich-dark-primary/10 w-full"
|
||||
>
|
||||
<div class="flex place-items-center place-content-center gap-1">
|
||||
<Icon path={mdiLicense} size="18" class="text-immich-dark-gray/75 dark:text-immich-gray/85" />
|
||||
<p class="text-immich-dark-gray/75 dark:text-immich-gray">{$t('license_info_unlicensed')}</p>
|
||||
</div>
|
||||
|
||||
<div class="text-immich-primary dark:text-immich-dark-primary flex place-items-center gap-[2px] font-medium">
|
||||
{$t('license_button_buy')}
|
||||
|
||||
<span role="contentinfo">
|
||||
<Icon path={mdiInformationOutline}></Icon>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Portal target="body">
|
||||
{#if showMessage && getAccountAge() > 14}
|
||||
<div
|
||||
class="w-64 absolute bottom-[75px] left-[255px] bg-white dark:bg-gray-800 dark:text-white text-black rounded-xl z-10 shadow-2xl px-4 py-5"
|
||||
transition:fade={{ duration: 150 }}
|
||||
on:mouseover={() => (hoverMessage = true)}
|
||||
on:mouseleave={() => (hoverMessage = false)}
|
||||
on:focus={() => (hoverMessage = true)}
|
||||
on:blur={() => (hoverMessage = false)}
|
||||
role="dialog"
|
||||
>
|
||||
<div class="flex justify-between place-items-center">
|
||||
<Icon path={mdiLicense} size="44" class="text-immich-dark-gray/75 dark:text-immich-gray" />
|
||||
<CircleIconButton
|
||||
icon={mdiClose}
|
||||
on:click={() => {
|
||||
showMessage = false;
|
||||
}}
|
||||
title={$t('close')}
|
||||
size="18"
|
||||
class="text-immich-dark-gray/85 dark:text-immich-gray"
|
||||
/>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium my-3">{$t('license_trial_info_1')}</h1>
|
||||
<p class="text-immich-dark-gray/80 dark:text-immich-gray text-balance">
|
||||
{$t('license_trial_info_2')}
|
||||
<span class="text-immich-primary dark:text-immich-dark-primary font-semibold">
|
||||
{$t('license_trial_info_3', { values: { accountAge: getAccountAge() } })}</span
|
||||
>. {$t('license_trial_info_4')}
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<Button size="sm" fullwidth on:click={openLicenseModal}>{$t('license_button_buy_license')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</Portal>
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
<script lang="ts">
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { mdiClose, mdiInformationOutline } from '@mdi/js';
|
||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import LicenseModal from '$lib/components/shared-components/purchasing/purchase-modal.svelte';
|
||||
import { purchaseStore } from '$lib/stores/purchase.store';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { goto } from '$app/navigation';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { getAccountAge } from '$lib/utils/auth';
|
||||
import { fade } from 'svelte/transition';
|
||||
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
||||
import { updateMyPreferences } from '@immich/sdk';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { getButtonVisibility } from '$lib/utils/purchase-utils';
|
||||
|
||||
let showMessage = false;
|
||||
let isOpen = false;
|
||||
let hoverMessage = false;
|
||||
let hoverButton = false;
|
||||
|
||||
let showBuyButton = getButtonVisibility();
|
||||
|
||||
const { isPurchased } = purchaseStore;
|
||||
|
||||
const openPurchaseModal = () => {
|
||||
isOpen = true;
|
||||
showMessage = false;
|
||||
};
|
||||
|
||||
const onButtonHover = () => {
|
||||
showMessage = true;
|
||||
hoverButton = true;
|
||||
};
|
||||
|
||||
const hideButton = async (always: boolean) => {
|
||||
const hideBuyButtonUntil = new Date();
|
||||
|
||||
if (always) {
|
||||
hideBuyButtonUntil.setFullYear(2124); // see ya in 100 years
|
||||
} else {
|
||||
hideBuyButtonUntil.setDate(hideBuyButtonUntil.getDate() + 30);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await updateMyPreferences({
|
||||
userPreferencesUpdateDto: {
|
||||
purchase: {
|
||||
hideBuyButtonUntil: hideBuyButtonUntil.toISOString(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
preferences.set(response);
|
||||
showBuyButton = getButtonVisibility();
|
||||
showMessage = false;
|
||||
} catch (error) {
|
||||
handleError(error, 'Error hiding buy button');
|
||||
}
|
||||
};
|
||||
|
||||
$: if (showMessage && !hoverMessage && !hoverButton) {
|
||||
setTimeout(() => {
|
||||
if (!hoverMessage && !hoverButton) {
|
||||
showMessage = false;
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if isOpen}
|
||||
<LicenseModal onClose={() => (isOpen = false)} />
|
||||
{/if}
|
||||
|
||||
{#if getAccountAge() > 14}
|
||||
<div class="hidden md:block license-status pl-4 text-sm">
|
||||
{#if $isPurchased && $preferences.purchase.showSupportBadge}
|
||||
<button
|
||||
on:click={() => goto(`${AppRoute.USER_SETTINGS}?isOpen=user-purchase-settings`)}
|
||||
class="w-full"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="flex gap-1 mt-2 place-items-center dark:bg-immich-dark-primary/10 bg-gray-200/50 p-2 border supporter-effect rounded-lg transition-all"
|
||||
>
|
||||
<div class="h-6 w-6">
|
||||
<ImmichLogo noText />
|
||||
</div>
|
||||
<p class="dark:text-gray-100">Supporter</p>
|
||||
</div>
|
||||
</button>
|
||||
{:else if !$isPurchased && showBuyButton}
|
||||
<button
|
||||
type="button"
|
||||
on:click={openPurchaseModal}
|
||||
on:mouseover={onButtonHover}
|
||||
on:mouseleave={() => (hoverButton = false)}
|
||||
on:focus={onButtonHover}
|
||||
on:blur={() => (hoverButton = false)}
|
||||
class="p-2 flex justify-between place-items-center place-content-center border border-immich-primary/20 dark:border-immich-dark-primary/10 mt-2 rounded-lg shadow-md dark:bg-immich-dark-primary/10 w-full"
|
||||
>
|
||||
<div class="flex justify-between w-full place-items-center place-content-center">
|
||||
<div class="flex place-items-center place-content-center gap-1">
|
||||
<div class="h-6 w-6">
|
||||
<ImmichLogo noText />
|
||||
</div>
|
||||
<p class="flex text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
{$t('purchase_button_buy_immich')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Icon
|
||||
path={mdiInformationOutline}
|
||||
class="flex text-immich-primary dark:text-immich-dark-primary font-medium"
|
||||
size="18"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Portal target="body">
|
||||
{#if showMessage}
|
||||
<div
|
||||
class="w-[500px] absolute bottom-[75px] left-[255px] bg-gray-50 dark:border-gray-800 border border-gray-200 dark:bg-immich-dark-gray dark:text-white text-black rounded-3xl z-10 shadow-2xl px-8 py-6"
|
||||
transition:fade={{ duration: 150 }}
|
||||
on:mouseover={() => (hoverMessage = true)}
|
||||
on:mouseleave={() => (hoverMessage = false)}
|
||||
on:focus={() => (hoverMessage = true)}
|
||||
on:blur={() => (hoverMessage = false)}
|
||||
role="dialog"
|
||||
>
|
||||
<div class="flex justify-between place-items-center">
|
||||
<div class="h-10 w-10">
|
||||
<ImmichLogo noText />
|
||||
</div>
|
||||
<CircleIconButton
|
||||
icon={mdiClose}
|
||||
on:click={() => {
|
||||
showMessage = false;
|
||||
}}
|
||||
title={$t('close')}
|
||||
size="18"
|
||||
class="text-immich-dark-gray/85 dark:text-immich-gray"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h1 class="text-lg font-medium my-3 dark:text-immich-dark-primary text-immich-primary">
|
||||
{$t('purchase_panel_title')}
|
||||
</h1>
|
||||
|
||||
<div class="text-gray-800 dark:text-white my-4">
|
||||
<p>
|
||||
{$t('purchase_panel_info_1')}
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
{$t('purchase_panel_info_2')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button class="mt-2" fullwidth on:click={openPurchaseModal}>{$t('purchase_button_buy_immich')}</Button>
|
||||
<div class="mt-3 flex gap-4">
|
||||
<Button size="sm" fullwidth shadow={false} color="transparent-gray" on:click={() => hideButton(true)}>
|
||||
{$t('purchase_button_never_show_again')}
|
||||
</Button>
|
||||
<Button size="sm" fullwidth shadow={false} color="transparent-gray" on:click={() => hideButton(false)}>
|
||||
{$t('purchase_button_reminder')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</Portal>
|
||||
Loading…
Add table
Add a link
Reference in a new issue