Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
22 changes: 19 additions & 3 deletions browser-extension/src/entrypoints/content.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CONFIG } from '../lib/config'
import type { CommentEvent, CommentSpot } from '../lib/enhancer'
import type { CommentEvent, CommentSpot, StrippedLocation } from '../lib/enhancer'
import { logger } from '../lib/logger'
import { EnhancerRegistry, TextareaRegistry } from '../lib/registries'

Expand All @@ -9,6 +9,18 @@ const enhancedTextareas = new TextareaRegistry()
// Expose for debugging in har:view
;(window as any).gitcassoTextareaRegistry = enhancedTextareas

function detectLocation(): StrippedLocation {
if ((window as any).gitcassoMockLocation) {
return (window as any).gitcassoMockLocation
}
const result = {
host: window.location.host,
pathname: window.location.pathname,
}
logger.debug('[gitcasso] detectLocation called, returning:', result)
return result
}

function sendEventToBackground(type: 'ENHANCED' | 'DESTROYED', spot: CommentSpot): void {
const message: CommentEvent = {
spot,
Expand All @@ -26,6 +38,7 @@ enhancedTextareas.setEventHandlers(

export default defineContentScript({
main() {
logger.debug('Main was called')
const textAreasOnPageLoad = document.querySelectorAll<HTMLTextAreaElement>(`textarea`)
for (const textarea of textAreasOnPageLoad) {
enhanceMaybe(textarea)
Expand All @@ -35,7 +48,7 @@ export default defineContentScript({
childList: true,
subtree: true,
})
logger.debug('Extension loaded with', enhancers.getEnhancerCount, 'handlers')
logger.debug('Extension loaded with', enhancers.getEnhancerCount(), 'handlers')
},
matches: ['<all_urls>'],
runAt: 'document_end',
Expand Down Expand Up @@ -80,6 +93,7 @@ function handleMutations(mutations: MutationRecord[]): void {
}

function enhanceMaybe(textarea: HTMLTextAreaElement) {
logger.debug('[gitcasso] enhanceMaybe called for textarea:', textarea.id, textarea.className)
if (enhancedTextareas.get(textarea)) {
logger.debug('textarea already registered {}', textarea)
return
Expand All @@ -89,7 +103,9 @@ function enhanceMaybe(textarea: HTMLTextAreaElement) {
injectStyles()

try {
const enhancedTextarea = enhancers.tryToEnhance(textarea)
const location = detectLocation()
logger.debug('[gitcasso] Calling tryToEnhance with location:', location)
const enhancedTextarea = enhancers.tryToEnhance(textarea, location)
if (enhancedTextarea) {
logger.debug(
'Identified textarea:',
Expand Down
11 changes: 10 additions & 1 deletion browser-extension/src/lib/enhancer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ export interface CommentEvent {
draft?: string
}

/**
* Minimal location information that enhancers need for routing decisions.
* Avoids dependency on global window/location objects for better testability.
*/
export interface StrippedLocation {
host: string
pathname: string
}

/** Wraps the textareas of a given platform with Gitcasso's enhancements. */
export interface CommentEnhancer<Spot extends CommentSpot = CommentSpot> {
/** Guarantees to only return a type within this list. */
Expand All @@ -27,7 +36,7 @@ export interface CommentEnhancer<Spot extends CommentSpot = CommentSpot> {
* Whenever a new `textarea` is added to any webpage, this method is called.
* If we return non-null, then we become the handler for that text area.
*/
tryToEnhance(textarea: HTMLTextAreaElement): Spot | null
tryToEnhance(textarea: HTMLTextAreaElement, location: StrippedLocation): Spot | null
/** This gets called the first time that `tryToEnhance` returns non-null. */
prepareForFirstEnhancement(): void
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { OverTypeInstance } from 'overtype'
import type { ReactNode } from 'react'
import type { CommentEnhancer, CommentSpot } from '../enhancer'
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '../enhancer'

/** Used when an entry is in the table which we don't recognize. */
export class CommentEnhancerMissing implements CommentEnhancer {
Expand Down Expand Up @@ -40,7 +40,7 @@ export class CommentEnhancerMissing implements CommentEnhancer {
forSpotTypes(): string[] {
throw new Error('Method not implemented.')
}
tryToEnhance(_textarea: HTMLTextAreaElement): CommentSpot | null {
tryToEnhance(_textarea: HTMLTextAreaElement, _location: StrippedLocation): CommentSpot | null {
throw new Error('Method not implemented.')
}
prepareForFirstEnhancement(): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IssueOpenedIcon } from '@primer/octicons-react'
import OverType, { type OverTypeInstance } from 'overtype'
import type React from 'react'
import type { CommentEnhancer, CommentSpot } from '@/lib/enhancer'
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '@/lib/enhancer'
import { logger } from '@/lib/logger'
import { modifyDOM } from '../modifyDOM'
import { commonGithubOptions } from './ghOptions'
Expand All @@ -20,19 +20,22 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer<GitHubIssu
return ['GH_ISSUE_ADD_COMMENT']
}

tryToEnhance(textarea: HTMLTextAreaElement): GitHubIssueAddCommentSpot | null {
tryToEnhance(
textarea: HTMLTextAreaElement,
location: StrippedLocation,
): GitHubIssueAddCommentSpot | null {
if (textarea.id === 'feedback') {
return null
}
if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') {
if (location.host !== 'github.com') {
return null
}

// Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456
logger.debug(`${this.constructor.name} examing url`, window.location.pathname)
logger.debug(`${this.constructor.name} examing url`, location.pathname)

const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/(\d+))/)
logger.debug(`${this.constructor.name} found match`, window.location.pathname)
const match = location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/(\d+))/)
logger.debug(`${this.constructor.name} found match`, location.pathname)
if (!match) return null

const [, owner, repo, numberStr] = match
Expand All @@ -41,7 +44,7 @@ export class GitHubIssueAddCommentEnhancer implements CommentEnhancer<GitHubIssu
const unique_key = `github.com:${slug}:${number}`
const title = 'TODO_TITLE'
return {
domain: 'github.com',
domain: location.host,
number,
slug,
title,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OverType, { type OverTypeInstance } from 'overtype'
import type { CommentEnhancer, CommentSpot } from '../../enhancer'
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '../../enhancer'
import { logger } from '../../logger'
import { modifyDOM } from '../modifyDOM'
import { commonGithubOptions } from './ghOptions'
Expand All @@ -16,23 +16,26 @@ export class GitHubIssueNewCommentEnhancer implements CommentEnhancer<GitHubIssu
return ['GH_ISSUE_NEW_COMMENT']
}

tryToEnhance(_textarea: HTMLTextAreaElement): GitHubIssueNewCommentSpot | null {
if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') {
tryToEnhance(
_textarea: HTMLTextAreaElement,
location: StrippedLocation,
): GitHubIssueNewCommentSpot | null {
if (location.host !== 'github.com') {
return null
}

// Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456
logger.debug(`${this.constructor.name} examing url`, window.location.pathname)
logger.debug(`${this.constructor.name} examing url`, location.pathname)

const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/)
logger.debug(`${this.constructor.name} found match`, window.location.pathname)
const match = location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/issues\/new)/)
logger.debug(`${this.constructor.name} found match`, location.pathname)

if (!match) return null
const [, owner, repo] = match
const slug = `${owner}/${repo}`
const unique_key = `github.com:${slug}:new`
return {
domain: 'github.com',
domain: location.host,
slug,
type: 'GH_ISSUE_NEW_COMMENT',
unique_key,
Expand Down
20 changes: 10 additions & 10 deletions browser-extension/src/lib/enhancers/github/githubPRAddComment.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import OverType, { type OverTypeInstance } from 'overtype'
import type React from 'react'
import type { CommentEnhancer, CommentSpot } from '@/lib/enhancer'
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '@/lib/enhancer'
import { logger } from '@/lib/logger'
import { modifyDOM } from '../modifyDOM'
import { commonGithubOptions } from './ghOptions'
Expand All @@ -19,28 +19,28 @@ export class GitHubPRAddCommentEnhancer implements CommentEnhancer<GitHubPRAddCo
return ['GH_PR_ADD_COMMENT']
}

tryToEnhance(_textarea: HTMLTextAreaElement): GitHubPRAddCommentSpot | null {
tryToEnhance(
_textarea: HTMLTextAreaElement,
location: StrippedLocation,
): GitHubPRAddCommentSpot | null {
// Only handle github.com domains TODO: identify GitHub Enterprise somehow
if (
document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com' ||
_textarea.id !== 'new_comment_field'
) {
if (location.host !== 'github.com' || _textarea.id !== 'new_comment_field') {
return null
}

// Parse GitHub URL structure: /owner/repo/issues/123 or /owner/repo/pull/456
logger.debug(`${this.constructor.name} examing url`, window.location.pathname)
logger.debug(`${this.constructor.name} examing url`, location.pathname)

const match = window.location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/pull\/(\d+))/)
logger.debug(`${this.constructor.name} found match`, window.location.pathname)
const match = location.pathname.match(/^\/([^/]+)\/([^/]+)(?:\/pull\/(\d+))/)
logger.debug(`${this.constructor.name} found match`, location.pathname)
if (!match) return null
const [, owner, repo, numberStr] = match
const slug = `${owner}/${repo}`
const number = parseInt(numberStr!, 10)
const unique_key = `github.com:${slug}:${number}`
const title = 'TODO_TITLE'
return {
domain: 'github.com',
domain: location.host,
number,
slug,
title,
Expand Down
17 changes: 10 additions & 7 deletions browser-extension/src/lib/enhancers/github/githubPRNewComment.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OverType, { type OverTypeInstance } from 'overtype'
import type { CommentEnhancer, CommentSpot } from '../../enhancer'
import type { CommentEnhancer, CommentSpot, StrippedLocation } from '../../enhancer'
import { logger } from '../../logger'
import { modifyDOM } from '../modifyDOM'
import { commonGithubOptions } from './ghOptions'
Expand All @@ -16,22 +16,25 @@ export class GitHubPRNewCommentEnhancer implements CommentEnhancer<GitHubPRNewCo
return ['GH_PR_NEW_COMMENT']
}

tryToEnhance(textarea: HTMLTextAreaElement): GitHubPRNewCommentSpot | null {
tryToEnhance(
textarea: HTMLTextAreaElement,
location: StrippedLocation,
): GitHubPRNewCommentSpot | null {
if (textarea.id === 'feedback') {
return null
}
if (document.querySelector('meta[name="hostname"]')?.getAttribute('content') !== 'github.com') {
if (location.host !== 'github.com') {
return null
}

// /owner/repo/compare/feature/more-enhancers?expand=1
// or /owner/repo/compare/feat/issue-static-and-dynamic...feature/more-enhancers?expand=1
logger.info(`${this.constructor.name} examing url`, window.location.pathname)
logger.info(`${this.constructor.name} examing url`, location.pathname)

const match = window.location.pathname.match(
const match = location.pathname.match(
/^\/([^/]+)\/([^/]+)\/compare\/(?:([^.?]+)\.\.\.)?([^?]+)/,
)
logger.info(`${this.constructor.name} found match`, window.location.pathname, match)
logger.info(`${this.constructor.name} found match`, location.pathname, match)

if (!match) return null
const [, owner, repo, baseBranch, compareBranch] = match
Expand All @@ -40,7 +43,7 @@ export class GitHubPRNewCommentEnhancer implements CommentEnhancer<GitHubPRNewCo
: `${owner}/${repo}/${compareBranch}`
const unique_key = `github.com:${slug}`
return {
domain: 'github.com',
domain: location.host,
slug,
type: 'GH_PR_NEW_COMMENT',
unique_key,
Expand Down
6 changes: 3 additions & 3 deletions browser-extension/src/lib/registries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { OverTypeInstance } from 'overtype'
import OverType from 'overtype'
import type { CommentEnhancer, CommentSpot } from './enhancer'
import type { CommentEnhancer, CommentSpot, StrippedLocation } from './enhancer'
import { CommentEnhancerMissing } from './enhancers/CommentEnhancerMissing'
import { GitHubIssueAddCommentEnhancer } from './enhancers/github/githubIssueAddComment'
import { GitHubIssueNewCommentEnhancer } from './enhancers/github/githubIssueNewComment'
Expand Down Expand Up @@ -59,10 +59,10 @@ export class EnhancerRegistry {
return (this.byType.get(spot.type) || new CommentEnhancerMissing()) as CommentEnhancer<T>
}

tryToEnhance(textarea: HTMLTextAreaElement): EnhancedTextarea | null {
tryToEnhance(textarea: HTMLTextAreaElement, location: StrippedLocation): EnhancedTextarea | null {
for (const enhancer of this.enhancers) {
try {
const spot = enhancer.tryToEnhance(textarea)
const spot = enhancer.tryToEnhance(textarea, location)
if (spot) {
// Prepare enhancer on first use
if (!this.preparedEnhancers.has(enhancer)) {
Expand Down
Loading