@norcy/react-native-toolkit 0.3.29 → 0.3.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,36 +1,63 @@
1
- import cloudbase from '@cloudbase/js-sdk';
2
1
  import adapter from '@cloudbase/adapter-rn';
3
- import { Platform } from 'react-native';
2
+ import cloudbase from '@cloudbase/js-sdk';
4
3
  import {
5
4
  LoginAuthDataType,
6
5
  LoginType,
7
6
  LoginUserDataType,
8
7
  UserType,
9
8
  } from './constant';
9
+ import { SyncPrefData } from './SyncPrefData';
10
10
  import { ToolkitConfig } from './Tool';
11
11
 
12
12
  cloudbase.useAdapters(adapter);
13
13
 
14
- /**
15
- * CloudBase 自定义登录所需的云函数约定(需部署到 CloudBase 并开启 HTTP 访问服务):
16
- *
17
- * ### loginWithAuthData(通过 ToolkitConfig.CloudBaseAuthUrl HTTP 调用)
18
- * 请求体: { action, authData: { openid, unionid?, loginType }, userData?: { nickname, headimgurl, email }, source? }
19
- * 响应体: { ticket: string, user: UserProfile, isNew: boolean }
20
- * 逻辑: 根据 openid+loginType 查找 user_profile,不存在则创建用户(CreateUser API + user_profile 文档),
21
- * 用 app.auth().createTicket(uid) 签发 ticket 并连同 user_profile 一起返回。
22
- *
23
- * ### destroyUser(已登录态下通过 callFunction 调用,云函数名 "auth")
24
- * event: { action: 'destroyUser' }
25
- * 逻辑: 从 context.auth 获取 uid,删除用户系统记录(DeleteUsers API)及 user_profile 文档。
26
- */
27
-
28
14
  let app: cloudbase.app.App | null = null;
29
- let cachedProfile: any = null;
30
15
  let authUrl = '';
16
+ const CACHED_PROFILE_KEY = 'CloudBaseAuth_CachedProfile_v1';
17
+ const LOGIN_TYPE_MAP: Record<string, number> = {
18
+ wechat: LoginType.LoginTypeWeChat,
19
+ visitor: LoginType.LoginTypeVisitor,
20
+ huawei: LoginType.LoginTypeHuawei,
21
+ apple: LoginType.LoginTypeApple,
22
+ };
31
23
 
32
- const ensureInit = () => {
24
+ interface CloudBaseProfile {
25
+ _id: string;
26
+ nickname: string;
27
+ email: string;
28
+ headimgurl: string;
29
+ loginType: string;
30
+ vipType?: string;
31
+ vipEndTime?: string | number | Date;
32
+ createdAt?: string | number | Date;
33
+ attr?: unknown;
34
+ webdav?: unknown;
35
+ }
36
+
37
+ interface AuthResponse {
38
+ ticket: string;
39
+ user: CloudBaseProfile;
40
+ isNew?: boolean;
41
+ }
42
+
43
+ const toLocalUser = (profile: CloudBaseProfile): UserType => {
44
+ return {
45
+ id: profile._id,
46
+ nickname: profile.nickname,
47
+ email: profile.email,
48
+ headimgurl: profile.headimgurl,
49
+ type: LOGIN_TYPE_MAP[profile.loginType],
50
+ vipType: profile.vipType,
51
+ vipEndTime: profile.vipEndTime ? new Date(profile.vipEndTime) : undefined,
52
+ createdAt: profile.createdAt ? new Date(profile.createdAt) : undefined,
53
+ attr: profile.attr,
54
+ webdav: profile.webdav,
55
+ };
56
+ };
57
+
58
+ const getApp = () => {
33
59
  if (app) return app;
60
+
34
61
  const {
35
62
  CloudBaseEnv,
36
63
  CloudBaseAccessKey,
@@ -42,6 +69,7 @@ const ensureInit = () => {
42
69
  'CloudBase config missing. Set CloudBaseEnv / CloudBaseAccessKey / CloudBaseAuthUrl in ToolkitConfig.'
43
70
  );
44
71
  }
72
+
45
73
  app = cloudbase.init({
46
74
  env: CloudBaseEnv,
47
75
  region: CloudBaseRegion || 'ap-shanghai',
@@ -52,174 +80,105 @@ const ensureInit = () => {
52
80
  return app;
53
81
  };
54
82
 
55
- const getLoginTypeNum = (loginType?: string) => {
56
- if (loginType === 'wechat') {
57
- return LoginType.LoginTypeWeChat;
83
+ const setCachedProfile = (profile?: CloudBaseProfile) => {
84
+ if (!profile) {
85
+ SyncPrefData.delete(CACHED_PROFILE_KEY);
86
+ } else {
87
+ SyncPrefData.set(CACHED_PROFILE_KEY, JSON.stringify(profile));
58
88
  }
59
- if (loginType === 'visitor') {
60
- return LoginType.LoginTypeVisitor;
61
- }
62
- if (loginType === 'huawei') {
63
- return LoginType.LoginTypeHuawei;
64
- }
65
- return LoginType.LoginTypeApple;
66
89
  };
67
90
 
68
- const profileToLocalUser = (profile: any): UserType => {
69
- return {
70
- id: profile._id ?? '',
71
- nickname: profile.nickname,
72
- email: profile.email,
73
- headimgurl: profile.headimgurl,
74
- type: profile.type ?? getLoginTypeNum(profile.loginType),
75
- vipType: profile.vipType,
76
- vipEndTime: profile.vipEndTime ? new Date(profile.vipEndTime) : undefined,
77
- createdAt: profile.createdAt ? new Date(profile.createdAt) : undefined,
78
- attr: profile.attr,
79
- webdav: profile.webdav,
80
- };
91
+ const getUid = () => {
92
+ return getApp().auth.hasLoginState()?.user.uid;
93
+ };
94
+
95
+ const requestAuth = async (body: Record<string, unknown>) => {
96
+ getApp();
97
+ const response = await fetch(authUrl, {
98
+ method: 'POST',
99
+ headers: { 'Content-Type': 'application/json' },
100
+ body: JSON.stringify(body),
101
+ });
102
+
103
+ if (!response.ok) {
104
+ throw new Error(`CloudBase auth failed: ${response.status}`);
105
+ }
106
+
107
+ return response.json() as Promise<AuthResponse>;
81
108
  };
82
109
 
83
110
  export const CloudBaseAuth = {
84
- getCurrentUser: async (): Promise<UserType | null> => {
85
- const cbApp = ensureInit();
86
- const loginState = cbApp.auth.hasLoginState();
87
- if (!loginState) {
88
- cachedProfile = null;
111
+ getLocalUser: async (): Promise<UserType | null> => {
112
+ const uid = getUid();
113
+ if (!uid) {
114
+ setCachedProfile();
89
115
  return null;
90
116
  }
91
117
 
92
- if (cachedProfile) {
93
- return profileToLocalUser(cachedProfile);
118
+ const raw = SyncPrefData.getString(CACHED_PROFILE_KEY);
119
+ const profile = raw ? (JSON.parse(raw) as CloudBaseProfile) : null;
120
+ if (profile?._id === uid) {
121
+ return toLocalUser(profile);
94
122
  }
95
123
 
96
- const uid = cbApp.auth.currentUser?.uid ?? loginState.user?.uid;
97
- if (!uid) return null;
98
-
99
- try {
100
- const db = cbApp.database();
101
- const { data } = await db.collection('user-profile').doc(uid).get();
102
- if (!data || (Array.isArray(data) && data.length === 0)) return null;
103
- cachedProfile = Array.isArray(data) ? data[0] : data;
104
- return profileToLocalUser(cachedProfile);
105
- } catch (error) {
106
- console.log('CloudBase getCurrentUser error', error);
107
- return null;
108
- }
124
+ await CloudBaseAuth.logout();
125
+ return null;
109
126
  },
110
127
 
111
- loginWithAuthData: async (
128
+ login: async (
112
129
  authData: LoginAuthDataType,
113
130
  userData?: LoginUserDataType
114
131
  ): Promise<UserType> => {
115
- console.log('CloudBase 开始登录', userData);
116
- const cbApp = ensureInit();
117
- const loginType = authData.loginType || 'apple';
118
-
119
- const response = await fetch(authUrl, {
120
- method: 'POST',
121
- headers: { 'Content-Type': 'application/json' },
122
- body: JSON.stringify({
123
- action: 'loginWithAuthData',
124
- authData: {
125
- openid: authData.openid,
126
- unionid: authData.unionid,
127
- loginType,
128
- },
129
- userData: userData
130
- ? {
131
- nickname: userData.nickname,
132
- headimgurl: userData.headimgurl,
133
- email: userData.email,
134
- }
135
- : undefined,
136
- source: Platform.OS === 'android' ? 2 : undefined,
137
- }),
132
+ const { ticket, user, isNew } = await requestAuth({
133
+ action: 'login',
134
+ authData: {
135
+ openid: authData.openid,
136
+ unionid: authData.unionid,
137
+ loginType: authData.loginType,
138
+ },
139
+ userData: userData
140
+ ? {
141
+ nickname: userData.nickname,
142
+ headimgurl: userData.headimgurl,
143
+ email: userData.email,
144
+ }
145
+ : undefined,
138
146
  });
139
147
 
140
- if (!response.ok) {
141
- const text = await response.text().catch(() => '');
142
- console.log('CloudBase 登录失败', response.status, text);
143
- throw new Error(`CloudBase auth request failed: ${response.status}`);
144
- }
145
-
146
- const result = await response.json();
147
- const { ticket, user, isNew } = result;
148
-
149
- if (!ticket) {
150
- console.log('CloudBase 登录失败: no ticket returned');
151
- throw new Error('CloudBase auth: server did not return a ticket');
152
- }
153
-
154
- if (!user) {
155
- console.log('CloudBase 登录失败: no user returned', result);
156
- throw new Error('CloudBase auth: server did not return a user profile');
157
- }
158
-
159
- const signInResult: any = await cbApp.auth.signInWithCustomTicket(() =>
160
- Promise.resolve(ticket)
161
- );
162
- if (signInResult?.error) {
163
- console.log('CloudBase 登录失败', signInResult.error);
164
- throw signInResult.error;
165
- }
166
- console.log('CloudBase 登录成功');
167
-
168
- cachedProfile = user;
169
- const localUser = profileToLocalUser(user);
170
- localUser.isNew = isNew ?? false;
148
+ await getApp().auth.signInWithCustomTicket(() => Promise.resolve(ticket));
149
+ setCachedProfile(user);
150
+ const localUser = toLocalUser(user);
151
+ localUser.isNew = isNew;
171
152
  return localUser;
172
153
  },
173
154
 
174
- logout: () => {
175
- const cbApp = ensureInit();
176
- cbApp.auth.signOut().catch((e: any) => {
177
- console.log('CloudBase signOut error', e);
178
- });
179
- cachedProfile = null;
155
+ logout: async () => {
156
+ await getApp().auth.signOut();
157
+ setCachedProfile();
180
158
  },
181
159
 
182
160
  updateUser: async (keys: string[], values: any[]): Promise<UserType> => {
183
- const cbApp = ensureInit();
184
- const uid = cbApp.auth.currentUser?.uid;
161
+ const uid = getUid();
185
162
  if (!uid) throw new Error('User Not Login Yet');
186
163
 
187
- const db = cbApp.database();
188
- const updateData: Record<string, any> = {};
189
- const oldValues: any[] = [];
190
-
191
- for (let i = 0; i < keys.length; i++) {
192
- oldValues.push(cachedProfile?.[keys[i]]);
193
- updateData[keys[i]] = values[i];
194
- }
195
-
196
- try {
197
- await db.collection('user-profile').doc(uid).update(updateData);
198
- const { data } = await db.collection('user-profile').doc(uid).get();
199
- cachedProfile = Array.isArray(data) ? data[0] : data;
200
- return profileToLocalUser(cachedProfile);
201
- } catch (error) {
202
- if (cachedProfile) {
203
- for (let i = 0; i < keys.length; i++) {
204
- cachedProfile[keys[i]] = oldValues[i];
205
- }
206
- }
207
- throw error;
208
- }
164
+ const { user } = await requestAuth({
165
+ action: 'updateUser',
166
+ uid,
167
+ keys,
168
+ values,
169
+ });
170
+ setCachedProfile(user);
171
+ return toLocalUser(user);
209
172
  },
210
173
 
211
174
  destroyUser: async () => {
212
- const cbApp = ensureInit();
213
- const uid = cbApp.auth.currentUser?.uid;
175
+ const uid = getUid();
214
176
  if (!uid) throw new Error('User Not Login Yet');
215
177
 
216
- console.log('get user success');
217
- await cbApp.callFunction({
178
+ await getApp().callFunction({
218
179
  name: 'auth',
219
180
  data: { action: 'destroyUser' },
220
181
  });
221
- console.log('delete user success');
222
- await cbApp.auth.signOut();
223
- cachedProfile = null;
182
+ await CloudBaseAuth.logout();
224
183
  },
225
184
  };
@@ -1,5 +1,4 @@
1
1
  import { User } from 'leancloud-storage';
2
- import { Platform } from 'react-native';
3
2
  import {
4
3
  LoginAuthDataType,
5
4
  LoginType,
@@ -38,12 +37,12 @@ const getLoginTypeNum = (loginType?: string) => {
38
37
  };
39
38
 
40
39
  export const LeanCloudAuth = {
41
- getCurrentUser: async (): Promise<UserType | null> => {
40
+ getLocalUser: async (): Promise<UserType | null> => {
42
41
  const user = await AV.User.currentAsync();
43
42
  return user ? AVUserToLocalUser(user) : null;
44
43
  },
45
44
 
46
- loginWithAuthData: async (
45
+ login: async (
47
46
  authData: LoginAuthDataType,
48
47
  userData?: LoginUserDataType
49
48
  ): Promise<UserType> => {
@@ -80,9 +79,6 @@ export const LeanCloudAuth = {
80
79
  newUser.set('nickname', userData?.nickname);
81
80
  newUser.set('headimgurl', userData?.headimgurl);
82
81
  newUser.set('type', getLoginTypeNum(loginType));
83
- if (Platform.OS === 'android') {
84
- newUser.set('source', 2);
85
- }
86
82
  try {
87
83
  const user: User = await newUser.loginWithAuthData(authData, loginType);
88
84
  console.log('Lean Cloud 注册成功');
@@ -96,8 +92,8 @@ export const LeanCloudAuth = {
96
92
  }
97
93
  },
98
94
 
99
- logout: () => {
100
- AV.User.logOut();
95
+ logout: async () => {
96
+ await AV.User.logOut();
101
97
  },
102
98
 
103
99
  updateUser: async (keys: string[], values: any[]): Promise<UserType> => {
@@ -55,7 +55,7 @@ const doLogin = async (
55
55
  userData?: LoginUserDataType
56
56
  ) => {
57
57
  try {
58
- const user = await Auth.loginWithAuthData(authData, userData);
58
+ const user = await Auth.login(authData, userData);
59
59
  onLoginFinish({ user });
60
60
  } catch (error: any) {
61
61
  onLoginFinish({ error });
@@ -182,11 +182,16 @@ export const LoginManager = {
182
182
  }
183
183
  },
184
184
 
185
- logOut: (callback?: () => void) => {
186
- console.log('onLogout');
187
- Auth.logOut();
188
- callback?.();
189
- Notification.postNotification('onLogout');
185
+ logOut: async (callback?: (error?: unknown) => void) => {
186
+ try {
187
+ await Auth.logOut();
188
+ console.log('onLogout');
189
+ callback?.();
190
+ Notification.postNotification('onLogout');
191
+ } catch (e) {
192
+ console.error(e);
193
+ callback?.(e);
194
+ }
190
195
  },
191
196
 
192
197
  isLogin: () => {