This commit is contained in:
Christian Beutel
2024-09-06 13:25:08 +02:00
parent b29696f326
commit 3fefab45a2
12 changed files with 193 additions and 50 deletions

View File

@@ -0,0 +1,89 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("e864strfxo14pm4")
if err != nil {
return err
}
// update
edit_photos := &schema.SchemaField{}
if err := json.Unmarshal([]byte(`{
"system": false,
"id": "aqbpyawe",
"name": "photos",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [
"image/jpeg",
"image/vnd.mozilla.apng",
"image/png",
"image/webp",
"image/svg+xml",
"image/heic"
],
"thumbs": [],
"maxSelect": 99,
"maxSize": 5242880,
"protected": false
}
}`), edit_photos); err != nil {
return err
}
collection.Schema.AddField(edit_photos)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("e864strfxo14pm4")
if err != nil {
return err
}
// update
edit_photos := &schema.SchemaField{}
if err := json.Unmarshal([]byte(`{
"system": false,
"id": "aqbpyawe",
"name": "photos",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [
"image/jpeg",
"image/vnd.mozilla.apng",
"image/png",
"image/webp",
"image/svg+xml"
],
"thumbs": [],
"maxSelect": 99,
"maxSize": 5242880,
"protected": false
}
}`), edit_photos); err != nil {
return err
}
collection.Schema.AddField(edit_photos)
return dao.SaveCollection(collection)
})
}

6
web/package-lock.json generated
View File

@@ -17,6 +17,7 @@
"@types/xmldom": "^0.1.34",
"canvg": "^4.0.1",
"crypto-random-string": "^5.0.0",
"heic2any": "^0.0.4",
"instead": "^1.0.3",
"isomorphic-xml2js": "^0.1.3",
"jspdf": "^2.5.1",
@@ -2813,6 +2814,11 @@
"node": ">= 0.4"
}
},
"node_modules/heic2any": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/heic2any/-/heic2any-0.0.4.tgz",
"integrity": "sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA=="
},
"node_modules/html-encoding-sniffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",

View File

@@ -41,6 +41,7 @@
"@types/xmldom": "^0.1.34",
"canvg": "^4.0.1",
"crypto-random-string": "^5.0.0",
"heic2any": "^0.0.4",
"instead": "^1.0.3",
"isomorphic-xml2js": "^0.1.3",
"jspdf": "^2.5.1",

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { getFileURL, readAsDataURLAsync } from "$lib/util/file_util";
import { onMount } from "svelte";
import PhotoCard from "../photo_card.svelte";
export let id: string;
@@ -42,7 +43,7 @@
document.getElementById(`${id}-photo-input`)!.click();
}
function handlePhotoSelection(files?: FileList | null) {
async function handlePhotoSelection(files?: FileList | null) {
if (!files) {
files = (
document.getElementById(`${id}-photo-input`) as HTMLInputElement
@@ -54,13 +55,25 @@
}
for (const file of files) {
let photoFile = file;
if (!file.type.startsWith("image")) {
continue;
} else if (file.type === "image/heic") {
const heic2any = (await import("heic2any")).default
photoFile = new File(
[
(await heic2any({
blob: file,
toType: "image/jpeg",
})) as Blob,
],
file.name,
);
}
if (!photoFiles) {
photoFiles = [];
}
photoFiles = [...photoFiles, file];
photoFiles = [...photoFiles, photoFile];
}
}

View File

@@ -14,6 +14,8 @@
$: thumbnail = trail.photos.length
? getFileURL(trail, trail.photos[trail.thumbnail])
: "/imgs/default_thumbnail.webp";
$: trailIsShared = (trail.expand?.trail_share_via_trail?.length ?? 0) > 0;
</script>
<div
@@ -27,12 +29,18 @@
>
<img src={thumbnail} alt="" />
</div>
{#if trail.public || trail.expand?.trail_share_via_trail?.length}
{#if trail.public || trailIsShared}
<div
class="flex absolute top-4 right-4 w-8 h-8 rounded-full items-center justify-center bg-background text-content"
class="flex absolute top-4 right-4 {trail.public && trailIsShared
? 'w-14'
: 'w-8'} h-8 rounded-full items-center justify-center bg-background text-content"
>
{#if trail.public}
<span class="tooltip" data-title={$_("public")}>
<span
class="tooltip"
class:mr-2={trail.public && trailIsShared}
data-title={$_("public")}
>
<i class="fa fa-globe"></i>
</span>
{/if}

View File

@@ -109,14 +109,23 @@
gpx(new DOMParser().parseFromString(fileData, "text/xml")),
);
}
if (!exportSettings.photos && !exportSettings.summitLog) {
const blob = new Blob([fileData], {
type: "text/plain",
});
saveAs(blob, `${trail.name}.${exportSettings.fileFormat}`);
} else {
const zip = new JSZip();
zip.file(`${trail.name}.${exportSettings.fileFormat}`, fileData);
zip.file(
`${trail.name}.${exportSettings.fileFormat}`,
fileData,
);
if (exportSettings.photos) {
const photoFolder = zip.folder($_("photos"));
for (const photo of trail.photos) {
const photoURL = getFileURL(trail, photo);
const photoBlob = await fetch(photoURL).then((response) =>
response.blob(),
const photoBlob = await fetch(photoURL).then(
(response) => response.blob(),
);
const photoData = new File([photoBlob], photo);
photoFolder?.file(photo, photoData, { base64: true });
@@ -134,6 +143,7 @@
}
const blob = await zip.generateAsync({ type: "blob" });
saveAs(blob, `${trail.name}.zip`);
}
} catch (e) {
console.error(e);
show_toast({

View File

@@ -47,6 +47,9 @@
...($currentUser ? [$_("comment", { values: { n: 2 } })] : []),
];
const trailIsShared =
(trail.expand?.trail_share_via_trail?.length ?? 0) > 0;
let map: Map;
let activeTab = 0;
@@ -161,7 +164,8 @@
</script>
<div
class="trail-info-panel mx-auto border border-input-border rounded-3xl h-full" style="max-width: min(100%, 64rem);"
class="trail-info-panel mx-auto border border-input-border rounded-3xl h-full"
style="max-width: min(100%, 64rem);"
>
<div class="trail-info-panel-header">
<section class="relative h-80">
@@ -169,19 +173,23 @@
<div
class="absolute bottom-0 w-full h-1/2 bg-gradient-to-b from-transparent to-black opacity-50"
></div>
{#if trail.public || trail.expand?.trail_share_via_trail?.length}
{#if trail.public || trailIsShared}
<div
class="flex absolute top-8 right-10 w-8 h-8 rounded-full items-center justify-center bg-white text-primary"
class="flex absolute top-8 right-6 {trail.public &&
trailIsShared
? 'w-16'
: 'w-8'} h-8 rounded-full items-center justify-center bg-white text-primary"
>
{#if trail.public}
<span
class="tooltip text-2xl"
class:mr-3={trail.public && trailIsShared}
data-title={$_("public")}
>
<i class="fa fa-globe"></i>
</span>
{/if}
{#if trail.expand?.trail_share_via_trail?.length}
{#if trailIsShared}
<TrailShareInfo {trail} large={true}></TrailShareInfo>
{/if}
</div>
@@ -266,9 +274,7 @@
<hr class="border-separator" />
{/if}
</div>
<section
class="trail-info-panel-tabs px-4 py-2 bg-background sticky top-0"
>
<section class="trail-info-panel-tabs px-4 py-2 bg-background sticky top-0">
<Tabs {tabs} bind:activeTab></Tabs>
</section>
<section class="trail-info-panel-content px-8">

View File

@@ -42,10 +42,10 @@
selectedDisplayOption = storedDisplayOption;
}
if (storedSort) {
filter.sort = storedSort as typeof filter.sort
filter.sort = storedSort as typeof filter.sort;
}
if (storedSortOrder) {
filter.sortOrder = storedSortOrder as typeof filter.sortOrder
filter.sortOrder = storedSortOrder as typeof filter.sortOrder;
}
if (storedSort || storedSortOrder) {
dispatch("update", filter);
@@ -57,7 +57,7 @@
}
function setSort() {
localStorage.setItem("sort", filter.sort)
localStorage.setItem("sort", filter.sort);
dispatch("update", filter);
}
@@ -67,7 +67,7 @@
} else {
filter.sortOrder = "+";
}
localStorage.setItem("sort_order", filter.sortOrder)
localStorage.setItem("sort_order", filter.sortOrder);
dispatch("update", filter);
}
</script>
@@ -130,6 +130,11 @@
</a>
{/each}
</div>
<Pagination
page={pagination.page}
totalPages={pagination.totalPages}
on:pagination
></Pagination>
</div>
<style>

View File

@@ -17,7 +17,7 @@
</svelte:head>
<main class="grid grid-cols-1 md:grid-cols-[458px_1fr] gap-x-1 gap-y-4">
<TrailInfoPanel trail={$trail} {markers}></TrailInfoPanel>
<div id="trail-details" class=" sticky top-0 min-h-[600px]">
<div id="trail-details" class=" sticky top-[62px]">
<MapWithElevation trail={$trail} bind:markers></MapWithElevation>
</div>
</main>
@@ -25,7 +25,7 @@
<style>
@media only screen and (min-width: 768px) {
#trail-details {
max-height: calc(100vh - 124px);
height: calc(100vh - 124px);
}
}
</style>

View File

@@ -26,7 +26,9 @@ export const load: ServerLoad = async ({ params, locals, url, fetch }) => {
if (paramCategory) {
filter.category.push(paramCategory);
}
const response = await trails_search_filter(filter, 1, fetch);
const page = url.searchParams.get("page") ?? "1";
const response = await trails_search_filter(filter, parseInt(page), fetch);
await categories_index(fetch)
return {

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import { goto } from "$app/navigation";
import { page } from "$app/stores";
import TrailFilterPanel from "$lib/components/trail/trail_filter_panel.svelte";
import TrailList from "$lib/components/trail/trail_list.svelte";
@@ -21,7 +22,7 @@
});
async function handleFilterUpdate() {
const response = await trails_search_filter(filter, 1);
const response = await trails_search_filter(filter, pagination.page);
pagination.page = response.page;
pagination.totalPages = response.totalPages;
}
@@ -29,6 +30,8 @@
async function paginate(page: number) {
pagination.page = page;
const response = await trails_search_filter(filter, page);
$page.url.searchParams.set("page", page.toString());
goto(`?${$page.url.searchParams.toString()}`);
}
</script>
@@ -37,7 +40,7 @@
</svelte:head>
<main
class="grid grid-cols-1 md:grid-cols-[300px_1fr] gap-8 max-w-7xl mx-6 md:mx-auto"
class="grid grid-cols-1 md:grid-cols-[300px_1fr] items-start gap-8 max-w-7xl mx-6 md:mx-auto"
>
<TrailFilterPanel
categories={$categories}