From b84be60b9422b80cc36d1b185458cb5d8affdca1 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Tue, 27 Aug 2024 22:27:19 +0200 Subject: [PATCH] finish refactor --- src/background.ts | 3 +- src/components/Options.tsx | 2 +- src/core/WakaTimeCore.ts | 3 +- src/types/heartbeats.ts | 1 + src/types/sites.ts | 1 + src/utils/changeExtensionStatus.ts | 2 +- src/utils/heartbeat.ts | 232 --------------- src/utils/{index.ts => operatingSystem.ts} | 11 - src/utils/sites.ts | 320 +++++++++++++++++++++ src/wakatimeScript.ts | 57 +--- 10 files changed, 329 insertions(+), 303 deletions(-) delete mode 100644 src/utils/heartbeat.ts rename src/utils/{index.ts => operatingSystem.ts} (60%) create mode 100644 src/utils/sites.ts diff --git a/src/background.ts b/src/background.ts index d357d63..3f0242d 100644 --- a/src/background.ts +++ b/src/background.ts @@ -10,7 +10,7 @@ browser.alarms.onAlarm.addListener(async (alarm) => { // Checks if the user is online and if there are cached heartbeats requests, // if so then procedd to send these payload to wakatime api if (navigator.onLine) { - await WakaTimeCore.sendCachedHeartbeatsRequest(); + await WakaTimeCore.sendHeartbeats(); } } }); @@ -30,7 +30,6 @@ browser.tabs.onActivated.addListener(async (activeInfo) => { */ browser.windows.onFocusChanged.addListener(async (windowId) => { if (windowId != browser.windows.WINDOW_ID_NONE) { - console.log('recording a heartbeat - active window changed'); const tabs: browser.Tabs.Tab[] = await browser.tabs.query({ active: true, currentWindow: true, diff --git a/src/components/Options.tsx b/src/components/Options.tsx index 15de2ca..6dac946 100644 --- a/src/components/Options.tsx +++ b/src/components/Options.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import config, { SuccessOrFailType } from '../config/config'; -import { IS_CHROME } from '../utils'; import apiKeyInvalid from '../utils/apiKey'; +import { IS_CHROME } from '../utils/operatingSystem'; import { getSettings, saveSettings, Settings } from '../utils/settings'; import { logUserIn } from '../utils/user'; import SitesList from './SitesList'; diff --git a/src/core/WakaTimeCore.ts b/src/core/WakaTimeCore.ts index 0b39caf..0f253bd 100644 --- a/src/core/WakaTimeCore.ts +++ b/src/core/WakaTimeCore.ts @@ -5,9 +5,9 @@ import browser, { Tabs } from 'webextension-polyfill'; import moment from 'moment'; import { v4 as uuid4 } from 'uuid'; import { OptionalHeartbeat } from '../types/sites'; -import { getOperatingSystem, IS_EDGE, IS_FIREFOX } from '../utils'; import { changeExtensionStatus } from '../utils/changeExtensionStatus'; import getDomainFromUrl, { getDomain } from '../utils/getDomainFromUrl'; +import { getOperatingSystem, IS_EDGE, IS_FIREFOX } from '../utils/operatingSystem'; import { getSettings, Settings } from '../utils/settings'; import config, { ExtensionStatus } from '../config/config'; @@ -149,6 +149,7 @@ class WakaTimeCore { entity: heartbeat?.entity ?? entity, id: uuid4(), language: heartbeat?.language, + plugin: heartbeat?.plugin, project: heartbeat?.project ?? '<>', time: this.getCurrentTime(), type: heartbeat?.entityType ?? (settings.loggingType as EntityType), diff --git a/src/types/heartbeats.ts b/src/types/heartbeats.ts index 065bdf5..2fdc6f1 100644 --- a/src/types/heartbeats.ts +++ b/src/types/heartbeats.ts @@ -13,6 +13,7 @@ export enum Category { browsing = 'browsing', code_reviewing = 'code reviewing', coding = 'coding', + communicating = 'communicating', debugging = 'debugging', designing = 'designing', meeting = 'meeting', diff --git a/src/types/sites.ts b/src/types/sites.ts index 1a7e7d9..8e27eda 100644 --- a/src/types/sites.ts +++ b/src/types/sites.ts @@ -21,6 +21,7 @@ export interface OptionalHeartbeat { entity?: string; entityType?: EntityType; language?: string | null; + plugin?: string; project?: string | null; } diff --git a/src/utils/changeExtensionStatus.ts b/src/utils/changeExtensionStatus.ts index 62f6763..548f326 100644 --- a/src/utils/changeExtensionStatus.ts +++ b/src/utils/changeExtensionStatus.ts @@ -1,6 +1,6 @@ import browser from 'webextension-polyfill'; import config, { ExtensionStatus } from '../config/config'; -import { IS_FIREFOX } from '.'; +import { IS_FIREFOX } from './operatingSystem'; type ColorIconTypes = 'gray' | 'red' | 'white' | ''; diff --git a/src/utils/heartbeat.ts b/src/utils/heartbeat.ts deleted file mode 100644 index d4b0f79..0000000 --- a/src/utils/heartbeat.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { HeartbeatParser, KnownSite, SiteParser, StackExchangeSite } from '../types/sites'; -import { STACKEXCHANGE_SITES } from './stackexchange-sites'; - -const GitHub: HeartbeatParser = (url: string) => { - const { hostname } = new URL(url); - const match = url.match(/(?<=github\.(?:com|dev)\/[^/]+\/)([^/?#]+)/); - - if (match) { - if (hostname.endsWith('.com')) { - const root = parse(html); - const repoName = root - .querySelector('meta[name=octolytics-dimension-repository_nwo]') - ?.getAttribute('content'); - if (!repoName || repoName.split('/')[1] !== match[0]) { - return null; - } - } - return match[0]; - } - - return null; -}; - -const GitLab: HeartbeatParser = (url: string, html: string): string | null => { - const match = url.match(/(?<=gitlab\.com\/[^/]+\/)([^/?#]+)/); - - if (match) { - const root = parse(html); - const repoName = root.querySelector('body')?.getAttribute('data-project-full-path'); - if (!repoName || repoName.split('/')[1] !== match[0]) { - return null; - } - return match[0]; - } - - return null; -}; - -const BitBucket: HeartbeatParser = (url: string, html: string): string | null => { - const match = url.match(/(?<=bitbucket\.org\/[^/]+\/)([^/?#]+)/); - - if (match) { - const root = parse(html); - // this regex extracts the project name from the title - // eg. title: jhondoe / my-test-repo — Bitbucket - const match2 = root.querySelector('title')?.textContent.match(/(?<=\/\s)([^/\s]+)(?=\s—)/); - if (match2 && match2[0] === match[0]) { - return match[0]; - } - } - - return null; -}; - -const TravisCI: HeartbeatParser = (url: string, html: string): string | null => { - const match = url.match(/(?<=app\.travis-ci\.com\/[^/]+\/[^/]+\/)([^/?#]+)/); - - if (match) { - const root = parse(html); - const projectName = root.querySelector('#ember737')?.textContent; - if (projectName === match[0]) { - return match[0]; - } - } - - return null; -}; - -const CircleCI: HeartbeatParser = (url: string, html: string): string | null => { - const projectPageMatch = url.match( - /(?<=app\.circleci\.com\/projects\/[^/]+\/[^/]+\/[^/]+\/)([^/?#]+)/, - ); - - if (projectPageMatch) { - const root = parse(html); - const seconndBreadcrumbLabel = root.querySelector( - '#__next > div:nth-child(2) > div > div > main > div > header > div:nth-child(1) > ol > li:nth-child(2) > div > div > span', - )?.textContent; - const seconndBreadcrumbValue = root.querySelector( - '#__next > div:nth-child(2) > div > div > main > div > header > div:nth-child(1) > ol > li:nth-child(2) > div > span', - )?.textContent; - if (seconndBreadcrumbLabel === 'Project' && seconndBreadcrumbValue === projectPageMatch[0]) { - return projectPageMatch[0]; - } - } - - const settingsPageMatch = url.match( - /(?<=app\.circleci\.com\/settings\/project\/[^/]+\/[^/]+\/)([^/?#]+)/, - ); - if (settingsPageMatch) { - const root = parse(html); - const pageTitle = root.querySelector( - '#__next > div > div:nth-child(1) > header > div > div:nth-child(2) > h1', - )?.textContent; - const pageSubtitle = root.querySelector( - '#__next > div > div:nth-child(1) > header > div > div:nth-child(2) > div', - )?.textContent; - if (pageTitle === 'Project Settings' && pageSubtitle === settingsPageMatch[0]) { - return settingsPageMatch[0]; - } - } - - return null; -}; - -const Vercel: HeartbeatParser = (url: string, html: string): string | null => { - const match = url.match(/(?<=vercel\.com\/[^/]+\/)([^/?#]+)/); - - if (match) { - const root = parse(html); - // this regex extracts the project name from the title - // eg. title: test-website - Overview – Vercel - const match2 = root.querySelector('title')?.textContent.match(/^[^\s]+(?=\s-\s)/); - if (match2 && match2[0] === match[0]) { - return match[0]; - } - } - - return null; -}; - -const StackOverflow: HeartbeatParser = (url: string, html: string): string | null => { - const match = url.match(/(?<=vercel\.com\/[^/]+\/)([^/?#]+)/); - - if (match) { - const root = parse(html); - // this regex extracts the project name from the title - // eg. title: test-website - Overview – Vercel - const match2 = root.querySelector('title')?.textContent.match(/^[^\s]+(?=\s-\s)/); - if (match2 && match2[0] === match[0]) { - return match[0]; - } - } - - return null; -}; - -const CODE_REVIEW_URL_REG_LIST = [/github.com\/[^/]+\/[^/]+\/pull\/\d+\/files/]; - -export const isCodeReviewing = (url: string): boolean => { - for (const reg of CODE_REVIEW_URL_REG_LIST) { - if (url.match(reg)) { - return true; - } - } - return false; -}; - -export const getHtmlContentByTabId = async (tabId: number): Promise => { - const response = (await browser.tabs.sendMessage(tabId, { message: 'get_html' })) as { - html: string; - }; - return response.html; -}; - -const _normalizeUrl = (url?: string | null) => { - if (!url) { - return ''; - } - if (url.startsWith('http://')) { - url = url.substring('http://'.length); - } - if (url.startsWith('https://')) { - url = url.substring('https://'.length); - } - if (url.startsWith('www.')) { - url = url.substring('www.'.length); - } - if (url.endsWith('/')) { - url = url.substring(0, url.length - 1); - } - return url; -}; - -const stackExchangeDomains = (STACKEXCHANGE_SITES as StackExchangeSite[]).map((site) => { - return _normalizeUrl(site.site_url); -}); - -const SITES: Record = { - bitbucket: { - parser: BitBucket, - urls: [/^https?:\/\/(.+\.)?bitbucket.org\//], - }, - circleci: { - parser: CircleCI, - urls: [/^https?:\/\/(.+\.)?circleci.com\//], - }, - github: { - parser: GitHub, - urls: [ - /^https?:\/\/(.+\.)?github.com\//, - /^https?:\/\/(.+\.)?github.dev\//, - /^https?:\/\/(.+\.)?github.blog\//, - /^https?:\/\/(.+\.)?github.io\//, - /^https?:\/\/(.+\.)?github.community\//, - // /^https?:\/\/(.+\.)?ghcr.io\//, - // /^https?:\/\/(.+\.)?githubapp.com\//, - // /^https?:\/\/(.+\.)?githubassets.com\//, - // /^https?:\/\/(.+\.)?githubusercontent.com\//, - // /^https?:\/\/(.+\.)?githubnext.com\//, - ], - }, - gitlab: { - parser: GitLab, - urls: [/^https?:\/\/(.+\.)?gitlab.com\//], - }, - stackoverflow: { - parser: StackOverflow, - urls: stackExchangeDomains, - }, - travisci: { - parser: TravisCI, - urls: [/^https?:\/\/(.+\.)?travis-ci.com\//], - }, - vercel: { - parser: Vercel, - urls: [/^https?:\/\/(.+\.)?vercel.com\//], - }, -}; - -const match = (url: string, pattern: RegExp | string): boolean => { - if (typeof pattern === 'string') { - return _normalizeUrl(url).startsWith(_normalizeUrl(pattern)); - } - return pattern.test(url); -}; - -export const getSite = (url: string): SiteParser | undefined => { - return Object.values(SITES).find((site) => { - return site.urls.some((re) => match(url, re)); - }); -}; diff --git a/src/utils/index.ts b/src/utils/operatingSystem.ts similarity index 60% rename from src/utils/index.ts rename to src/utils/operatingSystem.ts index cb3cbbe..1c3f5b6 100644 --- a/src/utils/index.ts +++ b/src/utils/operatingSystem.ts @@ -2,17 +2,6 @@ export const IS_EDGE = navigator.userAgent.includes('Edg'); export const IS_FIREFOX = navigator.userAgent.includes('Firefox'); export const IS_CHROME = IS_EDGE === false && IS_FIREFOX === false; -const CODE_REVIEW_URL_REG_LIST = [/github.com\/[^/]+\/[^/]+\/pull\/\d+\/files/]; - -export const isCodeReviewing = (url: string): boolean => { - for (const reg of CODE_REVIEW_URL_REG_LIST) { - if (url.match(reg)) { - return true; - } - } - return false; -}; - export const getOperatingSystem = (): Promise => { return new Promise((resolve) => { chrome.runtime.getPlatformInfo(function (info) { diff --git a/src/utils/sites.ts b/src/utils/sites.ts new file mode 100644 index 0000000..6c1c912 --- /dev/null +++ b/src/utils/sites.ts @@ -0,0 +1,320 @@ +import { Category, EntityType } from '../types/heartbeats'; +import { + HeartbeatParser, + KnownSite, + OptionalHeartbeat, + SiteParser, + StackExchangeSite, +} from '../types/sites'; +import { STACKEXCHANGE_SITES } from './stackexchange-sites'; + +const GitHub: HeartbeatParser = (url: string) => { + const { hostname } = new URL(url); + const match = url.match(/(?<=github\.(?:com|dev)\/[^/]+\/)([^/?#]+)/); + if (!match) return; + + if (hostname.endsWith('.dev')) { + return { + project: match[0], + }; + } + + const body = document.getElementsByTagName('body').item(0); + if (!body) return; + + const repo = body + .querySelector('meta[name=octolytics-dimension-repository_nwo]') + ?.getAttribute('content'); + if (repo?.split('/')[1] !== match[0]) return; + + // TODO: parse language associated with this repo from the DOM + // TODO: parse branch associated with the PR url from the DOM + + const re = new RegExp(/github.com\/[^/]+\/[^/]+\/pull\/\d+\/files/); + const category: Category | undefined = re.test(url) ? Category.code_reviewing : undefined; + + return { + category, + project: match[0], + }; +}; + +const GitLab: HeartbeatParser = (url: string) => { + const match = url.match(/(?<=gitlab\.com\/[^/]+\/)([^/?#]+)/); + if (!match) return; + + const repoName = document.querySelector('body')?.getAttribute('data-project-full-path'); + if (!repoName || repoName.split('/')[1] !== match[0]) return; + + return { + project: match[0], + }; +}; + +const BitBucket: HeartbeatParser = (url: string) => { + const match = url.match(/(?<=bitbucket\.org\/[^/]+\/)([^/?#]+)/); + if (!match) return; + + // this regex extracts the project name from the title + // eg. title: jhondoe / my-test-repo — Bitbucket + const match2 = document.querySelector('title')?.textContent?.match(/(?<=\/\s)([^/\s]+)(?=\s—)/); + if (!match2 || match2[0] !== match[0]) return; + + return { + project: match[0], + }; +}; + +const TravisCI: HeartbeatParser = (url: string) => { + const match = url.match(/(?<=app\.travis-ci\.com\/[^/]+\/[^/]+\/)([^/?#]+)/); + if (!match) return; + + const projectName = document.querySelector('#ember737')?.textContent; + if (projectName !== match[0]) return; + + return { + project: match[0], + }; +}; + +const CircleCI: HeartbeatParser = (url: string) => { + const projectPageMatch = url.match( + /(?<=app\.circleci\.com\/projects\/[^/]+\/[^/]+\/[^/]+\/)([^/?#]+)/, + ); + + if (projectPageMatch) { + const seconndBreadcrumbLabel = document.querySelector( + '#__next > div:nth-child(2) > div > div > main > div > header > div:nth-child(1) > ol > li:nth-child(2) > div > div > span', + )?.textContent; + const seconndBreadcrumbValue = document.querySelector( + '#__next > div:nth-child(2) > div > div > main > div > header > div:nth-child(1) > ol > li:nth-child(2) > div > span', + )?.textContent; + if (seconndBreadcrumbLabel === 'Project' && seconndBreadcrumbValue === projectPageMatch[0]) { + return { project: projectPageMatch[0] }; + } + } + + const settingsPageMatch = url.match( + /(?<=app\.circleci\.com\/settings\/project\/[^/]+\/[^/]+\/)([^/?#]+)/, + ); + if (settingsPageMatch) { + const pageTitle = document.querySelector( + '#__next > div > div:nth-child(1) > header > div > div:nth-child(2) > h1', + )?.textContent; + const pageSubtitle = document.querySelector( + '#__next > div > div:nth-child(1) > header > div > div:nth-child(2) > div', + )?.textContent; + if (pageTitle === 'Project Settings' && pageSubtitle === settingsPageMatch[0]) { + return { project: settingsPageMatch[0] }; + } + } + + return undefined; +}; + +const Vercel: HeartbeatParser = (url: string) => { + const match = url.match(/(?<=vercel\.com\/[^/]+\/)([^/?#]+)/); + if (!match) return; + + // this regex extracts the project name from the title + // eg. title: test-website - Overview – Vercel + const match2 = document.querySelector('title')?.textContent?.match(/^[^\s]+(?=\s-\s)/); + if (!match2 || match2[0] !== match[0]) return; + + return { project: match[0] }; +}; + +const StackOverflow: HeartbeatParser = (_url: string) => { + const tags = Array.from(document.querySelectorAll('.post-tag').values()) + .map((el) => el.textContent) + .filter(Boolean) as string[]; + if (tags.length === 0) return; + + const languages = Array.from(document.querySelectorAll('code[data-highlighted="yes"]')) + .map((code) => { + const cls = Array.from(code.classList.values()).find((c) => c.startsWith('language-')); + return cls?.substring('language-'.length); + }) + .filter(Boolean) as string[]; + + for (const lang of languages) { + if (tags.includes(lang)) { + return { language: lang }; + } + } + + return undefined; +}; + +const Canva: HeartbeatParser = (_url: string): OptionalHeartbeat | undefined => { + const projectName = (document.head.querySelector('meta[property="og:title"]') as HTMLMetaElement) + .content; + if (!projectName) return; + + // make sure the page title matches the design input element's value, meaning this is a design file + const canvaProjectInput = Array.from( + document.querySelector('nav')?.querySelectorAll('input') ?? [], + ).find((inp) => inp.value === projectName); + if (!canvaProjectInput) return; + + return { + category: Category.designing, + language: 'Image (svg)', + plugin: 'Canva', + project: projectName, + }; +}; + +const Figma: HeartbeatParser = (_url: string): OptionalHeartbeat | undefined => { + const figmaProject = document.getElementsByClassName('gpu-view-content'); + if (figmaProject.length === 0) return; + + const project = (document.querySelector('span[data-testid="filename"]') as HTMLElement).innerText; + return { + category: Category.designing, + language: 'Image (svg)', + plugin: 'Figma', + project, + }; +}; + +const GoogleMeet: HeartbeatParser = (_url: string): OptionalHeartbeat | undefined => { + const meetId = document.querySelector('[data-meeting-title]')?.getAttribute('data-meeting-title'); + if (!meetId) return; + + return { + category: Category.meeting, + plugin: 'Google Meet', + project: meetId, + }; +}; + +const Slack: HeartbeatParser = (_url: string): OptionalHeartbeat | undefined => { + const title = document.querySelector('title')?.textContent?.split(' - '); + if (!title || title.length < 3 || title[-1] !== 'Slack') { + return { + category: Category.communicating, + plugin: 'Slack', + }; + } + + const entity = title[0]; + const project = title[1]; + + return { + category: Category.communicating, + entity, + entityType: EntityType.app, + plugin: 'Slack', + project, + }; +}; + +const Zoom: HeartbeatParser = (_url: string): OptionalHeartbeat | undefined => { + const entity = document.querySelector('title')?.textContent; + + return { + category: Category.communicating, + entity: entity ?? undefined, + entityType: entity ? EntityType.app : undefined, + plugin: 'Zoom', + }; +}; + +const _normalizeUrl = (url?: string | null) => { + if (!url) { + return ''; + } + if (url.startsWith('http://')) { + url = url.substring('http://'.length); + } + if (url.startsWith('https://')) { + url = url.substring('https://'.length); + } + if (url.startsWith('www.')) { + url = url.substring('www.'.length); + } + if (url.endsWith('/')) { + url = url.substring(0, url.length - 1); + } + return url; +}; + +const stackExchangeDomains = (STACKEXCHANGE_SITES as StackExchangeSite[]).map((site) => { + return _normalizeUrl(site.site_url); +}); + +const SITES: Record = { + bitbucket: { + parser: BitBucket, + urls: [/^https?:\/\/(.+\.)?bitbucket.org\//], + }, + canva: { + parser: Canva, + urls: ['canva.com'], + }, + circleci: { + parser: CircleCI, + urls: [/^https?:\/\/(.+\.)?circleci.com\//], + }, + figma: { + parser: Figma, + urls: ['figma.com'], + }, + github: { + parser: GitHub, + urls: [ + /^https?:\/\/(.+\.)?github.com\//, + /^https?:\/\/(.+\.)?github.dev\//, + /^https?:\/\/(.+\.)?github.blog\//, + /^https?:\/\/(.+\.)?github.io\//, + /^https?:\/\/(.+\.)?github.community\//, + // /^https?:\/\/(.+\.)?ghcr.io\//, + // /^https?:\/\/(.+\.)?githubapp.com\//, + // /^https?:\/\/(.+\.)?githubassets.com\//, + // /^https?:\/\/(.+\.)?githubusercontent.com\//, + // /^https?:\/\/(.+\.)?githubnext.com\//, + ], + }, + gitlab: { + parser: GitLab, + urls: [/^https?:\/\/(.+\.)?gitlab.com\//], + }, + googlemeet: { + parser: GoogleMeet, + urls: [/^https?:\/\/meet.google.com\//], + }, + slack: { + parser: Slack, + urls: [/^https:\/\/app.slack.com\/client\//], + }, + stackoverflow: { + parser: StackOverflow, + urls: stackExchangeDomains, + }, + travisci: { + parser: TravisCI, + urls: [/^https?:\/\/(.+\.)?travis-ci.com\//], + }, + vercel: { + parser: Vercel, + urls: [/^https?:\/\/(.+\.)?vercel.com\//], + }, + zoom: { + parser: Zoom, + urls: [/^https:\/\/(.+\.)?zoom.us\/[^?]+\/join/], + }, +}; + +const match = (url: string, pattern: RegExp | string): boolean => { + if (typeof pattern === 'string') { + return _normalizeUrl(url).startsWith(_normalizeUrl(pattern)); + } + return pattern.test(url); +}; + +export const getSite = (url: string): SiteParser | undefined => { + return Object.values(SITES).find((site) => { + return site.urls.some((re) => match(url, re)); + }); +}; diff --git a/src/wakatimeScript.ts b/src/wakatimeScript.ts index 3fe09f5..f8c92b7 100644 --- a/src/wakatimeScript.ts +++ b/src/wakatimeScript.ts @@ -1,61 +1,8 @@ -import { getSite } from './utils/heartbeat'; +import { getSite } from './utils/sites'; const oneMinute = 60000; const fiveMinutes = 300000; -interface DesignProject { - category: string; - editor: string; - language: string; - project: string; -} - -const parseCanva = (): DesignProject | undefined => { - const projectName = (document.head.querySelector('meta[property="og:title"]') as HTMLMetaElement) - .content; - if (!projectName) return; - - // make sure the page title matches the design input element's value, meaning this is a design file - const canvaProjectInput = Array.from( - document.querySelector('nav')?.querySelectorAll('input') ?? [], - ).find((inp) => inp.value === projectName); - if (!canvaProjectInput) return; - - return { - category: 'designing', - editor: 'Canva', - language: 'Canva Design', - project: projectName, - }; -}; - -const parseFigma = (): DesignProject | undefined => { - const figmaProject = document.getElementsByClassName('gpu-view-content'); - if (figmaProject.length === 0) return; - - const projectName = (document.querySelector('span[data-testid="filename"]') as HTMLElement) - .innerText; - return { - category: 'designing', - editor: 'Figma', - language: 'Figma Design', - project: projectName, - }; -}; - -const parseMeet = (): DesignProject | undefined => { - const meetId = document.querySelector('[data-meeting-title]')?.getAttribute('data-meeting-title'); - if (!meetId) { - return; - } - return { - category: 'meeting', - editor: 'Meet', - language: 'Google Meet', - project: meetId, - }; -}; - /** * Debounces the execution of a function. * @@ -112,6 +59,6 @@ const checkIfInAMeeting = () => { }; // Google Meet -if (window.location.href.startsWith('https://meet.google.com')) { +if (window.location.href.startsWith('https://meet.google.com/')) { checkIfInAMeeting(); }