Skip to content

Commit

Permalink
feat: add to highlights button (#64)
Browse files Browse the repository at this point in the history
* feat: add-to-highlights-button

* feat: improved-dom-update-watcher

* chore: formatting

* refactor: replace profileScreen.ts with github.ts

* refactor: parameterize delayInMs for reuse, implicit inference

* chore: 25ms delay for github.ts domupdate

* docs: Add documentation link README.md
  • Loading branch information
Anush008 authored May 4, 2023
1 parent f5ccf59 commit e2120a1
Show file tree
Hide file tree
Showing 15 changed files with 164 additions and 83 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
</a>
</p>

## Documentation

The documentation for the project can be found [here](https://docs.opensauced.pizza/chrome-extension/introduction-to-the-chrome-extension/).

## Running the project locally

To run the project, you'll need the following software binaries installed on your development machines:
Expand All @@ -36,7 +40,7 @@ To run a local instance of the project:
npm run dev
```

## Installing the extension on a Chromium based browser:
## Installing the local build on a Chromium based browser:
After running the above commands,
1. Navigate to `chrome://extensions`.
2. Enable the `Developer Mode`.
Expand Down
5 changes: 3 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"action": { "default_popup": "index.html" },
"content_scripts": [
{
"js": ["src/content-scripts/profileScreen.ts"],
"matches": ["https://github.com/*"]
"js": ["src/content-scripts/github.ts"],
"matches": ["https://github.com/*"],
"run": "document_end"
}
],
"background": {
Expand Down
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ export const OPEN_SAUCED_SESSION_ENDPOINT = "https://api.opensauced.pizza/v1/aut
export const OPEN_SAUCED_INSIGHTS_DOMAIN = "insights.opensauced.pizza";
export const GITHUB_PROFILE_MENU_SELECTOR = ".p-nickname.vcard-username.d-block";
export const GITHUB_PROFILE_EDIT_MENU_SELECTOR = "button.js-profile-editable-edit-button";
export const GITHUB_PR_AUTHOR_USERNAME_SELECTOR = "author Link--primary text-bold css-overflow-wrap-anywhere";
export const GITHUB_LOGGED_IN_USER_USERNAME_SELECTOR = "meta[name=\"user-login\"]";
export const GITHUB_PR_COMMENT_HEADER_SELECTOR = "timeline-comment-header clearfix d-flex";
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createHtmlElement } from "../../../utils/createHtmlElement";
import openSaucedLogoIcon from "../../../assets/opensauced-icon.svg";

export const AddPRToHighlightsButton = () => {
const addPRToHighlightsButton = createHtmlElement("a", {
href: `https://insights.opensauced.pizza/feed?url=${encodeURIComponent(window.location.href)}`,
target: "_blank",
rel: "noopener noreferrer",
innerHTML: `<span aria-label="Add PR to OpenSauced highlights." data-view-component="true" class="tooltipped tooltipped-n">
<img data-view-component="true" class="mr-1 mt-1" height="16px" width="16px" src=${chrome.runtime.getURL(openSaucedLogoIcon)}>
</span>`,
});

return addPRToHighlightsButton;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import twitterSocialIcon from "../../../assets/twitter-icon.svg";
import linkedInSocailIcon from "../../../assets/linkedin-icon.svg";

interface Socials {
emailAddress?: string;
twitterUsername?: string;
linkedInUsername?: string;
emailAddress?: string | null;
twitterUsername?: string | null;
linkedInUsername?: string | null;
}

export const InviteToOpenSaucedModal = (
Expand Down
36 changes: 36 additions & 0 deletions src/content-scripts/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {
getGithubUsername,
isGithubProfilePage,
isGithubPullRequestPage,
} from "../utils/urlMatchers";
import { isOpenSaucedUser } from "../utils/fetchOpenSaucedApiData";
import injectViewOnOpenSauced from "../utils/dom-utils/viewOnOpenSauced";
import injectInviteToOpenSauced from "../utils/dom-utils/inviteToOpenSauced";
import { prefersDarkMode } from "../utils/colorPreference";
import injectAddPRToHighlightsButton from "../utils/dom-utils/addPRToHighlights";
import domUpdateWatch from "../utils/dom-utils/domUpdateWatcher";

const processGithubPage = async () => {
if (prefersDarkMode(document.cookie)) {
document.documentElement.classList.add("dark");
}

if (isGithubPullRequestPage(window.location.href)) {
injectAddPRToHighlightsButton();
} else if (isGithubProfilePage(window.location.href)) {
const username = getGithubUsername(window.location.href);

if (!username) {
return;
}
if (await isOpenSaucedUser(username)) {
injectViewOnOpenSauced(username);
} else {
injectInviteToOpenSauced(username);
}
}

domUpdateWatch(processGithubPage, 25);
};

void processGithubPage();
32 changes: 0 additions & 32 deletions src/content-scripts/profileScreen.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/utils/checkAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ export const checkAuthentication = () => {
},
);
};

export const isLoggedIn = async () =>

// only a valid auth token can exist in the storage due to the check in line 23
Object.entries(await chrome.storage.sync.get(OPEN_SAUCED_AUTH_TOKEN_KEY)).length !== 0;

2 changes: 1 addition & 1 deletion src/utils/createHtmlElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type CssDeclaration = keyof Omit<CSSStyleDeclaration, "length" | "parentRule">;

export function createHtmlElement<T extends keyof HTMLElementTagNameMap> (
nodeName: T,
props: ElementProps,
props: ElementProps = {},
) {
const { style, ...nonStyleProps } = props;
const element = Object.assign(document.createElement(nodeName), nonStyleProps);
Expand Down
40 changes: 40 additions & 0 deletions src/utils/dom-utils/addPRToHighlights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { AddPRToHighlightsButton } from "../../content-scripts/components/AddPRToHighlights/AddPRToHighlightsButton";
import {
GITHUB_LOGGED_IN_USER_USERNAME_SELECTOR,
GITHUB_PR_AUTHOR_USERNAME_SELECTOR,
GITHUB_PR_COMMENT_HEADER_SELECTOR,
} from "../../constants";
import { isLoggedIn } from "../checkAuthentication";

const injectAddPRToHighlightsButton = async () => {
if (!(await isLoggedIn())) {
return;
}

const prAuthorUserName = document.getElementsByClassName(
GITHUB_PR_AUTHOR_USERNAME_SELECTOR,
)[0].textContent;
const loggedInUserUserName = document
.querySelector(GITHUB_LOGGED_IN_USER_USERNAME_SELECTOR)
?.getAttribute("content");

if (loggedInUserUserName && prAuthorUserName === loggedInUserUserName) {
const commentFormatRow = document.getElementsByClassName(
GITHUB_PR_COMMENT_HEADER_SELECTOR,
)[0];
const addPRToHighlightsButton = AddPRToHighlightsButton();

if (
!commentFormatRow.lastElementChild?.previousElementSibling?.isEqualNode(
addPRToHighlightsButton,
)
) {
commentFormatRow.insertBefore(
addPRToHighlightsButton,
commentFormatRow.lastElementChild,
);
}
}
};

export default injectAddPRToHighlightsButton;
21 changes: 21 additions & 0 deletions src/utils/dom-utils/domUpdateWatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const domUpdateWatch = (callback: () => void, delayInMs = 0) => {
const oldLocation = document.location.href;
const observer = new MutationObserver(
(_: unknown, observer: MutationObserver) => {
const newLocation = document.location.href;

if (oldLocation === newLocation || document.readyState !== "complete") {
return;
}
observer.disconnect();
setTimeout(callback, delayInMs);
},
);

observer.observe(document.body, {
childList: true,
subtree: true,
});
};

export default domUpdateWatch;
50 changes: 17 additions & 33 deletions src/utils/dom-utils/inviteToOpenSauced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,20 @@ import { InviteToOpenSaucedModal } from "../../content-scripts/components/Invite
import { getTwitterUsername, getLinkedInUsername } from "../urlMatchers";

const injectOpenSaucedInviteButton = (username: string) => {
const emailAddress: string | undefined = (() => {
const element = document.querySelector(`a[href^="mailto:"]`);

if (element instanceof HTMLAnchorElement) {
return element.href;
}
return undefined;
})();
const twitterUrl: string | undefined = (() => {
const element = document.querySelector(`a[href*="twitter.com"]`);

if (element instanceof HTMLAnchorElement) {
return element.href;
}
return undefined;
})();
const linkedInUrl: string | undefined = (() => {
const element = document.querySelector(`a[href*="linkedin.com"]`);

if (element instanceof HTMLAnchorElement) {
return element.href;
}
return undefined;
})();
const emailAddress = document
.querySelector(`a[href^="mailto:"]`)
?.getAttribute("href")
?.replace("mailto:", "");
const twitterUrl = document
.querySelector(`a[href*="twitter.com"]`)
?.getAttribute("href");
const linkedInUrl = document
.querySelector(`a[href*="linkedin.com"]`)
?.getAttribute("href");

if (!(emailAddress || twitterUrl || linkedInUrl)) {
return;
}
return;
}

const twitterUsername = twitterUrl && getTwitterUsername(twitterUrl);
const linkedInUsername = linkedInUrl && getLinkedInUsername(linkedInUrl);
Expand All @@ -48,13 +34,11 @@ const injectOpenSaucedInviteButton = (username: string) => {

const userBio = document.querySelector(GITHUB_PROFILE_MENU_SELECTOR);

if (!userBio?.parentNode) {
return;
}
if (userBio.lastChild?.isEqualNode(inviteToOpenSaucedButton)) {
return;
}
userBio.append(inviteToOpenSaucedButton);

if (userBio?.lastChild?.isEqualNode(inviteToOpenSaucedButton)) {
return;
}
userBio?.append(inviteToOpenSaucedButton);
document.body.appendChild(inviteToOpenSaucedModal);
};

Expand Down
7 changes: 2 additions & 5 deletions src/utils/dom-utils/viewOnOpenSauced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ const injectViewOnOpenSaucedButton = (username: string) => {
`${GITHUB_PROFILE_MENU_SELECTOR}, ${GITHUB_PROFILE_EDIT_MENU_SELECTOR}`,
);

if (!userBio?.parentNode) {
if (userBio?.lastChild?.isEqualNode(viewOnOpenSaucedButton)) {
return;
}
if (userBio.lastChild?.isEqualNode(viewOnOpenSaucedButton)) {
return;
}
userBio.append(viewOnOpenSaucedButton);
userBio?.append(viewOnOpenSaucedButton);
};

export default injectViewOnOpenSaucedButton;
12 changes: 12 additions & 0 deletions src/utils/urlMatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ export const getTwitterUsername = (url: string) => {

return match ? match[1] : undefined;
};

export const isGithubPullRequestPage = (url: string) => {
const githubPullRequestPattern = /github\.com\/[\w.-]+\/[^/]+\/pull\/\d+/;

return githubPullRequestPattern.test(url);
};

export const isGithubProfilePage = (url: string) => {
const githubProfilePattern = /github\.com\/[^/]+$/;

return githubProfilePattern.test(url);
};
6 changes: 0 additions & 6 deletions src/worker/background.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { checkAuthentication } from "../utils/checkAuthentication";
import { SUPABASE_AUTH_COOKIE_NAME, OPEN_SAUCED_INSIGHTS_DOMAIN } from "../constants";

chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
if (changeInfo.url?.includes("github.com")) {
void chrome.tabs.sendMessage(tabId, { message: "GITHUB_URL_CHANGED" });
}
});

chrome.cookies.onChanged.addListener(changeInfo => {
if (
changeInfo.cookie.name === SUPABASE_AUTH_COOKIE_NAME ||
Expand Down

0 comments on commit e2120a1

Please sign in to comment.