Skip to content

Commit

Permalink
Merge branch 'feat/filters' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
0x4007 committed Dec 4, 2023
2 parents b375ed2 + 398f9cf commit dbea8f0
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 138 deletions.
19 changes: 19 additions & 0 deletions src/home/authentication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { displayGitHubUserInformation } from "./display-github-user-information";
import { getGitHubAccessToken } from "./get-github-access-token";
import { GitHubUser, getGitHubUser } from "./get-github-user";
import { renderGitHubLoginButton } from "./github-login-button";

export function authentication() {
const accessToken = getGitHubAccessToken();
if (!accessToken) {
renderGitHubLoginButton();
}

getGitHubUser()
.then((gitHubUser: null | GitHubUser) => {
if (gitHubUser) {
displayGitHubUserInformation(gitHubUser);
}
})
.catch((error) => console.error(error));
}
94 changes: 0 additions & 94 deletions src/home/display-github-issues.ts

This file was deleted.

7 changes: 1 addition & 6 deletions src/home/display-github-user-information.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { GitHubUser } from "./authenticated-get-github-user";
import { GitHubUser } from "./get-github-user";
import { getSupabase } from "./github-login-button";

export function displayGitHubUserInformation(gitHubUser: GitHubUser) {
const toolbar = document.getElementById("toolbar");
const authenticated = document.createElement("div");
authenticated.id = "authenticated";
if (!toolbar) throw new Error("toolbar not found");
// const div = document.createElement("div");
// div.textContent = `Logged in as ${gitHubUser.login}`;
// authenticated.appendChild(div);

const img = document.createElement("img");
img.src = gitHubUser.avatar_url;
Expand All @@ -35,7 +32,5 @@ async function signOut() {
console.error("Error logging out:", error);
alert(error);
}
// localStorage.removeItem("provider_token");
// localStorage.removeItem("expires_at");
window.location.reload();
}
141 changes: 141 additions & 0 deletions src/home/fetch-github-issues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { Octokit } from "@octokit/rest";
import { getGitHubAccessToken } from "./get-github-access-token";
import { GitHubIssue } from "./github-types";
import { renderGitHubIssues } from "./render-github-issues";

export type GitHubIssueWithNewFlag = GitHubIssue & { isNew?: boolean };

export type Sorting = "priority" | "time" | "price";

export async function fetchGitHubIssues(sorting?: Sorting) {
const container = document.getElementById("issues-container") as HTMLDivElement;
if (!container) {
throw new Error("Could not find issues container");
}
await fetchIssues(container, sorting);
}

export function sortIssuesBy(issues: GitHubIssue[], sortBy: string) {
switch (sortBy) {
case "priority":
return sortIssuesByPriority(issues);
case "time":
return sortIssuesByTime(issues);
case "price":
return sortIssuesByPrice(issues);
default:
return issues;
}
}

function sortIssuesByPriority(issues: GitHubIssue[]) {
return issues.sort((a, b) => {
const priorityRegex = /Priority: (\d+)/;
const aPriorityMatch = a.labels.find((label) => priorityRegex.test(label.name));
const bPriorityMatch = b.labels.find((label) => priorityRegex.test(label.name));
const aPriority = aPriorityMatch ? parseInt(aPriorityMatch.name.match(priorityRegex)![1], 10) : 0;
const bPriority = bPriorityMatch ? parseInt(bPriorityMatch.name.match(priorityRegex)![1], 10) : 0;
return bPriority - aPriority;
});
}

function sortIssuesByTime(issues: GitHubIssue[]) {
return issues.sort((a, b) => {
const aTimeValue = a.labels.reduce((acc, label) => acc + calculateLabelValue(label.name), 0);
const bTimeValue = b.labels.reduce((acc, label) => acc + calculateLabelValue(label.name), 0);
return bTimeValue - aTimeValue;
});
}

function sortIssuesByPrice(issues: GitHubIssue[]) {
return issues.sort((a, b) => {
const aPriceLabel = a.labels.find((label) => label.name.startsWith("Pricing: "));
const bPriceLabel = b.labels.find((label) => label.name.startsWith("Pricing: "));
const aPrice = aPriceLabel ? parseInt(aPriceLabel.name.match(/Pricing: (\d+)/)![1], 10) : 0;
const bPrice = bPriceLabel ? parseInt(bPriceLabel.name.match(/Pricing: (\d+)/)![1], 10) : 0;
return bPrice - aPrice;
});
}

function calculateLabelValue(label: string): number {
const matches = label.match(/\d+/);
const number = matches && matches.length > 0 ? parseInt(matches[0]) || 0 : 0;

if (label.toLowerCase().includes("minute")) return number * 0.002;
if (label.toLowerCase().includes("hour")) return number * 0.125;
if (label.toLowerCase().includes("day")) return 1 + (number - 1) * 0.25;
if (label.toLowerCase().includes("week")) return number + 1;
if (label.toLowerCase().includes("month")) return 5 + (number - 1) * 8;
return 0;
}

async function fetchIssues(container: HTMLDivElement, sorting?: Sorting) {
let issues;
try {
issues = await fetchCachedIssues();
if (issues) {
await displayIssues(issues, container, sorting);
issues = await fetchNewIssues();
} else {
issues = await fetchNewIssues();
await displayIssues(issues, container, sorting);
}
} catch (error) {
console.error(error);
}
}

async function displayIssues(issues: GitHubIssueWithNewFlag[], container: HTMLDivElement, sorting?: Sorting) {
let sortedIssues = issues;

if (!sorting) {
// Sort the fresh issues
const sortedIssuesByTime = sortIssuesByTime(sortedIssues);
const sortedIssuesByPriority = sortIssuesByPriority(sortedIssuesByTime);
sortedIssues = sortedIssuesByPriority;
} else {
sortedIssues = sortIssuesBy(sortedIssues, sorting);
}
// Pass the fresh issues to the homeController
if (container.classList.contains("ready")) {
container.classList.remove("ready");
container.innerHTML = "";
}
await renderGitHubIssues(container, sortedIssues);
}

async function fetchNewIssues(): Promise<GitHubIssueWithNewFlag[]> {
const octokit = new Octokit({ auth: getGitHubAccessToken() });

try {
const { data: rateLimit } = await octokit.request("GET /rate_limit");
console.log("Rate limit remaining: ", rateLimit.rate.remaining);
} catch (error) {
console.error(error);
}
// Fetch fresh issues and mark them as new
const freshIssues: GitHubIssue[] = await octokit.paginate("GET /repos/ubiquity/devpool-directory/issues", {
state: "open",
});
const freshIssuesWithNewFlag = freshIssues.map((issue) => ({ ...issue, isNew: true })) as GitHubIssueWithNewFlag[];

// Remove the 'isNew' flag before saving to localStorage
const issuesToSave = freshIssuesWithNewFlag.map(({ ...issue }) => {
delete issue.isNew;
return issue;
});
localStorage.setItem("githubIssues", JSON.stringify(issuesToSave));
return freshIssuesWithNewFlag;
}

async function fetchCachedIssues(): Promise<GitHubIssue[] | null> {
const cachedIssues = localStorage.getItem("githubIssues");
if (cachedIssues) {
try {
return JSON.parse(cachedIssues);
} catch (error) {
console.error(error);
}
}
return null;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { renderGitHubLoginButton } from "./github-login-button";

export function checkForGitHubAccessToken(): string | null {
export function getGitHubAccessToken(): string | null {
const oauthToken = getLocalStoreOauth();

const expiresAt = oauthToken?.expires_at;
Expand All @@ -13,9 +11,8 @@ export function checkForGitHubAccessToken(): string | null {
const accessToken = oauthToken?.provider_token;
if (accessToken) {
return accessToken;
} else {
renderGitHubLoginButton();
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Octokit } from "@octokit/rest";
import { getLocalStoreOauth } from "./check-for-github-access-token";
import { getLocalStoreOauth } from "./get-github-access-token";

export async function authenticatedGetGitHubUser(): Promise<GitHubUser | null> {
const activeSessionToken = await getActiveSessionToken();
export async function getGitHubUser(): Promise<GitHubUser | null> {
const activeSessionToken = await getSessionToken();
if (activeSessionToken) {
return getGitHubUser(activeSessionToken);
return getNewGitHubUser(activeSessionToken);
} else {
return null;
}
}

async function getActiveSessionToken(): Promise<string | null> {
async function getSessionToken(): Promise<string | null> {
const cachedSessionToken = getLocalStoreOauth();
if (cachedSessionToken) {
return cachedSessionToken.provider_token;
Expand All @@ -36,7 +36,7 @@ async function getNewSessionToken(): Promise<string | null> {
return providerToken;
}

async function getGitHubUser(providerToken: string): Promise<GitHubUser> {
async function getNewGitHubUser(providerToken: string): Promise<GitHubUser> {
const octokit = new Octokit({ auth: providerToken });
const response = (await octokit.request("GET /user")) as GitHubUserResponse;
return response.data;
Expand Down
29 changes: 16 additions & 13 deletions src/home/home.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { GitHubUser, authenticatedGetGitHubUser } from "./authenticated-get-github-user";
import { checkForGitHubAccessToken } from "./check-for-github-access-token";
import { displayGitHubIssues } from "./display-github-issues";
import { displayGitHubUserInformation } from "./display-github-user-information";
import { authentication } from "./authentication";
import { Sorting, fetchGitHubIssues } from "./fetch-github-issues";

const gitHubToken = checkForGitHubAccessToken();
fetchGitHubIssues().catch((error) => console.error(error));
authentication();
filterButtons();

displayGitHubIssues(gitHubToken)
.then(authenticatedGetGitHubUser)
.then((gitHubUser: null | GitHubUser) => {
if (gitHubUser) {
displayGitHubUserInformation(gitHubUser);
}
})
.catch((error) => console.error(error));
function filterButtons() {
const filters = document.getElementById("filters");
if (!filters) throw new Error("filters not found");
const buttons = filters.querySelectorAll("input");

buttons.forEach((button) => {
button.addEventListener("click", () => {
fetchGitHubIssues(button.value as Sorting).catch((error) => console.error(error));
});
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GitHubIssueWithNewFlag } from "./display-github-issues";
import { GitHubIssueWithNewFlag } from "./fetch-github-issues";

export async function homeController(container: HTMLDivElement, issues: GitHubIssueWithNewFlag[]) {
export async function renderGitHubIssues(container: HTMLDivElement, issues: GitHubIssueWithNewFlag[]) {
const avatarCache: Record<string, string> = JSON.parse(localStorage.getItem("avatarCache") || "{}");
const fetchInProgress = new Set(); // Track in-progress fetches
const existingIssueIds = new Set(Array.from(container.querySelectorAll(".issue-element-inner")).map((element) => element.getAttribute("data-issue-id")));
Expand Down Expand Up @@ -53,9 +53,9 @@ export async function homeController(container: HTMLDivElement, issues: GitHubIs
// Remove the prefix from the label name
const name = label.name.replace(/(Time|Pricing|Priority): /, "");
if (label.name.startsWith("Pricing: ")) {
return `<div class="label pricing">${name}</div>`;
return `<label class="pricing">${name}</label>`;
} else {
return `<div class="label">${name}</div>`;
return `<label class="label">${name}</label>`;
}
});

Expand Down
7 changes: 7 additions & 0 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
<link rel="stylesheet" href="style/inverted-style.css" />
</head>
<body>
<div id="filters">
<div class="labels">
<input type="radio" id="price" name="filter" value="price" /><label for="price">Price</label>
<input type="radio" id="time" name="filter" value="time" /><label for="time">Time</label>
<input type="radio" id="priority" name="filter" value="priority" /><label for="priority">Priority</label>
</div>
</div>
<div id="issues-container"></div>
<div id="toolbar" data-authenticated="false"
><div id="branding"
Expand Down
Loading

0 comments on commit dbea8f0

Please sign in to comment.