diff --git a/src/scripts/background/install.ts b/src/scripts/background/install.ts index 4dae5aa..f8357a6 100644 --- a/src/scripts/background/install.ts +++ b/src/scripts/background/install.ts @@ -2,6 +2,7 @@ import { setLocal } from '../lib/chromeStorage'; import { checkIfUpdateNeeded } from './database'; import { donationReminderAllowed } from './donation'; import { initializePageAction } from './pageAction'; +import { DEFAULT_LIST_STYLE } from "../constants"; export async function handleExtensionInstalled(reason:chrome.runtime.InstalledDetails): Promise { const donationAllowed = donationReminderAllowed(navigator.userAgent); @@ -13,6 +14,7 @@ export async function handleExtensionInstalled(reason:chrome.runtime.InstalledDe active: false, allowedPlattform: donationAllowed, }, + pointListStyle: DEFAULT_LIST_STYLE }); await checkIfUpdateNeeded(true, reason); diff --git a/src/scripts/constants.ts b/src/scripts/constants.ts index 9cd109e..6c14641 100644 --- a/src/scripts/constants.ts +++ b/src/scripts/constants.ts @@ -12,4 +12,6 @@ export const API_HEADERS = { apikey: atob('Y29uZ3JhdHMgb24gZ2V0dGluZyB0aGUga2V5IDpQ'), }; -export const SUPPORTED_LANGUAGES = ['en', 'de', 'nl', 'fr', 'es'] as const; \ No newline at end of file +export const SUPPORTED_LANGUAGES = ['en', 'de', 'nl', 'fr', 'es'] as const; + +export const DEFAULT_LIST_STYLE :"docCategories" | "unified" = "docCategories" as const; diff --git a/src/scripts/views/popup/service.ts b/src/scripts/views/popup/service.ts index 184cd6c..1b3fb79 100644 --- a/src/scripts/views/popup/service.ts +++ b/src/scripts/views/popup/service.ts @@ -1,19 +1,27 @@ -import { getApiUrl, getLanguage, isCuratorMode } from './state'; +import { getApiUrl, getLanguage, isCuratorMode, getPointListStyle } from './state'; import { applyHeaderColor } from './theme'; interface ServicePoint { status: string; title: string; - case?: { + case: { classification?: string; localized_title?: string | null; }; + document_id?: number +} + +interface ServiceDocument { + id: number + name: string + url: string } interface ServiceResponse { name: string; rating?: string; points: ServicePoint[]; + documents: ServiceDocument[] } interface SearchResponse { @@ -23,6 +31,13 @@ interface SearchResponse { }>; } +interface FilteredPoints { + blocker: ServicePoint[]; + bad: ServicePoint[]; + good: ServicePoint[]; + neutral: ServicePoint[]; +} + export async function displayServiceDetails( id: string, options: { unverified?: boolean } = {} @@ -52,7 +67,17 @@ export async function displayServiceDetails( updatePointsCount(data.points.length); revealLoadedState(options.unverified === true); - populateList(data.points); + const pointListStyle = getPointListStyle() + + if (pointListStyle === "docCategories") { + populateListDocCategories(data.points, data.documents); + } else if (pointListStyle === "unified") { + populateListUnified(data.points) + } else { + console.error("Unsupported pointListStyle", pointListStyle); + } + + } catch (error) { hideLoadingState(); showErrorOverlay( @@ -164,7 +189,19 @@ function revealLoadedState(unverified: boolean): void { } } -function populateList(points: ServicePoint[]): void { +function populateListUnified(allPoints: ServicePoint[]) { + const documentList = document.getElementById('documentList'); + const doc = document.createElement('div'); + const temp = ` +
+
+ ... +
+
` + ; + doc.innerHTML = temp.trim(); + documentList!.appendChild(doc.firstChild!); + const pointsList = document.getElementById('pointList'); if (!pointsList) { return; @@ -173,79 +210,162 @@ function populateList(points: ServicePoint[]): void { pointsList.style.display = 'block'; pointsList.innerHTML = ''; - const filteredPoints = filterPoints(points); - - appendPointGroup(filteredPoints.blocker, pointsList, false); - appendPointGroup(filteredPoints.bad, pointsList, false); - appendPointGroup(filteredPoints.good, pointsList, false); - appendPointGroup(filteredPoints.neutral, pointsList, true); -} + const filteredPoints = filterPoints(allPoints); -function filterPoints(points: ServicePoint[]): { - blocker: ServicePoint[]; - bad: ServicePoint[]; - good: ServicePoint[]; - neutral: ServicePoint[]; -} { - const curatedPoints = points.filter((point) => { - if (!isCuratorMode()) { - return point.status === 'approved'; - } - return point.status === 'approved' || point.status === 'pending'; - }); - - return { - blocker: curatedPoints.filter( - (point) => point.case?.classification === 'blocker' - ), - bad: curatedPoints.filter( - (point) => point.case?.classification === 'bad' - ), - good: curatedPoints.filter( - (point) => point.case?.classification === 'good' - ), - neutral: curatedPoints.filter( - (point) => point.case?.classification === 'neutral' - ), - }; + createPointList(filteredPoints.blocker, pointsList, false); + createPointList(filteredPoints.bad, pointsList, false); + createPointList(filteredPoints.good, pointsList, false); + createPointList(filteredPoints.neutral, pointsList, true); } -function appendPointGroup( - points: ServicePoint[], - container: HTMLElement, - isLastGroup: boolean -): void { - let added = 0; - points.forEach((point, index) => { - const wrapper = document.createElement('div'); - const classification = point.case?.classification ?? 'neutral'; - const pointTitle = point.case?.localized_title ?? point.title; - wrapper.innerHTML = ` -
- -

${pointTitle}

- ${renderCuratorTag(point.status)} +function populateListDocCategories(allPoints: ServicePoint[], documents: ServiceDocument[]) { + const documentList = document.getElementById('documentList'); + //sort docuements alphabetically + try { + documents.sort((a, b) => + a.name.localeCompare(b.name) + ) + } catch (error) { + console.warn(error) + } + console.log(documents) + // Split points by Document and display them seperatly + for (let i of documents) { + const element = i; + + const docPoints = allPoints.filter((point:ServicePoint) => point.document_id === element.id) + const sortedPoints = filterPoints(docPoints) + + if (sortedPoints.blocker.length + sortedPoints.bad.length + sortedPoints.neutral.length + sortedPoints.good.length > 0) { + const doc = document.createElement('div'); + const temp = ` +
+
+

${element.name}

+ Read Original> +
+
+ ... +
+
`; + doc.innerHTML = temp.trim(); + documentList!.appendChild(doc.firstChild!); + + const pointsList = document.getElementById(`pointList_${element.id}`)! + + createSortetPoints(sortedPoints,pointsList) + } else { //documents without points + const docsWithoutPointsWraper = document.getElementById('docsWithoutPointsWraper') + const docsWithoutPoints = document.getElementById('docsWithoutPoints') + + if (docsWithoutPoints?.style.display === "none") { + docsWithoutPoints.style.display = "block" + } + const doc = document.createElement('div'); + const temp = ` +
+

${element.name}

+ Read Original> +
`; + doc.innerHTML = temp.trim(); + docsWithoutPointsWraper!.appendChild(doc.firstChild!); + } + } + //display points not liked to a document + const noDocPoints = allPoints.filter((point: ServicePoint) => point.document_id === null) + if (noDocPoints.length > 0) { + const doc = document.createElement('div'); + const temp = ` +
+
+

Points not linked to a Document

- `.trim(); - if (wrapper.firstChild) { - container.appendChild(wrapper.firstChild as HTMLElement); +
+ ... +
+
`; + doc.innerHTML = temp.trim(); + documentList!.appendChild(doc.firstChild!); + const sortedPoints = filterPoints(noDocPoints) + const pointsList = document.getElementById(`pointList_unlinkedPoints`)! + createSortetPoints(sortedPoints,pointsList) + + } +} +function filterPoints(points:ServicePoint[]) { + if (isCuratorMode()) { + points = points.filter( + (point) => + point.status === 'approved' || point.status === 'pending' + ); + } else { + points = points.filter((point) => point.status === 'approved'); } - added += 1; + let filteredPoints:FilteredPoints = { + blocker: [], + bad: [], + good: [], + neutral: [] + } + filteredPoints.blocker = points.filter( + (point) => point.case.classification === 'blocker' + ); + filteredPoints.bad = points.filter( + (point) => point.case.classification === 'bad' + ); + filteredPoints.good = points.filter( + (point) => point.case.classification === 'good' + ); + filteredPoints.neutral = points.filter( + (point) => point.case.classification === 'neutral' + ); + return filteredPoints +} + +function createSortetPoints(sortedPoints:FilteredPoints,pointsList:HTMLElement) { + if (sortedPoints.blocker) { + createPointList(sortedPoints.blocker, pointsList, false); + } + if (sortedPoints.bad) { + createPointList(sortedPoints.bad, pointsList, false); + } + if (sortedPoints.good) { + createPointList(sortedPoints.good, pointsList, false); + } + if (sortedPoints.neutral) { + createPointList(sortedPoints.neutral, pointsList, true); + } +} - if (index !== points.length - 1) { +function createPointList(pointsFiltered: ServicePoint[], pointsList: HTMLElement, last: boolean) { + let added = 0; + for (let i = 0; i < pointsFiltered.length; i++) { + const point = document.createElement('div'); + const pointTitle = pointsFiltered[i]!.case?.localized_title ?? pointsFiltered[i]!.title; + + let temp = ` +
+ +

${pointTitle}

+ ${renderCuratorTag(pointsFiltered[i]!.status)} +
`; + point.innerHTML = temp.trim(); + pointsList.appendChild(point.firstChild!); + added++; + if (i !== pointsFiltered.length - 1) { const divider = document.createElement('hr'); - container.appendChild(divider); + pointsList.appendChild(divider); } - }); - - if (added > 0 && !isLastGroup) { + } + if (added !== 0 && !last) { const divider = document.createElement('hr'); divider.classList.add('group'); - container.appendChild(divider); + pointsList.appendChild(divider); } } + function renderCuratorTag(status: string): string { if (!isCuratorMode() || status === 'approved') { return ''; diff --git a/src/scripts/views/popup/state.ts b/src/scripts/views/popup/state.ts index f1e1ed2..6cf6df0 100644 --- a/src/scripts/views/popup/state.ts +++ b/src/scripts/views/popup/state.ts @@ -1,4 +1,4 @@ -import { DEFAULT_API_URL } from '../../constants'; +import { DEFAULT_API_URL} from '../../constants'; import { getLocal } from '../../lib/chromeStorage'; import { SupportedLanguage, @@ -8,11 +8,13 @@ import { let curatorMode = false; let apiUrl = DEFAULT_API_URL; let language: SupportedLanguage = 'en'; +let pointListStyle:"docCategories" | "unified" = "unified" export interface PopupPreferences { darkmode: boolean; curatorMode: boolean; language: SupportedLanguage; + pointListStyle:"docCategories" | "unified" } export function isCuratorMode(): boolean { @@ -31,11 +33,16 @@ export function setApiUrl(url: string): void { apiUrl = url; } +export function getPointListStyle() { + return pointListStyle +} + export async function hydrateState(): Promise { - const result = await getLocal(['darkmode', 'curatorMode', 'api', 'language']); + const result = await getLocal(['darkmode', 'curatorMode', 'api', 'language', 'pointListStyle']); const darkmode = Boolean(result['darkmode']); const storedCuratorMode = Boolean(result['curatorMode']); + pointListStyle = result['pointListStyle'] as "docCategories" | "unified" setCuratorMode(storedCuratorMode); const api = result['api']; @@ -52,6 +59,7 @@ export async function hydrateState(): Promise { darkmode, curatorMode: storedCuratorMode, language: resolvedLanguage, + pointListStyle, }; } diff --git a/src/scripts/views/settings/handlers.ts b/src/scripts/views/settings/handlers.ts index a32ddc0..63e7c83 100644 --- a/src/scripts/views/settings/handlers.ts +++ b/src/scripts/views/settings/handlers.ts @@ -8,6 +8,8 @@ export function registerSettingsHandlers(): void { const curatorModeInput = document.getElementById('curatorMode') as HTMLInputElement | null; const apiInput = document.getElementById('api') as HTMLInputElement | null; const languageSelect = document.getElementById('language') as HTMLSelectElement | null; + const pointListStyleSelect = document.getElementById('pointListStyle') as HTMLSelectElement | null; + if (updateInput) { updateInput.addEventListener('change', () => { @@ -52,4 +54,14 @@ export function registerSettingsHandlers(): void { void setLocal({ language: normalized }); }); } + + if (pointListStyleSelect) { + pointListStyleSelect.addEventListener('change', () => { + const normalized = pointListStyleSelect.value ?? 'docCategories'; + if (pointListStyleSelect.value !== normalized) { + pointListStyleSelect.value = normalized; + } + void setLocal({ pointListStyle: normalized }); + }); + } } diff --git a/src/scripts/views/settings/state.ts b/src/scripts/views/settings/state.ts index a86d711..8547a88 100644 --- a/src/scripts/views/settings/state.ts +++ b/src/scripts/views/settings/state.ts @@ -13,6 +13,7 @@ export async function populateSettingsForm(): Promise { 'sentry', 'api', 'language', + 'pointListStyle' ]); if (Array.isArray(result['db'])) { @@ -56,6 +57,9 @@ export async function populateSettingsForm(): Promise { const language = resolveLanguage(result['language']); elements.languageSelect.value = language; } + if (elements.pointListStyle) { + elements.pointListStyle.value = String(result['pointListStyle']); + } } function collectElements() { @@ -69,6 +73,7 @@ function collectElements() { date: document.getElementById('date') as HTMLElement | null, indexed: document.getElementById('indexed') as HTMLElement | null, days: document.getElementById('days') as HTMLElement | null, + pointListStyle: document.getElementById('pointListStyle') as HTMLSelectElement | null }; } diff --git a/src/views/popup.html b/src/views/popup.html index a695411..18cc688 100644 --- a/src/views/popup.html +++ b/src/views/popup.html @@ -99,9 +99,15 @@

Points for ...:

-
+
+
+
+
+ icon displaying Bulletpoints +

Summary Style

+
+

+ How points are displayed in the popup +

+
+ +
+
+

Database Settings

diff --git a/src/views/style/popup.css b/src/views/style/popup.css index 438c439..8aaab1f 100644 --- a/src/views/style/popup.css +++ b/src/views/style/popup.css @@ -1,8 +1,8 @@ @font-face { font-family: 'Open Sans'; src: - url('fonts/OpenSans-Regular.woff2') format('woff2'), - url('fonts/OpenSans-Regular.woff') format('woff'); + url('../fonts/OpenSans-Regular.woff2') format('woff2'), + url('../fonts/OpenSans-Regular.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap; @@ -270,27 +270,47 @@ h3 { margin: 0; } -#pointList > hr { +.pointList > hr { border: none; height: 1px; width: 90%; background-color: #e7e7e7; } -#pointList > hr.group { +.pointList > hr.group { border: none; height: 1px; width: 90%; background-color: #cccbcb; } -#pointList { +.pointList { margin: 0.5rem; padding: 0.4rem; background-color: #fefefe; border-radius: 1rem; } +.documentHeader { + display: flex; + justify-content: space-around; + margin-bottom: -1.1rem; +} +.documentHeader > a { + display: flex; + align-items: center; + text-decoration: none; + color: #04040461; + font-size: small; +} +.documentHeader > a:hover{ + text-decoration: underline; + +} +.documentHeader > h3 { + all:revert +} + button { border: none; background-color: transparent; @@ -324,18 +344,22 @@ button { ); } -.dark-mode #pointList { +.dark-mode .pointList { background-color: #1c1c1e; } -.dark-mode #pointList > hr.group { +.dark-mode .pointList > hr.group { background-color: #232323; } -.dark-mode #pointList > hr { +.dark-mode .pointList > hr { background-color: #1b1b1d; } +.dark-mode .documentHeader a { + color: #cccbcb; +} + #toggleButton, #settingsButton, #sourceButton {