From 4d7110c58dc888da4304d089b76f3cf73a10da48 Mon Sep 17 00:00:00 2001 From: fyah <727475183@qq.com> Date: Wed, 13 May 2026 17:38:35 +0800 Subject: [PATCH] add shenyan-app RN project + PWA push/noise updates --- PWA/package-lock.json | 63 - PWA/public/favicon.svg | 15 +- PWA/public/icon-192.png | Bin 187 -> 3407 bytes PWA/public/icon-512.png | Bin 842 -> 12554 bytes PWA/public/sw-push.js | 26 + PWA/public/weather-cloudy.svg | 4 + PWA/public/weather-fog.svg | 6 + PWA/public/weather-partly-cloudy.svg | 13 + PWA/public/weather-rain.svg | 8 + PWA/public/weather-snow.svg | 12 + PWA/public/weather-storm.svg | 6 + PWA/public/weather-sunny.svg | 11 + PWA/src/App.tsx | 50 +- PWA/src/components/ChatArea.tsx | 14 +- PWA/src/components/InputArea.tsx | 28 +- PWA/src/index.css | 7 - PWA/src/sw.ts | 32 + PWA/tsconfig.json | 3 +- PWA/vite.config.ts | 10 + shenyan-app/.bundle/config | 2 + shenyan-app/.eslintrc.js | 4 + shenyan-app/.gitignore | 75 + shenyan-app/.prettierrc.js | 5 + shenyan-app/.watchmanconfig | 1 + shenyan-app/Gemfile | 17 + shenyan-app/README.md | 97 + shenyan-app/__tests__/App.test.tsx | 13 + shenyan-app/android/app/build.gradle | 119 + shenyan-app/android/app/debug.keystore | Bin 0 -> 2257 bytes shenyan-app/android/app/proguard-rules.pro | 10 + .../android/app/src/main/AndroidManifest.xml | 28 + .../main/java/com/shenyanapp/MainActivity.kt | 22 + .../java/com/shenyanapp/MainApplication.kt | 27 + .../res/drawable/rn_edit_text_material.xml | 37 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 9 + .../main/res/xml/network_security_config.xml | 6 + shenyan-app/android/build.gradle | 21 + shenyan-app/android/gradle.properties | 44 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 46175 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + shenyan-app/android/gradlew | 248 + shenyan-app/android/gradlew.bat | 98 + shenyan-app/android/settings.gradle | 6 + shenyan-app/app.json | 4 + shenyan-app/babel.config.js | 3 + shenyan-app/index.js | 9 + shenyan-app/ios/.xcode.env | 11 + shenyan-app/ios/Podfile | 34 + .../ios/ShenYanApp.xcodeproj/project.pbxproj | 475 + .../xcschemes/ShenYanApp.xcscheme | 88 + shenyan-app/ios/ShenYanApp/AppDelegate.swift | 48 + .../AppIcon.appiconset/Contents.json | 53 + .../ShenYanApp/Images.xcassets/Contents.json | 6 + shenyan-app/ios/ShenYanApp/Info.plist | 59 + .../ios/ShenYanApp/LaunchScreen.storyboard | 47 + .../ios/ShenYanApp/PrivacyInfo.xcprivacy | 37 + shenyan-app/jest.config.js | 3 + shenyan-app/metro.config.js | 11 + shenyan-app/package-lock.json | 12183 ++++++++++++++++ shenyan-app/package.json | 44 + shenyan-app/src/App.tsx | 115 + shenyan-app/src/api.ts | 35 + shenyan-app/src/components/ChatArea.tsx | 49 + shenyan-app/src/components/InputArea.tsx | 88 + shenyan-app/src/components/MessageBubble.tsx | 53 + shenyan-app/src/storage.ts | 42 + shenyan-app/src/types.ts | 5 + shenyan-app/tsconfig.json | 8 + shenyan-room/_library.md | 3 +- shenyan-room/技术/PWA对话前端/2026-05-09.md | 37 + shenyan-room/记忆库规范.md | 44 +- 82 files changed, 14677 insertions(+), 124 deletions(-) create mode 100644 PWA/public/sw-push.js create mode 100644 PWA/public/weather-cloudy.svg create mode 100644 PWA/public/weather-fog.svg create mode 100644 PWA/public/weather-partly-cloudy.svg create mode 100644 PWA/public/weather-rain.svg create mode 100644 PWA/public/weather-snow.svg create mode 100644 PWA/public/weather-storm.svg create mode 100644 PWA/public/weather-sunny.svg create mode 100644 PWA/src/sw.ts create mode 100644 shenyan-app/.bundle/config create mode 100644 shenyan-app/.eslintrc.js create mode 100644 shenyan-app/.gitignore create mode 100644 shenyan-app/.prettierrc.js create mode 100644 shenyan-app/.watchmanconfig create mode 100644 shenyan-app/Gemfile create mode 100644 shenyan-app/README.md create mode 100644 shenyan-app/__tests__/App.test.tsx create mode 100644 shenyan-app/android/app/build.gradle create mode 100644 shenyan-app/android/app/debug.keystore create mode 100644 shenyan-app/android/app/proguard-rules.pro create mode 100644 shenyan-app/android/app/src/main/AndroidManifest.xml create mode 100644 shenyan-app/android/app/src/main/java/com/shenyanapp/MainActivity.kt create mode 100644 shenyan-app/android/app/src/main/java/com/shenyanapp/MainApplication.kt create mode 100644 shenyan-app/android/app/src/main/res/drawable/rn_edit_text_material.xml create mode 100644 shenyan-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 shenyan-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 shenyan-app/android/app/src/main/res/values/strings.xml create mode 100644 shenyan-app/android/app/src/main/res/values/styles.xml create mode 100644 shenyan-app/android/app/src/main/res/xml/network_security_config.xml create mode 100644 shenyan-app/android/build.gradle create mode 100644 shenyan-app/android/gradle.properties create mode 100644 shenyan-app/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 shenyan-app/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 shenyan-app/android/gradlew create mode 100644 shenyan-app/android/gradlew.bat create mode 100644 shenyan-app/android/settings.gradle create mode 100644 shenyan-app/app.json create mode 100644 shenyan-app/babel.config.js create mode 100644 shenyan-app/index.js create mode 100644 shenyan-app/ios/.xcode.env create mode 100644 shenyan-app/ios/Podfile create mode 100644 shenyan-app/ios/ShenYanApp.xcodeproj/project.pbxproj create mode 100644 shenyan-app/ios/ShenYanApp.xcodeproj/xcshareddata/xcschemes/ShenYanApp.xcscheme create mode 100644 shenyan-app/ios/ShenYanApp/AppDelegate.swift create mode 100644 shenyan-app/ios/ShenYanApp/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 shenyan-app/ios/ShenYanApp/Images.xcassets/Contents.json create mode 100644 shenyan-app/ios/ShenYanApp/Info.plist create mode 100644 shenyan-app/ios/ShenYanApp/LaunchScreen.storyboard create mode 100644 shenyan-app/ios/ShenYanApp/PrivacyInfo.xcprivacy create mode 100644 shenyan-app/jest.config.js create mode 100644 shenyan-app/metro.config.js create mode 100644 shenyan-app/package-lock.json create mode 100644 shenyan-app/package.json create mode 100644 shenyan-app/src/App.tsx create mode 100644 shenyan-app/src/api.ts create mode 100644 shenyan-app/src/components/ChatArea.tsx create mode 100644 shenyan-app/src/components/InputArea.tsx create mode 100644 shenyan-app/src/components/MessageBubble.tsx create mode 100644 shenyan-app/src/storage.ts create mode 100644 shenyan-app/src/types.ts create mode 100644 shenyan-app/tsconfig.json diff --git a/PWA/package-lock.json b/PWA/package-lock.json index 4e833c4..4e570c0 100644 --- a/PWA/package-lock.json +++ b/PWA/package-lock.json @@ -2363,9 +2363,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2380,9 +2377,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2397,9 +2391,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2414,9 +2405,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2431,9 +2419,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2448,9 +2433,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2465,9 +2447,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2482,9 +2461,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2499,9 +2475,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2516,9 +2489,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2533,9 +2503,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2550,9 +2517,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2567,9 +2531,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2793,9 +2754,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2813,9 +2771,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2833,9 +2788,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2853,9 +2805,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5015,9 +4964,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5039,9 +4985,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5063,9 +5006,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5087,9 +5027,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ diff --git a/PWA/public/favicon.svg b/PWA/public/favicon.svg index 7b4341b..30aa78f 100644 --- a/PWA/public/favicon.svg +++ b/PWA/public/favicon.svg @@ -1,4 +1,11 @@ - - - E - + + + + + + + + + + + \ No newline at end of file diff --git a/PWA/public/icon-192.png b/PWA/public/icon-192.png index 50e43b8cb811afea9ccaab04db366dd734cdc3d5..cb6e15acc49a0a26110a3f7791c908711c8588d3 100644 GIT binary patch literal 3407 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 h9~J*5c;e&c&{s#)OS>6Bu literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE2}s`E_d5VcHF>%?hE&XXd(e;($UC&)tA8%H f{sJJAUZ6r^!+8cq?Z^j}AR9eh{an^LB{Ts5$afa9 diff --git a/PWA/public/icon-512.png b/PWA/public/icon-512.png index 5220d0f9dbfa747cdb356761a73beb1b1f1c96bb..b24e40ed9c3d8acb089fcca9ad3b418da75de8f8 100644 GIT binary patch literal 12554 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 Hq3`||=?)pu literal 842 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&t&wwUqN(1_ow9PZ!6KiaBo&HUh;M4lno_ o9RH9Zlo7-k1*0J_;zHm+KGPNk#z+z0_aLu(y85}Sb4q9e0Lk|gumAu6 diff --git a/PWA/public/sw-push.js b/PWA/public/sw-push.js new file mode 100644 index 0000000..8dc1afb --- /dev/null +++ b/PWA/public/sw-push.js @@ -0,0 +1,26 @@ +self.addEventListener('push', function (event) { + var data = {}; + try { data = event.data.json(); } catch (e) { data = { title: '沈晏', body: event.data ? event.data.text() : '' }; } + event.waitUntil( + self.registration.showNotification(data.title || '沈晏', { + body: data.body || '', + icon: '/icon-192.png', + badge: '/icon-192.png', + vibrate: [200, 100, 200], + tag: 'shenyan-push', + renotify: true, + }) + ); +}); + +self.addEventListener('notificationclick', function (event) { + event.notification.close(); + event.waitUntil( + clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (list) { + for (var i = 0; i < list.length; i++) { + if (list[i].url && 'focus' in list[i]) return list[i].focus(); + } + if (clients.openWindow) return clients.openWindow('/'); + }) + ); +}); diff --git a/PWA/public/weather-cloudy.svg b/PWA/public/weather-cloudy.svg new file mode 100644 index 0000000..e707729 --- /dev/null +++ b/PWA/public/weather-cloudy.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/PWA/public/weather-fog.svg b/PWA/public/weather-fog.svg new file mode 100644 index 0000000..a26ed71 --- /dev/null +++ b/PWA/public/weather-fog.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/PWA/public/weather-partly-cloudy.svg b/PWA/public/weather-partly-cloudy.svg new file mode 100644 index 0000000..5c70072 --- /dev/null +++ b/PWA/public/weather-partly-cloudy.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/PWA/public/weather-rain.svg b/PWA/public/weather-rain.svg new file mode 100644 index 0000000..64c9c7b --- /dev/null +++ b/PWA/public/weather-rain.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/PWA/public/weather-snow.svg b/PWA/public/weather-snow.svg new file mode 100644 index 0000000..95cc2cc --- /dev/null +++ b/PWA/public/weather-snow.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/PWA/public/weather-storm.svg b/PWA/public/weather-storm.svg new file mode 100644 index 0000000..5dfc9ad --- /dev/null +++ b/PWA/public/weather-storm.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/PWA/public/weather-sunny.svg b/PWA/public/weather-sunny.svg new file mode 100644 index 0000000..30aa78f --- /dev/null +++ b/PWA/public/weather-sunny.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/PWA/src/App.tsx b/PWA/src/App.tsx index 01b31fb..ebae70f 100644 --- a/PWA/src/App.tsx +++ b/PWA/src/App.tsx @@ -46,6 +46,29 @@ async function saveRemote(msgs: Message[]) { } catch { /* silent */ } } +async function setupPush() { + if (!('serviceWorker' in navigator) || !('PushManager' in window)) return + try { + const reg = await navigator.serviceWorker.ready + const existing = await reg.pushManager.getSubscription() + if (existing) return // already subscribed + + const res = await fetch('/push/vapid-public-key') + const { key } = await res.json() + + const sub = await reg.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: key, + }) + + await fetch('/push/subscribe', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(sub), + }) + } catch { /* silent — user may have denied */ } +} + export default function App() { const [messages, setMessages] = useState(loadLocal) const [loading, setLoading] = useState(false) @@ -59,14 +82,14 @@ export default function App() { setMessages(remote) nextId.current = Math.max(...remote.map(m => m.id)) + 1 } else { - const local = loadLocal() - if (local.length > 0) { - nextId.current = Math.max(...local.map(m => m.id)) + 1 - saveRemote(local) + if (messages.length > 0) { + nextId.current = Math.max(...messages.map(m => m.id)) + 1 + saveRemote(messages) } } setSynced(true) })() + setupPush() }, []) useEffect(() => { @@ -105,14 +128,9 @@ export default function App() { } } - const handleClear = () => { - setMessages([]) - localStorage.removeItem(STORAGE_KEY) - } - return (
-
- 沈晏 - {messages.length > 0 && ( - - )} -
diff --git a/PWA/src/components/ChatArea.tsx b/PWA/src/components/ChatArea.tsx index a420dc4..67d7516 100644 --- a/PWA/src/components/ChatArea.tsx +++ b/PWA/src/components/ChatArea.tsx @@ -2,22 +2,24 @@ import { useRef, useEffect } from 'react' import type { Message } from '../App' export default function ChatArea({ messages }: { messages: Message[] }) { - const bottom = useRef(null) + const container = useRef(null) useEffect(() => { - bottom.current?.scrollIntoView({ behavior: 'smooth' }) + if (container.current) { + container.current.scrollTop = container.current.scrollHeight + } }, [messages]) return ( -
-
+
+
{messages.map((m) => (
))} -
+
) diff --git a/PWA/src/components/InputArea.tsx b/PWA/src/components/InputArea.tsx index aac5424..9ffaf10 100644 --- a/PWA/src/components/InputArea.tsx +++ b/PWA/src/components/InputArea.tsx @@ -12,11 +12,28 @@ export default function InputArea({ onSend, disabled }: { onSend: (text: string) return (
+