Skip to content

Commit

Permalink
feat: HUGE refac (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
zugdev committed Dec 5, 2024
1 parent fde134f commit d0e6ed2
Show file tree
Hide file tree
Showing 21 changed files with 123 additions and 211 deletions.
12 changes: 0 additions & 12 deletions src/home/fetch-github/cache-integrity.ts

This file was deleted.

25 changes: 11 additions & 14 deletions src/home/fetch-github/fetch-and-display-previews.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { GitHubIssue } from "../github-types";
import { taskManager } from "../home";
import { applyAvatarsToIssues, renderGitHubIssues } from "../rendering/render-github-issues";
import { GitHubNotifications } from "../github-types";
import { notifications } from "../home";
import { applyAvatarsToIssues, renderNotifications } from "../rendering/render-github-issues";
import { renderOrgHeaderLabel } from "../rendering/render-org-header";
import { closeModal } from "../rendering/render-preview-modal";
import { filterIssuesBySearch } from "../sorting/filter-issues-by-search";
import { Sorting } from "../sorting/generate-sorting-buttons";
import { sortIssuesController } from "../sorting/sort-issues-controller";
import { checkCacheIntegrityAndSyncTasks } from "./cache-integrity";

export type Options = {
ordering: "normal" | "reverse";
Expand Down Expand Up @@ -35,7 +34,7 @@ viewToggle.addEventListener("click", () => {
});

function getProposalsOnlyFilter(getProposals: boolean) {
return (issue: GitHubIssue) => {
return (issue: GitHubNotifications) => {
if (!issue?.labels) return false;

const hasPriceLabel = issue.labels.some((label) => {
Expand All @@ -47,7 +46,7 @@ function getProposalsOnlyFilter(getProposals: boolean) {
};
}

function filterIssuesByOrganization(issues: GitHubIssue[]): GitHubIssue[] {
function filterIssuesByOrganization(issues: GitHubNotifications): GitHubNotifications {
// get organization name from first thing after / in URL
const pathSegments = window.location.pathname.split("/").filter(Boolean);
const urlOrgName = pathSegments.length > 0 ? pathSegments[0] : null;
Expand All @@ -73,7 +72,7 @@ function filterIssuesByOrganization(issues: GitHubIssue[]): GitHubIssue[] {
}

// checks the cache's integrity, sorts issues, checks Directory/Proposals toggle, renders them and applies avatars
export async function displayGitHubIssues({
export async function displayNotifications({
sorting,
options = { ordering: "normal" },
skipAnimation = false,
Expand All @@ -82,12 +81,10 @@ export async function displayGitHubIssues({
options?: { ordering: string };
skipAnimation?: boolean;
} = {}) {
await checkCacheIntegrityAndSyncTasks();
const cachedTasks = taskManager.getTasks();
const sortedIssues = sortIssuesController(cachedTasks, sorting, options);
let sortedAndFiltered = sortedIssues.filter(getProposalsOnlyFilter(isProposalOnlyViewer));
sortedAndFiltered = filterIssuesByOrganization(sortedAndFiltered);
renderGitHubIssues(sortedAndFiltered, skipAnimation);
//const sortedIssues = sortIssuesController(cachedTasks, sorting, options);
//let sortedAndFiltered = sortedIssues.filter(getProposalsOnlyFilter(isProposalOnlyViewer));
//sortedAndFiltered = filterIssuesByOrganization(sortedAndFiltered);
renderNotifications(notifications, skipAnimation);
applyAvatarsToIssues();
}

Expand All @@ -101,6 +98,6 @@ export async function searchDisplayGitHubIssues({
const searchResult = filterIssuesBySearch(searchText);
let filteredIssues = searchResult.filter(getProposalsOnlyFilter(isProposalOnlyViewer));
filteredIssues = filterIssuesByOrganization(filteredIssues);
renderGitHubIssues(filteredIssues, skipAnimation);
renderNotifications(filteredIssues, skipAnimation);
applyAvatarsToIssues();
}
10 changes: 4 additions & 6 deletions src/home/fetch-github/fetch-avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Octokit } from "@octokit/rest";
import { getGitHubAccessToken } from "../getters/get-github-access-token";
import { getImageFromCache, saveImageToCache } from "../getters/get-indexed-db";
import { renderErrorInModal } from "../rendering/display-popup-modal";
import { organizationImageCache } from "./fetch-issues-full";
import { GitHubIssue } from "../github-types";
import { taskManager } from "../home";
import { organizationImageCache } from "./fetch-notifications";
import { GitHubNotifications } from "../github-types";
import { notifications } from "../home";

// Map to track ongoing avatar fetches
const pendingFetches: Map<string, Promise<Blob | void>> = new Map();
Expand Down Expand Up @@ -101,10 +101,8 @@ export async function fetchAvatar(orgName: string): Promise<Blob | void> {

// fetches avatars for all tasks (issues) cached. it will fetch only once per organization, remaining are returned from cache
export async function fetchAvatars() {
const cachedTasks = taskManager.getTasks();

// fetches avatar for each organization for each task, but fetchAvatar() will only fetch once per organization, remaining are returned from cache
const avatarPromises = cachedTasks.map(async (task: GitHubIssue) => {
const avatarPromises = notifications.map(async (task: GitHubNotifications) => {
const [orgName] = task.repository_url.split("/").slice(-2);
if (orgName) {
return fetchAvatar(orgName);
Expand Down
54 changes: 0 additions & 54 deletions src/home/fetch-github/fetch-issues-full.ts

This file was deleted.

41 changes: 41 additions & 0 deletions src/home/fetch-github/fetch-notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Octokit } from "@octokit/rest";
import { GitHubNotification, GitHubNotifications } from "../github-types";
import { getGitHubAccessToken } from "../getters/get-github-access-token";
import { handleRateLimit } from "./handle-rate-limit";
import { RequestError } from "@octokit/request-error";
export const organizationImageCache = new Map<string, Blob | null>(); // this should be declared in image related script

// Fetches notifications from GitHub, must be authenticated
export async function fetchNotifications(): Promise<GitHubNotifications | null> {
const providerToken = await getGitHubAccessToken();
const octokit = new Octokit({ auth: providerToken });

try {
const response = ((await octokit.request("GET /notifications")).data) as GitHubNotifications;
console.log("unfiltered", response);
const filtered = filterNotifications(response);
console.log("filtered", filtered);
return filtered;
} catch(error) {
if (!!error && typeof error === "object" && "status" in error && error.status === 403) {
await handleRateLimit(providerToken ? octokit : undefined, error as RequestError);
}
console.warn("You have been logged out. Please login again.", error);
}
return null;
}

function filterNotifications(notifications: GitHubNotification[]): GitHubNotifications {
return notifications.filter(notification => {
if (notification.reason === 'ci_activity') {
return false;
}

const repoName = notification.repository.full_name.split('/')[0];
if (repoName !== 'ubiquity' && repoName !== 'ubiquity-os' && repoName !== 'ubiquity-os-marketplace') {
return false;
}

return true;
});
}
10 changes: 5 additions & 5 deletions src/home/getters/get-indexed-db.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GitHubIssue } from "../github-types";
import { GitHubNotifications } from "../github-types";

// this file contains functions to save and retrieve issues/images from IndexedDB which is client-side in-browser storage
export async function saveImageToCache({
Expand Down Expand Up @@ -88,19 +88,19 @@ async function openIssuesDB(): Promise<IDBDatabase> {
});
}
// Saves fetched issues into IndexedDB and removes stale issues
export async function saveIssuesToCache(cachedIssues: GitHubIssue[], fetchedIssues: GitHubIssue[]): Promise<void> {
export async function saveIssuesToCache(cachedIssues: GitHubNotifications, fetchedNotifications: GitHubNotifications): Promise<void> {
const db = await openIssuesDB();
const transaction = db.transaction("issues", "readwrite");
const store = transaction.objectStore("issues");

// Identify and remove stale issues (in cache but not in fetched list)
const staleIssues = cachedIssues.filter((cachedIssue) => !fetchedIssues.some((issue) => issue.id === cachedIssue.id));
const staleIssues = cachedIssues.filter((cachedIssue) => !fetchedNotifications.some((issue) => issue.id === cachedIssue.id));
for (const issue of staleIssues) {
store.delete(issue.id);
}

// Save or update fetched issues
for (const issue of fetchedIssues) {
for (const issue of fetchedNotifications) {
store.put(issue);
}

Expand All @@ -111,7 +111,7 @@ export async function saveIssuesToCache(cachedIssues: GitHubIssue[], fetchedIssu
}

// Retrieves issues from IndexedDB
export async function getIssuesFromCache(): Promise<GitHubIssue[]> {
export async function getIssuesFromCache(): Promise<GitHubNotifications> {
const db = await openIssuesDB();
const transaction = db.transaction("issues", "readonly");
const store = transaction.objectStore("issues");
Expand Down
5 changes: 3 additions & 2 deletions src/home/github-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ export const GITHUB_TASKS_STORAGE_KEY = "gitHubTasks";

export type TaskStorageItems = {
timestamp: number; // in milliseconds
tasks: GitHubIssue[];
tasks: GitHubNotifications;
loggedIn: boolean;
};

export type GitHubUserResponse = RestEndpointMethodTypes["users"]["getByUsername"]["response"];
export type GitHubUser = GitHubUserResponse["data"];
export type GitHubIssue = RestEndpointMethodTypes["issues"]["get"]["response"]["data"];
export type GitHubNotifications = RestEndpointMethodTypes["activity"]["listNotificationsForAuthenticatedUser"]["response"]["data"];
export type GitHubNotification = GitHubNotifications[0];
export type GitHubLabel =
| {
id?: number;
Expand Down
21 changes: 8 additions & 13 deletions src/home/home.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { grid } from "../the-grid";
import { authentication } from "./authentication";
import { displayGitHubIssues } from "./fetch-github/fetch-and-display-previews";
import { postLoadUpdateIssues } from "./fetch-github/fetch-issues-full";
import { displayNotifications } from "./fetch-github/fetch-and-display-previews";
import { fetchNotifications } from "./fetch-github/fetch-notifications";
import { readyToolbar } from "./ready-toolbar";
import { renderServiceMessage } from "./render-service-message";
import { renderErrorInModal } from "./rendering/display-popup-modal";
import { loadIssueFromUrl } from "./rendering/render-github-issues";
import { renderGitRevision } from "./rendering/render-github-login-button";
import { generateSortingToolbar } from "./sorting/generate-sorting-buttons";
import { TaskManager } from "./task-manager";

// All unhandled errors are caught and displayed in a modal
window.addEventListener("error", (event: ErrorEvent) => renderErrorInModal(event.error));
Expand All @@ -20,25 +18,22 @@ window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) =>
});

renderGitRevision();
generateSortingToolbar();
//generateSortingToolbar();
renderServiceMessage();

grid(document.getElementById("grid") as HTMLElement, () => document.body.classList.add("grid-loaded")); // @DEV: display grid background
const container = document.getElementById("issues-container") as HTMLDivElement;
export const notificationsContainer = document.getElementById("issues-container") as HTMLDivElement;

if (!container) {
if (!notificationsContainer) {
throw new Error("Could not find issues container");
}

export const taskManager = new TaskManager(container);
export const notifications = void fetchNotifications();

void (async function home() {
void authentication();
void readyToolbar();
await taskManager.syncTasks(); // Sync tasks from cache on load
loadIssueFromUrl(); // Load issue preview from URL if present
void displayGitHubIssues(); // Display issues from cache
await postLoadUpdateIssues(); // Update cache and issues if cache is outdated
// void readyToolbar();
void displayNotifications();

// Register service worker for PWA
// if ("serviceWorker" in navigator) {
Expand Down
9 changes: 4 additions & 5 deletions src/home/issues-search.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { GitHubIssue } from "./github-types";
import { TaskManager } from "./task-manager";
import { GitHubNotifications } from "./github-types";
import { SearchResult, SearchWeights, SearchConfig } from "./types/search-types";
import { SearchScorer } from "./search/search-scorer";

Expand All @@ -25,7 +24,7 @@ export class IssueSearch {
this._searchScorer = new SearchScorer(this._config);
}

public async initializeIssues(issues: GitHubIssue[]) {
public async initializeIssues(issues: GitHubNotifications) {
this._searchableIssues.clear();
issues.forEach((issue) => {
const searchableContent = this._getSearchableContent(issue);
Expand Down Expand Up @@ -66,7 +65,7 @@ export class IssueSearch {
return results;
}

private _calculateIssueRelevance(issue: GitHubIssue, searchTerms: string[], enableFuzzy: boolean): SearchResult {
private _calculateIssueRelevance(issue: GitHubNotifications, searchTerms: string[], enableFuzzy: boolean): SearchResult {
const matchDetails = {
titleMatches: [] as string[],
bodyMatches: [] as string[],
Expand Down Expand Up @@ -133,7 +132,7 @@ export class IssueSearch {
.map((term) => term.toLowerCase());
}

private _getSearchableContent(issue: GitHubIssue): string {
private _getSearchableContent(issue: GitHubNotifications): string {
// Remove URLs from the content
const removeUrls = (text: string): string => {
return text.replace(/(?:https?:\/\/|http?:\/\/|www\.)[^\s]+/g, "");
Expand Down
Loading

0 comments on commit d0e6ed2

Please sign in to comment.