diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
index 889afe9..05d4612 100644
--- a/.gitea/workflows/build.yml
+++ b/.gitea/workflows/build.yml
@@ -1,4 +1,4 @@
-name: Frontend CI
+name: Frontend CI
on:
push:
@@ -20,7 +20,7 @@ on:
env:
REGISTRY: code.littlelan.cn
- IMAGE_NAME: carrot_bbs/frontend-web
+ IMAGE_NAME: with_you/frontend-web
OTA_PLATFORM: android
OTA_PUBLISH_URL: https://updates.littlelan.cn/admin/publish
OTA_MANIFEST_URL: https://updates.littlelan.cn/api/manifest
@@ -150,6 +150,33 @@ jobs:
- name: Generate Android native project
run: npx expo prebuild --platform android
+ - name: Decode Android signing keystore
+ run: |
+ echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > android/app/withyou-release-key.keystore
+
+ - name: Patch app/build.gradle with signing config
+ run: |
+ cd android/app
+ python3 -c "
+ import re
+ with open('build.gradle', 'r') as f:
+ content = f.read()
+ release_signing = '''
+ signingConfigs {
+ release {
+ storeFile file(WITHYOU_UPLOAD_STORE_FILE)
+ storePassword WITHYOU_UPLOAD_STORE_PASSWORD
+ keyAlias WITHYOU_UPLOAD_KEY_ALIAS
+ keyPassword WITHYOU_UPLOAD_KEY_PASSWORD
+ }
+ }
+ '''
+ content = content.replace('signingConfigs {}', release_signing)
+ content = content.replace('signingConfig signingConfigs.debug', 'signingConfig signingConfigs.release')
+ with open('build.gradle', 'w') as f:
+ f.write(content)
+ "
+
- name: Configure Gradle with Aliyun Maven mirror
run: |
cd android
@@ -198,7 +225,7 @@ jobs:
}
expoAutolinking.useExpoModules()
- rootProject.name = '萝卜社区'
+ rootProject.name = '威友'
expoAutolinking.useExpoVersionCatalog()
@@ -260,6 +287,16 @@ jobs:
expo.useLegacyPackaging=false
PROPS_EOF
+ - name: Configure Gradle signing properties
+ run: |
+ cd android
+ cat >> gradle.properties << SIGNING_PROPS
+ WITHYOU_UPLOAD_STORE_FILE=withyou-release-key.keystore
+ WITHYOU_UPLOAD_STORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
+ WITHYOU_UPLOAD_KEY_ALIAS=withyou-key
+ WITHYOU_UPLOAD_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}
+ SIGNING_PROPS
+
- name: Build Android release APK (arm64 only)
run: |
cd android
@@ -269,7 +306,7 @@ jobs:
- name: Upload APK artifact
uses: actions/upload-artifact@v3
with:
- name: carrot-bbs-android-release-apk
+ name: withyou-android-release-apk
path: android/app/build/outputs/apk/release/app-release.apk
- name: Resolve runtime version
@@ -350,8 +387,8 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64
provenance: false
- cache-from: type=registry,ref=code.littlelan.cn/carrot_bbs/frontend-web:buildcache
- cache-to: type=registry,ref=code.littlelan.cn/carrot_bbs/frontend-web:buildcache,mode=max
+ cache-from: type=registry,ref=code.littlelan.cn/with_you/frontend-web:buildcache
+ cache-to: type=registry,ref=code.littlelan.cn/with_you/frontend-web:buildcache,mode=max
- name: Show image tags
run: |
diff --git a/app.config.js b/app.config.js
index 69b32bf..36c9d22 100644
--- a/app.config.js
+++ b/app.config.js
@@ -1,7 +1,7 @@
const appJson = require('./app.json');
const isDevVariant = process.env.APP_VARIANT === 'dev';
-const releaseApiBaseUrl = 'https://bbs.littlelan.cn/api/v1';
+const releaseApiBaseUrl = 'https://withyou.littlelan.cn/api/v1';
const releaseUpdatesBaseUrl = 'https://updates.littlelan.cn';
const devApiBaseUrl = process.env.EXPO_PUBLIC_API_BASE_URL || 'http://192.168.31.238:8080/api/v1';
diff --git a/app.json b/app.json
index 9e69d98..162f4e3 100644
--- a/app.json
+++ b/app.json
@@ -1,12 +1,12 @@
-{
+{
"expo": {
- "name": "萝卜社区",
+ "name": "威友",
"slug": "qojo",
- "version": "1.0.13",
+ "version": "0.1.sha",
"orientation": "default",
"icon": "./assets/icon.png",
"userInterfaceStyle": "automatic",
- "scheme": "carrotbbs",
+ "scheme": "withyou",
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
@@ -15,7 +15,7 @@
"ios": {
"supportsTablet": true,
"infoPlist": {
- "NSMicrophoneUsageDescription": "允许萝卜社区访问您的麦克风以进行语音通话",
+ "NSMicrophoneUsageDescription": "允许威友访问您的麦克风以进行语音通话",
"UIBackgroundModes": [
"fetch",
"remote-notification",
@@ -25,7 +25,7 @@
],
"ITSAppUsesNonExemptEncryption": false
},
- "bundleIdentifier": "skin.carrot.bbs"
+ "bundleIdentifier": "cn.qczlit.withyou"
},
"android": {
"adaptiveIcon": {
@@ -35,7 +35,7 @@
"monochromeImage": "./assets/android-icon-monochrome.png"
},
"predictiveBackGestureEnabled": false,
- "package": "skin.carrot.bbs",
+ "package": "cn.qczlit.withyou",
"versionCode": 7,
"permissions": [
"VIBRATE",
@@ -66,10 +66,10 @@
[
"./plugins/withForegroundService",
{
- "notificationTitle": "萝卜社区",
+ "notificationTitle": "威友",
"notificationBody": "正在后台同步消息",
"notificationIcon": "ic_notification",
- "channelId": "carrot_sync_foreground",
+ "channelId": "withyou_sync_foreground",
"channelName": "消息同步"
}
],
@@ -86,7 +86,7 @@
[
"expo-camera",
{
- "cameraPermission": "允许萝卜社区访问您的相机以扫描二维码"
+ "cameraPermission": "允许威友访问您的相机以扫描二维码"
}
],
[
@@ -104,16 +104,16 @@
[
"expo-media-library",
{
- "photosPermission": "允许萝卜社区访问您的照片以发布内容",
- "savePhotosPermission": "允许萝卜社区保存照片到您的相册",
+ "photosPermission": "允许威友访问您的照片以发布内容",
+ "savePhotosPermission": "允许威友保存照片到您的相册",
"isAccessMediaLocationEnabled": true
}
],
[
"expo-image-picker",
{
- "photosPermission": "允许萝卜社区访问您的照片以选择图片",
- "cameraPermission": "允许萝卜社区访问您的相机以拍摄图片",
+ "photosPermission": "允许威友访问您的照片以选择图片",
+ "cameraPermission": "允许威友访问您的相机以拍摄图片",
"colors": {
"cropToolbarColor": "#111827",
"cropToolbarIconColor": "#ffffff",
diff --git a/eas.json b/eas.json
index a403e31..0ce5949 100644
--- a/eas.json
+++ b/eas.json
@@ -27,6 +27,9 @@
},
"production": {
"autoIncrement": true,
+ "android": {
+ "buildType": "apk"
+ },
"ios": {
"resourceClass": "m-medium"
}
diff --git a/metro.config.js b/metro.config.js
index f84ee65..b9c2726 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -16,6 +16,9 @@ config.resolver.alias = {
'@': path.resolve(__dirname, 'src'),
};
+// Ensure platform-specific files are resolved correctly
+config.resolver.platforms = ['ios', 'android', 'native', 'web'];
+
// NOTE: enhanceMiddleware is marked as deprecated in Metro's types,
// but there's currently no official alternative for custom dev server headers.
// For production, configure COEP/COOP headers on your hosting platform.
diff --git a/package-lock.json b/package-lock.json
index dad9e6e..985133f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
- "name": "carrot_bbs",
- "version": "1.0.3",
+ "name": "with_you",
+ "version": "0.1.sha",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "carrot_bbs",
- "version": "1.0.3",
+ "name": "with_you",
+ "version": "0.1.sha",
"dependencies": {
"@expo/vector-icons": "^15.1.1",
"@react-native-async-storage/async-storage": "^2.2.0",
diff --git a/package.json b/package.json
index c5beccb..723bf2e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
- "name": "carrot_bbs",
- "version": "1.0.3",
+ "name": "with_you",
+ "version": "0.1.sha",
"main": "expo-router/entry",
"scripts": {
"start": "expo start",
diff --git a/plugins/withForegroundService.js b/plugins/withForegroundService.js
index bb8badb..7c66c08 100644
--- a/plugins/withForegroundService.js
+++ b/plugins/withForegroundService.js
@@ -1,4 +1,4 @@
-const { withAndroidManifest, withDangerousMod } = require('@expo/config-plugins');
+const { withAndroidManifest, withDangerousMod } = require('@expo/config-plugins');
const fs = require('fs');
const path = require('path');
@@ -18,10 +18,10 @@ const FOREGROUND_SERVICE_DATA_SYNC = 'android.permission.FOREGROUND_SERVICE_DATA
*/
const withForegroundService = (config, options = {}) => {
const {
- notificationTitle = '萝卜社区',
+ notificationTitle = '威友',
notificationBody = '正在后台同步消息',
notificationIcon = 'ic_notification',
- channelId = 'carrot_sync_foreground',
+ channelId = 'withyou_sync_foreground',
channelName = '消息同步',
} = options;
diff --git a/src/app-navigation/AppDesktopShell.tsx b/src/app-navigation/AppDesktopShell.tsx
index 0d79850..300c5ba 100644
--- a/src/app-navigation/AppDesktopShell.tsx
+++ b/src/app-navigation/AppDesktopShell.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
View,
StyleSheet,
@@ -187,7 +187,7 @@ export function AppDesktopShell() {
>
- {!isCollapsed && 胡萝卜BBS}
+ {!isCollapsed && 威友}
{NAV_ITEMS.map((item) => {
diff --git a/src/components/business/QRCodeScanner.tsx b/src/components/business/QRCodeScanner.tsx
index 852a379..711fdce 100644
--- a/src/components/business/QRCodeScanner.tsx
+++ b/src/components/business/QRCodeScanner.tsx
@@ -221,7 +221,7 @@ const QRCodeScannerNative: React.FC = ({ visible, onClose })
setScanned(true);
onClose();
- if (data.startsWith('carrotbbs://qrcode/login')) {
+ if (data.startsWith('withyou://qrcode/login')) {
const sessionId = extractSessionId(data);
if (sessionId) {
router.push(hrefs.hrefQrLoginConfirm(sessionId));
diff --git a/src/components/business/ShareSheet/ShareSheet.tsx b/src/components/business/ShareSheet/ShareSheet.tsx
index f8e9d66..cade4f4 100644
--- a/src/components/business/ShareSheet/ShareSheet.tsx
+++ b/src/components/business/ShareSheet/ShareSheet.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo } from 'react';
+import React, { useEffect, useMemo } from 'react';
import { Share as RNShare, Platform } from 'react-native';
export interface ShareSheetProps {
@@ -18,7 +18,7 @@ const ShareSheet: React.FC = ({
}) => {
const shareText = useMemo(() => {
- const title = postTitle || '胡萝卜BBS帖子';
+ const title = postTitle || '威友帖子';
return `${title} ${postUrl || ''}`;
}, [postTitle, postUrl]);
diff --git a/src/components/common/ImageGallery.tsx b/src/components/common/ImageGallery.tsx
index 18e06d8..b6d755d 100644
--- a/src/components/common/ImageGallery.tsx
+++ b/src/components/common/ImageGallery.tsx
@@ -218,7 +218,7 @@ export const ImageGallery: React.FC = ({
const allowedExts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
const fileExt = allowedExts.includes(ext) ? ext : 'jpg';
- const fileName = `carrot_${Date.now()}.${fileExt}`;
+ const fileName = `withyou_${Date.now()}.${fileExt}`;
const destination = new File(Paths.cache, fileName);
// File.downloadFileAsync 是新版 expo-file-system/next 的静态方法
diff --git a/src/components/common/PagerView.web.tsx b/src/components/common/PagerView.web.tsx
index 93ac8bf..d679b00 100644
--- a/src/components/common/PagerView.web.tsx
+++ b/src/components/common/PagerView.web.tsx
@@ -7,11 +7,18 @@ type PagerViewProps = {
onPageSelected?: (e: { nativeEvent: { position: number } }) => void;
overdrag?: boolean;
children: React.ReactNode;
+ testID?: string;
+};
+
+type PagerViewRef = {
+ setPage: (page: number) => void;
+ setPageWithoutAnimation: (page: number) => void;
+ setScrollEnabled: (scrollEnabled: boolean) => void;
};
function PagerViewInternal(
{ style, children }: PagerViewProps,
- _ref: ForwardedRef<{ setPage: (page: number) => void }>
+ _ref: ForwardedRef
) {
const childrenArray = React.Children.toArray(children);
return (
diff --git a/src/database/core/DatabaseConfig.ts b/src/database/core/DatabaseConfig.ts
index d87dcfb..ecfff7f 100644
--- a/src/database/core/DatabaseConfig.ts
+++ b/src/database/core/DatabaseConfig.ts
@@ -1,5 +1,5 @@
-const DEFAULT_DB_NAME = 'carrot_bbs.db';
-const DB_NAME_PREFIX = 'carrot_bbs_';
+const DEFAULT_DB_NAME = 'with_you.db';
+const DB_NAME_PREFIX = 'with_you_';
export function getDbName(userId: string | null): string {
return userId ? `${DB_NAME_PREFIX}${userId}.db` : DEFAULT_DB_NAME;
diff --git a/src/screens/auth/ForgotPasswordScreen.tsx b/src/screens/auth/ForgotPasswordScreen.tsx
index 421636c..748228a 100644
--- a/src/screens/auth/ForgotPasswordScreen.tsx
+++ b/src/screens/auth/ForgotPasswordScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 忘记密码页 ForgotPasswordScreen(简洁表单样式)
- * 胡萝卜BBS - 找回密码
+ * 威友 - 找回密码
*
* 设计风格:
* - 纯白背景,扁平化设计
diff --git a/src/screens/auth/LoginScreen.tsx b/src/screens/auth/LoginScreen.tsx
index 42d8410..bee897d 100644
--- a/src/screens/auth/LoginScreen.tsx
+++ b/src/screens/auth/LoginScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 登录页 LoginScreen(简洁表单样式)
- * 胡萝卜BBS - 用户登录
+ * 威友 - 用户登录
*
* 设计风格:
* - 纯白背景,扁平化设计
diff --git a/src/screens/auth/RegisterScreen.tsx b/src/screens/auth/RegisterScreen.tsx
index 3b965bc..bafa7b7 100644
--- a/src/screens/auth/RegisterScreen.tsx
+++ b/src/screens/auth/RegisterScreen.tsx
@@ -1,13 +1,13 @@
-/**
+/**
* 注册页面(分步向导版)
- * 胡萝卜BBS - 用户注册流程
+ * 威友 - 用户注册流程
*
* 步骤:
* 1. 输入邮箱
* 2. 填写验证码
* 3. 设置用户信息
*
- * 使用 registerStore 持久化存储步骤数据
+ * 使用 registerStore 纯内存状态管理
*/
import React, { useEffect, useMemo, useRef } from 'react';
@@ -42,7 +42,7 @@ import { RegisterStep1Email } from './RegisterStep1Email';
import { RegisterStep2Verification } from './RegisterStep2Verification';
import { RegisterStep3Profile } from './RegisterStep3Profile';
-// 胡萝卜橙主题色
+// 威友橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FF8C5A',
@@ -87,16 +87,9 @@ export const RegisterScreen: React.FC = () => {
const fadeAnim = useRef(new Animated.Value(0)).current;
const slideAnim = useRef(new Animated.Value(20)).current;
- // 恢复持久化的数据
- const [isHydrated, setIsHydrated] = React.useState(false);
-
+ // 页面挂载时重置倒计时,避免上次残留的倒计时导致无法重新获取验证码
useEffect(() => {
- const hydrateData = async () => {
- const { hydrate } = useRegisterStore.getState();
- await hydrate();
- setIsHydrated(true);
- };
- hydrateData();
+ setCountdown(0);
}, []);
// 检查是否已登录
diff --git a/src/screens/auth/WelcomeScreen.tsx b/src/screens/auth/WelcomeScreen.tsx
index efa0de5..93e46e4 100644
--- a/src/screens/auth/WelcomeScreen.tsx
+++ b/src/screens/auth/WelcomeScreen.tsx
@@ -1,9 +1,9 @@
-/**
+/**
* 欢迎页 WelcomeScreen
- * 胡萝卜BBS - 用户欢迎页
+ * 威友 - 用户欢迎页
*
- * 亮色模式:白色主色调 + 胡萝卜橙辅色
- * 暗色模式:深色背景 + 胡萝卜橙辅色
+ * 亮色模式:白色主色调 + 威友橙辅色
+ * 暗色模式:深色背景 + 威友橙辅色
*/
import React, { useEffect, useRef, useMemo } from 'react';
@@ -89,7 +89,7 @@ export const WelcomeScreen: React.FC = () => {
{/* 品牌区域 */}
Welcome To
- 萝卜社区
+ 威友
@@ -139,7 +139,7 @@ function createWelcomeStyles(colors: AppColors) {
paddingBottom: 40,
},
- // 装饰性背景元素 - 使用胡萝卜橙的淡色
+ // 装饰性背景元素 - 使用威友橙的淡色
decorCircle1: {
position: 'absolute',
top: -100,
@@ -147,7 +147,7 @@ function createWelcomeStyles(colors: AppColors) {
width: 400,
height: 400,
borderRadius: 200,
- backgroundColor: colors.primary.main + '08', // 非常淡的胡萝卜橙
+ backgroundColor: colors.primary.main + '08', // 非常淡的威友橙
},
decorCircle2: {
position: 'absolute',
diff --git a/src/screens/auth/index.ts b/src/screens/auth/index.ts
index fbc0e2c..c46c6f8 100644
--- a/src/screens/auth/index.ts
+++ b/src/screens/auth/index.ts
@@ -1,6 +1,6 @@
-/**
+/**
* 认证屏幕导出
- * 胡萝卜BBS - 登录注册模块
+ * 威友 - 登录注册模块
*/
export { LoginScreen } from './LoginScreen';
diff --git a/src/screens/create/CreatePostScreen.tsx b/src/screens/create/CreatePostScreen.tsx
index f1630ae..3bd3ce4 100644
--- a/src/screens/create/CreatePostScreen.tsx
+++ b/src/screens/create/CreatePostScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 发帖页 CreatePostScreen(响应式适配)
- * 胡萝卜BBS - 发布新帖子
+ * 威友 - 发布新帖子
* 参考微博发帖界面设计
* 表单在宽屏下居中显示
* 图片选择器在宽屏下显示更大的预览
diff --git a/src/screens/home/HomeScreen.tsx b/src/screens/home/HomeScreen.tsx
index 31ad571..e488659 100644
--- a/src/screens/home/HomeScreen.tsx
+++ b/src/screens/home/HomeScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 首页 HomeScreen
- * 胡萝卜BBS - 首页展示
+ * 威友 - 首页展示
* 支持列表和多列网格模式(响应式布局)
*/
diff --git a/src/screens/home/PostDetailScreen.tsx b/src/screens/home/PostDetailScreen.tsx
index 404baaa..55d3ea7 100644
--- a/src/screens/home/PostDetailScreen.tsx
+++ b/src/screens/home/PostDetailScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 帖子详情页 PostDetailScreen - QQ频道风格(响应式版本)
- * 胡萝卜BBS - 显示完整帖子内容和评论
+ * 威友 - 显示完整帖子内容和评论
* 桌面端使用双栏布局,移动端使用单栏布局
*/
diff --git a/src/screens/home/SearchScreen.tsx b/src/screens/home/SearchScreen.tsx
index a5e797b..078efbf 100644
--- a/src/screens/home/SearchScreen.tsx
+++ b/src/screens/home/SearchScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 搜索页 SearchScreen(响应式版本)
- * 胡萝卜BBS - 搜索帖子、用户
+ * 威友 - 搜索帖子、用户
* 支持响应式布局,宽屏下显示更大的搜索结果区域
*/
diff --git a/src/screens/message/ChatScreen.tsx b/src/screens/message/ChatScreen.tsx
index 70c1531..2d846a5 100644
--- a/src/screens/message/ChatScreen.tsx
+++ b/src/screens/message/ChatScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 聊天页 ChatScreen
- * 胡萝卜BBS - 私信/群聊聊天界面
+ * 威友 - 私信/群聊聊天界面
* 高级现代化设计
* 支持群聊功能:显示发送者头像和昵称、@提及功能
* 支持响应式布局(桌面端宽屏优化)
diff --git a/src/screens/message/MessageListScreen.tsx b/src/screens/message/MessageListScreen.tsx
index 2bf1293..3070e9c 100644
--- a/src/screens/message/MessageListScreen.tsx
+++ b/src/screens/message/MessageListScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 消息列表页 MessageListScreen
- * 胡萝卜BBS - 消息列表
+ * 威友 - 消息列表
*
* 【重构后】使用MessageManager架构
* - 纯渲染组件,不直接管理数据
diff --git a/src/screens/message/NotificationsScreen.tsx b/src/screens/message/NotificationsScreen.tsx
index d8c6175..e9ab95d 100644
--- a/src/screens/message/NotificationsScreen.tsx
+++ b/src/screens/message/NotificationsScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 通知页 NotificationsScreen(扁平化风格)
- * 胡萝卜BBS - 系统消息列表
+ * 威友 - 系统消息列表
* 【游标分页】使用 messageService.getSystemMessagesCursor
* 支持响应式布局
*
diff --git a/src/screens/message/PrivateChatInfoScreen.tsx b/src/screens/message/PrivateChatInfoScreen.tsx
index 7b68f32..0fa4f71 100644
--- a/src/screens/message/PrivateChatInfoScreen.tsx
+++ b/src/screens/message/PrivateChatInfoScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* PrivateChatInfoScreen 私聊聊天管理页面
- * 胡萝卜BBS - 私聊设置界面
+ * 威友 - 私聊设置界面
* 参考QQ和微信的实现,提供聊天管理功能
*/
diff --git a/src/screens/profile/AboutScreen.tsx b/src/screens/profile/AboutScreen.tsx
index 107298b..ad0d18e 100644
--- a/src/screens/profile/AboutScreen.tsx
+++ b/src/screens/profile/AboutScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 关于我们页面 AboutScreen
- * 胡萝卜BBS - 应用信息、版本检查与更新
+ * 威友 - 应用信息、版本检查与更新
* 扁平化设计风格,参考登录、注册、设置页面
*/
@@ -39,7 +39,7 @@ const THEME_COLORS = {
primaryDark: '#E55A2B',
};
-const APP_NAME = '萝卜社区';
+const APP_NAME = '威友';
const APP_SLOGAN = '连接校园,分享生活';
const APP_VERSION = Constants.expoConfig?.version || '1.0.0';
const BUILD_NUMBER = Constants.expoConfig?.android?.versionCode || Constants.expoConfig?.ios?.buildNumber || '1';
diff --git a/src/screens/profile/ChatSettingsScreen.tsx b/src/screens/profile/ChatSettingsScreen.tsx
index eba1175..ab52423 100644
--- a/src/screens/profile/ChatSettingsScreen.tsx
+++ b/src/screens/profile/ChatSettingsScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 聊天设置页 ChatSettingsScreen
- * 胡萝卜BBS - 聊天个性化设置
+ * 威友 - 聊天个性化设置
*/
import React, { useMemo, useCallback, useRef } from 'react';
diff --git a/src/screens/profile/DataStorageScreen.tsx b/src/screens/profile/DataStorageScreen.tsx
index b490251..9f4024e 100644
--- a/src/screens/profile/DataStorageScreen.tsx
+++ b/src/screens/profile/DataStorageScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 数据与存储设置页 DataStorageScreen(响应式适配)
- * 胡萝卜BBS - 缓存管理、存储统计
+ * 威友 - 缓存管理、存储统计
*/
import React, { useState, useEffect, useCallback, useMemo } from 'react';
diff --git a/src/screens/profile/EditProfileScreen.tsx b/src/screens/profile/EditProfileScreen.tsx
index 64056f0..1b3d6e5 100644
--- a/src/screens/profile/EditProfileScreen.tsx
+++ b/src/screens/profile/EditProfileScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 编辑资料页 EditProfileScreen(响应式适配)
- * 胡萝卜BBS - 编辑用户资料
+ * 威友 - 编辑用户资料
* 与用户资料页样式完全一致
* 表单在宽屏下居中显示
*/
diff --git a/src/screens/profile/NotificationSettingsScreen.tsx b/src/screens/profile/NotificationSettingsScreen.tsx
index eb16454..16df45d 100644
--- a/src/screens/profile/NotificationSettingsScreen.tsx
+++ b/src/screens/profile/NotificationSettingsScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 通知设置页 NotificationSettingsScreen(响应式适配)
- * 胡萝卜BBS - 通知相关设置
+ * 威友 - 通知相关设置
* 在宽屏下居中显示
*/
diff --git a/src/screens/profile/PrivacyPolicyScreen.tsx b/src/screens/profile/PrivacyPolicyScreen.tsx
index 6ffffca..76f3e31 100644
--- a/src/screens/profile/PrivacyPolicyScreen.tsx
+++ b/src/screens/profile/PrivacyPolicyScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 隐私政策页面 PrivacyPolicyScreen
- * 胡萝卜BBS - 隐私政策
+ * 威友 - 隐私政策
* 扁平化设计风格
*/
@@ -22,7 +22,7 @@ import { useResponsive, useResponsiveSpacing } from '../../hooks';
// 内容最大宽度
const CONTENT_MAX_WIDTH = 400;
-// 胡萝卜橙主题色
+// 威友橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
};
@@ -34,7 +34,7 @@ const LAST_UPDATED = '2026年4月4日';
const PRIVACY_SECTIONS = [
{
title: '引言',
- content: `萝卜社区(以下简称"我们"或"本应用")非常重视用户的隐私和个人信息保护。我们深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。
+ content: `威友(以下简称"我们"或"本应用")非常重视用户的隐私和个人信息保护。我们深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。
本隐私政策旨在帮助您了解我们如何收集、使用、存储、共享和保护您的个人信息,以及您享有的相关权利。请您在使用我们的服务前,仔细阅读并理解本隐私政策的全部内容。
@@ -293,7 +293,7 @@ export const PrivacyPolicyScreen: React.FC = () => {
我们承诺保护您的隐私
- 萝卜社区致力于保护用户的个人信息安全。我们仅收集提供服务所必需的信息,并采取严格的安全措施保护您的数据。
+ 威友致力于保护用户的个人信息安全。我们仅收集提供服务所必需的信息,并采取严格的安全措施保护您的数据。
diff --git a/src/screens/profile/ProfileScreen.tsx b/src/screens/profile/ProfileScreen.tsx
index fe79f24..55f89eb 100644
--- a/src/screens/profile/ProfileScreen.tsx
+++ b/src/screens/profile/ProfileScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 个人主页 ProfileScreen
- * 胡萝卜BBS - 当前用户个人主页
+ * 威友 - 当前用户个人主页
* 使用统一的 UserProfileScreen 组件,mode='self'
*/
diff --git a/src/screens/profile/SettingsScreen.tsx b/src/screens/profile/SettingsScreen.tsx
index 5146f64..2ca6cf9 100644
--- a/src/screens/profile/SettingsScreen.tsx
+++ b/src/screens/profile/SettingsScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 设置页 SettingsScreen(响应式适配)
- * 胡萝卜BBS - 应用设置
+ * 威友 - 应用设置
* 在宽屏下居中显示,最大宽度限制
*/
@@ -363,7 +363,7 @@ export const SettingsScreen: React.FC = () => {
- 萝卜社区 v{APP_VERSION}
+ 威友 v{APP_VERSION}
© 2026 青春之旅电子信息科技(威海)有限公司
diff --git a/src/screens/profile/TermsOfServiceScreen.tsx b/src/screens/profile/TermsOfServiceScreen.tsx
index 7b953b3..2f15c9e 100644
--- a/src/screens/profile/TermsOfServiceScreen.tsx
+++ b/src/screens/profile/TermsOfServiceScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 用户协议页面 TermsOfServiceScreen
- * 胡萝卜BBS - 用户服务协议
+ * 威友 - 用户服务协议
* 扁平化设计风格
*/
@@ -22,7 +22,7 @@ import { useResponsive, useResponsiveSpacing } from '../../hooks';
// 内容最大宽度
const CONTENT_MAX_WIDTH = 400;
-// 胡萝卜橙主题色
+// 威友橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
};
@@ -34,7 +34,7 @@ const LAST_UPDATED = '2026年4月4日';
const TERMS_SECTIONS = [
{
title: '一、协议的范围',
- content: `1.1 本协议是您与萝卜社区(以下简称"我们"或"本应用")之间关于您使用本应用服务所订立的协议。
+ content: `1.1 本协议是您与威友(以下简称"我们"或"本应用")之间关于您使用本应用服务所订立的协议。
1.2 本协议内容包括协议正文及所有我们已经发布或将来可能发布的各类规则、公告或通知。所有规则为本协议不可分割的组成部分,与协议正文具有同等法律效力。
diff --git a/src/screens/profile/UserProfileScreen.tsx b/src/screens/profile/UserProfileScreen.tsx
index 0e7fa57..bef5e15 100644
--- a/src/screens/profile/UserProfileScreen.tsx
+++ b/src/screens/profile/UserProfileScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 统一的用户主页组件
- * 胡萝卜BBS - 支持两种模式:self(当前用户)和 other(其他用户)
+ * 威友 - 支持两种模式:self(当前用户)和 other(其他用户)
* 采用现代卡片式设计,优化视觉层次和交互体验
* 支持桌面端双栏布局
*/
diff --git a/src/screens/profile/UserScreen.tsx b/src/screens/profile/UserScreen.tsx
index 7aa668f..d1943f0 100644
--- a/src/screens/profile/UserScreen.tsx
+++ b/src/screens/profile/UserScreen.tsx
@@ -1,6 +1,6 @@
-/**
+/**
* 用户主页 UserScreen
- * 胡萝卜BBS - 查看其他用户资料
+ * 威友 - 查看其他用户资料
* 使用统一的 UserProfileScreen 组件,mode='other'
*/
diff --git a/src/services/background/BackgroundSyncManager.ts b/src/services/background/BackgroundSyncManager.ts
index f27b647..abe6bfe 100644
--- a/src/services/background/BackgroundSyncManager.ts
+++ b/src/services/background/BackgroundSyncManager.ts
@@ -1,4 +1,4 @@
-/**
+/**
* 后台同步管理器
*
* 整合前台服务、后台同步、消息状态管理
@@ -86,7 +86,7 @@ class BackgroundSyncManager {
if (mode === BackgroundSyncMode.REALTIME) {
// 切换到实时模式:立即启动前台服务(无论前台还是后台)
const started = await ForegroundServiceModule.start({
- title: '萝卜社区',
+ title: '威友',
body: '正在后台同步消息',
});
if (started) {
@@ -134,7 +134,7 @@ class BackgroundSyncManager {
if (this.mode === BackgroundSyncMode.REALTIME) {
// 确保前台服务正在运行
await ForegroundServiceModule.start({
- title: '萝卜社区',
+ title: '威友',
body: '正在后台同步消息',
});
@@ -154,7 +154,7 @@ class BackgroundSyncManager {
// 实时模式下:前台服务保持运行,只更新通知文字
if (this.mode === BackgroundSyncMode.REALTIME) {
- await ForegroundServiceModule.update('萝卜社区', '正在后台同步消息');
+ await ForegroundServiceModule.update('威友', '正在后台同步消息');
}
// 立即同步一次(获取离线消息)
@@ -235,12 +235,12 @@ class BackgroundSyncManager {
if (totalUnread > 0) {
await ForegroundServiceModule.update(
- '萝卜社区',
+ '威友',
`您有 ${totalUnread} 条未读消息`
);
} else {
await ForegroundServiceModule.update(
- '萝卜社区',
+ '威友',
'正在后台同步消息'
);
}
diff --git a/src/services/background/ForegroundServiceModule.ts b/src/services/background/ForegroundServiceModule.ts
index 3bb3a9f..ff29481 100644
--- a/src/services/background/ForegroundServiceModule.ts
+++ b/src/services/background/ForegroundServiceModule.ts
@@ -1,4 +1,4 @@
-/**
+/**
* 前台服务模块
*
* 提供 React Native 层对 Android 前台服务的控制接口
@@ -40,7 +40,7 @@ export const ForegroundServiceModule = {
try {
const result = await ForegroundService.start(
- options.title || '萝卜社区',
+ options.title || '威友',
options.body || '正在后台同步消息'
);
console.log('[ForegroundService] 服务已启动');
diff --git a/src/services/core/api.ts b/src/services/core/api.ts
index 21f793e..79c2ae5 100644
--- a/src/services/core/api.ts
+++ b/src/services/core/api.ts
@@ -11,13 +11,13 @@ import { Platform } from 'react-native';
import { eventBus } from '@/core/events/EventBus';
-// 生产地址 https://bbs.littlelan.cn
+// 生产地址 https://withyou.littlelan.cn
const getBaseUrl = () => {
const configuredBaseUrl = Constants.expoConfig?.extra?.apiBaseUrl;
if (typeof configuredBaseUrl === 'string' && configuredBaseUrl.trim().length > 0) {
return configuredBaseUrl;
}
- return 'https://bbs.littlelan.cn/api/v1';
+ return 'https://withyou.littlelan.cn/api/v1';
};
const BASE_URL = getBaseUrl();
diff --git a/src/services/platform/apkUpdateService.ts b/src/services/platform/apkUpdateService.ts
index 35c7d0b..0860d19 100644
--- a/src/services/platform/apkUpdateService.ts
+++ b/src/services/platform/apkUpdateService.ts
@@ -144,7 +144,7 @@ async function downloadAndInstallAPK(downloadUrl: string, version: string): Prom
}
try {
- const downloadPath = `${FileSystem.documentDirectory}carrot-bbs-${version}.apk`;
+ const downloadPath = `${FileSystem.documentDirectory}with-you-${version}.apk`;
// 显示下载进度
showConfirm({
diff --git a/src/services/webrtc/WebRTCManager.ts b/src/services/webrtc/WebRTCManager.ts
index 1a5ecf5..36eb0e8 100644
--- a/src/services/webrtc/WebRTCManager.ts
+++ b/src/services/webrtc/WebRTCManager.ts
@@ -96,22 +96,24 @@ class WebRTCManager {
console.log('[WebRTC] ontrack: no track in event');
return;
}
- // Official react-native-webrtc approach: use event.streams[0] if available
- if (event.streams && event.streams[0]) {
- this.remoteStream = event.streams[0];
- } else {
- // Fallback: manually construct stream
- if (!this.remoteStream) {
- this.remoteStream = new MediaStream();
- }
- // Check if track already exists to avoid duplicates
- const existingTracks = this.remoteStream.getTracks();
- const trackExists = existingTracks.some(t => t.id === event.track.id);
- if (!trackExists) {
- this.remoteStream.addTrack(event.track);
- }
+
+ // Always manually construct/append to remoteStream to ensure all tracks are collected
+ // react-native-webrtc may fire ontrack per-track and event.streams[0] might not contain all tracks
+ if (!this.remoteStream) {
+ this.remoteStream = new MediaStream();
}
- this.emit({ type: 'remotestream', stream: this.remoteStream! });
+
+ // Check if track already exists to avoid duplicates
+ const existingTracks = this.remoteStream.getTracks();
+ const trackExists = existingTracks.some(t => t.id === event.track.id);
+ if (!trackExists) {
+ this.remoteStream.addTrack(event.track);
+ console.log('[WebRTC] Added remote track:', event.track.kind, 'total tracks:', this.remoteStream.getTracks().length);
+ } else {
+ console.log('[WebRTC] Remote track already exists:', event.track.kind);
+ }
+
+ this.emit({ type: 'remotestream', stream: this.remoteStream });
};
// @ts-ignore
@@ -655,7 +657,8 @@ class WebRTCManager {
}
if (this.remoteStream) {
- this.remoteStream.getTracks().forEach((track) => track.stop());
+ // Do not stop remote tracks; they are managed by the peer connection
+ // and will be ended automatically when the connection closes
this.remoteStream = null;
}
diff --git a/src/stores/auth/registerStore.ts b/src/stores/auth/registerStore.ts
index 8268c77..6453417 100644
--- a/src/stores/auth/registerStore.ts
+++ b/src/stores/auth/registerStore.ts
@@ -1,12 +1,9 @@
/**
* 注册流程状态管理
- * 使用 Zustand + AsyncStorage 实现步骤数据的持久化
+ * 使用 Zustand 纯内存状态,不持久化到 AsyncStorage
*/
import { create } from 'zustand';
-import AsyncStorage from '@react-native-async-storage/async-storage';
-
-const STORAGE_KEY = 'register_form_data';
export type RegisterStep = 0 | 1 | 2;
@@ -42,9 +39,6 @@ interface RegisterState {
resetRegisterData: () => void;
goToNextStep: () => void;
goToPrevStep: () => void;
- // 持久化
- hydrate: () => Promise;
- persistData: () => Promise;
}
const initialFormData: RegisterFormData = {
@@ -63,31 +57,18 @@ const initialCompletedSteps: Record = {
2: false,
};
-export const useRegisterStore = create((set, get) => ({
+export const useRegisterStore = create((set) => ({
currentStep: 0,
formData: initialFormData,
countdown: 0,
completedSteps: initialCompletedSteps,
- setCurrentStep: (step) => {
- set({ currentStep: step });
- get().persistData();
- },
+ setCurrentStep: (step) => set({ currentStep: step }),
updateFormData: (data) =>
- set((state) => {
- const newFormData = { ...state.formData, ...data };
- // 异步保存到 AsyncStorage
- AsyncStorage.setItem(
- STORAGE_KEY,
- JSON.stringify({
- currentStep: state.currentStep,
- formData: newFormData,
- completedSteps: state.completedSteps,
- })
- ).catch(() => {});
- return { formData: newFormData };
- }),
+ set((state) => ({
+ formData: { ...state.formData, ...data },
+ })),
setCountdown: (seconds) => set({ countdown: seconds }),
@@ -97,81 +78,33 @@ export const useRegisterStore = create((set, get) => ({
})),
markStepCompleted: (step) =>
- set((state) => {
- const newCompletedSteps = { ...state.completedSteps, [step]: true };
- // 异步保存到 AsyncStorage
- AsyncStorage.setItem(
- STORAGE_KEY,
- JSON.stringify({
- currentStep: state.currentStep,
- formData: state.formData,
- completedSteps: newCompletedSteps,
- })
- ).catch(() => {});
- return { completedSteps: newCompletedSteps };
- }),
+ set((state) => ({
+ completedSteps: { ...state.completedSteps, [step]: true },
+ })),
- resetRegisterData: () => {
+ resetRegisterData: () =>
set({
currentStep: 0,
formData: initialFormData,
countdown: 0,
completedSteps: initialCompletedSteps,
- });
- AsyncStorage.removeItem(STORAGE_KEY).catch(() => {});
- },
+ }),
- goToNextStep: () => {
- const { currentStep } = get();
- if (currentStep < 2) {
- const newStep = (currentStep + 1) as RegisterStep;
- set({ currentStep: newStep });
- get().persistData();
- }
- },
-
- goToPrevStep: () => {
- const { currentStep } = get();
- if (currentStep > 0) {
- const newStep = (currentStep - 1) as RegisterStep;
- set({ currentStep: newStep });
- get().persistData();
- }
- },
-
- // 从 AsyncStorage 恢复数据
- hydrate: async () => {
- try {
- const raw = await AsyncStorage.getItem(STORAGE_KEY);
- if (raw) {
- const data = JSON.parse(raw);
- set({
- currentStep: data.currentStep ?? 0,
- formData: { ...initialFormData, ...data.formData },
- completedSteps: { ...initialCompletedSteps, ...data.completedSteps },
- });
+ goToNextStep: () =>
+ set((state) => {
+ if (state.currentStep < 2) {
+ return { currentStep: (state.currentStep + 1) as RegisterStep };
}
- } catch {
- // 忽略错误
- }
- },
+ return state;
+ }),
- // 保存数据到 AsyncStorage
- persistData: async () => {
- const { currentStep, formData, completedSteps } = get();
- try {
- await AsyncStorage.setItem(
- STORAGE_KEY,
- JSON.stringify({
- currentStep,
- formData,
- completedSteps,
- })
- );
- } catch {
- // 忽略错误
- }
- },
+ goToPrevStep: () =>
+ set((state) => {
+ if (state.currentStep > 0) {
+ return { currentStep: (state.currentStep - 1) as RegisterStep };
+ }
+ return state;
+ }),
}));
// 导出便捷 hooks
diff --git a/src/stores/call/callStore.ts b/src/stores/call/callStore.ts
index 8b116e7..65aeab5 100644
--- a/src/stores/call/callStore.ts
+++ b/src/stores/call/callStore.ts
@@ -146,13 +146,20 @@ function setupWebRTCEvents(callId: string, myUserId: string): void {
* Handle remote stream with video track detection
*/
function handleRemoteStream(stream: MediaStream): void {
- callStore.setState({ peerStream: stream });
+ // Create a new MediaStream instance to force zustand subscribers to update
+ // because react-native-webrtc may add tracks to the same stream object reference
+ const newStream = new MediaStream();
+ stream.getTracks().forEach((track) => {
+ newStream.addTrack(track);
+ });
+
+ callStore.setState({ peerStream: newStream });
// Detect video tracks in remote stream
- const videoTracks = stream.getVideoTracks();
+ const videoTracks = newStream.getVideoTracks();
const hasPeerVideo = videoTracks.length > 0 && videoTracks.some((t) => t.enabled);
- console.log('[CallStore] Remote stream received, hasVideo:', hasPeerVideo, 'videoTracks:', videoTracks.length);
+ console.log('[CallStore] Remote stream received, hasVideo:', hasPeerVideo, 'videoTracks:', videoTracks.length, 'audioTracks:', newStream.getAudioTracks().length);
callStore.setState((s) => ({
currentCall: s.currentCall
diff --git a/src/theme/index.ts b/src/theme/index.ts
index 5ee08b0..86dd118 100644
--- a/src/theme/index.ts
+++ b/src/theme/index.ts
@@ -1,5 +1,5 @@
-/**
- * 胡萝卜BBS 主题:明暗色板、Paper 主题、设计 token
+/**
+ * 威友 主题:明暗色板、Paper 主题、设计 token
* 新代码请优先使用 useAppColors();静态 colors 为浅色快照,与深色模式不同步。
*/
diff --git a/src/types/index.ts b/src/types/index.ts
index 3e4fd0c..5c6b7ab 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -1,5 +1,5 @@
-// ============================================
-// Carrot BBS - Core Type Definitions
+// ============================================
+// WithYou - Core Type Definitions
// 使用后端DTO类型,保持snake_case命名规范
// ============================================