Skip to content

Commit

Permalink
v2.9.0: Export, import and delete saved routes
Browse files Browse the repository at this point in the history
  • Loading branch information
soufianesakhi committed Jul 16, 2023
1 parent 07e631c commit 4193f5d
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 9 deletions.
2 changes: 2 additions & 0 deletions playlist-editor/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import New from "./views/New.svelte";
import Recent from "./views/Recent.svelte";
import Saved from "./views/Saved.svelte";
import Manage from "./views/Manage.svelte";
const routes = {
"/": Recent,
Expand All @@ -12,6 +13,7 @@
"/saved": Saved,
"/editor": PlaylistEditor,
"/playlist-builder": PlaylistEditor,
"/manage": Manage,
"*": Recent,
};
</script>
Expand Down
6 changes: 3 additions & 3 deletions playlist-editor/src/components/PlaylistEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
const isPlaylistBuilder = location.hash.startsWith("#/playlist-builder");
let loading = true;
let dataLoaded = false;
let videos = [];
let videos = [] as Video[];
async function loadPageVideos(page) {
loading = true;
Expand Down Expand Up @@ -70,7 +70,7 @@
const defaultPageSize = 50;
let currentPage = 1;
let pageSize = defaultPageSize;
$: paginatedVideos = paginate({ items: videos, pageSize, currentPage });
$: paginatedVideos = paginate({ items: videos, pageSize, currentPage }) as Video[];
async function updatePaginationPage(e) {
currentPage = e.detail.page;
Expand Down Expand Up @@ -240,7 +240,7 @@
acc[videoId] = video;
}
return acc;
}, {});
}, {} as Video[]);
const uniqueVideos = Object.values(videosMap);
const duplicatesCount = videos.length - uniqueVideos.length;
Expand Down
1 change: 1 addition & 0 deletions playlist-editor/src/components/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
>
<a href="#/new" class:active={isActive("new")}>New playlist</a>
<a href="#/saved" class:active={isActive("saved")}>Saved playlists</a>
<a href="#/manage" class:active={isActive("settings")}>Manage playlists</a>
</div>

<style>
Expand Down
40 changes: 39 additions & 1 deletion playlist-editor/src/services/storage-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ window.savePlaylist = async (playlist: Playlist) => {
return id;
};

window.importPlaylists = async (playlistsExport: PlaylistExport[]) => {
const ids = await window.generatePlaylistIds(playlistsExport.length);
const playlists = playlistsExport.map((p) => ({ ...p, id: ids.shift() }));
await Promise.all(
playlists.map((playlist) =>
window.storeObject(
PLAYLIST_KEY_PREFIX + playlist.id,
playlistToDto(playlist)
)
)
);
};

window.removePlaylist = async (playlist: Playlist) => {
if (!playlist.saved) {
// Recent playlist
Expand All @@ -53,6 +66,13 @@ window.removePlaylist = async (playlist: Playlist) => {
}
};

window.removeSavedPlaylists = async () => {
const ids = (await window.getPlaylists()).map(({ id }) => id);
await Promise.all(
ids.map((id) => window.removeObject(PLAYLIST_KEY_PREFIX + id))
);
};

window.getPlaylists = async () => {
const allItems = await window.fetchAllObjects();
return Object.keys(allItems)
Expand Down Expand Up @@ -108,6 +128,16 @@ if (typeof browser != "undefined") {
storage.set(obj);
return count;
};

window.generatePlaylistIds = async (size: number) => {
const obj = await storage.get(ID_COUNTER_KEY);
let count = obj[ID_COUNTER_KEY] || 0;
count++;
const ids = [...Array(size).keys()].map((i) => i + count);
obj[ID_COUNTER_KEY] = ids[ids.length - 1];
storage.set(obj);
return ids;
};
} else if (window.location.protocol.startsWith("http")) {
// Development mode fallback

Expand All @@ -131,6 +161,14 @@ if (typeof browser != "undefined") {
window.generatePlaylistId = async () => {
return Date.now().toString();
};

window.generatePlaylistIds = async (size: number) => {
const count = Date.now();
const ids = [...Array(size).keys()].map((i) => (i + count).toString());
return ids;
};

window.removeSavedPlaylists = async () => {};
}

const DEFAULT_SETTINGS: Settings = {
Expand All @@ -140,7 +178,7 @@ const DEFAULT_SETTINGS: Settings = {
disableThumbnails: false,
openPlaylistBuilderAfterAdd: false,
defaultEditorPage: "/recent",
createdPlaylistStorage: "recent"
createdPlaylistStorage: "recent",
};
window.getSettings = async () => {
const settings = { ...DEFAULT_SETTINGS };
Expand Down
6 changes: 6 additions & 0 deletions playlist-editor/src/types/model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ interface Video {
thumbnailUrl: string;
}

interface PlaylistExport {
title: string;
videos: string[];
timestamp: number;
}

interface Playlist {
id: string;
title: string;
Expand Down
6 changes: 6 additions & 0 deletions playlist-editor/src/types/services.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ type RemoveObject = (id: string) => Promise<void>;
type FetchObject = <T>(id: string, defaultValue: T) => Promise<T>;
type FetchAllObjects = () => Promise<{ [key: string]: any }>;
type GeneratePlaylistId = () => Promise<string>;
type GeneratePlaylistIds = (size: number) => Promise<string[]>;
type ImportPlaylists = (playlistsExport: PlaylistExport[]) => Promise<void>;
type RemoveSavedPlaylists = () => Promise<void>;
type SavePlaylist = (playlist: Playlist) => Promise<string>;
type RemovePlaylist = (playlist: Playlist) => Promise<void>;
type GetPlaylists = () => Promise<Playlist[]>;
Expand All @@ -26,6 +29,9 @@ interface Window {
storeObject: StoreObject;
removeObject: RemoveObject;
generatePlaylistId: GeneratePlaylistId;
generatePlaylistIds: GeneratePlaylistIds;
importPlaylists: ImportPlaylists;
removeSavedPlaylists: RemoveSavedPlaylists;
savePlaylist: SavePlaylist;
removePlaylist: RemovePlaylist;
getPlaylists: GetPlaylists;
Expand Down
77 changes: 77 additions & 0 deletions playlist-editor/src/views/Manage.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script lang="ts">
import Sidebar from "../components/Sidebar.svelte";
function exportFile(content: string, filename?: string) {
var textToSaveAsBlob = new Blob([content], { type: "application/json" });
var textToSaveAsURL = window.URL.createObjectURL(textToSaveAsBlob);
var downloadLink = document.createElement("a");
downloadLink.download = filename ? filename : "export.json";
downloadLink.href = textToSaveAsURL;
downloadLink.onclick = function () {
downloadLink.remove();
};
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
}
async function exportSavedPlaylists() {
const playlists = await window.getPlaylists();
const playlistsExports: PlaylistExport[] = playlists.map(
({ title, videos, timestamp }) => ({
title,
videos,
timestamp,
})
);
exportFile(JSON.stringify(playlistsExports), "saved-playlists.json");
}
async function removeSavedPlaylists() {
if (confirm("All the saved playlists are about to be deleted")) {
await window.removeSavedPlaylists();
alert("All saved playlists were successfully removed");
}
}
async function importSavedPlaylists() {
const fileInput = document.getElementById(
"ImportSavedPlaylists"
) as HTMLInputElement;
fileInput.onchange = () => {
let fr = new FileReader();
fr.onload = async () => {
try {
let playlistsExport: PlaylistExport[] = JSON.parse(
fr.result as string
);
await window.importPlaylists(playlistsExport);
alert("The playlists were successfully imported");
} catch (e) {
console.log(e);
alert("The file is incorrectly formatted");
}
fileInput.value = null;
};
const file = fileInput.files[0];
fr.readAsText(file);
};
fileInput.click();
}
</script>

<Sidebar />

<main style="padding: 5rem;">
<button on:click={exportSavedPlaylists}>Export saved playlists</button>
<button on:click={importSavedPlaylists} style="margin-top: 1rem;"
>Import saved playlists</button
>
<button on:click={removeSavedPlaylists} style="margin-top: 1rem;"
>Delete all saved playlists</button
>
</main>

<input id="ImportSavedPlaylists" style="visibility: hidden;" type="file" />

<style>
</style>
2 changes: 1 addition & 1 deletion src/editor/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/editor/main.js

Large diffs are not rendered by default.

Loading

0 comments on commit 4193f5d

Please sign in to comment.