Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(grid): sorting options #545

Merged
merged 17 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class App extends React.Component<{
schemes,
activeScheme,
},
sort: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.sort, "stars"),
};

if (!this.CONFIG.activeTab || !this.CONFIG.tabs.filter(tab => tab.name === this.CONFIG.activeTab).length) {
Expand Down
66 changes: 35 additions & 31 deletions src/components/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Option } from "react-dropdown";
const Spicetify = window.Spicetify;

import { CardItem, CardType, Config, SchemeIni, Snippet, TabItemConfig } from "../types/marketplace-types";
import { getLocalStorageDataFromKey, generateSchemesOptions, injectColourScheme } from "../logic/Utils";
import { getLocalStorageDataFromKey, generateSchemesOptions, injectColourScheme, generateSortOptions, sortExtensions, sortThemes } from "../logic/Utils";
import { LOCALSTORAGE_KEYS, ITEMS_PER_REQUEST, MARKETPLACE_VERSION, LATEST_RELEASE } from "../constants";
import { openModal } from "../logic/LaunchModals";
import {
Expand Down Expand Up @@ -52,7 +52,7 @@ class Grid extends React.Component<

// Fetches the sorting options, fetched from SortBox.js
this.sortConfig = {
by: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.sortBy, "top"),
by: getLocalStorageDataFromKey(LOCALSTORAGE_KEYS.sort, "top"),
};

this.state = {
Expand Down Expand Up @@ -133,7 +133,7 @@ class Grid extends React.Component<
updateSort(sortByValue) {
if (sortByValue) {
this.sortConfig.by = sortByValue;
localStorage.setItem(LOCALSTORAGE_KEYS.sortBy, sortByValue);
localStorage.setItem(LOCALSTORAGE_KEYS.sort, sortByValue);
}

// this.requestPage = null;
Expand Down Expand Up @@ -188,27 +188,26 @@ class Grid extends React.Component<
switch (activeTab) {
case "Extensions": {
const pageOfRepos = await getTaggedRepos("spicetify-extensions", this.requestPage, this.BLACKLIST);
const extensions: CardItem[] = [];
for (const repo of pageOfRepos.items) {
const extensions = await fetchExtensionManifest(
const repoExtensions = await fetchExtensionManifest(
repo.contents_url,
repo.default_branch,
repo.stargazers_count,
this.CONFIG.visual.hideInstalled,
true,
);

// I believe this stops the requests when switching tabs?
if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) {
// Stop this queue from continuing to fetch and append to cards list
return -1;
theRealPadster marked this conversation as resolved.
Show resolved Hide resolved
if (repoExtensions && repoExtensions.length) {
extensions.push(...repoExtensions.map((extension) => ({
...extension, lastUpdated: repo.pushed_at,
})));
}
}

if (extensions && extensions.length) {
// console.log(`${repo.name} has ${extensions.length} extensions:`, extensions);
extensions.forEach((extension) => {
Object.assign(extension, { lastUpdated: repo.pushed_at });
this.appendCard(extension, "extension", activeTab);
});
}
sortExtensions(extensions, localStorage.getItem("marketplace:sort") || "stars");

for (const extension of extensions) {
this.appendCard(extension, "extension", activeTab);
}

// First result is null or -1 so it coerces to 1
Expand Down Expand Up @@ -250,38 +249,37 @@ class Grid extends React.Component<
// installed extension do them all in one go, since it's local
} case "Themes": {
const pageOfRepos = await getTaggedRepos("spicetify-themes", this.requestPage, this.BLACKLIST);
const themes: CardItem[] = [];
for (const repo of pageOfRepos.items) {

const themes = await fetchThemeManifest(
const repoThemes = await fetchThemeManifest(
repo.contents_url,
repo.default_branch,
repo.stargazers_count,
);
// I believe this stops the requests when switching tabs?
if (this.requestQueue.length > 1 && queue !== this.requestQueue[0]) {
// Stop this queue from continuing to fetch and append to cards list
return -1;
}
theRealPadster marked this conversation as resolved.
Show resolved Hide resolved

if (themes && themes.length) {
themes.forEach((theme) => {
Object.assign(theme, { lastUpdated: repo.pushed_at });
this.appendCard(theme, "theme", activeTab);
});
if (repoThemes && repoThemes.length) {
themes.push(...repoThemes.map((theme) => ({
...theme, lastUpdated: repo.pushed_at,
})));
theRealPadster marked this conversation as resolved.
Show resolved Hide resolved
CharlieS1103 marked this conversation as resolved.
Show resolved Hide resolved
}
}

// First request is null, so coerces to 1
sortThemes(themes, localStorage.getItem("marketplace:sort") || "stars");

for (const theme of themes) {
this.appendCard(theme, "theme", activeTab);
}

const currentPage = this.requestPage > -1 && this.requestPage ? this.requestPage : 1;
// -1 because the page number is 1-indexed
theRealPadster marked this conversation as resolved.
Show resolved Hide resolved
const soFarResults = ITEMS_PER_REQUEST * (currentPage - 1) + pageOfRepos.page_count;
const remainingResults = pageOfRepos.total_count - soFarResults;

console.debug(`Parsed ${soFarResults}/${pageOfRepos.total_count} themes`);
if (remainingResults > 0) return currentPage + 1;
else console.debug("No more theme results");
break;
} case "Apps": {
}
case "Apps": {
const pageOfRepos = await getTaggedRepos("spicetify-apps", this.requestPage, this.BLACKLIST);
theRealPadster marked this conversation as resolved.
Show resolved Hide resolved
for (const repo of pageOfRepos.items) {

Expand Down Expand Up @@ -534,6 +532,12 @@ class Grid extends React.Component<
this.setState({ searchValue: event.target.value });
}} />
</div>
{/* Generate a new box for sorting options */}
<SortBox
onChange={(value) => this.updateSort(value)}
sortBoxOptions={generateSortOptions()}
sortBySelectedFn={(a) => a.key === this.CONFIG.sort} />

<Spicetify.ReactComponent.TooltipWrapper label={t("settings.title")} renderInline={true} placement="bottom">
<button type="button" aria-label={t("settings.title")} className="marketplace-header-icon-button" id="marketplace-settings-button"
onClick={() => openModal("SETTINGS", this.CONFIG, this.updateAppConfig)}
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const LOCALSTORAGE_KEYS = {
installedThemes: "marketplace:installed-themes",
activeTab: "marketplace:active-tab",
tabs: "marketplace:tabs",
sortBy: "marketplace:sort-by",
sort: "marketplace:sort",
// Theme installed store the localsorage key of the theme (e.g. marketplace:installed:NYRI4/Comfy-spicetify/user.css)
themeInstalled: "marketplace:theme-installed",
albumArtBasedColor: "marketplace:albumArtBasedColors",
Expand Down
5 changes: 3 additions & 2 deletions src/logic/FetchRemotes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BLACKLIST_URL, ITEMS_PER_REQUEST } from "../constants";
import { CardItem, Snippet } from "../types/marketplace-types";
import { CardItem, Manifest, Snippet } from "../types/marketplace-types";
import { addToSessionStorage, processAuthors } from "./Utils";

import { RepoTopic } from "../types/marketplace-types";
import { RepoTopic, Extension } from "../types/marketplace-types";
import snippetsJSON from "../resources/snippets";

// TODO: add sort type, order, etc?
Expand Down Expand Up @@ -300,3 +300,4 @@ export const fetchCssSnippets = async () => {
}, []);
return snippets;
};

52 changes: 51 additions & 1 deletion src/logic/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CardProps } from "../components/Card/Card";
import { Author, CardItem, ColourScheme, SchemeIni, Snippet, SortBoxOption } from "../types/marketplace-types";
import { Author, CardItem, ColourScheme, Extension, SchemeIni, Snippet, SortBoxOption } from "../types/marketplace-types";
import Chroma from "chroma-js";
import { LOCALSTORAGE_KEYS } from "../constants";
/**
Expand Down Expand Up @@ -191,6 +191,15 @@ export const generateSchemesOptions = (schemes: SchemeIni) => {
));
};

export const generateSortOptions = () => {
return [
{ key: "newest", value: "Newest" },
{ key: "stars", value: "Stars" },
{ key: "a-z", value: "A-Z" },
{ key: "oldest", value: "Oldest" },
{ key: "z-a", value: "Z-A" },
];
};
/**
* Reset Marketplace localStorage keys
* @param categories The categories to reset. If none provided, reset everything.
Expand Down Expand Up @@ -579,3 +588,44 @@ export const addExtensionToSpicetifyConfig = (main?: string) => {
}
};

export const sortExtensions = (extensions: Extension[], sortOption: string) => {
switch (sortOption) {
case "a-z":
extensions.sort((a, b) => a.manifest.name.localeCompare(b.manifest.name));
break;
case "z-a":
extensions.sort((a, b) => b.manifest.name.localeCompare(a.manifest.name));
break;
case "newest":
extensions.sort((a, b) => new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime());
break;
case "oldest":
extensions.sort((a, b) => new Date(a.lastUpdated).getTime() - new Date(b.lastUpdated).getTime());
break;
case "stars":
default:
extensions.sort((a, b) => b.stars - a.stars);
break;
}
};
// TODO: Create theme type
export const sortThemes = (themes: CardItem[], sortOption: string) => {
theRealPadster marked this conversation as resolved.
Show resolved Hide resolved
switch (sortOption) {
case "a-z":
themes.sort((a, b) => a.manifest.name.localeCompare(b.manifest.name));
break;
case "z-a":
themes.sort((a, b) => b.manifest.name.localeCompare(a.manifest.name));
break;
case "newest":
themes.sort((a, b) => new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime());
break;
case "oldest":
themes.sort((a, b) => new Date(a.lastUpdated).getTime() - new Date(b.lastUpdated).getTime());
break;
case "stars":
default:
themes.sort((a, b) => b.stars - a.stars);
break;
}
};
Empty file added src/types/Manifest.ts
Empty file.
26 changes: 25 additions & 1 deletion src/types/marketplace-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ export type CardItem = {
stars: number;
tags: string[];
lastUpdated: string;

name: string;
lastUpdated: string;
stargazers_count: number;
// For themes only
cssURL?: string;
schemesURL?: string;
Expand All @@ -104,6 +106,27 @@ export type CardItem = {
description: undefined;
};

export type Extension = {
manifest: {
name: string;
description: string;
preview: string;
main: string;
readme: string;
};
title: string;
subtitle: string;
authors: { name: string; url: string }[];
user: string;
repo: string;
branch: string;
imageURL: string;
extensionURL: string;
readmeURL: string;
stars: number;
lastUpdated: string;
};

// TODO: use this in `fetchThemeManifest()`
// export type ThemeCardItem = CardItem & {
// cssURL?: string;
Expand Down Expand Up @@ -161,4 +184,5 @@ export type Config = {
schemes?: SchemeIni;
activeScheme?: string | null;
},
sort: string;
};