From 5da4c02bc35acc95aaa9257d5512545ff85d34a7 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 12 Oct 2024 20:50:12 +0200 Subject: [PATCH 01/16] feat(sidebar): add info next to red dot for offline subs without last update timestamp --- src/components/sidebar/sidebar.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index c066faa9..d68e21c7 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -11,6 +11,7 @@ import SearchBar from '../search-bar'; import SubscribeButton from '../subscribe-button'; import packageJson from '../../../package.json'; import LoadingEllipsis from '../loading-ellipsis'; +import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; const { version } = packageJson; const commitRef = process.env.REACT_APP_COMMIT_REF; @@ -135,10 +136,10 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit const { cid, downvoteCount, timestamp, upvoteCount } = comment || {}; const { allActiveUserCount, hourActiveUserCount } = useSubplebbitStats({ subplebbitAddress: address }); - const isOnline = updatedAt && updatedAt > Date.now() / 1000 - 60 * 60; + const { isOffline, offlineTitle } = useIsSubplebbitOffline(subplebbit || {}); const onlineNotice = t('users_online', { count: hourActiveUserCount }); - const offlineNotice = updatedAt && t('posts_last_synced', { dateAgo: getFormattedTimeAgo(updatedAt) }); - const onlineStatus = isOnline ? onlineNotice : offlineNotice; + const offlineNotice = updatedAt ? t('posts_last_synced', { dateAgo: getFormattedTimeAgo(updatedAt) }) : offlineTitle; + const onlineStatus = !isOffline ? onlineNotice : offlineNotice; const subCreatedButNotYetPublishedStatus = ; @@ -217,7 +218,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit {t('members_count', { count: allActiveUserCount })}
- + {isSubCreatedButNotYetPublished ? subCreatedButNotYetPublishedStatus : onlineStatus}
{description && ( From b3bbb9c04d88a75d2956d606a8023bf6120964e9 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 12 Oct 2024 21:27:48 +0200 Subject: [PATCH 02/16] feat(post): add client-side thumbnail fetching for webpage links from sites with CORS access --- src/components/post/post.tsx | 18 ++++++++++-- src/components/reply/reply.tsx | 20 +++++++++++-- src/lib/utils/media-utils.ts | 54 ++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/components/post/post.tsx b/src/components/post/post.tsx index ba216165..efccc192 100644 --- a/src/components/post/post.tsx +++ b/src/components/post/post.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import styles from './post.module.css'; import { Link, useLocation, useParams } from 'react-router-dom'; import { Comment, useAuthorAddress, useBlock, useComment, useEditedComment, useSubplebbit, useSubscribe } from '@plebbit/plebbit-react-hooks'; import { useTranslation } from 'react-i18next'; import { isAllView, isPostView, isProfileHiddenView, isSubplebbitView } from '../../lib/utils/view-utils'; -import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; +import { fetchWebpageThumbnailIfNeeded, getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; import { getHostname } from '../../lib/utils/url-utils'; import { getFormattedTimeAgo, formatLocalizedUTCTimestamp } from '../../lib/utils/time-utils'; import CommentEditForm from '../comment-edit-form'; @@ -131,7 +131,19 @@ const Post = ({ index, post = {} }: PostProps) => { const postScore = upvoteCount === 0 && downvoteCount === 0 ? '•' : upvoteCount - downvoteCount || '?'; const postTitle = (title?.length > 300 ? title?.slice(0, 300) + '...' : title) || (content?.length > 300 ? content?.slice(0, 300) + '...' : content); - const commentMediaInfo = getCommentMediaInfo(post); + // some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false + const initialCommentMediaInfo = useMemo(() => getCommentMediaInfo(post), [post]); + const [commentMediaInfo, setCommentMediaInfo] = useState(initialCommentMediaInfo); + const fetchThumbnail = useCallback(async () => { + if (initialCommentMediaInfo?.type === 'webpage' && !initialCommentMediaInfo.thumbnail) { + const newMediaInfo = await fetchWebpageThumbnailIfNeeded(initialCommentMediaInfo); + setCommentMediaInfo(newMediaInfo); + } + }, [initialCommentMediaInfo]); + useEffect(() => { + fetchThumbnail(); + }, [fetchThumbnail]); + const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const linkUrl = getHostname(link); const linkClass = `${isInPostView ? (link ? styles.externalLink : styles.internalLink) : styles.link} ${pinned ? styles.pinnedLink : ''}`; diff --git a/src/components/reply/reply.tsx b/src/components/reply/reply.tsx index cb7e9c29..2e294a01 100644 --- a/src/components/reply/reply.tsx +++ b/src/components/reply/reply.tsx @@ -1,11 +1,11 @@ -import { Fragment, useEffect, useMemo, useState } from 'react'; +import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'; import { Comment, useAccountComment, useAuthorAddress, useAuthorAvatar, useBlock, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils'; import { Link, useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import styles from './reply.module.css'; import useReplies from '../../hooks/use-replies'; -import { CommentMediaInfo, getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; +import { CommentMediaInfo, fetchWebpageThumbnailIfNeeded, getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils'; import { formatLocalizedUTCTimestamp, getFormattedTimeAgo } from '../../lib/utils/time-utils'; import CommentEditForm from '../comment-edit-form'; import LoadingEllipsis from '../loading-ellipsis/'; @@ -295,7 +295,21 @@ const Reply = ({ cidOfReplyWithContext, depth = 0, isSingleComment, isSingleRepl const showCommentEditForm = () => setIsEditing(true); const hideCommentEditForm = () => setIsEditing(false); - const commentMediaInfo = getCommentMediaInfo(reply); + // some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false + const initialCommentMediaInfo = useMemo(() => getCommentMediaInfo(reply), [reply]); + const [commentMediaInfo, setCommentMediaInfo] = useState(initialCommentMediaInfo); + + const fetchThumbnail = useCallback(async () => { + if (initialCommentMediaInfo?.type === 'webpage' && !initialCommentMediaInfo.thumbnail) { + const newMediaInfo = await fetchWebpageThumbnailIfNeeded(initialCommentMediaInfo); + setCommentMediaInfo(newMediaInfo); + } + }, [initialCommentMediaInfo]); + + useEffect(() => { + fetchThumbnail(); + }, [fetchThumbnail]); + const hasThumbnail = getHasThumbnail(commentMediaInfo, link); const { t, i18n } = useTranslation(); diff --git a/src/lib/utils/media-utils.ts b/src/lib/utils/media-utils.ts index 7822d706..8fe2563d 100644 --- a/src/lib/utils/media-utils.ts +++ b/src/lib/utils/media-utils.ts @@ -91,3 +91,57 @@ export const getCommentMediaInfo = (comment: Comment): CommentMediaInfo | undefi } return; }; + +// some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false +const fetchWebpageThumbnail = async (url: string): Promise => { + try { + const response = await fetch(url); + const html = await response.text(); + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + + // Try to find Open Graph image + const ogImage = doc.querySelector('meta[property="og:image"]'); + if (ogImage && ogImage.getAttribute('content')) { + return ogImage.getAttribute('content')!; + } + + // If no Open Graph image, try to find the first image + const firstImage = doc.querySelector('img'); + if (firstImage && firstImage.getAttribute('src')) { + return new URL(firstImage.getAttribute('src')!, url).href; + } + + return undefined; + } catch (error) { + console.error('Error fetching webpage thumbnail:', error); + return undefined; + } +}; + +export const fetchWebpageThumbnailIfNeeded = async (commentMediaInfo: CommentMediaInfo): Promise => { + if (commentMediaInfo.type === 'webpage' && !commentMediaInfo.thumbnail) { + const cachedThumbnail = getCachedThumbnail(commentMediaInfo.url); + if (cachedThumbnail) { + return { ...commentMediaInfo, thumbnail: cachedThumbnail }; + } + const thumbnail = await fetchWebpageThumbnail(commentMediaInfo.url); + if (thumbnail) { + setCachedThumbnail(commentMediaInfo.url, thumbnail); + } + return { ...commentMediaInfo, thumbnail }; + } + return commentMediaInfo; +}; +const THUMBNAIL_CACHE_KEY = 'webpageThumbnailCache'; + +export const getCachedThumbnail = (url: string): string | null => { + const cache = JSON.parse(localStorage.getItem(THUMBNAIL_CACHE_KEY) || '{}'); + return cache[url] || null; +}; + +export const setCachedThumbnail = (url: string, thumbnail: string): void => { + const cache = JSON.parse(localStorage.getItem(THUMBNAIL_CACHE_KEY) || '{}'); + cache[url] = thumbnail; + localStorage.setItem(THUMBNAIL_CACHE_KEY, JSON.stringify(cache)); +}; From 18b5a8e6b5576770c8b2435348acb3e8a5de33d8 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 12 Oct 2024 22:33:15 +0200 Subject: [PATCH 03/16] add support to next images, default non-direct links to "webpage" type --- src/lib/utils/media-utils.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib/utils/media-utils.ts b/src/lib/utils/media-utils.ts index 8fe2563d..c6073b80 100644 --- a/src/lib/utils/media-utils.ts +++ b/src/lib/utils/media-utils.ts @@ -54,9 +54,12 @@ export const getLinkMediaInfo = memoize( let type: string = 'webpage'; let mime: string | undefined; + if (url.pathname === '/_next/image' && url.search.startsWith('?url=')) { + return { url: link, type: 'image' }; + } + try { - const fileName = url.pathname.slice(url.pathname.lastIndexOf('/') + 1).toLowerCase(); - mime = extName(fileName)[0]?.mime; + mime = extName(new URL(link).pathname.toLowerCase().replace('/', ''))[0]?.mime; if (mime) { if (mime.startsWith('image')) { type = mime === 'image/gif' ? 'gif' : 'image'; @@ -67,6 +70,10 @@ export const getLinkMediaInfo = memoize( } } + if (!url.pathname.includes('.')) { + type = 'webpage'; + } + if (canEmbed(url) || url.host.startsWith('yt.')) { type = 'iframe'; patternThumbnailUrl = getPatternThumbnailUrl(url); From e1c02cdf8f60c9373c87f0fdac0b40d31e9255d1 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 13 Oct 2024 13:08:50 +0200 Subject: [PATCH 04/16] perf(gifs): cache first frame so gifs don't reload all the time when navigating --- src/hooks/use-fetch-gif-first-frame.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/hooks/use-fetch-gif-first-frame.ts b/src/hooks/use-fetch-gif-first-frame.ts index 26301cee..97c86cba 100644 --- a/src/hooks/use-fetch-gif-first-frame.ts +++ b/src/hooks/use-fetch-gif-first-frame.ts @@ -1,5 +1,18 @@ import { useEffect, useState } from 'react'; +const GIF_FRAME_CACHE_KEY = 'gifFrameCache'; + +const getCachedGifFrame = (url: string): string | null => { + const cache = JSON.parse(localStorage.getItem(GIF_FRAME_CACHE_KEY) || '{}'); + return cache[url] || null; +}; + +const setCachedGifFrame = (url: string, frameUrl: string): void => { + const cache = JSON.parse(localStorage.getItem(GIF_FRAME_CACHE_KEY) || '{}'); + cache[url] = frameUrl; + localStorage.setItem(GIF_FRAME_CACHE_KEY, JSON.stringify(cache)); +}; + export const fetchImage = (url: string): Promise => { return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); @@ -62,9 +75,18 @@ const useFetchGifFirstFrame = (url: string | undefined) => { const fetchFrame = async () => { try { + const cachedFrame = getCachedGifFrame(url); + if (cachedFrame) { + if (isActive) setFrameUrl(cachedFrame); + return; + } + const blob = typeof url === 'string' ? await parseGif(await fetchImage(url)) : await parseGif(await readImage(url as File)); const objectUrl = URL.createObjectURL(blob); - if (isActive) setFrameUrl(objectUrl); + if (isActive) { + setFrameUrl(objectUrl); + setCachedGifFrame(url, objectUrl); + } } catch (error) { console.error('Failed to load GIF frame:', error); if (isActive) setFrameUrl(null); From 442824adb7db232fb193fac4250b336934535ac5 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 13 Oct 2024 18:27:11 +0200 Subject: [PATCH 05/16] update android configuration for capacitor --- android/app/build.gradle | 7 +- android/app/src/main/AndroidManifest.xml | 7 +- android/build.gradle | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/variables.gradle | 10 +- package.json | 8 +- yarn.lock | 128 ++++++++++-------- 7 files changed, 97 insertions(+), 71 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index d2ca0a7b..564f6c30 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' android { + namespace "seedit.android" compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId "seedit.android" @@ -24,6 +25,9 @@ android { } repositories { + google() + mavenCentral() + maven { url "https://maven.google.com" } flatDir{ dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' } @@ -33,10 +37,11 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" implementation project(':capacitor-android') + implementation project(':capacitor-cordova-android-plugins') testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" - implementation project(':capacitor-cordova-android-plugins') + implementation "org.apache.cordova:framework:$cordovaAndroidVersion" } apply from: 'capacitor.build.gradle' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 755f6149..fd7168b4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,5 @@ - - + + android:launchMode="singleTask" + android:exported="true"> diff --git a/android/build.gradle b/android/build.gradle index ad085c34..10c52591 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -4,10 +4,10 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.4.2' classpath 'com.google.gms:google-services:4.3.5' // NOTE: Do not place your application dependencies here; they belong @@ -20,7 +20,7 @@ apply from: "variables.gradle" allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c4101c3..f42e62f3 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/variables.gradle b/android/variables.gradle index 967549c5..22e638b3 100644 --- a/android/variables.gradle +++ b/android/variables.gradle @@ -1,7 +1,7 @@ ext { - minSdkVersion = 21 - compileSdkVersion = 30 - targetSdkVersion = 30 + minSdkVersion = 22 + compileSdkVersion = 33 + targetSdkVersion = 33 androidxActivityVersion = '1.2.0' androidxAppCompatVersion = '1.2.0' androidxCoordinatorLayoutVersion = '1.1.0' @@ -10,5 +10,5 @@ ext { junitVersion = '4.13.1' androidxJunitVersion = '1.1.2' androidxEspressoCoreVersion = '3.3.0' - cordovaAndroidVersion = '7.0.0' -} \ No newline at end of file + cordovaAndroidVersion = '10.1.1' +} diff --git a/package.json b/package.json index dfe99605..422eb00e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "private": true, "dependencies": { "@capacitor/app": "1.1.1", - "@capacitor/device": "^6.0.1", + "@capacitor/device": "6.0.1", "@floating-ui/react": "0.26.1", "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#e5b30dcd62f1f355bc3ce52555e7f71ea4cae06b", "@testing-library/jest-dom": "5.14.1", @@ -84,9 +84,9 @@ ] }, "devDependencies": { - "@capacitor/android": "3.6.0", - "@capacitor/cli": "3.6.0", - "@capacitor/core": "3.6.0", + "@capacitor/android": "5.0.0", + "@capacitor/cli": "5.0.0", + "@capacitor/core": "5.0.0", "@electron/rebuild": "3.6.0", "@types/memoizee": "0.4.9", "concurrently": "8.0.1", diff --git a/yarn.lock b/yarn.lock index c3d34eb4..afd82867 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1209,46 +1209,47 @@ ipaddr.js "^2.1.0" punycode "^2.3.1" -"@capacitor/android@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@capacitor/android/-/android-3.6.0.tgz#60438a0614415c7c242dda48a031cd45335e067a" - integrity sha512-X6n0OLy7BE3c6qfVuL7UYyq/aIwEsqIAqtyDOwMdj5k+P1rLQVsGaWERXUtC0BGeoKBD5YgbWiyKwAwg5Spjdg== +"@capacitor/android@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@capacitor/android/-/android-5.0.0.tgz#4e716738e87733b24c4a60e4c2e73fc4027fd2df" + integrity sha512-jEhx0OW8uypxlW94bXn7Q6YtwBOg3MH49DxH+jSrroRPr8Xb55w3puVSof9O2BQaxOq1mXKTjwz6TOIargGHwQ== "@capacitor/app@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@capacitor/app/-/app-1.1.1.tgz#bde7202faad1b47c4ae2c4a622285d7569384c5e" integrity sha512-8ADkldHnoE1xkWvPUsGlERVGm6/Zvcxy6hCI80AxydIKyaCG7kbDAvUclebbnw/eFRxj2zBoVatGLjmJNvTbYw== -"@capacitor/cli@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@capacitor/cli/-/cli-3.6.0.tgz#31c07f364828e750d20a8e6bb2d516f0d21be81d" - integrity sha512-YNQNM1wvy3zJkKNWcNL5KzIqgV5J1YzO27MHSxZyVA6+XmWCZi8Qz/Cq/uDrepsuBG67QdIvtOeL+JnXARedfw== +"@capacitor/cli@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@capacitor/cli/-/cli-5.0.0.tgz#baade00bb120ae2aa466ddf06adb3effb80cad06" + integrity sha512-d11L9xnnxmjyTHD3vjhOGnr7zN+uIzIeW4QrQC131rovP1cdFYVjG1DrmQa/mx6s49d2Bv7myNLHtl0j+dQIow== dependencies: - "@ionic/cli-framework-output" "^2.2.1" - "@ionic/utils-fs" "^3.1.5" - "@ionic/utils-subprocess" "^2.1.6" - "@ionic/utils-terminal" "^2.3.0" - commander "^6.0.0" - debug "^4.2.0" + "@ionic/cli-framework-output" "^2.2.5" + "@ionic/utils-fs" "^3.1.6" + "@ionic/utils-subprocess" "^2.1.11" + "@ionic/utils-terminal" "^2.3.3" + commander "^9.3.0" + debug "^4.3.4" env-paths "^2.2.0" - kleur "^4.1.1" - native-run "^1.5.0" - open "^7.4.2" - plist "^3.0.2" - prompts "^2.3.2" - semver "^7.3.2" + kleur "^4.1.4" + native-run "^1.7.2" + open "^8.4.0" + plist "^3.0.5" + prompts "^2.4.2" + rimraf "^4.4.1" + semver "^7.3.7" tar "^6.1.11" - tslib "^2.1.0" - xml2js "^0.4.23" + tslib "^2.4.0" + xml2js "^0.5.0" -"@capacitor/core@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@capacitor/core/-/core-3.6.0.tgz#5481a3c1c4d03cec77bb102fa5f7cbb0bd4c6f4d" - integrity sha512-F94ozABHXxq1xMcNGMnOVP73WZDWYR1PrJEfVfl9Ja0BXGEJURIxmHBc842OX3rrt3+g0ELHyhX9LTH16eeonw== +"@capacitor/core@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@capacitor/core/-/core-5.0.0.tgz#31264d3708cb9c8de35024ad0be51c48adc560df" + integrity sha512-Fm8jNUPpCBPQVKLEwMt7D+w/7sh1AGiLVpL+D6Xc7iRdrPU/3NI9bgzca3uzDA1SOV8O+tVxk7bjZGXPbiTRAA== dependencies: tslib "^2.1.0" -"@capacitor/device@^6.0.1": +"@capacitor/device@6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@capacitor/device/-/device-6.0.1.tgz#bb5cb2b9e9c37142b71d51e4c6eed84625ce821e" integrity sha512-Tlz67DAO5GKb5YAfupXiENZxDww6mHnG9iKI+8D5SVF82VLpEv5r9qwKtiounuQB2y2HWiHV8tlOk7DqnLVUqQ== @@ -2290,7 +2291,7 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-5.0.0.tgz#bf344cc75136039bc41bcf5d1ddbcb40405fca3b" integrity sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg== -"@ionic/cli-framework-output@^2.2.1": +"@ionic/cli-framework-output@^2.2.5": version "2.2.8" resolved "https://registry.yarnpkg.com/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz#29d541acc7773a6aaceec5f3b079937fbcef5402" integrity sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g== @@ -2345,7 +2346,7 @@ debug "^4.0.0" tslib "^2.0.1" -"@ionic/utils-subprocess@^2.1.6": +"@ionic/utils-subprocess@^2.1.11": version "2.1.14" resolved "https://registry.yarnpkg.com/@ionic/utils-subprocess/-/utils-subprocess-2.1.14.tgz#06224bdc6d9891ed86b1e556fc172a0eeabdc846" integrity sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg== @@ -2374,7 +2375,7 @@ untildify "^4.0.0" wrap-ansi "^7.0.0" -"@ionic/utils-terminal@2.3.5", "@ionic/utils-terminal@^2.3.0", "@ionic/utils-terminal@^2.3.3": +"@ionic/utils-terminal@2.3.5", "@ionic/utils-terminal@^2.3.3": version "2.3.5" resolved "https://registry.yarnpkg.com/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz#a48465f40496ee8f29c6d92e4506d5f19762ac3c" integrity sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A== @@ -6191,11 +6192,6 @@ commander@^5.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== -commander@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" - integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== - commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" @@ -6206,6 +6202,11 @@ commander@^8.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@^9.3.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + commitizen@^4.0.3: version "4.3.0" resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.3.0.tgz#0d056c542a2d2b1f9b9aba981aa32575b2849924" @@ -8964,6 +8965,16 @@ glob@^8.0.1, glob@^8.0.3: minimatch "^5.0.1" once "^1.3.0" +glob@^9.2.0: + version "9.3.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" + integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== + dependencies: + fs.realpath "^1.0.0" + minimatch "^8.0.2" + minipass "^4.2.4" + path-scurry "^1.6.1" + global-agent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" @@ -10111,7 +10122,7 @@ is-windows@^1.0.1: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^2.1.1, is-wsl@^2.2.0: +is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -11240,7 +11251,7 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -kleur@^4.0.3, kleur@^4.1.1: +kleur@^4.0.3, kleur@^4.1.4: version "4.1.5" resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== @@ -12349,6 +12360,13 @@ minimatch@^5.0.1, minimatch@^5.1.1: dependencies: brace-expansion "^2.0.1" +minimatch@^8.0.2: + version "8.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" + integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== + dependencies: + brace-expansion "^2.0.1" + minimatch@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" @@ -12432,6 +12450,11 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3. dependencies: yallist "^4.0.0" +minipass@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" + integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== + minipass@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" @@ -12594,7 +12617,7 @@ native-fetch@^4.0.2: resolved "https://registry.yarnpkg.com/native-fetch/-/native-fetch-4.0.2.tgz#75c8a44c5f3bb021713e5e24f2846750883e49af" integrity sha512-4QcVlKFtv2EYVS5MBgsGX5+NWKtbDbIECdUXDBGDMAZXq3Jkv9zf+y8iS7Ub8fEdga3GpYeazp9gauNqXHJOCg== -native-run@^1.5.0: +native-run@^1.7.2: version "1.7.4" resolved "https://registry.yarnpkg.com/native-run/-/native-run-1.7.4.tgz#b98b74812805cef8665cfceec651e66e662123e3" integrity sha512-yDEwTp66vmXpqFiSQzz4sVQgyq5U58gGRovglY4GHh12ITyWa6mh6Lbpm2gViVOVD1JYFtYnwcgr7GTFBinXNA== @@ -13013,14 +13036,6 @@ open-graph-scraper@6.3.3: undici "^6.4.0" validator "^13.11.0" -open@^7.4.2: - version "7.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" - integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== - dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" - open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -13324,7 +13339,7 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.11.1: +path-scurry@^1.11.1, path-scurry@^1.6.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== @@ -13448,7 +13463,7 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" -plist@^3.0.2, plist@^3.0.4, plist@^3.0.5, plist@^3.0.6: +plist@^3.0.4, plist@^3.0.5, plist@^3.0.6: version "3.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== @@ -14132,7 +14147,7 @@ promise@^8.1.0: dependencies: asap "~2.0.6" -prompts@^2.0.1, prompts@^2.3.2, prompts@^2.4.2: +prompts@^2.0.1, prompts@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== @@ -14907,6 +14922,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" + integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== + dependencies: + glob "^9.2.0" + roarr@^2.15.3: version "2.15.4" resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" @@ -17580,10 +17602,10 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xml2js@^0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== +xml2js@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== dependencies: sax ">=0.6.0" xmlbuilder "~11.0.0" From 7d0d00168082d14dd9c1b735dbd86c31d8fff889 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 13 Oct 2024 18:29:53 +0200 Subject: [PATCH 06/16] add isAndroid flag --- android/app/capacitor.build.gradle | 1 + android/app/src/main/assets/capacitor.plugins.json | 4 ++++ capacitor.config.ts | 7 ++++++- src/lib/utils/platform.ts | 8 ++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/lib/utils/platform.ts diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index 0929acbc..7f3e2041 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -10,6 +10,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-app') + implementation project(':capacitor-device') } diff --git a/android/app/src/main/assets/capacitor.plugins.json b/android/app/src/main/assets/capacitor.plugins.json index 21a0521e..037e5b17 100644 --- a/android/app/src/main/assets/capacitor.plugins.json +++ b/android/app/src/main/assets/capacitor.plugins.json @@ -2,5 +2,9 @@ { "pkg": "@capacitor/app", "classpath": "com.capacitorjs.plugins.app.AppPlugin" + }, + { + "pkg": "@capacitor/device", + "classpath": "com.capacitorjs.plugins.device.DevicePlugin" } ] diff --git a/capacitor.config.ts b/capacitor.config.ts index 095f3310..d040fba1 100644 --- a/capacitor.config.ts +++ b/capacitor.config.ts @@ -4,7 +4,12 @@ const config: CapacitorConfig = { appId: 'seedit.android', appName: 'seedit', webDir: 'build', - bundledWebRuntime: false + bundledWebRuntime: false, + plugins: { + Device: { + lazyLoad: true, + }, + }, }; export default config; diff --git a/src/lib/utils/platform.ts b/src/lib/utils/platform.ts new file mode 100644 index 00000000..65593fe6 --- /dev/null +++ b/src/lib/utils/platform.ts @@ -0,0 +1,8 @@ +import { Capacitor } from '@capacitor/core'; +import { Device } from '@capacitor/device'; + +export const isAndroid = Capacitor.getPlatform() === 'android'; + +export async function getDeviceInfo() { + return await Device.getInfo(); +} From d328c5c2772050a523e3485b3c31153edd1d08e7 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 13 Oct 2024 18:33:42 +0200 Subject: [PATCH 07/16] feat(account settings): alert user account is stored locally and specify location --- .../account-settings/account-settings.module.css | 12 ++++++++++++ .../settings/account-settings/account-settings.tsx | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/src/views/settings/account-settings/account-settings.module.css b/src/views/settings/account-settings/account-settings.module.css index 46b185b8..b2496b00 100644 --- a/src/views/settings/account-settings/account-settings.module.css +++ b/src/views/settings/account-settings/account-settings.module.css @@ -24,6 +24,14 @@ margin-left: 0px; } +.warning { + text-transform: lowercase; + font-size: 0.8em; + color: var(--red); + padding-bottom: 10px; + padding-right: 10px; +} + .createAccount button { margin-left: 0px; margin-bottom: 10px; @@ -53,4 +61,8 @@ .deleteAccountBox, .deleteAccountBox button { color: var(--red); +} + +.deleteAccountBox { + padding-top: 10px; } \ No newline at end of file diff --git a/src/views/settings/account-settings/account-settings.tsx b/src/views/settings/account-settings/account-settings.tsx index 85ba58f5..ee20ec70 100644 --- a/src/views/settings/account-settings/account-settings.tsx +++ b/src/views/settings/account-settings/account-settings.tsx @@ -3,6 +3,7 @@ import { createAccount, deleteAccount, exportAccount, importAccount, setAccount, import stringify from 'json-stringify-pretty-compact'; import { useTranslation } from 'react-i18next'; import styles from './account-settings.module.css'; +import { isAndroid } from '../../../lib/utils/platform'; const AccountSettings = () => { const { t } = useTranslation(); @@ -183,6 +184,12 @@ const AccountSettings = () => {
{t('a_new_account')}
+
+ {t('stored_locally', { + location: window.isElectron ? 'this desktop app' : isAndroid ? 'this mobile app' : window.location.hostname, + interpolation: { escapeValue: false }, + })} +
{t('account_data_preview')}