diff --git a/locales/de.json b/locales/de.json index e985d80..1f767cf 100644 --- a/locales/de.json +++ b/locales/de.json @@ -115,5 +115,12 @@ }, "incognito": { "title": "Inkognito Modus bis {{time}}" + }, + "communityNotes": { + "title": "Community Note", + "rate": "Diese Notiz bewerten", + "add": "Notiz schreiben", + "readMore": "Mehr lesen...", + "none": "keine" } } diff --git a/locales/en.json b/locales/en.json index c2b95c5..d5b0ca8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -115,5 +115,12 @@ }, "incognito": { "title": "Incognito mode until {{time}}" + }, + "communityNotes": { + "title": "Community Note", + "rate": "Rate this note", + "add": "Write a note", + "readMore": "Read more...", + "none": "none" } } diff --git a/src/common/bridge.js b/src/common/bridge.js index e08d2ee..cea7fd2 100644 --- a/src/common/bridge.js +++ b/src/common/bridge.js @@ -130,6 +130,15 @@ export async function setTheme({ isDark }) { ); } +// Community Notes + +export async function getCommunityNotes({ videoUrl }) { + return sendMessageToBackground( + messages.COMMUNITY_NOTES_GET, + { videoUrl }, + ); +} + // ------------------------------------------------------------------------------------------------------------------------ // Hooks // ------------------------------------------------------------------------------------------------------------------------ @@ -208,3 +217,16 @@ export function useVideoAnalytics({ videoUrl, accountIds }) { data: query.data?.data, }; } + +export function useCommunityNotes({ videoUrl }) { + const query = useQuery({ + queryKey: ['community-notes', videoUrl], + queryFn: () => getCommunityNotes({ videoUrl }), + enabled: !!videoUrl, + }); + + return { + ...query, + data: query.data?.data, + }; +} diff --git a/src/entries/background/common/api.js b/src/entries/background/common/api.js index e610c41..ee0e51f 100644 --- a/src/entries/background/common/api.js +++ b/src/entries/background/common/api.js @@ -207,6 +207,17 @@ export async function getVideoAnalytics({ videoUrl, accountIds }) { return analytics; } +export async function getCommunityNotes({ videoUrl }) { + const { data: notes } = await api('community/notes/for-video', { + token: await storageGetToken(), + query: { + video_url: videoUrl, + }, + }); + + return notes; +} + export async function updateUserIncognitoMode({ length }) { const { data } = await api('extension/visibility', { method: 'POST', diff --git a/src/entries/background/common/controllers.js b/src/entries/background/common/controllers.js index 07644ee..c31eb06 100644 --- a/src/entries/background/common/controllers.js +++ b/src/entries/background/common/controllers.js @@ -29,6 +29,7 @@ async function getResponse(type, data) { [messages.REACTIONS_GET_FOR_VIDEO]: api.getReactionsForVideo, [messages.REACTIONS_GET_ORIGINAL_VIDEOS]: api.getOriginalVideosForVideo, [messages.VIDEO_ANALYTICS_GET]: api.getVideoAnalytics, + [messages.COMMUNITY_NOTES_GET]: api.getCommunityNotes, [messages.TOGGLE_INCOGNITO_MODE]: api.updateUserIncognitoMode, }; diff --git a/src/entries/contentScript/App.jsx b/src/entries/contentScript/App.jsx index a91fa09..299305b 100644 --- a/src/entries/contentScript/App.jsx +++ b/src/entries/contentScript/App.jsx @@ -24,6 +24,7 @@ import { useBackgroundEvents } from '~/entries/contentScript/hooks/useBackground import AnalyticsNotice from '~/entries/contentScript/components/AnalyticsNotice'; import Footer from '~/entries/contentScript/components/Footer'; import { storageGetCompact, storageSetCompact } from '~/entries/background/common/storage'; +import CommunityNoteNotice from '~/entries/contentScript/components/CommunityNoteNotice'; function App() { const { t, i18n } = useTranslation(); @@ -156,6 +157,8 @@ function App() { + + {state !== STATE_LIVE && ( )} diff --git a/src/entries/contentScript/components/Card.jsx b/src/entries/contentScript/components/Card.jsx index 4cf33b6..be72ead 100644 --- a/src/entries/contentScript/components/Card.jsx +++ b/src/entries/contentScript/components/Card.jsx @@ -41,6 +41,7 @@ function Card({ green: 'bg-[#01FF94]', // TODO red: 'bg-[#FF5C00]', yellow: 'bg-[#FFA800]', + dashed: 'border-2 border-dashed border-gray-200 dark:border-gray-600', // Brand primary: 'bg-gradient-to-r from-primary-gradient-from to-primary-gradient-to', 'brand-viewer': 'bg-gradient-to-r from-brand-viewer-gradient-from to-brand-viewer-gradient-to', @@ -129,6 +130,7 @@ Card.propTypes = { 'green', 'red', 'yellow', + 'dashed', 'brand-viewer', 'brand-creator', 'primary', diff --git a/src/entries/contentScript/components/CommunityNoteNotice.jsx b/src/entries/contentScript/components/CommunityNoteNotice.jsx new file mode 100644 index 0000000..3b07d59 --- /dev/null +++ b/src/entries/contentScript/components/CommunityNoteNotice.jsx @@ -0,0 +1,104 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import classNames from 'classnames'; +import Card from '~/entries/contentScript/components/Card'; +import { useAppStore } from '~/entries/contentScript/state'; +import { useCommunityNotes } from '~/common/bridge'; +import { usePage } from '~/hooks/usePage'; +import { buildFrontendUrl } from '~/common/utility'; +import { strLimit } from '~/common/pretty'; + +function CommunityNoteNotice() { + const { t } = useTranslation(); + const compact = useAppStore((state) => state.isCompact); + + const { currentUrl } = usePage(); + const [isExpanded, setIsExpanded] = useState(false); + + const { data: notes } = useCommunityNotes({ videoUrl: currentUrl }); + + const writeNoteLink = ( + + {t('communityNotes.add')} + + ); + + if (!notes?.length) { + return ( + + {writeNoteLink} + + ); + } + + const note = notes[0]; + const expandable = note.content.length > 100; + + return ( + + + + ); +} + +CommunityNoteNotice.propTypes = {}; + +export default CommunityNoteNotice; diff --git a/src/hooks/usePage.js b/src/hooks/usePage.js index 1160373..41316d0 100644 --- a/src/hooks/usePage.js +++ b/src/hooks/usePage.js @@ -57,11 +57,6 @@ export function usePage() { log.debug('---> new channel', channelInfo); setCurrentChannel(channelInfo); } - - console.log( - document.querySelector('#above-the-fold ytd-channel-name yt-formatted-string a'), - document.querySelector('span[itemprop="name"] link[itemprop="url"]'), - ); } if (!hasChannel) { diff --git a/src/messages.js b/src/messages.js index 9241fdb..bdb9b2c 100644 --- a/src/messages.js +++ b/src/messages.js @@ -13,6 +13,7 @@ export const REACTIONS_GET_FOR_VIDEO = 'REACTIONS_GET_FOR_VIDEO'; export const REACTIONS_GET_ORIGINAL_VIDEOS = 'REACTIONS_GET_ORIGINAL_VIDEOS'; export const CONTENT_RATINGS_GET = 'CONTENT_RATINGS_GET'; export const VIDEO_ANALYTICS_GET = 'VIDEO_ANALYTICS_GET'; +export const COMMUNITY_NOTES_GET = 'COMMUNITY_NOTES_GET'; export const SETTING_UPDATE_VISIBLE = 'SETTING_UPDATE_VISIBLE'; export const OPEN_POPUP = 'OPEN_POPUP'; export const SET_BROWSER_THEME = 'SET_BROWSER_THEME';