Skip to content

Commit 1adb389

Browse files
committed
feat(import): finalize basic bookmark import flow
Signed-off-by: Robert Goniszewski <[email protected]>
1 parent 437bc12 commit 1adb389

File tree

7 files changed

+104
-67
lines changed

7 files changed

+104
-67
lines changed

src/lib/components/BulkList/BulkList.svelte

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
import { importBookmarkStore } from '$lib/stores/import-bookmarks.store';
33
import { type Readable } from 'svelte/store';
44
import BulkListItem from '../BulkListItem/BulkListItem.svelte';
5+
import type { BookmarkEdit } from '$lib/types/Bookmark.type';
56
6-
export let itemList: Readable<BulkListItem[]>;
7+
export let itemList: Readable<BookmarkEdit[]>;
78
export let isLoading: boolean;
89
910
const selectAllItems = ({ target }: Event) => {
@@ -40,17 +41,17 @@ const selectAllItems = ({ target }: Event) => {
4041
category={item.category}
4142
selected={item.selected}
4243
isLoading={isLoading}
43-
metadataFetched={!!item.contentHtml}
44+
metadataFetched={!!item.domain}
4445
metadata={item} />
4546
{/each}
4647
</tbody>
4748
<!-- foot -->
4849
<tfoot>
4950
<tr>
5051
<th></th>
51-
<th>Name</th>
52-
<th>Job</th>
53-
<th>Favorite Color</th>
52+
<th>URL</th>
53+
<th>Title</th>
54+
<th>Category</th>
5455
<th></th>
5556
</tr>
5657
</tfoot>

src/lib/components/BulkListItem/BulkListItem.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
1212
export let id: number;
1313
export let selected = false;
14-
export let icon: string;
14+
export let icon: string | null;
1515
export let url: string;
1616
export let title: string;
1717
export let category: {
@@ -55,7 +55,7 @@ const onRemoveItem = () => {
5555
<div class="avatar">
5656
<div class="mask mask-squircle h-12 w-12">
5757
{#if icon}
58-
<img src={icon} alt="Avatar Tailwind CSS Component" />
58+
<img src={icon} alt="Icon" />
5959
{:else}
6060
<IconPhotoX class="m-2 h-8 w-8 opacity-80" />
6161
{/if}

src/lib/database/repositories/Category.repository.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export const getOrCreateCategory = async (
113113
categoryData: Omit<typeof categorySchema.$inferInsert, 'ownerId'>
114114
): Promise<Category> => {
115115
const category = (await db.query.categorySchema.findFirst({
116-
where: and(eq(categorySchema.ownerId, ownerId), eq(categorySchema.name, categoryData.name)),
116+
where: and(eq(categorySchema.ownerId, ownerId), eq(categorySchema.slug, categoryData.slug)),
117117
with: mapRelationsToWithStatements([CategoryRelations.OWNER])
118118
})) as CategoryDbo | undefined;
119119

src/lib/types/Bookmark.type.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export type BookmarkForIndex = Omit<Bookmark, 'mainImage' | 'icon' | 'screenshot
2929
category: Omit<Category, 'parent' | 'owner'>;
3030
};
3131

32-
export type BookmarkEdit = Partial<Metadata> & {
32+
export type BookmarkEdit = Metadata & {
3333
id: number;
3434
icon: string | null;
3535
url: string;

src/lib/utils/bookmark-import/execute-import.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,19 @@ export async function executeImport(
2424
.insert(bookmarkSchema)
2525
.values({
2626
url: bookmark.url,
27+
domain: bookmark.domain,
2728
title: bookmark.title,
28-
slug: createSlug(bookmark.title),
29-
domain: new URL(bookmark.url).hostname,
3029
description: bookmark.description || null,
30+
iconUrl: bookmark.iconUrl,
31+
mainImageUrl: bookmark.mainImageUrl,
32+
importance: bookmark.importance,
33+
flagged: bookmark.flagged,
34+
contentHtml: bookmark.contentHtml,
35+
contentText: bookmark.contentText,
36+
contentType: bookmark.contentType,
37+
author: bookmark.author,
38+
contentPublishedDate: bookmark.contentPublishedDate,
39+
slug: createSlug(bookmark.title),
3140
ownerId: userId,
3241
categoryId: category.id,
3342
created: new Date(),

src/routes/import/html/+page.server.ts

+18-17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { executeImport } from '$lib/utils/bookmark-import/execute-import';
22
import joi from 'joi';
33

44
import type { Actions } from './$types';
5+
import type { BookmarkEdit } from '$lib/types/Bookmark.type';
56

67
export const actions: Actions = {
78
default: async ({ locals, request }) => {
@@ -15,18 +16,30 @@ export const actions: Actions = {
1516
}
1617

1718
const requestBody = await request.formData();
18-
const bookmarks = JSON.parse(requestBody.get('bookmarks') as string);
19-
20-
console.log('Parsed bookmarks:', bookmarks);
19+
const bookmarks:BookmarkEdit[] = JSON.parse(requestBody.get('bookmarks') as string);
2120

2221
const validationSchema = joi
2322
.array()
2423
.items(
2524
joi.object({
2625
url: joi.string().uri().required(),
26+
domain: joi.string().allow('').optional(),
2727
title: joi.string().required(),
28-
description: joi.string().allow('').optional(),
29-
category: joi.string().required(),
28+
description: joi.string().allow(null, '').optional(),
29+
category: joi.object({
30+
id: joi.number(),
31+
name: joi.string().required(),
32+
}).required(),
33+
mainImageUrl: joi.string().allow(null, '').optional(),
34+
iconUrl: joi.string().allow(null, '').optional(),
35+
author: joi.string().allow(null, '').optional(),
36+
contentText: joi.string().allow(null, '').optional(),
37+
contentHtml: joi.string().allow(null, '').optional(),
38+
contentType: joi.string().allow(null, '').optional(),
39+
contentPublishedDate: joi.date().allow(null).optional(),
40+
importance: joi.number().allow(null).required(),
41+
flagged: joi.boolean().allow(null).required(),
42+
note: joi.string().allow(null, '').optional(),
3043
bookmarkTags: joi
3144
.array()
3245
.items(
@@ -49,18 +62,6 @@ export const actions: Actions = {
4962
}
5063

5164
try {
52-
console.log('Importing bookmarks...');
53-
console.log(
54-
'await executeImport(bookmarks, ownerId);',
55-
JSON.stringify(
56-
{
57-
bookmarks,
58-
ownerId
59-
},
60-
null,
61-
2
62-
)
63-
);
6465
const result = await executeImport(bookmarks, ownerId);
6566

6667
console.log('executeImport result:', JSON.stringify(result, null, 2));

src/routes/import/html/+page.svelte

+65-39
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { editBookmarkCategoriesStore, editBookmarkStore } from '$lib/stores/edit
88
import { importBookmarkStore } from '$lib/stores/import-bookmarks.store';
99
import type { ImportExecutionResult } from '$lib/types/BookmarkImport.type';
1010
import type { BulkListItem } from '$lib/types/common/BulkList.type';
11+
import type { Metadata } from '$lib/types/Metadata.type';
1112
import { importBookmarks } from '$lib/utils/import-bookmarks';
1213
import { showToast } from '$lib/utils/show-toast';
1314
import { derived, writable } from 'svelte/store';
@@ -45,15 +46,15 @@ const processMetadataQueue = async (items: BulkListItem[]) => {
4546
body: JSON.stringify({ url: item.url }),
4647
headers: { 'Content-Type': 'application/json' }
4748
});
48-
const { metadata } = await response.json();
49+
const { metadata }: { metadata: Metadata } = await response.json();
4950
processedItems.update((count) => count + 1);
5051
5152
return {
52-
...metadata,
5353
...item,
54+
...metadata,
55+
title: item.title || metadata.title,
5456
imported: true,
55-
icon: item.icon || metadata.iconUrl,
56-
title: item.title || metadata.title
57+
icon: item.icon || metadata.iconUrl
5758
};
5859
} catch (error) {
5960
console.error(`Failed to fetch metadata for ${item.url}:`, error);
@@ -113,7 +114,16 @@ const onFileSelected = async (event: Event) => {
113114
selected: false,
114115
importance: null,
115116
flagged: null,
116-
note: null
117+
note: null,
118+
domain: '',
119+
author: null,
120+
contentHtml: null,
121+
contentText: null,
122+
contentPublishedDate: null,
123+
contentType: null,
124+
mainImageUrl: null,
125+
iconUrl: null,
126+
imported: true
117127
}));
118128
$editBookmarkCategoriesStore = [...new Set(updatedBookmarks.map((item) => item.category.name))];
119129
@@ -160,39 +170,55 @@ const onSetSelectedCategory = () => {
160170
class="file-input file-input-bordered file-input-primary file-input-md w-full max-w-xs"
161171
on:change={onFileSelected} />
162172
{:else if $step === 2}
163-
<form
164-
method="POST"
165-
use:enhance={({ formData }) => {
166-
formData.set(
167-
'bookmarks',
168-
JSON.stringify(
169-
$importBookmarkStore.map((bookmark) => ({
170-
url: bookmark.url,
171-
title: bookmark.title,
172-
description: bookmark.description,
173-
category: bookmark.category.name,
174-
bookmarkTags: bookmark.bookmarkTags
175-
}))
176-
)
177-
);
173+
<div class="flex max-w-6xl flex-col">
174+
<form
175+
method="POST"
176+
use:enhance={({ formData }) => {
177+
formData.set(
178+
'bookmarks',
179+
JSON.stringify(
180+
$importBookmarkStore.map((bookmark) => ({
181+
url: bookmark.url,
182+
domain: bookmark.domain,
183+
title: bookmark.title,
184+
description: bookmark.description,
185+
category: bookmark.category,
186+
mainImageUrl: bookmark.mainImageUrl,
187+
iconUrl: bookmark.iconUrl,
188+
author: bookmark.author,
189+
contentText: bookmark.contentText,
190+
contentHtml: bookmark.contentHtml,
191+
contentType: bookmark.contentType,
192+
contentPublishedDate: bookmark.contentPublishedDate,
193+
importance: bookmark.importance,
194+
flagged: bookmark.flagged,
195+
note: bookmark.note,
196+
bookmarkTags: bookmark.bookmarkTags
197+
}))
198+
)
199+
);
178200

179-
return async ({ update, result }) => {
180-
if (result.type === 'success') {
181-
showToast.success('Bookmarks imported successfully');
182-
step.set(3);
183-
} else {
184-
showToast.error('Failed to import bookmarks');
185-
}
186-
update();
187-
};
188-
}}>
189-
<div class="flex max-w-6xl flex-col">
201+
return async ({ update, result }) => {
202+
if (result.type === 'success' && result?.data?.data) {
203+
showToast.success('Bookmarks imported successfully');
204+
const { data } = result.data;
205+
if (data) {
206+
// @ts-ignore-next-line
207+
importResult.set(data);
208+
step.set(3);
209+
}
210+
} else {
211+
showToast.error('Failed to import bookmarks');
212+
}
213+
update();
214+
};
215+
}}>
190216
<div class="mb-4 flex w-full gap-2 pl-12">
191217
<button
192218
type="submit"
193219
class="btn btn-primary btn-sm"
194220
disabled={$isFetchingMetadata || $importBookmarkStore.length === 0}
195-
aria-label="Import selected bookmarks">
221+
aria-label="Import bookmarks">
196222
{#if $isFetchingMetadata}
197223
<span class="loading loading-spinner loading-xs"></span>
198224
{/if}
@@ -246,13 +272,13 @@ const onSetSelectedCategory = () => {
246272
</div>
247273

248274
<BulkList itemList={currentItems} isLoading={$isFetchingMetadata} />
249-
<Pagination
250-
page={$page.data.page}
251-
limit={$page.data.limit}
252-
items={$itemsCount}
253-
position="right" />
254-
</div>
255-
</form>
275+
</form>
276+
<Pagination
277+
page={$page.data.page}
278+
limit={$page.data.limit}
279+
items={$itemsCount}
280+
position="right" />
281+
</div>
256282
{:else if $step === 3}
257283
<div class="flex flex-col items-center justify-center">
258284
<h1 class="mb-8 text-2xl font-bold">Import results</h1>

0 commit comments

Comments
 (0)