Compare commits
2 Commits
263f959565
...
5b25f21f72
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b25f21f72 | |||
| 1536b8bd1b |
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { FSNavigator } from "./FSNavigator";
|
import type { FSNavigator } from "./FSNavigator";
|
||||||
import { fs_node_icon, fs_share_hotlink_url, fs_share_url, fs_update, type FSNode, type FSPermissions } from "lib/FilesystemAPI.svelte";
|
import { fs_check_is_error, fs_node_icon, fs_share_hotlink_url, fs_share_url, fs_update, type FSNode, type FSPermissions } from "lib/FilesystemAPI.svelte";
|
||||||
import { copy_text } from "util/Util";
|
import { copy_text } from "util/Util";
|
||||||
import CopyButton from "layout/CopyButton.svelte";
|
import CopyButton from "layout/CopyButton.svelte";
|
||||||
import Dialog from "layout/Dialog.svelte";
|
import Dialog from "layout/Dialog.svelte";
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
|
import { get_user } from "lib/NovaAPI";
|
||||||
|
|
||||||
let { nav }: {
|
let { nav }: {
|
||||||
nav: FSNavigator;
|
nav: FSNavigator;
|
||||||
@@ -19,13 +20,33 @@ let is_parent = $state(false)
|
|||||||
let parent_node: FSNode = $state()
|
let parent_node: FSNode = $state()
|
||||||
|
|
||||||
let dialog: Dialog = $state()
|
let dialog: Dialog = $state()
|
||||||
|
let dialog_not_allowed: Dialog = $state()
|
||||||
export const open = async (e: MouseEvent, p: FSNode[]) => {
|
export const open = async (e: MouseEvent, p: FSNode[]) => {
|
||||||
path = p
|
path = p
|
||||||
base = path[path.length-1]
|
base = path[path.length-1]
|
||||||
|
|
||||||
|
const user = await get_user()
|
||||||
|
if (user.subscription.file_sharing === false && base.created_by === user.id) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
share_url = fs_share_url(path)
|
share_url = fs_share_url(path)
|
||||||
if (share_url === "") {
|
if (share_url === "") {
|
||||||
// File is not public, make public
|
// File is not public, make public
|
||||||
await make_public()
|
try {
|
||||||
|
await make_public()
|
||||||
|
} catch (err) {
|
||||||
|
if (fs_check_is_error(err, "sharing_not_allowed")) {
|
||||||
|
if (e.target instanceof HTMLButtonElement) {
|
||||||
|
dialog_not_allowed.open(e.target.getBoundingClientRect())
|
||||||
|
} else {
|
||||||
|
dialog_not_allowed.open((e.target as HTMLElement).parentElement.getBoundingClientRect())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
alert("Error file making file public: "+err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await share()
|
await share()
|
||||||
@@ -85,6 +106,16 @@ const share = async () => {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<Dialog bind:this={dialog_not_allowed}>
|
||||||
|
<div class="dialog_inner">
|
||||||
|
<div class="highlight_yellow" transition:fade>
|
||||||
|
Sharing is not allowed on free accounts. Upgrade to premium to
|
||||||
|
enable this feature
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
<Dialog bind:this={dialog}>
|
<Dialog bind:this={dialog}>
|
||||||
<div class="dialog_inner">
|
<div class="dialog_inner">
|
||||||
{#if toast !== ""}
|
{#if toast !== ""}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import BrandingOptions from "./BrandingOptions.svelte";
|
|||||||
import { branding_from_props } from "./Branding";
|
import { branding_from_props } from "./Branding";
|
||||||
import FileOptions from "./FileOptions.svelte";
|
import FileOptions from "./FileOptions.svelte";
|
||||||
import SharingOptions from "./SharingOptions.svelte";
|
import SharingOptions from "./SharingOptions.svelte";
|
||||||
import AccessControl from "./AccessControl.svelte";
|
|
||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
import { loading_finish, loading_start } from "lib/Loading";
|
import { loading_finish, loading_start } from "lib/Loading";
|
||||||
|
|
||||||
@@ -120,12 +119,6 @@ const save = async (e: SubmitEvent) => {
|
|||||||
<i class="icon">share</i>
|
<i class="icon">share</i>
|
||||||
Sharing
|
Sharing
|
||||||
</button>
|
</button>
|
||||||
{#if $nav.permissions.owner}
|
|
||||||
<button class:button_highlight={tab === "access"} onclick={() => tab = "access"}>
|
|
||||||
<i class="icon">key</i>
|
|
||||||
Access control
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
<button class:button_highlight={tab === "branding"} onclick={() => tab = "branding"}>
|
<button class:button_highlight={tab === "branding"} onclick={() => tab = "branding"}>
|
||||||
<i class="icon">palette</i>
|
<i class="icon">palette</i>
|
||||||
Branding
|
Branding
|
||||||
@@ -148,8 +141,6 @@ const save = async (e: SubmitEvent) => {
|
|||||||
bind:file
|
bind:file
|
||||||
bind:options
|
bind:options
|
||||||
/>
|
/>
|
||||||
{:else if tab === "access"}
|
|
||||||
<AccessControl bind:options />
|
|
||||||
{:else if tab === "branding"}
|
{:else if tab === "branding"}
|
||||||
<BrandingOptions
|
<BrandingOptions
|
||||||
bind:enabled={branding_enabled}
|
bind:enabled={branding_enabled}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { run } from 'svelte/legacy';
|
|
||||||
import { domain_url } from "util/Util";
|
import { domain_url } from "util/Util";
|
||||||
import CopyButton from "layout/CopyButton.svelte";
|
import CopyButton from "layout/CopyButton.svelte";
|
||||||
import { formatDate } from "util/Formatting";
|
import { formatDate } from "util/Formatting";
|
||||||
import { type FSNode, type NodeOptions } from "lib/FilesystemAPI.svelte";
|
import { type FSNode, type NodeOptions } from "lib/FilesystemAPI.svelte";
|
||||||
import AccessControl from "./AccessControl.svelte";
|
import AccessControl from "./AccessControl.svelte";
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { user } from "lib/UserStore";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
file = $bindable(),
|
file = $bindable(),
|
||||||
@@ -45,11 +46,18 @@ const toggle_example = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let share_link = $derived(window.location.protocol+"//"+window.location.host+"/d/"+file.id)
|
let share_link = $derived(window.location.protocol+"//"+window.location.host+"/d/"+file.id)
|
||||||
run(() => {
|
onMount(() => {
|
||||||
embed_iframe(file, options)
|
embed_iframe(file, options)
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if $user.subscription.file_sharing === false}
|
||||||
|
<div class="highlight_yellow">
|
||||||
|
Your account does not support sharing features. Access controls will not
|
||||||
|
have any effect until you upgrade to premium.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Public link</legend>
|
<legend>Public link</legend>
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,9 @@ import Euro from "util/Euro.svelte";
|
|||||||
<Checkout/>
|
<Checkout/>
|
||||||
{:else}
|
{:else}
|
||||||
<p>
|
<p>
|
||||||
You are currently logged in as '{$user.username}'. Use the
|
Pick one of the subscription plans above to upgrade your
|
||||||
form below to activate prepaid on this account.
|
account to premium.
|
||||||
</p>
|
</p>
|
||||||
<Checkout/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,49 +28,23 @@ let upload_widget
|
|||||||
content delivery. We will store and serve your files at an extremely
|
content delivery. We will store and serve your files at an extremely
|
||||||
competitive rate.
|
competitive rate.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
<h2>Features</h2>
|
||||||
</div>
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>Features</h1>
|
|
||||||
</header>
|
|
||||||
<div class="page_content">
|
|
||||||
<section>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<span class="bold">Unlimited</span> storage space
|
Cloud storage with <a href="/filesystem#toc_7">rclone</a> and
|
||||||
|
FTPS support
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Cloud storage with <a href="/filesystem#toc_7">rclone</a> and <a
|
Customizable download pages (custom branding colours, header and
|
||||||
href="/filesystem#toc_10">FTPS</a> support
|
background images)
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="bold">Customizable</span> download pages (custom
|
Fault tolerance for up to four storage server failures
|
||||||
branding colours, header and background images)
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="bold">Fault tolerance</span> for up to four storage
|
Caching servers highly optimized for long-distance data transfer
|
||||||
server failures
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span class="bold">1.8 Terabits per second</span> of total bandwidth
|
|
||||||
capacity
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Twenty-three high-performance caching servers optimized for data
|
|
||||||
transfer over long distances
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
File caching in
|
|
||||||
<span class="bold">Portland</span>,
|
|
||||||
<span class="bold">Querétaro</span>,
|
|
||||||
<span class="bold">New York</span>,
|
|
||||||
<span class="bold">São Paulo</span>,
|
|
||||||
<span class="bold">Frankfurt</span> and
|
|
||||||
<span class="bold">Singapore</span>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<img class="image" src="/res/img/map.webp" alt="A globe showing datacenter locations"/>
|
|
||||||
<h3>What you don't get</h3>
|
<h3>What you don't get</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@@ -78,10 +52,7 @@ let upload_widget
|
|||||||
in case of emergency
|
in case of emergency
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Commercial support - If you have a question, please check the <a
|
Commercial support - I'm busy, please don't bother me
|
||||||
href="/about">Q&A page</a> first. If that doesn't answer it, try
|
|
||||||
the <a href="https://discord.gg/UDjaBGwr4p"
|
|
||||||
target="_blank">support channel</a> on Discord.
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
@@ -142,43 +113,8 @@ header > h1 {
|
|||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
}
|
}
|
||||||
.image {
|
|
||||||
max-width: 100%;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
.bold {
|
.bold {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--highlight_color);
|
color: var(--highlight_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prices {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
.prices > div {
|
|
||||||
flex: 1 0 200px;
|
|
||||||
min-width: 200px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 2px solid var(--card_color);
|
|
||||||
}
|
|
||||||
.prices > div > div {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
padding: 4px;
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
.prices > div > div:nth-child(2) {
|
|
||||||
background: var(--card_color);
|
|
||||||
}
|
|
||||||
@media(max-width: 1000px) {
|
|
||||||
.prices > div {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ const plans: Plan[] = [
|
|||||||
<div class="feat_table">
|
<div class="feat_table">
|
||||||
{#each plans as plan (plan.title)}
|
{#each plans as plan (plan.title)}
|
||||||
<div class="plan">
|
<div class="plan">
|
||||||
<div class="title">
|
<div>
|
||||||
<span class="bold">{plan.title}</span>
|
<span class="title">{plan.title}</span>
|
||||||
<br/>
|
<br/>
|
||||||
<Euro amount={plan.monthly}/> / month
|
<Euro amount={plan.monthly}/> / month
|
||||||
</div>
|
</div>
|
||||||
@@ -212,6 +212,9 @@ const plans: Plan[] = [
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
.plan > .order {
|
.plan > .order {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -314,6 +314,26 @@ export const fs_check_response = async (resp: Response) => {
|
|||||||
return JSON.parse(text)
|
return JSON.parse(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fs_check_is_error = (err: any, target: string) => {
|
||||||
|
if (typeof err.value !== "string") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.value === target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.value === "multiple_errors" && Array.isArray(err.errors)) {
|
||||||
|
for (const embedded_err of err.errors) {
|
||||||
|
if (embedded_err.value === target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
export const fs_path_url = (path: string) => {
|
export const fs_path_url = (path: string) => {
|
||||||
if (!path || path.length === 0) {
|
if (!path || path.length === 0) {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ const load_page = async (pathname: string, history: boolean): Promise<boolean> =
|
|||||||
|
|
||||||
window.document.title = current_page.title+" / Nova"
|
window.document.title = current_page.title+" / Nova"
|
||||||
|
|
||||||
if(history) {
|
if (history) {
|
||||||
window.history.pushState({}, window.document.title, pathname)
|
window.history.pushState({}, window.document.title, pathname)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ const load_page = async (pathname: string, history: boolean): Promise<boolean> =
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const click = (e: MouseEvent) => {
|
const click = async (e: MouseEvent) => {
|
||||||
const origin = (e.target as Element).closest("a");
|
const origin = (e.target as Element).closest("a");
|
||||||
|
|
||||||
if (origin === null) {
|
if (origin === null) {
|
||||||
@@ -145,7 +145,7 @@ const click = (e: MouseEvent) => {
|
|||||||
|
|
||||||
// Try to load the page, if the page was found we cancel the browser
|
// Try to load the page, if the page was found we cancel the browser
|
||||||
// navigation event so we can handle it ourselves
|
// navigation event so we can handle it ourselves
|
||||||
if (load_page(url.pathname, true)) {
|
if (await load_page(url.pathname, true) === true) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ const popstate = (e: PopStateEvent) => {
|
|||||||
{#if current_page !== null}
|
{#if current_page !== null}
|
||||||
<current_page.component />
|
<current_page.component />
|
||||||
|
|
||||||
{#if current_page.footer === undefined || current_page.footer === true}
|
{#if current_page.footer !== false}
|
||||||
<Footer/>
|
<Footer/>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user