generated from ubiquity/ts-template
-
Notifications
You must be signed in to change notification settings - Fork 1
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
leaderboard init #1
Closed
Closed
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
456707c
chore: leaderboard init
Keyrxng 33d8c10
chore: types and remove unused
Keyrxng 63904bd
chore: split methods
Keyrxng 2af987b
chore: keyboard nav
Keyrxng f95405c
chore cspell, eslint
Keyrxng 60d163f
chore: readme
Keyrxng 46cda63
fix: env vars
Keyrxng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
MY_SECRET="MY_SECRET" | ||
SUPABASE_URL="" | ||
SUPABASE_ANON_KEY="" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,9 @@ | ||
# `@ubiquity/ts-template` | ||
# `@ubiquity/leaderboard.ubq.fi` | ||
|
||
This template repository includes support for the following: | ||
This is an up-to-date leaderboard of the top contributors to the Ubiquity ecosystem based on earnings from completed Devpool Directory bounties. | ||
|
||
- TypeScript | ||
- Environment Variables | ||
- Conventional Commits | ||
- Automatic deployment to Cloudflare Pages | ||
|
||
## Testing | ||
|
||
### Cypress | ||
|
||
To test with Cypress Studio UI, run | ||
|
||
```shell | ||
yarn cy:open | ||
``` | ||
|
||
Otherwise to simply run the tests through the console, run | ||
|
||
```shell | ||
yarn cy:run | ||
``` | ||
|
||
### Jest | ||
|
||
To start Jest tests, run | ||
|
||
```shell | ||
yarn test | ||
``` | ||
### TODO | ||
- [ ] Pull data from Supabase instead of from static repo file | ||
- [ ] Improve the hunter metadata/markers displayed in the additional details modal | ||
- [ ] Add filters based on markers such as XP, Karma, Top n, etc. | ||
- [ ] Add pagination and cap displayed entries? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,77 @@ | ||
import { config } from "dotenv"; | ||
import esbuild from "esbuild"; | ||
const typescriptEntries = ["static/main.ts"]; | ||
// const cssEntries = ["static/style.css"]; | ||
const entries = [ | ||
...typescriptEntries, | ||
// ...cssEntries | ||
]; | ||
import { invertColors } from "./plugins/invert-colors"; | ||
import { execSync } from "child_process"; | ||
config(); | ||
|
||
const typescriptEntries = ["src/home/home.ts"]; | ||
const cssEntries = ["static/style/style.css"]; | ||
const entries = [...typescriptEntries, ...cssEntries, "static/favicon.svg", "static/icon-512x512.png"]; | ||
|
||
export const esBuildContext: esbuild.BuildOptions = { | ||
plugins: [invertColors], | ||
sourcemap: true, | ||
entryPoints: entries, | ||
bundle: true, | ||
minify: false, | ||
loader: { | ||
".png": "dataurl", | ||
".woff": "dataurl", | ||
".woff2": "dataurl", | ||
".eot": "dataurl", | ||
".ttf": "dataurl", | ||
".svg": "dataurl", | ||
".png": "file", | ||
".woff": "file", | ||
".woff2": "file", | ||
".eot": "file", | ||
".ttf": "file", | ||
".svg": "file", | ||
".json": "file", | ||
}, | ||
outdir: "static/dist", | ||
define: createEnvDefines(["SUPABASE_URL", "SUPABASE_ANON_KEY"], { | ||
SUPABASE_STORAGE_KEY: generateSupabaseStorageKey(), | ||
commitHash: execSync(`git rev-parse --short HEAD`).toString().trim(), | ||
}), | ||
}; | ||
|
||
esbuild | ||
.build(esBuildContext) | ||
.then(() => { | ||
console.log("\tesbuild complete"); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
process.exit(1); | ||
}); | ||
.then(() => console.log("\tesbuild complete")) | ||
.catch(console.error); | ||
|
||
function createEnvDefines(environmentVariables: string[], generatedAtBuild: Record<string, unknown>): Record<string, string> { | ||
const defines: Record<string, string> = {}; | ||
for (const name of environmentVariables) { | ||
const envVar = process.env[name]; | ||
if (envVar !== undefined) { | ||
defines[name] = JSON.stringify(envVar); | ||
} else { | ||
throw new Error(`Missing environment variable: ${name}`); | ||
} | ||
} | ||
for (const key of Object.keys(generatedAtBuild)) { | ||
if (Object.prototype.hasOwnProperty.call(generatedAtBuild, key)) { | ||
defines[key] = JSON.stringify(generatedAtBuild[key]); | ||
} | ||
} | ||
return defines; | ||
} | ||
|
||
export function generateSupabaseStorageKey(): string | null { | ||
const SUPABASE_URL = process.env.SUPABASE_URL; | ||
if (!SUPABASE_URL) { | ||
console.error("SUPABASE_URL environment variable is not set"); | ||
return null; | ||
} | ||
|
||
const urlParts = SUPABASE_URL.split("."); | ||
if (urlParts.length === 0) { | ||
console.error("Invalid SUPABASE_URL environment variable"); | ||
return null; | ||
} | ||
|
||
const domain = urlParts[0]; | ||
const lastSlashIndex = domain.lastIndexOf("/"); | ||
if (lastSlashIndex === -1) { | ||
console.error("Invalid SUPABASE_URL format"); | ||
return null; | ||
} | ||
|
||
return domain.substring(lastSlashIndex + 1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import esbuild from "esbuild"; | ||
import fs from "fs"; | ||
import path from "path"; | ||
|
||
export const invertColors: esbuild.Plugin = { | ||
name: "invert-colors", | ||
setup(build) { | ||
build.onLoad({ filter: /\.css$/ }, async (args) => { | ||
const contents = await fs.promises.readFile(args.path, "utf8"); | ||
|
||
const updatedContents = contents.replace(/prefers-color-scheme: dark/g, "prefers-color-scheme: light"); | ||
|
||
// Invert greyscale colors and accommodate alpha channels in the CSS content | ||
const invertedContents = updatedContents.replace(/#([0-9A-Fa-f]{3,6})([0-9A-Fa-f]{2})?\b/g, (match, rgb, alpha) => { | ||
let color = rgb.startsWith("#") ? rgb.slice(1) : rgb; | ||
if (color.length === 3) { | ||
color = color | ||
.split("") | ||
.map((char: string) => char + char) | ||
.join(""); | ||
} | ||
const r = parseInt(color.slice(0, 2), 16); | ||
const g = parseInt(color.slice(2, 4), 16); | ||
const b = parseInt(color.slice(4, 6), 16); | ||
|
||
// Check if the color is greyscale (R, G, and B components are equal) | ||
if (r === g && g === b) { | ||
// Invert RGB values | ||
const invertedColorValue = (255 - r).toString(16).padStart(2, "0"); | ||
// Return the inverted greyscale color with alpha channel if present | ||
return `#${invertedColorValue}${invertedColorValue}${invertedColorValue}${alpha || ""}`; | ||
} | ||
|
||
// If the color is not greyscale, return it as is, including the alpha channel if present | ||
return `#${color}${alpha || ""}`; | ||
}); | ||
|
||
// Define the output path for the new CSS file | ||
const outputPath = path.resolve("static/style", "inverted-style.css"); | ||
const outputDir = path.dirname(outputPath); | ||
await fs.promises.mkdir(outputDir, { recursive: true }); | ||
// Write the new contents to the output file | ||
await fs.promises.writeFile(outputPath, invertedContents, "utf8"); | ||
|
||
// Return an empty result to esbuild since we're writing the file ourselves | ||
return { contents: "", loader: "css" }; | ||
}); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { getGitHubAccessToken } from "./getters/get-github-access-token"; | ||
import { getGitHubUser } from "./getters/get-github-user"; | ||
import { GitHubUser } from "./github-types"; | ||
import { displayGitHubUserInformation } from "./rendering/display-github-user-information"; | ||
import { renderGitHubLoginButton } from "./rendering/render-github-login-button"; | ||
|
||
export async function authentication() { | ||
const accessToken = await getGitHubAccessToken(); | ||
if (!accessToken) { | ||
renderGitHubLoginButton(); | ||
} | ||
|
||
const gitHubUser: null | GitHubUser = await getGitHubUser(); | ||
if (gitHubUser) { | ||
displayGitHubUserInformation(gitHubUser); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
declare const SUPABASE_STORAGE_KEY: string; // @DEV: passed in at build time check build/esbuild-build.ts | ||
import { checkSupabaseSession } from "../rendering/render-github-login-button"; | ||
import { getLocalStore } from "./get-local-store"; | ||
|
||
export async function getGitHubAccessToken(): Promise<string | null> { | ||
// better to use official function, looking up localstorage has flaws | ||
const authToken = await checkSupabaseSession(); | ||
|
||
const expiresAt = authToken?.expires_at; | ||
if (expiresAt && expiresAt < Date.now() / 1000) { | ||
localStorage.removeItem(`sb-${SUPABASE_STORAGE_KEY}-auth-token`); | ||
return null; | ||
} | ||
|
||
return authToken?.provider_token ?? null; | ||
} | ||
|
||
export function getGitHubUserName(): string | null { | ||
const authToken = getLocalStore(`sb-${SUPABASE_STORAGE_KEY}-auth-token`) as OauthToken | null; | ||
return authToken?.user?.user_metadata?.user_name ?? null; | ||
} | ||
|
||
export interface OauthToken { | ||
provider_token: string; | ||
access_token: string; | ||
expires_in: number; | ||
expires_at: number; | ||
refresh_token: string; | ||
token_type: string; | ||
user: { | ||
id: string; | ||
aud: string; | ||
role: string; | ||
email: string; | ||
email_confirmed_at: string; | ||
phone: string; | ||
confirmed_at: string; | ||
last_sign_in_at: string; | ||
app_metadata: { provider: string; providers: string[] }; | ||
user_metadata: { | ||
avatar_url: string; | ||
email: string; | ||
email_verified: boolean; | ||
full_name: string; | ||
iss: string; | ||
name: string; | ||
phone_verified: boolean; | ||
preferred_username: string; | ||
provider_id: string; | ||
sub: string; | ||
user_name: string; | ||
}; | ||
identities: [ | ||
{ | ||
id: string; | ||
user_id: string; | ||
identity_data: { | ||
avatar_url: string; | ||
email: string; | ||
email_verified: boolean; | ||
full_name: string; | ||
iss: string; | ||
name: string; | ||
phone_verified: boolean; | ||
preferred_username: string; | ||
provider_id: string; | ||
sub: string; | ||
user_name: string; | ||
}; | ||
provider: string; | ||
last_sign_in_at: string; | ||
created_at: string; | ||
updated_at: string; | ||
}, | ||
]; | ||
created_at: string; | ||
updated_at: string; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { Octokit } from "@octokit/rest"; | ||
import { GitHubUser, GitHubUserResponse } from "../github-types"; | ||
import { OauthToken } from "./get-github-access-token"; | ||
import { getLocalStore } from "./get-local-store"; | ||
declare const SUPABASE_STORAGE_KEY: string; // @DEV: passed in at build time check build/esbuild-build.ts | ||
|
||
export async function getGitHubUser(): Promise<GitHubUser | null> { | ||
const activeSessionToken = await getSessionToken(); | ||
if (activeSessionToken) { | ||
return getNewGitHubUser(activeSessionToken); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
async function getSessionToken(): Promise<string | null> { | ||
const cachedSessionToken = getLocalStore(`sb-${SUPABASE_STORAGE_KEY}-auth-token`) as OauthToken | null; | ||
if (cachedSessionToken) { | ||
return cachedSessionToken.provider_token; | ||
} | ||
const newSessionToken = await getNewSessionToken(); | ||
if (newSessionToken) { | ||
return newSessionToken; | ||
} | ||
return null; | ||
} | ||
|
||
async function getNewSessionToken(): Promise<string | null> { | ||
const hash = window.location.hash; | ||
const params = new URLSearchParams(hash.substr(1)); // remove the '#' and parse | ||
const providerToken = params.get("provider_token"); | ||
if (!providerToken) { | ||
return null; | ||
} | ||
return providerToken; | ||
} | ||
|
||
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; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This threw the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know the format had changed. Is this change across all repos?