@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 112
|
||||
versionName "1.8.8"
|
||||
versionCode 113
|
||||
versionName "1.8.9"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -57,7 +57,11 @@
|
||||
|
||||
|
||||
|
||||
<release version="1.8.8" date="2025-5-31">
|
||||
|
||||
<release version="1.8.9" date="2025-6-01">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.8.9</url>
|
||||
</release>
|
||||
<release version="1.8.8" date="2025-5-31">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.8.8</url>
|
||||
</release>
|
||||
<release version="1.8.7" date="2025-5-30">
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.8.8",
|
||||
"version": "1.8.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "Materialious",
|
||||
"version": "1.8.8",
|
||||
"version": "1.8.9",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.8.8",
|
||||
"version": "1.8.9",
|
||||
"description": "Modern material design for Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.8.8",
|
||||
"version": "1.8.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
'language',
|
||||
'statistics'
|
||||
],
|
||||
enableTooltips: true,
|
||||
enableTooltips: false,
|
||||
seekBarColors: {
|
||||
played: (await getDynamicTheme())['--primary']
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
|
||||
onMount(() => {
|
||||
if ($feedLastItemId)
|
||||
document.getElementById($feedLastItemId)?.scrollIntoView({ behavior: 'instant' });
|
||||
document
|
||||
.getElementById($feedLastItemId)
|
||||
?.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { DataConnection } from 'peerjs';
|
||||
import { persisted } from 'svelte-persisted-store';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import type { TitleCase } from './letterCasing';
|
||||
import type { PlaylistPageVideo, Video, VideoBase } from './api/model';
|
||||
import type { Channel, HashTag, Playlist, PlaylistPageVideo, Video, VideoBase } from './api/model';
|
||||
|
||||
function platformDependentDefault(givenValue: any, defaultValue: any): any {
|
||||
if (typeof givenValue !== 'undefined' && typeof givenValue !== null) {
|
||||
@@ -115,4 +115,7 @@ export const searchHistoryStore: Writable<string[]> = persisted('searchHistory',
|
||||
export const feedCacheStore: Writable<{
|
||||
[key: string]: (VideoBase | Video | PlaylistPageVideo)[];
|
||||
}> = writable({});
|
||||
export const searchCacheStore: Writable<{
|
||||
[searchType: string]: (Channel | Video | Playlist | HashTag)[];
|
||||
}> = writable({});
|
||||
export const feedLastItemId: Writable<string | undefined> = writable(undefined);
|
||||
|
||||
@@ -173,7 +173,10 @@
|
||||
<header></header>
|
||||
{#each getPages() as navPage}
|
||||
{#if !navPage.requiresAuth || isLoggedIn}
|
||||
<a href={navPage.href} class:active={$page.url.href.endsWith(navPage.href)}
|
||||
<a
|
||||
href={navPage.href}
|
||||
class:active={$page.url.href.endsWith(navPage.href)}
|
||||
data-sveltekit-preload-data="off"
|
||||
><i>{navPage.icon}</i>
|
||||
<div>{navPage.name}</div>
|
||||
</a>
|
||||
@@ -256,7 +259,11 @@
|
||||
<nav class="bottom s">
|
||||
{#each getPages() as navPage}
|
||||
{#if !navPage.requiresAuth || isLoggedIn}
|
||||
<a class="round" href={navPage.href} class:active={$page.url.href.endsWith(navPage.href)}
|
||||
<a
|
||||
class="round"
|
||||
href={navPage.href}
|
||||
class:active={$page.url.href.endsWith(navPage.href)}
|
||||
data-sveltekit-preload-data="off"
|
||||
><i>{navPage.icon}</i>
|
||||
{#if $page.url.href.endsWith(navPage.href)}
|
||||
<span style="font-size: .8em;">{navPage.name}</span>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import VideoList from '$lib/components/VideoList.svelte';
|
||||
import { feedCacheStore } from '$lib/store.js';
|
||||
|
||||
let { data } = $props();
|
||||
</script>
|
||||
@@ -11,5 +12,5 @@
|
||||
<p>Popular page has been disabled</p>
|
||||
</nav>
|
||||
{:else}
|
||||
<VideoList videos={data.popular} />
|
||||
<VideoList videos={$feedCacheStore.popular ?? []} />
|
||||
{/if}
|
||||
|
||||
@@ -24,7 +24,6 @@ export async function load() {
|
||||
}
|
||||
|
||||
return {
|
||||
popular: popular,
|
||||
popularDisabled: popularDisabled
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,33 +6,43 @@
|
||||
import PageLoading from '$lib/components/PageLoading.svelte';
|
||||
import PlaylistThumbnail from '$lib/components/PlaylistThumbnail.svelte';
|
||||
import Thumbnail from '$lib/components/Thumbnail.svelte';
|
||||
import { feedLastItemId, searchCacheStore } from '$lib/store.js';
|
||||
import { onMount } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
let searchResults = $state(data);
|
||||
|
||||
let currentType: 'playlist' | 'all' | 'video' | 'channel' = $state('all');
|
||||
let currentPage = 1;
|
||||
|
||||
onMount(() => {
|
||||
if ($feedLastItemId) {
|
||||
document
|
||||
.getElementById($feedLastItemId)
|
||||
?.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
|
||||
}
|
||||
});
|
||||
|
||||
async function changeType(type: 'playlist' | 'all' | 'video' | 'channel') {
|
||||
searchResults.searchType = type;
|
||||
currentType = type;
|
||||
currentPage = 1;
|
||||
searchResults.search = [];
|
||||
searchResults.search = await getSearch(searchResults.slug, { type: type });
|
||||
searchCacheStore.set({ [type]: await getSearch(data.slug, { type: type }) });
|
||||
}
|
||||
|
||||
async function loadMore(event: InfiniteEvent) {
|
||||
currentPage++;
|
||||
const newSearch = await getSearch(searchResults.slug, {
|
||||
const newSearch = await getSearch(data.slug, {
|
||||
page: currentPage.toString(),
|
||||
type: searchResults.searchType
|
||||
type: currentType
|
||||
});
|
||||
|
||||
if (newSearch.length === 0) {
|
||||
event.detail.complete();
|
||||
} else {
|
||||
searchResults.search = [...searchResults.search, ...newSearch];
|
||||
searchCacheStore.set({
|
||||
[currentType]: [...($searchCacheStore[currentType] ?? []), ...newSearch]
|
||||
});
|
||||
event.detail.loaded();
|
||||
}
|
||||
}
|
||||
@@ -40,16 +50,12 @@
|
||||
|
||||
<div style="margin-top: 1em;">
|
||||
<div class="tabs left-align min scroll">
|
||||
<a
|
||||
class:active={searchResults.searchType === 'all'}
|
||||
href="#all"
|
||||
onclick={async () => changeType('all')}
|
||||
>
|
||||
<a class:active={currentType === 'all'} href="#all" onclick={async () => changeType('all')}>
|
||||
<i>home</i>
|
||||
<span>{$_('videoTabs.all')}</span>
|
||||
</a>
|
||||
<a
|
||||
class:active={searchResults.searchType === 'video'}
|
||||
class:active={currentType === 'video'}
|
||||
href="#videos"
|
||||
onclick={async () => changeType('video')}
|
||||
>
|
||||
@@ -57,7 +63,7 @@
|
||||
<span>{$_('videoTabs.videos')}</span>
|
||||
</a>
|
||||
<a
|
||||
class:active={searchResults.searchType === 'playlist'}
|
||||
class:active={currentType === 'playlist'}
|
||||
href="#playlists"
|
||||
onclick={async () => changeType('playlist')}
|
||||
>
|
||||
@@ -65,7 +71,7 @@
|
||||
<span>{$_('videoTabs.playlists')}</span>
|
||||
</a>
|
||||
<a
|
||||
class:active={searchResults.searchType === 'channel'}
|
||||
class:active={currentType === 'channel'}
|
||||
href="#channels"
|
||||
onclick={async () => changeType('channel')}
|
||||
>
|
||||
@@ -75,14 +81,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if searchResults.search.length > 0}
|
||||
{#if $searchCacheStore[currentType].length > 0}
|
||||
<div class="page right active">
|
||||
<div class="space"></div>
|
||||
<div class="grid">
|
||||
{#each searchResults.search as item}
|
||||
{#each $searchCacheStore[currentType] as item}
|
||||
<ContentColumn>
|
||||
{#key item}
|
||||
<article class="no-padding" style="height: 100%;">
|
||||
<article
|
||||
class="no-padding"
|
||||
style="height: 100%;"
|
||||
onclick={() =>
|
||||
feedLastItemId.set(
|
||||
'videoId' in item ? item.videoId : 'authorId' in item ? item.authorId : item.title
|
||||
)}
|
||||
id={'videoId' in item
|
||||
? item.videoId
|
||||
: 'authorId' in item
|
||||
? item.authorId
|
||||
: item.title}
|
||||
>
|
||||
{#if item.type === 'video'}
|
||||
<Thumbnail video={item} />
|
||||
{:else if item.type === 'channel'}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { getSearch } from '$lib/api/index';
|
||||
import { searchCacheStore } from '$lib/store.js';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export async function load({ params, url }) {
|
||||
let type: 'playlist' | 'all' | 'video' | 'channel';
|
||||
@@ -11,16 +13,23 @@ export async function load({ params, url }) {
|
||||
type = 'all';
|
||||
}
|
||||
|
||||
let search;
|
||||
const search = get(searchCacheStore).all;
|
||||
|
||||
try {
|
||||
search = await getSearch(params.slug, { type: type });
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
if (!search) {
|
||||
try {
|
||||
searchCacheStore.set({ [type]: await getSearch(params.slug, { type: type }) });
|
||||
} catch (errorMessage: any) {
|
||||
error(500, errorMessage);
|
||||
}
|
||||
} else {
|
||||
getSearch(params.slug, { type: type }).then((newSearch) => {
|
||||
searchCacheStore.set({
|
||||
[type]: [...new Set([...newSearch, ...search])]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
search: search,
|
||||
slug: params.slug,
|
||||
searchType: type
|
||||
};
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
import { _ } from 'svelte-i18n';
|
||||
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
let currentPage = 1;
|
||||
let videos: (VideoBase | Video | PlaylistPageVideo)[] = $state(data.videos);
|
||||
let videos: (VideoBase | Video | PlaylistPageVideo)[] = $state($feedCacheStore.subscription);
|
||||
|
||||
async function loadMore(event: InfiniteEvent) {
|
||||
currentPage++;
|
||||
@@ -32,6 +30,6 @@
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<VideoList {videos} />
|
||||
<VideoList videos={videos ?? []} />
|
||||
|
||||
<InfiniteLoading on:infinite={loadMore} />
|
||||
|
||||
@@ -19,12 +19,10 @@ export async function load() {
|
||||
feedCacheStore.set({ subscription: videos });
|
||||
} else {
|
||||
await getFeed(100, 1).then((feeds) => {
|
||||
const newVideos = [...feeds.notifications, ...feeds.videos];
|
||||
feedCacheStore.set({ subscription: { ...Array.from(new Set(newVideos)), ...videos } });
|
||||
const newVideos = [...feeds.notifications, ...feeds.videos, ...videos];
|
||||
feedCacheStore.set({
|
||||
subscription: [...new Set(newVideos)]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
videos: videos
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import VideoList from '$lib/components/VideoList.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
import { feedCacheStore } from '$lib/store';
|
||||
</script>
|
||||
|
||||
<VideoList videos={data.trending} />
|
||||
<VideoList videos={$feedCacheStore.trending ?? []} />
|
||||
|
||||
@@ -17,6 +17,4 @@ export async function load() {
|
||||
} else {
|
||||
getTrending().then((newTrending) => feedCacheStore.set({ trending: newTrending }));
|
||||
}
|
||||
|
||||
return { trending: trending };
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
LATEST_VERSION = "1.8.8"
|
||||
LATEST_VERSION = "1.8.9"
|
||||
RELEASE_DATE = datetime.now().strftime("%Y-%-m-%d") # Format: YYYY-M-D
|
||||
|
||||
WORKING_DIR = os.path.join(
|
||||
|
||||
Reference in New Issue
Block a user