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

View File

@@ -16,7 +16,7 @@
"test:watch": "jest --watch" "test:watch": "jest --watch"
}, },
"dependencies": { "dependencies": {
"@expo/ui": "~56.0.17", "@expo/ui": "~56.0.18",
"@expo/vector-icons": "^15.1.1", "@expo/vector-icons": "^15.1.1",
"@livekit/react-native": "^2.11.0", "@livekit/react-native": "^2.11.0",
"@livekit/react-native-webrtc": "^144.1.0", "@livekit/react-native-webrtc": "^144.1.0",
@@ -26,31 +26,31 @@
"@types/react": "~19.2.14", "@types/react": "~19.2.14",
"axios": "^1.16.1", "axios": "^1.16.1",
"date-fns": "^4.4.0", "date-fns": "^4.4.0",
"expo": "~56.0.11", "expo": "~56.0.12",
"expo-background-task": "~56.0.18", "expo-background-task": "~56.0.19",
"expo-callkit-telecom": "^0.3.9", "expo-callkit-telecom": "^0.3.9",
"expo-camera": "~56.0.8", "expo-camera": "~56.0.8",
"expo-constants": "~56.0.18", "expo-constants": "~56.0.18",
"expo-dev-client": "~56.0.20", "expo-dev-client": "~56.0.20",
"expo-document-picker": "~56.0.4", "expo-document-picker": "~56.0.4",
"expo-file-system": "~56.0.8", "expo-file-system": "~56.0.8",
"expo-font": "~56.0.6", "expo-font": "~56.0.7",
"expo-haptics": "~56.0.3", "expo-haptics": "~56.0.3",
"expo-image": "~56.0.6", "expo-image": "~56.0.6",
"expo-image-picker": "~56.0.17", "expo-image-picker": "~56.0.18",
"expo-intent-launcher": "~56.0.4", "expo-intent-launcher": "~56.0.4",
"expo-linear-gradient": "~56.0.4", "expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.14", "expo-linking": "~56.0.14",
"expo-media-library": "~56.0.7", "expo-media-library": "~56.0.7",
"expo-notifications": "~56.0.17", "expo-notifications": "~56.0.18",
"expo-router": "~56.2.10", "expo-router": "~56.2.11",
"expo-splash-screen": "~56.0.10", "expo-splash-screen": "~56.0.10",
"expo-sqlite": "~56.0.5", "expo-sqlite": "~56.0.5",
"expo-status-bar": "~56.0.4", "expo-status-bar": "~56.0.4",
"expo-system-ui": "~56.0.5", "expo-system-ui": "~56.0.5",
"expo-task-manager": "~56.0.18", "expo-task-manager": "~56.0.19",
"expo-updates": "~56.0.19", "expo-updates": "~56.0.19",
"expo-video": "~56.1.3", "expo-video": "~56.1.4",
"jcore-react-native": "^2.3.6", "jcore-react-native": "^2.3.6",
"jpush-react-native": "^3.2.7", "jpush-react-native": "^3.2.7",
"katex": "^0.17.0", "katex": "^0.17.0",
@@ -89,7 +89,12 @@
"expo": { "expo": {
"install": { "install": {
"exclude": [ "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'; return '/profile/about';
} }
export function hrefProfileHelp(): string {
return '/profile/help';
}
export function hrefProfileTerms(): string { export function hrefProfileTerms(): string {
return '/terms'; 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()); router.push(hrefs.hrefProfileAbout());
break; break;
case 'help': case 'help':
Alert.alert('帮助与反馈', '帮助中心即将上线!'); router.push(hrefs.hrefProfileHelp());
break; break;
case 'edit_profile': case 'edit_profile':

View File

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

View File

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