@oxyhq/services 5.8.1 → 5.8.3

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.
Files changed (137) hide show
  1. package/lib/commonjs/index.js +9 -27
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/node/createAuth.js +7 -585
  4. package/lib/commonjs/node/createAuth.js.map +1 -1
  5. package/lib/commonjs/node/index.js +1 -38
  6. package/lib/commonjs/node/index.js.map +1 -1
  7. package/lib/commonjs/ui/components/FollowButton.js +100 -12
  8. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  9. package/lib/commonjs/ui/components/Header.js +40 -6
  10. package/lib/commonjs/ui/components/Header.js.map +1 -1
  11. package/lib/commonjs/ui/components/OxyProvider.js +5 -0
  12. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  13. package/lib/commonjs/ui/context/OxyContext.js +63 -125
  14. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  15. package/lib/commonjs/ui/hooks/index.js +6 -0
  16. package/lib/commonjs/ui/hooks/index.js.map +1 -1
  17. package/lib/commonjs/ui/hooks/useFollow.js +59 -2
  18. package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
  19. package/lib/commonjs/ui/navigation/OxyRouter.js +10 -0
  20. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  21. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +9 -0
  22. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  23. package/lib/commonjs/ui/screens/ProfileScreen.js +214 -37
  24. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  25. package/lib/commonjs/ui/screens/UserLinksScreen.js +90 -0
  26. package/lib/commonjs/ui/screens/UserLinksScreen.js.map +1 -0
  27. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js +9 -6
  28. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  29. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +3 -30
  30. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  31. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js +37 -46
  32. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  33. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +9 -12
  34. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js +9 -12
  36. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +9 -12
  38. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  39. package/lib/commonjs/ui/stores/authStore.js +24 -6
  40. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  41. package/lib/commonjs/ui/stores/followStore.js +106 -1
  42. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  43. package/lib/module/index.js +1 -3
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/module/node/createAuth.js +7 -584
  46. package/lib/module/node/createAuth.js.map +1 -1
  47. package/lib/module/node/index.js +1 -7
  48. package/lib/module/node/index.js.map +1 -1
  49. package/lib/module/ui/components/FollowButton.js +101 -13
  50. package/lib/module/ui/components/FollowButton.js.map +1 -1
  51. package/lib/module/ui/components/Header.js +40 -6
  52. package/lib/module/ui/components/Header.js.map +1 -1
  53. package/lib/module/ui/components/OxyProvider.js +5 -0
  54. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  55. package/lib/module/ui/context/OxyContext.js +63 -125
  56. package/lib/module/ui/context/OxyContext.js.map +1 -1
  57. package/lib/module/ui/hooks/index.js +1 -1
  58. package/lib/module/ui/hooks/index.js.map +1 -1
  59. package/lib/module/ui/hooks/useFollow.js +57 -1
  60. package/lib/module/ui/hooks/useFollow.js.map +1 -1
  61. package/lib/module/ui/navigation/OxyRouter.js +10 -0
  62. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  63. package/lib/module/ui/screens/AccountSettingsScreen.js +9 -0
  64. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  65. package/lib/module/ui/screens/ProfileScreen.js +214 -37
  66. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  67. package/lib/module/ui/screens/UserLinksScreen.js +85 -0
  68. package/lib/module/ui/screens/UserLinksScreen.js.map +1 -0
  69. package/lib/module/ui/screens/karma/KarmaAboutScreen.js +9 -6
  70. package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +1 -1
  71. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +3 -30
  72. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  73. package/lib/module/ui/screens/karma/KarmaFAQScreen.js +37 -46
  74. package/lib/module/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  75. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +9 -12
  76. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  77. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js +9 -12
  78. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
  79. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +9 -12
  80. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  81. package/lib/module/ui/stores/authStore.js +24 -6
  82. package/lib/module/ui/stores/authStore.js.map +1 -1
  83. package/lib/module/ui/stores/followStore.js +106 -1
  84. package/lib/module/ui/stores/followStore.js.map +1 -1
  85. package/lib/typescript/index.d.ts +1 -1
  86. package/lib/typescript/index.d.ts.map +1 -1
  87. package/lib/typescript/node/createAuth.d.ts +0 -112
  88. package/lib/typescript/node/createAuth.d.ts.map +1 -1
  89. package/lib/typescript/node/index.d.ts +0 -2
  90. package/lib/typescript/node/index.d.ts.map +1 -1
  91. package/lib/typescript/ui/components/FollowButton.d.ts +1 -0
  92. package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
  93. package/lib/typescript/ui/components/Header.d.ts +2 -0
  94. package/lib/typescript/ui/components/Header.d.ts.map +1 -1
  95. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  96. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  97. package/lib/typescript/ui/hooks/index.d.ts +1 -1
  98. package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
  99. package/lib/typescript/ui/hooks/useFollow.d.ts +20 -0
  100. package/lib/typescript/ui/hooks/useFollow.d.ts.map +1 -1
  101. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
  102. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  103. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  104. package/lib/typescript/ui/screens/UserLinksScreen.d.ts +15 -0
  105. package/lib/typescript/ui/screens/UserLinksScreen.d.ts.map +1 -0
  106. package/lib/typescript/ui/screens/karma/KarmaAboutScreen.d.ts.map +1 -1
  107. package/lib/typescript/ui/screens/karma/KarmaCenterScreen.d.ts.map +1 -1
  108. package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts.map +1 -1
  109. package/lib/typescript/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +1 -1
  110. package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
  111. package/lib/typescript/ui/screens/karma/KarmaRulesScreen.d.ts.map +1 -1
  112. package/lib/typescript/ui/stores/authStore.d.ts +3 -1
  113. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  114. package/lib/typescript/ui/stores/followStore.d.ts +10 -0
  115. package/lib/typescript/ui/stores/followStore.d.ts.map +1 -1
  116. package/package.json +3 -2
  117. package/src/index.ts +2 -10
  118. package/src/node/createAuth.ts +7 -623
  119. package/src/node/index.ts +1 -19
  120. package/src/ui/components/FollowButton.tsx +95 -11
  121. package/src/ui/components/Header.tsx +45 -4
  122. package/src/ui/components/OxyProvider.tsx +6 -0
  123. package/src/ui/context/OxyContext.tsx +65 -136
  124. package/src/ui/hooks/index.ts +1 -1
  125. package/src/ui/hooks/useFollow.ts +63 -0
  126. package/src/ui/navigation/OxyRouter.tsx +10 -0
  127. package/src/ui/screens/AccountSettingsScreen.tsx +8 -0
  128. package/src/ui/screens/ProfileScreen.tsx +191 -28
  129. package/src/ui/screens/UserLinksScreen.tsx +96 -0
  130. package/src/ui/screens/karma/KarmaAboutScreen.tsx +9 -2
  131. package/src/ui/screens/karma/KarmaCenterScreen.tsx +1 -20
  132. package/src/ui/screens/karma/KarmaFAQScreen.tsx +40 -24
  133. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +9 -3
  134. package/src/ui/screens/karma/KarmaRewardsScreen.tsx +9 -3
  135. package/src/ui/screens/karma/KarmaRulesScreen.tsx +9 -3
  136. package/src/ui/stores/authStore.ts +22 -7
  137. package/src/ui/stores/followStore.ts +102 -1
package/src/node/index.ts CHANGED
@@ -7,28 +7,10 @@ import { OxyServices, OXY_CLOUD_URL } from '../core'; // Adjusted path
7
7
  import { createAuth } from './createAuth';
8
8
  import * as Models from '../models/interfaces'; // Adjusted path
9
9
 
10
- // ------------- Enhanced Authentication Imports -------------
11
- import {
12
- OxyAuth,
13
- AuthRequest,
14
- AuthOptions,
15
- AuthMiddlewareOptions,
16
- TokenValidationResult
17
- } from './createAuth';
18
-
19
10
  // ------------- Core Exports -------------
20
11
  export { OxyServices, OXY_CLOUD_URL };
21
12
 
22
- // ------------- Enhanced Authentication Exports -------------
23
- export {
24
- OxyAuth,
25
- AuthRequest,
26
- AuthOptions,
27
- AuthMiddlewareOptions,
28
- TokenValidationResult
29
- };
30
-
31
- // Zero-config auth and session router (legacy)
13
+ // Zero-config auth and session router
32
14
  export { createAuth };
33
15
 
34
16
  // ------------- Model Exports -------------
@@ -21,6 +21,7 @@ import { useOxy } from '../context/OxyContext';
21
21
  import { fontFamilies } from '../styles/fonts';
22
22
  import { toast } from '../../lib/sonner';
23
23
  import { useFollow } from '../hooks/useFollow';
24
+ import { useThemeColors } from '../styles/theme';
24
25
 
25
26
  export interface FollowButtonProps {
26
27
  userId: string;
@@ -32,6 +33,7 @@ export interface FollowButtonProps {
32
33
  disabled?: boolean;
33
34
  showLoadingState?: boolean;
34
35
  preventParentActions?: boolean;
36
+ theme?: 'light' | 'dark';
35
37
  }
36
38
 
37
39
  const FollowButton: React.FC<FollowButtonProps> = ({
@@ -44,8 +46,10 @@ const FollowButton: React.FC<FollowButtonProps> = ({
44
46
  disabled = false,
45
47
  showLoadingState = true,
46
48
  preventParentActions = true,
49
+ theme = 'light',
47
50
  }) => {
48
51
  const { oxyServices, isAuthenticated } = useOxy();
52
+ const colors = useThemeColors(theme);
49
53
  const {
50
54
  isFollowing,
51
55
  isLoading,
@@ -96,19 +100,93 @@ const FollowButton: React.FC<FollowButtonProps> = ({
96
100
  }
97
101
  }, [disabled, isLoading, toggleFollow, onFollowChange, isFollowing, preventParentActions]);
98
102
 
99
- // Button styles
103
+ // Get button style based on size and follow state
100
104
  const getButtonStyle = (): StyleProp<ViewStyle> => {
101
- let baseStyle = styles.buttonMedium;
102
- if (size === 'small') baseStyle = styles.buttonSmall;
103
- if (size === 'large') baseStyle = styles.buttonLarge;
104
- return [baseStyle, isFollowing ? styles.following : styles.notFollowing, style];
105
+ const baseStyle = {
106
+ flexDirection: 'row' as const,
107
+ alignItems: 'center' as const,
108
+ justifyContent: 'center' as const,
109
+ borderWidth: 1,
110
+ ...Platform.select({
111
+ web: {
112
+ boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
113
+ },
114
+ default: {
115
+ shadowOffset: { width: 0, height: 2 },
116
+ shadowOpacity: 0.1,
117
+ shadowRadius: 4,
118
+ elevation: 2,
119
+ }
120
+ }),
121
+ };
122
+
123
+ // Size-specific styles
124
+ let sizeStyle = {};
125
+ if (size === 'small') {
126
+ sizeStyle = {
127
+ paddingVertical: 6,
128
+ paddingHorizontal: 12,
129
+ minWidth: 70,
130
+ borderRadius: 35,
131
+ };
132
+ } else if (size === 'large') {
133
+ sizeStyle = {
134
+ paddingVertical: 12,
135
+ paddingHorizontal: 24,
136
+ minWidth: 120,
137
+ borderRadius: 35,
138
+ };
139
+ } else {
140
+ // medium
141
+ sizeStyle = {
142
+ paddingVertical: 8,
143
+ paddingHorizontal: 16,
144
+ minWidth: 90,
145
+ borderRadius: 35,
146
+ };
147
+ }
148
+
149
+ // State-specific colors
150
+ let stateStyle = {};
151
+ if (isFollowing) {
152
+ stateStyle = {
153
+ backgroundColor: colors.primary,
154
+ borderColor: colors.primary,
155
+ shadowColor: colors.primary,
156
+ };
157
+ } else {
158
+ stateStyle = {
159
+ backgroundColor: 'transparent',
160
+ borderColor: colors.border,
161
+ shadowColor: colors.border,
162
+ };
163
+ }
164
+
165
+ return [baseStyle, sizeStyle, stateStyle, style];
105
166
  };
106
167
 
168
+ // Get text style based on size and follow state
107
169
  const getTextStyle = (): StyleProp<TextStyle> => {
108
- let baseStyle = styles.textMedium;
109
- if (size === 'small') baseStyle = styles.textSmall;
110
- if (size === 'large') baseStyle = styles.textLarge;
111
- return [baseStyle, isFollowing ? styles.textFollowing : styles.textNotFollowing, textStyle];
170
+ const baseTextStyle = {
171
+ fontFamily: fontFamilies.phuduSemiBold,
172
+ fontWeight: '600' as const,
173
+ };
174
+
175
+ // Size-specific text styles
176
+ let sizeTextStyle = {};
177
+ if (size === 'small') {
178
+ sizeTextStyle = { fontSize: 13 };
179
+ } else if (size === 'large') {
180
+ sizeTextStyle = { fontSize: 16 };
181
+ } else {
182
+ // medium
183
+ sizeTextStyle = { fontSize: 15 };
184
+ }
185
+
186
+ // State-specific text color
187
+ const textColor = isFollowing ? '#FFFFFF' : colors.text;
188
+
189
+ return [baseTextStyle, sizeTextStyle, { color: textColor }, textStyle];
112
190
  };
113
191
 
114
192
  return (
@@ -119,15 +197,21 @@ const FollowButton: React.FC<FollowButtonProps> = ({
119
197
  activeOpacity={0.8}
120
198
  >
121
199
  {showLoadingState && isLoading ? (
122
- <ActivityIndicator size={size === 'small' ? 'small' : 'large'} color={isFollowing ? '#fff' : '#007AFF'} />
200
+ <ActivityIndicator
201
+ size={size === 'small' ? 'small' : 'small'}
202
+ color={isFollowing ? '#FFFFFF' : colors.primary}
203
+ />
123
204
  ) : (
124
- <Text style={getTextStyle()}>{isFollowing ? 'Following' : 'Follow'}</Text>
205
+ <Text style={getTextStyle()}>
206
+ {isFollowing ? 'Following' : 'Follow'}
207
+ </Text>
125
208
  )}
126
209
  </TouchableOpacity>
127
210
  );
128
211
  };
129
212
 
130
213
  const styles = StyleSheet.create({
214
+ // Legacy styles kept for backward compatibility but not used in new implementation
131
215
  buttonSmall: {
132
216
  paddingVertical: 4,
133
217
  paddingHorizontal: 12,
@@ -28,6 +28,8 @@ export interface HeaderProps {
28
28
  showCloseButton?: boolean;
29
29
  variant?: 'default' | 'large' | 'minimal' | 'gradient';
30
30
  elevation?: 'none' | 'subtle' | 'prominent';
31
+ subtitleVariant?: 'default' | 'small' | 'large' | 'muted';
32
+ titleAlignment?: 'left' | 'center' | 'right';
31
33
  }
32
34
 
33
35
  const Header: React.FC<HeaderProps> = ({
@@ -41,6 +43,8 @@ const Header: React.FC<HeaderProps> = ({
41
43
  showCloseButton = false,
42
44
  variant = 'default',
43
45
  elevation = 'subtle',
46
+ subtitleVariant = 'default',
47
+ titleAlignment = 'left',
44
48
  }) => {
45
49
  const isDarkTheme = theme === 'dark';
46
50
 
@@ -139,11 +143,26 @@ const Header: React.FC<HeaderProps> = ({
139
143
 
140
144
  const subtitleStyle = variant === 'large' ? styles.subtitleLarge :
141
145
  variant === 'minimal' ? styles.subtitleMinimal :
142
- styles.subtitleDefault;
146
+ subtitleVariant === 'small' ? styles.subtitleSmall :
147
+ subtitleVariant === 'large' ? styles.subtitleLarge :
148
+ subtitleVariant === 'muted' ? styles.subtitleMuted :
149
+ styles.subtitleDefault;
150
+
151
+ const getTitleAlignment = () => {
152
+ switch (titleAlignment) {
153
+ case 'center':
154
+ return styles.titleContainerCenter;
155
+ case 'right':
156
+ return styles.titleContainerRight;
157
+ default:
158
+ return styles.titleContainerLeft;
159
+ }
160
+ };
143
161
 
144
162
  return (
145
163
  <View style={[
146
164
  styles.titleContainer,
165
+ getTitleAlignment(),
147
166
  variant === 'minimal' && styles.titleContainerMinimal
148
167
  ]}>
149
168
  <Text style={[titleStyle, { color: colors.text.primary }]}>
@@ -281,6 +300,15 @@ const styles = StyleSheet.create({
281
300
  alignItems: 'flex-start',
282
301
  justifyContent: 'center',
283
302
  },
303
+ titleContainerLeft: {
304
+ alignItems: 'flex-start',
305
+ },
306
+ titleContainerCenter: {
307
+ alignItems: 'center',
308
+ },
309
+ titleContainerRight: {
310
+ alignItems: 'flex-end',
311
+ },
284
312
  titleContainerMinimal: {
285
313
  alignItems: 'center',
286
314
  marginHorizontal: 16,
@@ -309,22 +337,35 @@ const styles = StyleSheet.create({
309
337
  },
310
338
  subtitleDefault: {
311
339
  fontSize: 14,
312
- fontFamily: fontFamilies.phuduMedium,
340
+ fontWeight: '400',
313
341
  lineHeight: 17,
314
342
  marginTop: 1,
315
343
  },
316
344
  subtitleLarge: {
317
345
  fontSize: 16,
318
- fontFamily: fontFamilies.phuduMedium,
346
+ fontWeight: '400',
319
347
  lineHeight: 19,
320
348
  marginTop: 3,
321
349
  },
322
350
  subtitleMinimal: {
323
351
  fontSize: 13,
324
- fontFamily: fontFamilies.phuduMedium,
352
+ fontWeight: '400',
325
353
  lineHeight: 15,
326
354
  marginTop: 1,
327
355
  },
356
+ subtitleSmall: {
357
+ fontSize: 12,
358
+ fontWeight: '400',
359
+ lineHeight: 14,
360
+ marginTop: 0,
361
+ },
362
+ subtitleMuted: {
363
+ fontSize: 14,
364
+ fontWeight: '400',
365
+ lineHeight: 17,
366
+ marginTop: 1,
367
+ opacity: 0.7,
368
+ },
328
369
  rightActionButton: {
329
370
  alignItems: 'center',
330
371
  justifyContent: 'center',
@@ -126,13 +126,17 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
126
126
  };
127
127
  }
128
128
  });
129
+
129
130
  // Add a method to navigate between screens
130
131
  // @ts-ignore
131
132
  bottomSheetRef.current._navigateToScreen = (screenName: string, props?: Record<string, any>) => {
133
+ console.log('_navigateToScreen called with:', screenName, props);
132
134
  if (navigationRef.current) {
135
+ console.log('Using navigationRef.current');
133
136
  navigationRef.current(screenName, props);
134
137
  return;
135
138
  }
139
+ console.log('navigationRef.current not available, using event system');
136
140
  if (typeof document !== 'undefined') {
137
141
  const event = new CustomEvent('oxy:navigate', { detail: { screen: screenName, props } });
138
142
  document.dispatchEvent(event);
@@ -140,6 +144,8 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
140
144
  (globalThis as any).oxyNavigateEvent = { screen: screenName, props };
141
145
  }
142
146
  };
147
+
148
+ console.log('Bottom sheet ref methods exposed:', Object.keys(bottomSheetRef.current));
143
149
  }
144
150
  }, [bottomSheetRef, modalRef]);
145
151
  // Keyboard handling (unchanged)
@@ -112,8 +112,7 @@ const getStorage = async (): Promise<StorageInterface> => {
112
112
 
113
113
  // Storage keys for secure sessions
114
114
  const getSecureStorageKeys = (prefix = 'oxy_secure') => ({
115
- sessions: `${prefix}_sessions`, // Array of SecureClientSession objects
116
- activeSessionId: `${prefix}_active_session_id`, // ID of currently active session
115
+ activeSessionId: `${prefix}_active_session_id`, // Only store the active session ID
117
116
  });
118
117
 
119
118
  export const OxyProvider: React.FC<OxyContextProviderProps> = ({
@@ -175,94 +174,60 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
175
174
  initStorage();
176
175
  }, []);
177
176
 
178
- // Effect to initialize authentication state
177
+ // Effect to initialize authentication state - only store session ID
179
178
  useEffect(() => {
180
179
  const initAuth = async () => {
181
180
  if (!storage) return;
182
181
 
183
182
  useAuthStore.setState({ isLoading: true });
184
183
  try {
185
- // Load stored sessions
186
- const sessionsData = await storage.getItem(keys.sessions);
184
+ // Only load the active session ID from storage
187
185
  const storedActiveSessionId = await storage.getItem(keys.activeSessionId);
188
186
 
189
- console.log('SecureAuth - sessionsData:', sessionsData);
190
187
  console.log('SecureAuth - activeSessionId:', storedActiveSessionId);
191
188
 
192
- if (sessionsData) {
193
- const parsedSessions: SecureClientSession[] = JSON.parse(sessionsData);
194
-
195
- // Migrate old session format to include user info
196
- const migratedSessions: SecureClientSession[] = [];
197
- let shouldUpdateStorage = false;
198
-
199
- for (const session of parsedSessions) {
200
- if (!session.userId) {
201
- // Session is missing user info, try to fetch it
202
- try {
203
- const sessionUser = await oxyServices.getUserBySession(session.sessionId);
204
- migratedSessions.push({
205
- ...session,
206
- userId: sessionUser.id
207
- });
208
- shouldUpdateStorage = true;
209
- console.log(`Migrated session ${session.sessionId} for user ${sessionUser.id}`);
210
- } catch (error) {
211
- // Session might be invalid, skip it
212
- console.log(`Removing invalid session ${session.sessionId}:`, error);
213
- shouldUpdateStorage = true;
189
+ if (storedActiveSessionId) {
190
+ // Validate the stored session with the backend
191
+ try {
192
+ const validation = await oxyServices.validateSession(storedActiveSessionId);
193
+
194
+ if (validation.valid) {
195
+ console.log('SecureAuth - session validated successfully');
196
+ setActiveSessionId(storedActiveSessionId);
197
+
198
+ // Get access token for API calls
199
+ await oxyServices.getTokenBySession(storedActiveSessionId);
200
+
201
+ // Load full user data from backend
202
+ const fullUser = await oxyServices.getUserBySession(storedActiveSessionId);
203
+ loginSuccess(fullUser);
204
+ setMinimalUser({
205
+ id: fullUser.id,
206
+ username: fullUser.username,
207
+ avatar: fullUser.avatar
208
+ });
209
+
210
+ // Load sessions from backend
211
+ const serverSessions = await oxyServices.getSessionsBySessionId(storedActiveSessionId);
212
+ const clientSessions: SecureClientSession[] = serverSessions.map(serverSession => ({
213
+ sessionId: serverSession.sessionId,
214
+ deviceId: serverSession.deviceId,
215
+ expiresAt: serverSession.expiresAt || new Date().toISOString(),
216
+ lastActive: serverSession.lastActive || new Date().toISOString(),
217
+ userId: serverSession.userId || fullUser.id
218
+ }));
219
+ setSessions(clientSessions);
220
+
221
+ if (onAuthStateChange) {
222
+ onAuthStateChange(fullUser);
214
223
  }
215
224
  } else {
216
- // Session already has user info
217
- migratedSessions.push(session);
218
- }
219
- }
220
-
221
- // Update storage if we made changes
222
- if (shouldUpdateStorage) {
223
- await saveSessionsToStorage(migratedSessions);
224
- }
225
-
226
- setSessions(migratedSessions);
227
-
228
- if (storedActiveSessionId && migratedSessions.length > 0) {
229
- const activeSession = migratedSessions.find(s => s.sessionId === storedActiveSessionId);
230
-
231
- if (activeSession) {
232
- console.log('SecureAuth - activeSession found:', activeSession);
233
-
234
- // Validate session
235
- try {
236
- const validation = await oxyServices.validateSession(activeSession.sessionId);
237
-
238
- if (validation.valid) {
239
- console.log('SecureAuth - session validated successfully');
240
- setActiveSessionId(activeSession.sessionId);
241
-
242
- // Get access token for API calls
243
- await oxyServices.getTokenBySession(activeSession.sessionId);
244
-
245
- // Load full user data
246
- const fullUser = await oxyServices.getUserBySession(activeSession.sessionId);
247
- loginSuccess(fullUser);
248
- setMinimalUser({
249
- id: fullUser.id,
250
- username: fullUser.username,
251
- avatar: fullUser.avatar
252
- });
253
-
254
- if (onAuthStateChange) {
255
- onAuthStateChange(fullUser);
256
- }
257
- } else {
258
- console.log('SecureAuth - session invalid, removing');
259
- await removeInvalidSession(activeSession.sessionId);
260
- }
261
- } catch (error) {
262
- console.error('SecureAuth - session validation error:', error);
263
- await removeInvalidSession(activeSession.sessionId);
264
- }
225
+ console.log('SecureAuth - session invalid, clearing storage');
226
+ await clearAllStorage();
265
227
  }
228
+ } catch (error) {
229
+ console.error('SecureAuth - session validation error:', error);
230
+ await clearAllStorage();
266
231
  }
267
232
  }
268
233
  } catch (err) {
@@ -276,15 +241,15 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
276
241
  if (storage) {
277
242
  initAuth();
278
243
  }
279
- }, [storage, oxyServices, keys, onAuthStateChange]);
244
+ }, [storage, oxyServices, keys, onAuthStateChange, loginSuccess, setMinimalUser]);
280
245
 
281
246
 
282
247
 
283
- // Remove invalid session
248
+ // Remove invalid session - refresh sessions from backend
284
249
  const removeInvalidSession = useCallback(async (sessionId: string): Promise<void> => {
250
+ // Remove from local state
285
251
  const filteredSessions = sessions.filter(s => s.sessionId !== sessionId);
286
252
  setSessions(filteredSessions);
287
- await saveSessionsToStorage(filteredSessions);
288
253
 
289
254
  // If there are other sessions, switch to the first one
290
255
  if (filteredSessions.length > 0) {
@@ -302,13 +267,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
302
267
  }
303
268
  }, [sessions, storage, keys, onAuthStateChange, logoutStore]);
304
269
 
305
- // Save sessions to storage
306
- const saveSessionsToStorage = useCallback(async (sessionsList: SecureClientSession[]): Promise<void> => {
307
- if (!storage) return;
308
- await storage.setItem(keys.sessions, JSON.stringify(sessionsList));
309
- }, [storage, keys.sessions]);
310
-
311
- // Save active session ID to storage
270
+ // Save active session ID to storage (only session ID, no user data)
312
271
  const saveActiveSessionId = useCallback(async (sessionId: string): Promise<void> => {
313
272
  if (!storage) return;
314
273
  await storage.setItem(keys.activeSessionId, sessionId);
@@ -318,7 +277,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
318
277
  const clearAllStorage = useCallback(async (): Promise<void> => {
319
278
  if (!storage) return;
320
279
  try {
321
- await storage.removeItem(keys.sessions);
322
280
  await storage.removeItem(keys.activeSessionId);
323
281
  } catch (err) {
324
282
  console.error('Clear secure storage error:', err);
@@ -357,7 +315,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
357
315
  }
358
316
  }, [oxyServices, onAuthStateChange, loginSuccess, saveActiveSessionId]);
359
317
 
360
- // Secure login method
318
+ // Secure login method - only store session ID, retrieve data from backend
361
319
  const login = useCallback(async (username: string, password: string, deviceName?: string): Promise<User> => {
362
320
  if (!storage) throw new Error('Storage not initialized');
363
321
  useAuthStore.setState({ isLoading: true, error: null });
@@ -379,56 +337,29 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
379
337
  deviceFingerprint
380
338
  );
381
339
 
382
- // Create client session object with user info for duplicate detection
383
- const clientSession: SecureClientSession = {
384
- sessionId: response.sessionId,
385
- deviceId: response.deviceId,
386
- expiresAt: response.expiresAt,
387
- lastActive: new Date().toISOString(),
388
- userId: response.user.id
389
- };
390
-
391
- // Check if this user already has a session (prevent duplicate accounts)
392
- const existingUserSessionIndex = sessions.findIndex(s =>
393
- s.userId === response.user.id
394
- );
395
-
396
- let updatedSessions: SecureClientSession[];
397
-
398
- if (existingUserSessionIndex !== -1) {
399
- // User already has a session - replace it with the new one (reused session scenario)
400
- const existingSession = sessions[existingUserSessionIndex];
401
- updatedSessions = [...sessions];
402
- updatedSessions[existingUserSessionIndex] = clientSession;
403
-
404
- console.log(`Reusing/updating existing session for user ${response.user.id}. Previous session: ${existingSession.sessionId}, New session: ${response.sessionId}`);
405
-
406
- // If the replaced session was the active one, update active session
407
- if (activeSessionId === existingSession.sessionId) {
408
- setActiveSessionId(response.sessionId);
409
- await saveActiveSessionId(response.sessionId);
410
- }
411
- } else {
412
- // Add new session for new user
413
- updatedSessions = [...sessions, clientSession];
414
- console.log(`Added new session for user ${response.user.id} on device ${response.deviceId}`);
415
- }
416
-
417
- setSessions(updatedSessions);
418
- await saveSessionsToStorage(updatedSessions);
419
-
420
- // Set as active session
340
+ // Set as active session (only store session ID)
421
341
  setActiveSessionId(response.sessionId);
422
342
  await saveActiveSessionId(response.sessionId);
423
343
 
424
344
  // Get access token for API calls
425
345
  await oxyServices.getTokenBySession(response.sessionId);
426
346
 
427
- // Load full user data
347
+ // Load full user data from backend
428
348
  const fullUser = await oxyServices.getUserBySession(response.sessionId);
429
349
  loginSuccess(fullUser);
430
350
  setMinimalUser(response.user);
431
351
 
352
+ // Load sessions from backend
353
+ const serverSessions = await oxyServices.getSessionsBySessionId(response.sessionId);
354
+ const clientSessions: SecureClientSession[] = serverSessions.map(serverSession => ({
355
+ sessionId: serverSession.sessionId,
356
+ deviceId: serverSession.deviceId,
357
+ expiresAt: serverSession.expiresAt || new Date().toISOString(),
358
+ lastActive: serverSession.lastActive || new Date().toISOString(),
359
+ userId: serverSession.userId || fullUser.id
360
+ }));
361
+ setSessions(clientSessions);
362
+
432
363
  if (onAuthStateChange) {
433
364
  onAuthStateChange(fullUser);
434
365
  }
@@ -440,7 +371,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
440
371
  } finally {
441
372
  useAuthStore.setState({ isLoading: false });
442
373
  }
443
- }, [storage, oxyServices, sessions, activeSessionId, saveSessionsToStorage, saveActiveSessionId, loginSuccess, setMinimalUser, onAuthStateChange, loginFailure]);
374
+ }, [storage, oxyServices, saveActiveSessionId, loginSuccess, setMinimalUser, onAuthStateChange, loginFailure]);
444
375
 
445
376
  // Logout method
446
377
  const logout = useCallback(async (targetSessionId?: string): Promise<void> => {
@@ -450,10 +381,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
450
381
  const sessionToLogout = targetSessionId || activeSessionId;
451
382
  await oxyServices.logoutSecureSession(activeSessionId, sessionToLogout);
452
383
 
453
- // Remove session from local storage
384
+ // Remove session from local state
454
385
  const filteredSessions = sessions.filter(s => s.sessionId !== sessionToLogout);
455
386
  setSessions(filteredSessions);
456
- await saveSessionsToStorage(filteredSessions);
457
387
 
458
388
  // If logging out active session
459
389
  if (sessionToLogout === activeSessionId) {
@@ -476,7 +406,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
476
406
  console.error('Logout error:', error);
477
407
  useAuthStore.setState({ error: 'Logout failed' });
478
408
  }
479
- }, [activeSessionId, oxyServices, sessions, saveSessionsToStorage, switchToSession, logoutStore, setMinimalUser, storage, keys.activeSessionId, onAuthStateChange]);
409
+ }, [activeSessionId, oxyServices, sessions, switchToSession, logoutStore, setMinimalUser, storage, keys.activeSessionId, onAuthStateChange]);
480
410
 
481
411
  // Logout all sessions
482
412
  const logoutAll = useCallback(async (): Promise<void> => {
@@ -598,8 +528,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
598
528
 
599
529
  console.log('refreshSessions: Updated sessions:', updatedSessions);
600
530
  setSessions(updatedSessions);
601
- await saveSessionsToStorage(updatedSessions);
602
- console.log('refreshSessions: Sessions saved to storage');
531
+ console.log('refreshSessions: Sessions updated in state');
603
532
  } catch (error) {
604
533
  console.error('Refresh sessions error:', error);
605
534
 
@@ -634,7 +563,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
634
563
  onAuthStateChange(null);
635
564
  }
636
565
  }
637
- }, [activeSessionId, oxyServices, user?.id, sessions, saveSessionsToStorage, switchToSession, logoutStore, setMinimalUser, clearAllStorage, onAuthStateChange]);
566
+ }, [activeSessionId, oxyServices, user?.id, sessions, switchToSession, logoutStore, setMinimalUser, clearAllStorage, onAuthStateChange]);
638
567
 
639
568
  // Device management methods
640
569
  const getDeviceSessions = useCallback(async (): Promise<any[]> => {
@@ -1 +1 @@
1
- export { useFollow } from './useFollow';
1
+ export { useFollow, useFollowerCounts } from './useFollow';