Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,6 @@
"path": "main.js",
"label": "main.js"
},
{
"path": "styles.css",
"label": "styles.css"
},
{
"path": "manifest.json",
"label": "manifest.json"
Expand Down
21 changes: 21 additions & 0 deletions src/SavedFeedsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Writable } from "svelte/store";
import { IPodNotes } from "./types/IPodNotes";
import { PodcastFeed } from "./types/PodcastFeed";
import { StoreController } from "./types/StoreController";

type TSavedFeedsStoreValue = { [podcastName: string]: PodcastFeed };

export class SavedFeedsController extends StoreController<TSavedFeedsStoreValue> {
private plugin: IPodNotes;

constructor(store: Writable<TSavedFeedsStoreValue>, plugin: IPodNotes) {
super(store)
this.plugin = plugin;
}

protected onChange(value: TSavedFeedsStoreValue) {
this.plugin.settings.savedFeeds = value;

this.plugin.saveSettings();
}
}
26 changes: 9 additions & 17 deletions src/episodeStatusController.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
import { Unsubscriber } from "svelte/store";
import { playedEpisodes } from "./store";
import { Writable } from "svelte/store";
import { StoreController } from "./types/StoreController";
import { IPodNotes } from "./types/IPodNotes";
import { PlayedEpisode } from "./types/playedEpisode";

export class EpisodeStatusController {
type TPlayedStoreValue = { [episodeName: string]: PlayedEpisode };

export class EpisodeStatusController extends StoreController<TPlayedStoreValue> {
private plugin: IPodNotes;
private unsubscribe: Unsubscriber;

constructor(plugin: IPodNotes) {
constructor(store: Writable<TPlayedStoreValue>, plugin: IPodNotes) {
super(store)
this.plugin = plugin;
}

public on(): EpisodeStatusController {
this.unsubscribe = playedEpisodes.subscribe(this.onStoreUpdate.bind(this));
return this;
}

public off(): EpisodeStatusController {
this.unsubscribe();
return this;
}

private onStoreUpdate(store: {[episodeName: string]: PlayedEpisode}) {
this.plugin.settings.playedEpisodes = store;
protected onChange(value: TPlayedStoreValue) {
this.plugin.settings.playedEpisodes = value;

this.plugin.saveSettings();
}
Expand Down
16 changes: 12 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FeedParser from 'src/parser/feedParser';
import { currentEpisode, playedEpisodes } from 'src/store';
import { currentEpisode, playedEpisodes, savedFeeds } from 'src/store';
import { Notice, Plugin, WorkspaceLeaf } from 'obsidian';
import { API } from 'src/API/API';
import { IAPI } from 'src/API/IAPI';
Expand All @@ -10,24 +10,31 @@ import { IPodNotesSettings } from './types/IPodNotesSettings';
import { plugin } from './store';
import { get } from 'svelte/store';
import { IPodNotes } from './types/IPodNotes';
import { EpisodeStatusController } from './episodeStatusController';
import { EpisodeStatusController } from './EpisodeStatusController';
import { StoreController } from './types/StoreController';
import { PlayedEpisode } from './types/playedEpisode';
import { PodcastFeed } from './types/PodcastFeed';
import { SavedFeedsController } from './SavedFeedsController';

export default class PodNotes extends Plugin implements IPodNotes {
public api: IAPI;
public settings: IPodNotesSettings;

private view: MainView;

private playedEpisodeController: EpisodeStatusController;
private playedEpisodeController: StoreController<{[episodeName: string]: PlayedEpisode}>;
private savedFeedsController: StoreController<{[podcastName: string]: PodcastFeed}>;

async onload() {
plugin.set(this);

await this.loadSettings();

playedEpisodes.set(this.settings.playedEpisodes);
savedFeeds.set(this.settings.savedFeeds);

this.playedEpisodeController = new EpisodeStatusController(this).on();
this.playedEpisodeController = new EpisodeStatusController(playedEpisodes, this).on();
this.savedFeedsController = new SavedFeedsController(savedFeeds, this).on();

this.addCommand({
id: 'start-playing',
Expand Down Expand Up @@ -109,6 +116,7 @@ export default class PodNotes extends Plugin implements IPodNotes {

onunload() {
this?.playedEpisodeController.off();
this?.savedFeedsController.off();
}

async loadSettings() {
Expand Down
2 changes: 2 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { writable } from 'svelte/store';
import type PodNotes from 'src/main';
import { Episode } from 'src/types/Episode';
import { PlayedEpisode } from 'src/types/playedEpisode';
import { PodcastFeed } from 'src/types/PodcastFeed';

export const plugin = writable<PodNotes>();
export const currentTime = writable<number>(0);
Expand All @@ -11,3 +12,4 @@ export const isPaused = writable<boolean>(true);
export const playedEpisodes = writable<{
[key: string]: PlayedEpisode;
}>({});
export const savedFeeds = writable<{[podcastName: string]: PodcastFeed}>({});
22 changes: 22 additions & 0 deletions src/types/StoreController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Unsubscriber, Writable } from "svelte/store";

export abstract class StoreController<T> {
protected unsubscribe: Unsubscriber;
protected store: Writable<T>;

constructor(store: Writable<T>) {
this.store = store;
}

public on(): StoreController<T> {
this.unsubscribe = this.store.subscribe(this.onChange.bind(this));
return this;
}

public off(): StoreController<T> {
this.unsubscribe();
return this;
}

protected abstract onChange(value: T): void;
}
4 changes: 3 additions & 1 deletion src/ui/PodcastView/PodcastGrid.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}
</script>

<div class="podcast-grid grid-3">
<div class="podcast-grid">
{#if feeds.length > 0}
{#each feeds as feed}
<img
Expand All @@ -36,6 +36,8 @@
}

.podcast-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 0rem;
}
</style>
13 changes: 10 additions & 3 deletions src/ui/PodcastView/PodcastView.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
<script lang="ts">
import { PodcastFeed } from "src/types/PodcastFeed";
import FeedGrid from "./PodcastGrid.svelte";
import { currentEpisode } from "src/store";
import { currentEpisode, savedFeeds } from "src/store";
import EpisodePlayer from "./EpisodePlayer.svelte";
import EpisodeList from "./EpisodeList.svelte";
import { Episode } from "src/types/Episode";
import FeedParser from "src/parser/feedParser";
import TopBar from "./TopBar.svelte";
import { ViewState } from "src/types/ViewState";
import { onDestroy } from "svelte";

export let feeds: PodcastFeed[] = [];
let feeds: PodcastFeed[] = [];
let selectedFeed: PodcastFeed | null = null;
let episodeList: Episode[] = [];

let viewState: ViewState;

const unsubscribe = savedFeeds.subscribe(storeValue => {
feeds = Object.values(storeValue);
});

function handleclickPodcast(event: CustomEvent<{ feed: PodcastFeed }>) {
const { feed } = event.detail;
selectedFeed = feed;
Expand All @@ -32,6 +37,8 @@

viewState = ViewState.Player;
}

onDestroy(unsubscribe);
</script>

<div class="podcast-view">
Expand All @@ -50,7 +57,7 @@
on:clickEpisode={handleClickEpisode}
/>
{:else if viewState === ViewState.PodcastGrid}
<FeedGrid {feeds} on:clickPodcast={handleclickPodcast} />
<FeedGrid feeds={feeds} on:clickPodcast={handleclickPodcast} />
{/if}
</div>

Expand Down
16 changes: 3 additions & 13 deletions src/ui/PodcastView/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { PodcastFeed } from 'src/types/PodcastFeed';
import { IPodNotes } from "../../types/IPodNotes";
import { ItemView, WorkspaceLeaf } from "obsidian";
import { VIEW_TYPE } from "../../constants";
Expand All @@ -24,24 +23,15 @@ export class MainView extends ItemView {
}

protected async onOpen(): Promise<void> {
this.render();
this.PodcastView = new PodcastView({
target: this.contentEl,
})
}

protected async onClose(): Promise<void> {
this.PodcastView?.$destroy();

this.contentEl.empty();
}

private render() {
const savedFeeds: PodcastFeed[] = Object.values(this.plugin.settings.savedFeeds);

this.PodcastView = new PodcastView({
target: this.contentEl,
props: {
feeds: savedFeeds,
}
})
}
}

14 changes: 11 additions & 3 deletions src/ui/settings/PodNotesSettingsTab.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { App, PluginSettingTab, Setting } from 'obsidian';
import PodNotes from '../../main';
import { PodcastQueryGrid } from './PodcastQueryGrid';
import PodcastQueryGrid from './PodcastQueryGrid.svelte';

export class PodNotesSettingsTab extends PluginSettingTab {
plugin: PodNotes;

private podcastQueryGrid: PodcastQueryGrid;

constructor(app: App, plugin: PodNotes) {
super(app, plugin);
Expand All @@ -21,8 +23,11 @@ export class PodNotesSettingsTab extends PluginSettingTab {
const settingsContainer = containerEl.createDiv();
settingsContainer.classList.add('settings-container');

PodcastQueryGrid(settingsContainer, this.plugin);

const queryGridContainer = settingsContainer.createDiv();
this.podcastQueryGrid = new PodcastQueryGrid({
target: queryGridContainer
});

const defaultPlaybackRateSetting = new Setting(this.containerEl);
defaultPlaybackRateSetting.setName('Default Playback Rate');
defaultPlaybackRateSetting.addSlider((slider) => slider
Expand All @@ -36,4 +41,7 @@ export class PodNotesSettingsTab extends PluginSettingTab {
);
}

hide(): void {
this.podcastQueryGrid?.$destroy();
}
}
90 changes: 90 additions & 0 deletions src/ui/settings/PodcastQueryGrid.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<script lang="ts">
import { debounce, TextComponent } from "obsidian";
import { consume } from "src/iTunesAPIConsumer";
import { savedFeeds } from "src/store";
import { PodcastFeed } from "src/types/PodcastFeed";
import { onMount } from "svelte";
import PodcastResultCard from "./PodcastResultCard.svelte";

let inputRef: HTMLSpanElement;
let searchResults: PodcastFeed[] = [];

let gridSizeClass: string = "grid-3";

if (searchResults.length % 3 === 0 || searchResults.length > 3) {
gridSizeClass = "grid-3";
} else if (searchResults.length % 2 === 0) {
gridSizeClass = "grid-2";
} else if (searchResults.length % 1 === 0) {
gridSizeClass = "grid-1";
}

onMount(() => {
const debouncedUpdate = debounce(async (value: string) => {
searchResults = await (await consume(value)).filter(
(feed: PodcastFeed) => !$savedFeeds[feed.title]
);
}, 300, true);

const textInput = new TextComponent(inputRef)
.setPlaceholder("Search...")
.onChange(debouncedUpdate)

textInput.inputEl.style.width = "100%";
textInput.inputEl.style.marginBottom = "1rem";
});

function addPodcast(event: CustomEvent<{ podcast: PodcastFeed }>) {
const { podcast } = event.detail;

savedFeeds.update(feeds => ({ ...feeds, [podcast.title]: podcast }));
}
</script>

<div class="podcast-query-container">
<h3 class="podcast-query-heading">Search for a podcast</h3>
<span bind:this={inputRef} />

<div
class={`
podcast-query-results
${gridSizeClass}
`}
>
{#each searchResults as podcast}
<PodcastResultCard
podcast={podcast}
on:addPodcast={addPodcast}
/>
{/each}
</div>
</div>

<style>
.podcast-query-container {
margin-bottom: 2rem;
}

.podcast-query-heading {
margin-bottom: 0.5rem;
}

.podcast-query-results {
width: 100%;
height: 100%;
display: grid;
grid-gap: 1rem;
}

.grid-3 {
grid-template-columns: repeat(3, 1fr);
}

.grid-2 {
grid-template-columns: repeat(2, 1fr);
}

.grid-1 {
grid-template-columns: repeat(1, 1fr);
}
</style>
Loading