feat(profile): add help screen and fix upload field name
All checks were successful
Frontend CI / ota (ios) (push) Successful in 2m46s
Frontend CI / ota (android) (push) Successful in 2m50s
Frontend CI / build-and-push-web (push) Successful in 3m54s
Frontend CI / build-android-apk (push) Successful in 40m29s

Add dedicated Help screen to profile tab with navigation integration.
Refactor SettingsScreen to navigate to HelpScreen instead of showing placeholder alert.
Fix API upload method to use correct field names ("file" for documents, "image" for media)
based on endpoint path, resolving upload failures for file uploads.
This commit is contained in:
lafay
2026-06-22 07:56:06 +08:00
parent 761f315a60
commit 2bad59afbb
9 changed files with 516 additions and 99 deletions

View File

@@ -17,6 +17,7 @@ export default function ProfileStackLayout() {
<Stack.Screen name="blocked-users" />
<Stack.Screen name="chat-settings" />
<Stack.Screen name="about" />
<Stack.Screen name="help" />
<Stack.Screen name="verification" />
<Stack.Screen name="data-storage" />
<Stack.Screen name="privacy-settings" />

View File

@@ -0,0 +1,5 @@
import { HelpScreen } from '../../../../src/screens/profile';
export default function HelpRoute() {
return <HelpScreen />;
}

172
package-lock.json generated
View File

@@ -8,7 +8,7 @@
"name": "with_you",
"version": "1.0.1",
"dependencies": {
"@expo/ui": "~56.0.17",
"@expo/ui": "~56.0.18",
"@expo/vector-icons": "^15.1.1",
"@livekit/react-native": "^2.11.0",
"@livekit/react-native-webrtc": "^144.1.0",
@@ -18,31 +18,31 @@
"@types/react": "~19.2.14",
"axios": "^1.16.1",
"date-fns": "^4.4.0",
"expo": "~56.0.11",
"expo-background-task": "~56.0.18",
"expo": "~56.0.12",
"expo-background-task": "~56.0.19",
"expo-callkit-telecom": "^0.3.9",
"expo-camera": "~56.0.8",
"expo-constants": "~56.0.18",
"expo-dev-client": "~56.0.20",
"expo-document-picker": "~56.0.4",
"expo-file-system": "~56.0.8",
"expo-font": "~56.0.6",
"expo-font": "~56.0.7",
"expo-haptics": "~56.0.3",
"expo-image": "~56.0.6",
"expo-image-picker": "~56.0.17",
"expo-image-picker": "~56.0.18",
"expo-intent-launcher": "~56.0.4",
"expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.14",
"expo-media-library": "~56.0.7",
"expo-notifications": "~56.0.17",
"expo-router": "~56.2.10",
"expo-notifications": "~56.0.18",
"expo-router": "~56.2.11",
"expo-splash-screen": "~56.0.10",
"expo-sqlite": "~56.0.5",
"expo-status-bar": "~56.0.4",
"expo-system-ui": "~56.0.5",
"expo-task-manager": "~56.0.18",
"expo-task-manager": "~56.0.19",
"expo-updates": "~56.0.19",
"expo-video": "~56.1.3",
"expo-video": "~56.1.4",
"jcore-react-native": "^2.3.6",
"jpush-react-native": "^3.2.7",
"katex": "^0.17.0",
@@ -1427,12 +1427,12 @@
}
},
"node_modules/@expo/config-plugins": {
"version": "56.0.8",
"resolved": "https://registry.npmmirror.com/@expo/config-plugins/-/config-plugins-56.0.8.tgz",
"integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==",
"version": "56.0.9",
"resolved": "https://registry.npmmirror.com/@expo/config-plugins/-/config-plugins-56.0.9.tgz",
"integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==",
"license": "MIT",
"dependencies": {
"@expo/config-types": "^56.0.5",
"@expo/config-types": "^56.0.6",
"@expo/json-file": "~10.2.0",
"@expo/plist": "^0.7.0",
"@expo/require-utils": "^56.1.3",
@@ -1460,9 +1460,9 @@
}
},
"node_modules/@expo/config-types": {
"version": "56.0.5",
"resolved": "https://registry.npmmirror.com/@expo/config-types/-/config-types-56.0.5.tgz",
"integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==",
"version": "56.0.6",
"resolved": "https://registry.npmmirror.com/@expo/config-types/-/config-types-56.0.6.tgz",
"integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==",
"license": "MIT"
},
"node_modules/@expo/config/node_modules/semver": {
@@ -1610,12 +1610,12 @@
}
},
"node_modules/@expo/inline-modules": {
"version": "0.0.11",
"resolved": "https://registry.npmmirror.com/@expo/inline-modules/-/inline-modules-0.0.11.tgz",
"integrity": "sha512-ZlIfKL61DPnW8YUTdMEjMA31xrDDV6p7Xi8rWYyhd5qXBV8MwGwjuJ7vKeaVaMjRqxJk1N9lv7zlfyvQpRCNNw==",
"version": "0.0.12",
"resolved": "https://registry.npmmirror.com/@expo/inline-modules/-/inline-modules-0.0.12.tgz",
"integrity": "sha512-SNIZr/HWfIQPTZBwmukItxpc7ws1SgMUywYq1dnQvDknQDjJcuWAasIRFUjsK15yQ1xb4G5CP7VHtbN3V4lENg==",
"license": "MIT",
"dependencies": {
"@expo/config-plugins": "~56.0.8"
"@expo/config-plugins": "~56.0.9"
}
},
"node_modules/@expo/json-file": {
@@ -1953,27 +1953,27 @@
}
},
"node_modules/@expo/prebuild-config": {
"version": "56.0.15",
"resolved": "https://registry.npmmirror.com/@expo/prebuild-config/-/prebuild-config-56.0.15.tgz",
"integrity": "sha512-6GC+QjdCkzp/5wjsqgfu/B2+2yf5MyZMtzf9szIPrLt9uKhzV2PdyM0vU0kvbj1YT8weHCtO7bsrzimman0sjA==",
"version": "56.0.16",
"resolved": "https://registry.npmmirror.com/@expo/prebuild-config/-/prebuild-config-56.0.16.tgz",
"integrity": "sha512-ce9ENfPWO4WUWUVQz0OaqL3KYZ7YofP8O35ncnn7CHCaKwQ7BqxcCGJbh+qvP1UjlWeNB3CjHPrXXJ3bnZwlJw==",
"license": "MIT",
"dependencies": {
"@expo/config": "~56.0.9",
"@expo/config-plugins": "~56.0.8",
"@expo/config-types": "^56.0.5",
"@expo/config-plugins": "~56.0.9",
"@expo/config-types": "^56.0.6",
"@expo/image-utils": "^0.10.1",
"@expo/json-file": "^10.2.0",
"@react-native/normalize-colors": "0.85.3",
"debug": "^4.3.1",
"expo-modules-autolinking": "~56.0.15",
"expo-modules-autolinking": "~56.0.16",
"resolve-from": "^5.0.0",
"semver": "^7.6.0"
}
},
"node_modules/@expo/prebuild-config/node_modules/semver": {
"version": "7.8.4",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.4.tgz",
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
"version": "7.8.5",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -2032,9 +2032,9 @@
"license": "MIT"
},
"node_modules/@expo/ui": {
"version": "56.0.17",
"resolved": "https://registry.npmmirror.com/@expo/ui/-/ui-56.0.17.tgz",
"integrity": "sha512-Jos9oGzurMDngrSWJesX3LSykPRvkUdANxtq2sPKBc6bAjadtZJCkthAYMaE3P9L5xrzbNRFo+2O76cRP0iYPw==",
"version": "56.0.18",
"resolved": "https://registry.npmmirror.com/@expo/ui/-/ui-56.0.18.tgz",
"integrity": "sha512-2XgH5obigGtXm8zlb/V3g87NSiIcBcJ1xoQOEQYPoExL1DCNsHzaIecTh1XG/f/45ardo4OZNJwpbfYJ9X3qrQ==",
"license": "MIT",
"dependencies": {
"sf-symbols-typescript": "^2.1.0",
@@ -6400,15 +6400,15 @@
}
},
"node_modules/expo": {
"version": "56.0.11",
"resolved": "https://registry.npmmirror.com/expo/-/expo-56.0.11.tgz",
"integrity": "sha512-YqF+q+JqfobDU5yFym3h1vQqzbl7rFiDB4wAJEyK6NK+KLeyf4pfzydQcNTyqLXQKcQBG1reBJExfDShoAYTzw==",
"version": "56.0.12",
"resolved": "https://registry.npmmirror.com/expo/-/expo-56.0.12.tgz",
"integrity": "sha512-FxgdI/Yqva6iJOThZIHfvxlKPxs4EC4uScUnEswwSArR/Fj9k430O13R590LcOQTsdNsjIs+GBHwjfoAY6vmAQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "^56.1.15",
"@expo/cli": "^56.1.16",
"@expo/config": "~56.0.9",
"@expo/config-plugins": "~56.0.8",
"@expo/config-plugins": "~56.0.9",
"@expo/devtools": "~56.0.2",
"@expo/dom-webview": "~56.0.5",
"@expo/fingerprint": "^0.19.4",
@@ -6421,10 +6421,10 @@
"expo-asset": "~56.0.17",
"expo-constants": "~56.0.18",
"expo-file-system": "~56.0.8",
"expo-font": "~56.0.6",
"expo-font": "~56.0.7",
"expo-keep-awake": "~56.0.3",
"expo-modules-autolinking": "~56.0.15",
"expo-modules-core": "~56.0.16",
"expo-modules-autolinking": "~56.0.16",
"expo-modules-core": "~56.0.17",
"pretty-format": "^29.7.0",
"react-refresh": "^0.14.2",
"whatwg-url-minimum": "^0.1.2"
@@ -6486,12 +6486,12 @@
}
},
"node_modules/expo-background-task": {
"version": "56.0.18",
"resolved": "https://registry.npmmirror.com/expo-background-task/-/expo-background-task-56.0.18.tgz",
"integrity": "sha512-jfEvLq/hZUWkef+lOt0sbe/Jd8wnK0fMgqsZhD1ulWk1IKB0AWsjmJ0iCTDMD9L9MDztpvKf2g/ygzljmo2eGA==",
"version": "56.0.19",
"resolved": "https://registry.npmmirror.com/expo-background-task/-/expo-background-task-56.0.19.tgz",
"integrity": "sha512-TuFqorlz4HXtWAwQGcg+/URXpBwjwNsAxdis/6m5mhtgBIVbX2gYYv/s6eIf8lh2cAI1zFkb+/bHHuC2aOAGGA==",
"license": "MIT",
"dependencies": {
"expo-task-manager": "~56.0.18"
"expo-task-manager": "~56.0.19"
},
"peerDependencies": {
"expo": "*"
@@ -6622,9 +6622,9 @@
}
},
"node_modules/expo-font": {
"version": "56.0.6",
"resolved": "https://registry.npmmirror.com/expo-font/-/expo-font-56.0.6.tgz",
"integrity": "sha512-D4s72Aei844C2s8Vy61qMr6wLEjv6BMrXA1oyRQ0x8LJBbpm5gyogUohc0lABUURVLCqsnBIDdztegl3hktmmg==",
"version": "56.0.7",
"resolved": "https://registry.npmmirror.com/expo-font/-/expo-font-56.0.7.tgz",
"integrity": "sha512-hpU/vRwPzsby9lPGkA4blDqLIIXYzoWnCZHr6PxvcWbY/uPObAiyhh6q+e0WYsB65SthK+PLH95jEnVag7fwEg==",
"license": "MIT",
"dependencies": {
"fontfaceobserver": "^2.1.0"
@@ -6685,9 +6685,9 @@
}
},
"node_modules/expo-image-picker": {
"version": "56.0.17",
"resolved": "https://registry.npmmirror.com/expo-image-picker/-/expo-image-picker-56.0.17.tgz",
"integrity": "sha512-/RojW1ptyQNV89hEeDdaoHCkn38+546MqakMyB0acFf0HLh5ZI7wxQCHkxr16qjuQMy11M+96GlX9Z6vgXF7Nw==",
"version": "56.0.18",
"resolved": "https://registry.npmmirror.com/expo-image-picker/-/expo-image-picker-56.0.18.tgz",
"integrity": "sha512-sCjQ8M27bhGUv2vUavIE+uWdYo79b2D7Q5h9B66BSDZ+Rd8YyLVSf7vYGfIzQ7nMVoENZ6c4xo/JiDkEeQ9iTg==",
"license": "MIT",
"dependencies": {
"expo-image-loader": "~56.0.3"
@@ -6769,9 +6769,9 @@
}
},
"node_modules/expo-modules-autolinking": {
"version": "56.0.15",
"resolved": "https://registry.npmmirror.com/expo-modules-autolinking/-/expo-modules-autolinking-56.0.15.tgz",
"integrity": "sha512-WqpBFwLzn7DsrUkWltIjVmAjwuI1VdQ2jRMlvk1nh2kVadwdJBkSjUBQWRifsEePNhiMT/rFOovBolUU/ARt5w==",
"version": "56.0.16",
"resolved": "https://registry.npmmirror.com/expo-modules-autolinking/-/expo-modules-autolinking-56.0.16.tgz",
"integrity": "sha512-9JnL4N46P8ubDpDIfWolDn7nxU2j1rY67xY/dNVuyH0m+HG+r/JI16VYtjIf4COpZtEuFo4D3h3MBeFzGucMnw==",
"license": "MIT",
"dependencies": {
"@expo/require-utils": "^56.1.3",
@@ -6793,13 +6793,13 @@
}
},
"node_modules/expo-modules-core": {
"version": "56.0.16",
"resolved": "https://registry.npmmirror.com/expo-modules-core/-/expo-modules-core-56.0.16.tgz",
"integrity": "sha512-IVdT0CnqOpQCPdemA5rb50CPbbhWeJePnvuH0yUmOmyMkNky8WVOdRQtVicoIv4CCG5hDrzPIxULD4YOHZ5CHg==",
"version": "56.0.17",
"resolved": "https://registry.npmmirror.com/expo-modules-core/-/expo-modules-core-56.0.17.tgz",
"integrity": "sha512-5J8whnT7Ccp+BrFClLmpF76omBqn95VZExroTm01Dgjm4vpty1Rb7U3we+ZUceNHtRd07Lw30u7FNfDgIhEbRQ==",
"license": "MIT",
"dependencies": {
"@expo/expo-modules-macros-plugin": "0.2.2",
"expo-modules-jsi": "~56.0.9",
"expo-modules-jsi": "~56.0.10",
"invariant": "^2.2.4"
},
"peerDependencies": {
@@ -6814,18 +6814,18 @@
}
},
"node_modules/expo-modules-jsi": {
"version": "56.0.9",
"resolved": "https://registry.npmmirror.com/expo-modules-jsi/-/expo-modules-jsi-56.0.9.tgz",
"integrity": "sha512-2lfDkRcsP/Qh2upS+nu0MS0tfGsghc6ehTivzbgM5nJz0MGYhAJxCJSeendWM+aOQutQAwzsoxrNT0nW8lRAwA==",
"version": "56.0.10",
"resolved": "https://registry.npmmirror.com/expo-modules-jsi/-/expo-modules-jsi-56.0.10.tgz",
"integrity": "sha512-fHZcFpYO/o62GYa6fJyAQJZcAShzhoN0iMMDzbr7vD3ewET6e1vAlTonbEakN9F0VHEgBFJ4NREy87uwVcpCuA==",
"license": "MIT",
"peerDependencies": {
"react-native": "*"
}
},
"node_modules/expo-notifications": {
"version": "56.0.17",
"resolved": "https://registry.npmmirror.com/expo-notifications/-/expo-notifications-56.0.17.tgz",
"integrity": "sha512-Yn6JUKmoBDkQeznbuUn4cHF2u44r1ErJTneW65MBFt7NLG8U8/VGQ4bBVwswm5nPH1/V92UoXPgvCssScPFRDg==",
"version": "56.0.18",
"resolved": "https://registry.npmmirror.com/expo-notifications/-/expo-notifications-56.0.18.tgz",
"integrity": "sha512-HHnrwyCLC5srFojcHYS2KskbNroy9o2fwPKdyhjrdjjrBu4sNRKm4LepcuZjDy98cZKEm89WIPW8O45vut8Rgw==",
"license": "MIT",
"dependencies": {
"@expo/image-utils": "^0.10.1",
@@ -6841,15 +6841,15 @@
}
},
"node_modules/expo-router": {
"version": "56.2.10",
"resolved": "https://registry.npmmirror.com/expo-router/-/expo-router-56.2.10.tgz",
"integrity": "sha512-u7WcdsFAjSrQS7Bdb1VbNPE3xNcd/BZ6qXSS31UAJKhaYIb+ik3jJSy/W5kY3qKipwbwlo3CSb1WnZ2XYs7F+Q==",
"version": "56.2.11",
"resolved": "https://registry.npmmirror.com/expo-router/-/expo-router-56.2.11.tgz",
"integrity": "sha512-08DBTrKv3QanOc9u1JNxSEChW9c/qNFbQ0dO28OLvufWWfdSRkSdHmh365D2FgoZg1qaOzZPCDuL3tM6nGSfkQ==",
"license": "MIT",
"dependencies": {
"@expo/log-box": "^56.0.13",
"@expo/metro-runtime": "^56.0.15",
"@expo/schema-utils": "^56.0.0",
"@expo/ui": "^56.0.17",
"@expo/ui": "^56.0.18",
"@radix-ui/react-slot": "^1.2.0",
"@radix-ui/react-tabs": "^1.1.12",
"@react-native-masked-view/masked-view": "^0.3.2",
@@ -7005,9 +7005,9 @@
}
},
"node_modules/expo-task-manager": {
"version": "56.0.18",
"resolved": "https://registry.npmmirror.com/expo-task-manager/-/expo-task-manager-56.0.18.tgz",
"integrity": "sha512-abTKDhlZ572wSwNuZ9HkDw6rl+kKLq0TkqheIEbGoRkMQVEGV3D5GYYY0gg84TO/HvAqiipdcBFQH8+9uHj70Q==",
"version": "56.0.19",
"resolved": "https://registry.npmmirror.com/expo-task-manager/-/expo-task-manager-56.0.19.tgz",
"integrity": "sha512-eDeBR8vXlOAXBck29AzPl+tv4QlJus+cgkQ0TGOBqAlgRPWPWd7ebhqrhb0KRieUZIvzlycfcxjHR/+AiEiMHA==",
"license": "MIT",
"dependencies": {
"unimodules-app-loader": "~56.0.0"
@@ -7070,9 +7070,9 @@
"license": "MIT"
},
"node_modules/expo-video": {
"version": "56.1.3",
"resolved": "https://registry.npmmirror.com/expo-video/-/expo-video-56.1.3.tgz",
"integrity": "sha512-Ht2BMdx11RnGKxILL/P4t/hl4kqWXq1vLaoOrLZlDed+IXgf8hdDI9jmtEEgsIHwf3jaHFXblJCRVuS9mIxKAw==",
"version": "56.1.4",
"resolved": "https://registry.npmmirror.com/expo-video/-/expo-video-56.1.4.tgz",
"integrity": "sha512-8mXZOnb7ZsWneDlcnRf0M2Ox9eb5dGnNiGiCkj9bwLPY+3jM2Pc1t5ymNUuhw8lSpT/rOnE0J0/81Y3+0k2Zcw==",
"license": "MIT",
"peerDependencies": {
"expo": "*",
@@ -7081,18 +7081,18 @@
}
},
"node_modules/expo/node_modules/@expo/cli": {
"version": "56.1.15",
"resolved": "https://registry.npmmirror.com/@expo/cli/-/cli-56.1.15.tgz",
"integrity": "sha512-ik6++YzURB2d/mSEfYwbuNa19uOWZwVHy9THCQ/pPr6mzplKl4w9I4nlYF9lx7oluNC3NvxsSZ8/rgpVKEOJTA==",
"version": "56.1.16",
"resolved": "https://registry.npmmirror.com/@expo/cli/-/cli-56.1.16.tgz",
"integrity": "sha512-VBQn0mqAwc67b9Cn0RVXyeodghomAx5xGRhA/bXaQzuxDjMQk0zIOb6pXMZX7yiIwJW66UZt/zQiJNSv6aWJYw==",
"license": "MIT",
"dependencies": {
"@expo/code-signing-certificates": "^0.0.6",
"@expo/config": "~56.0.9",
"@expo/config-plugins": "~56.0.8",
"@expo/config-plugins": "~56.0.9",
"@expo/devcert": "^1.2.1",
"@expo/env": "~2.3.0",
"@expo/image-utils": "^0.10.1",
"@expo/inline-modules": "^0.0.11",
"@expo/inline-modules": "^0.0.12",
"@expo/json-file": "^10.2.0",
"@expo/log-box": "^56.0.13",
"@expo/metro": "~56.0.0",
@@ -7101,9 +7101,9 @@
"@expo/osascript": "^2.6.0",
"@expo/package-manager": "^1.12.1",
"@expo/plist": "^0.7.0",
"@expo/prebuild-config": "^56.0.15",
"@expo/prebuild-config": "^56.0.16",
"@expo/require-utils": "^56.1.3",
"@expo/router-server": "^56.0.13",
"@expo/router-server": "^56.0.14",
"@expo/schema-utils": "^56.0.0",
"@expo/spawn-async": "^1.8.0",
"@expo/ws-tunnel": "^2.0.0",
@@ -7368,9 +7368,9 @@
}
},
"node_modules/expo/node_modules/semver": {
"version": "7.8.4",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.4.tgz",
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
"version": "7.8.5",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -11318,9 +11318,9 @@
}
},
"node_modules/npm-package-arg/node_modules/semver": {
"version": "7.8.4",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.4.tgz",
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
"version": "7.8.5",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"

View File

@@ -16,7 +16,7 @@
"test:watch": "jest --watch"
},
"dependencies": {
"@expo/ui": "~56.0.17",
"@expo/ui": "~56.0.18",
"@expo/vector-icons": "^15.1.1",
"@livekit/react-native": "^2.11.0",
"@livekit/react-native-webrtc": "^144.1.0",
@@ -26,31 +26,31 @@
"@types/react": "~19.2.14",
"axios": "^1.16.1",
"date-fns": "^4.4.0",
"expo": "~56.0.11",
"expo-background-task": "~56.0.18",
"expo": "~56.0.12",
"expo-background-task": "~56.0.19",
"expo-callkit-telecom": "^0.3.9",
"expo-camera": "~56.0.8",
"expo-constants": "~56.0.18",
"expo-dev-client": "~56.0.20",
"expo-document-picker": "~56.0.4",
"expo-file-system": "~56.0.8",
"expo-font": "~56.0.6",
"expo-font": "~56.0.7",
"expo-haptics": "~56.0.3",
"expo-image": "~56.0.6",
"expo-image-picker": "~56.0.17",
"expo-image-picker": "~56.0.18",
"expo-intent-launcher": "~56.0.4",
"expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.14",
"expo-media-library": "~56.0.7",
"expo-notifications": "~56.0.17",
"expo-router": "~56.2.10",
"expo-notifications": "~56.0.18",
"expo-router": "~56.2.11",
"expo-splash-screen": "~56.0.10",
"expo-sqlite": "~56.0.5",
"expo-status-bar": "~56.0.4",
"expo-system-ui": "~56.0.5",
"expo-task-manager": "~56.0.18",
"expo-task-manager": "~56.0.19",
"expo-updates": "~56.0.19",
"expo-video": "~56.1.3",
"expo-video": "~56.1.4",
"jcore-react-native": "^2.3.6",
"jpush-react-native": "^3.2.7",
"katex": "^0.17.0",
@@ -89,7 +89,12 @@
"expo": {
"install": {
"exclude": [
"expo-image"
"expo-image",
"@react-native-async-storage/async-storage",
"@shopify/flash-list",
"react-native-pager-view",
"react-native-gesture-handler",
"react-native-safe-area-context"
]
}
}

View File

@@ -202,6 +202,10 @@ export function hrefProfileAbout(): string {
return '/profile/about';
}
export function hrefProfileHelp(): string {
return '/profile/help';
}
export function hrefProfileTerms(): string {
return '/terms';
}

View File

@@ -0,0 +1,397 @@
/**
* 帮助与反馈页面 HelpScreen
* 威友 - 常见问题、官方联系方式
* 扁平化设计风格,可折叠问答,参考关于我们、设置页面
*/
import React, { useMemo, useState, useCallback } from 'react';
import {
View,
StyleSheet,
TouchableOpacity,
ScrollView,
Linking,
Alert,
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { StatusBar } from 'expo-status-bar';
import { useRouter } from 'expo-router';
import {
useAppColors,
spacing,
fontSizes,
borderRadius,
type AppColors,
} from '../../theme';
import { Text, SimpleHeader } from '../../components/common';
import { useResponsive, useResponsiveSpacing } from '../../hooks';
const CONTENT_MAX_WIDTH = 720;
// 威友橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FFF1EA',
};
// 官方联系邮箱
const CONTACT_EMAIL = 'system@qczlit.cn';
interface FAQItem {
q: string;
a: string;
/** 补充提示(如注意事项),与正文区分展示 */
note?: string;
}
interface FAQSection {
key: string;
title: string;
icon: string;
items: FAQItem[];
}
// 常见问题内容
const FAQ_SECTIONS: FAQSection[] = [
{
key: 'account',
title: '账号与认证',
icon: 'account-check-outline',
items: [
{
q: '如何通过身份认证?',
a: '进入「我的」页面,点击右上角齿轮状设置图标,选择「身份认证」。按提示填写学号/工号、真实姓名,并上传有效佐证材料。提交后通常等待 1-2 小时即可完成审核。',
},
{
q: '忘记密码怎么找回?',
a: '在登录页点击「忘记密码」,通过已绑定的手机号或校内邮箱进行重置。若均未绑定,请联系客服人工处理。',
},
{
q: '如何绑定手机号?',
a: '进入「我的」页面,点击「编辑资料」,选择「手机号」,输入手机号码并完成验证即可绑定。',
},
],
},
{
key: 'campus',
title: '校园工具',
icon: 'school-outline',
items: [
{
q: '如何查看课表?',
a: '进入「应用」页面,点击「课表」,点击左上角齿轮状设置图标,选择「同步教务系统」。输入学号/工号及教务系统密码即可同步。',
note: '该密码与统一身份认证密码不同。如遗忘,可登录新教务系统,进入「个人中心」→「修改密码」进行重置,默认密码为身份证号码后 6 位。',
},
{
q: '如何查找学习资料?',
a: '进入「应用」页面,点击「学习资料」,可按学科分类浏览,也可点击右上角搜索图标快速查找所需资料。',
},
],
},
{
key: 'notification',
title: '消息与通知',
icon: 'bell-outline',
items: [
{
q: '如何及时收到消息通知?',
a: '请先在手机系统「设置」→「通知管理」或「应用管理」中,找到本 App开启通知权限及自启动/后台运行权限;然后进入 App 内「我的」页面,点击右上角齿轮状设置图标,进入「通知管理」,确认所需消息提醒均已开启。设置完成后重启 App 即可生效。',
},
],
},
];
function createHelpStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.paper,
},
scrollContent: {
paddingVertical: spacing.lg,
},
content: {
maxWidth: CONTENT_MAX_WIDTH,
alignSelf: 'center',
width: '100%',
},
// 分组
section: {
marginBottom: spacing.xl,
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing['2xl'],
marginBottom: spacing.sm,
marginTop: spacing.sm,
},
sectionIcon: {
width: 22,
height: 22,
borderRadius: borderRadius.sm,
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.sm,
},
sectionTitle: {
fontWeight: '600',
fontSize: fontSizes.sm,
color: colors.text.secondary,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
// 问答卡片
faqList: {
backgroundColor: colors.background.paper,
borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: StyleSheet.hairlineWidth,
borderColor: colors.divider,
overflow: 'hidden',
},
faqItem: {
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.divider,
},
faqItemLast: {
borderBottomWidth: 0,
},
faqQuestion: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 16,
paddingHorizontal: spacing['2xl'],
},
questionText: {
flex: 1,
fontSize: fontSizes.lg,
fontWeight: '500',
color: colors.text.primary,
lineHeight: fontSizes.lg * 1.4,
},
faqAnswer: {
paddingHorizontal: spacing['2xl'],
paddingBottom: 16,
},
answerText: {
fontSize: fontSizes.md,
lineHeight: fontSizes.md * 1.7,
color: colors.text.secondary,
},
// 补充提示
noteContainer: {
flexDirection: 'row',
alignItems: 'flex-start',
marginTop: spacing.md,
padding: spacing.md,
backgroundColor: THEME_COLORS.primaryLight,
borderRadius: borderRadius.md,
gap: spacing.sm,
},
noteText: {
flex: 1,
fontSize: fontSizes.sm,
lineHeight: fontSizes.sm * 1.6,
color: colors.text.secondary,
},
// 联系我们
contactCard: {
marginHorizontal: spacing['2xl'],
marginTop: spacing.sm,
padding: spacing.xl,
borderRadius: borderRadius.xl,
backgroundColor: colors.background.default,
alignItems: 'center',
},
contactIconWrap: {
width: 52,
height: 52,
borderRadius: borderRadius.full,
backgroundColor: THEME_COLORS.primary,
justifyContent: 'center',
alignItems: 'center',
marginBottom: spacing.md,
},
contactTitle: {
fontSize: fontSizes.xl,
fontWeight: '600',
color: colors.text.primary,
marginBottom: spacing.xs,
},
contactDesc: {
fontSize: fontSizes.md,
color: colors.text.secondary,
textAlign: 'center',
marginBottom: spacing.lg,
lineHeight: fontSizes.md * 1.6,
},
contactButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: spacing.md,
paddingHorizontal: spacing.xl,
borderRadius: borderRadius.full,
backgroundColor: THEME_COLORS.primary,
gap: spacing.sm,
},
contactButtonText: {
fontSize: fontSizes.md,
fontWeight: '600',
color: '#fff',
},
// 底部版权
footer: {
alignItems: 'center',
marginTop: spacing.xl,
paddingBottom: spacing.xl,
},
footerText: {
fontSize: fontSizes.sm,
color: colors.text.hint,
textAlign: 'center',
},
});
}
export const HelpScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createHelpStyles(colors), [colors]);
const insets = useSafeAreaInsets();
const router = useRouter();
const { isMobile } = useResponsive();
const responsivePadding = useResponsiveSpacing({ xs: 8, sm: 12, md: 16, lg: 24, xl: 32 });
// 当前展开的问题 key同一时刻仅展开一个
const [expandedKey, setExpandedKey] = useState<string | null>(
`${FAQ_SECTIONS[0].key}-0`,
);
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
const toggleItem = useCallback((key: string) => {
setExpandedKey((prev) => (prev === key ? null : key));
}, []);
const handleContactEmail = useCallback(async () => {
const url = `mailto:${CONTACT_EMAIL}`;
try {
const canOpen = await Linking.canOpenURL(url);
if (canOpen) {
await Linking.openURL(url);
} else {
Alert.alert('联系我们', `请发送邮件至:${CONTACT_EMAIL}`);
}
} catch {
Alert.alert('联系我们', `请发送邮件至:${CONTACT_EMAIL}`);
}
}, []);
const renderFAQItem = (
section: FAQSection,
item: FAQItem,
index: number,
) => {
const key = `${section.key}-${index}`;
const isExpanded = expandedKey === key;
const isLast = index === section.items.length - 1;
return (
<View
key={key}
style={[styles.faqItem, isLast && styles.faqItemLast]}
>
<TouchableOpacity
style={styles.faqQuestion}
onPress={() => toggleItem(key)}
activeOpacity={0.7}
>
<Text style={styles.questionText}>{item.q}</Text>
<MaterialCommunityIcons
name={isExpanded ? 'chevron-up' : 'chevron-down'}
size={20}
color={colors.text.hint}
/>
</TouchableOpacity>
{isExpanded && (
<View style={styles.faqAnswer}>
<Text style={styles.answerText}>{item.a}</Text>
{item.note && (
<View style={styles.noteContainer}>
<MaterialCommunityIcons
name="alert-circle-outline"
size={16}
color={THEME_COLORS.primary}
/>
<Text style={styles.noteText}>{item.note}</Text>
</View>
)}
</View>
)}
</View>
);
};
const renderSection = (section: FAQSection) => (
<View key={section.key} style={styles.section}>
<View style={styles.sectionHeader}>
<View style={[styles.sectionIcon, { backgroundColor: THEME_COLORS.primaryLight }]}>
<MaterialCommunityIcons name={section.icon as any} size={14} color={THEME_COLORS.primary} />
</View>
<Text style={styles.sectionTitle}>{section.title}</Text>
</View>
<View style={styles.faqList}>
{section.items.map((item, index) => renderFAQItem(section, item, index))}
</View>
</View>
);
return (
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
<StatusBar style="auto" />
<SimpleHeader title="帮助与反馈" onBack={() => router.back()} />
<ScrollView
contentContainerStyle={[
styles.scrollContent,
{ paddingBottom: scrollBottomInset, paddingHorizontal: responsivePadding },
]}
showsVerticalScrollIndicator={false}
>
<View style={styles.content}>
{FAQ_SECTIONS.map(renderSection)}
{/* 联系我们 */}
<View style={styles.contactCard}>
<View style={styles.contactIconWrap}>
<MaterialCommunityIcons name="email-outline" size={26} color="#fff" />
</View>
<Text style={styles.contactTitle}></Text>
<Text style={styles.contactDesc}>
</Text>
<TouchableOpacity
style={styles.contactButton}
onPress={handleContactEmail}
activeOpacity={0.8}
>
<MaterialCommunityIcons name="email-fast-outline" size={18} color="#fff" />
<Text style={styles.contactButtonText}>{CONTACT_EMAIL}</Text>
</TouchableOpacity>
</View>
<View style={styles.footer}>
<Text style={styles.footerText}>© 2026 </Text>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
};
export default HelpScreen;

View File

@@ -271,7 +271,7 @@ export const SettingsScreen: React.FC = () => {
router.push(hrefs.hrefProfileAbout());
break;
case 'help':
Alert.alert('帮助与反馈', '帮助中心即将上线!');
router.push(hrefs.hrefProfileHelp());
break;
case 'edit_profile':

View File

@@ -15,6 +15,7 @@ export { NotificationSettingsScreen } from './NotificationSettingsScreen';
export { BlockedUsersScreen } from './BlockedUsersScreen';
export { AccountSecurityScreen } from './AccountSecurityScreen';
export { AboutScreen } from './AboutScreen';
export { HelpScreen } from './HelpScreen';
export { TermsOfServiceScreen } from './TermsOfServiceScreen';
export { PrivacyPolicyScreen } from './PrivacyPolicyScreen';
export { VerificationSettingsScreen } from './VerificationSettingsScreen';

View File

@@ -505,6 +505,10 @@ class ApiClient {
let data: ApiResponse<T>;
// 根据请求路径决定 form 字段名:
// 后端 UploadFile handler 期望字段名为 "file",其余(图片/头像/封面)为 "image"
const fieldName = path === '/uploads/files' ? 'file' : 'image';
if (Platform.OS === 'web') {
// Web 端fetch + Blob/File + FormData
const formData = new FormData();
@@ -513,7 +517,7 @@ class ApiClient {
const uploadFile = new File([blob], file.name, {
type: file.type || blob.type || 'application/octet-stream',
});
formData.append('image', uploadFile);
formData.append(fieldName, uploadFile);
if (additionalData) {
Object.keys(additionalData).forEach(key => {
@@ -534,7 +538,7 @@ class ApiClient {
const result = await fsFile.upload(`${this.baseUrl}${path}`, {
httpMethod: 'POST',
uploadType: UploadType.MULTIPART,
fieldName: 'image',
fieldName,
mimeType: file.type,
headers,
parameters: additionalData,