@oxyhq/services 5.4.8 → 5.5.1
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.
- package/lib/commonjs/core/index.js +0 -59
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +174 -17
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +8 -23
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +49 -38
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +2 -8
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/hooks/index.js +15 -2
- package/lib/commonjs/ui/hooks/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuthFetch.js +182 -0
- package/lib/commonjs/ui/hooks/useAuthFetch.js.map +1 -0
- package/lib/commonjs/ui/hooks/useFollow.js +10 -29
- package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
- package/lib/commonjs/ui/hooks/useOxyFollow.js +190 -0
- package/lib/commonjs/ui/hooks/useOxyFollow.js.map +1 -0
- package/lib/commonjs/ui/index.js +183 -0
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +18 -14
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js +37 -19
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +27 -9
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js +2 -8
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
- package/lib/commonjs/ui/store/index.js +51 -255
- package/lib/commonjs/ui/store/index.js.map +1 -1
- package/lib/commonjs/ui/store/setupOxyStore.js +63 -0
- package/lib/commonjs/ui/store/setupOxyStore.js.map +1 -0
- package/lib/commonjs/ui/store/slices/authSlice.js +56 -0
- package/lib/commonjs/ui/store/slices/authSlice.js.map +1 -0
- package/lib/commonjs/ui/store/slices/followSlice.js +238 -0
- package/lib/commonjs/ui/store/slices/followSlice.js.map +1 -0
- package/lib/commonjs/ui/store/slices/index.js +129 -0
- package/lib/commonjs/ui/store/slices/index.js.map +1 -0
- package/lib/commonjs/ui/store/slices/types.js +19 -0
- package/lib/commonjs/ui/store/slices/types.js.map +1 -0
- package/lib/commonjs/ui/styles/index.js +11 -0
- package/lib/commonjs/ui/styles/index.js.map +1 -1
- package/lib/commonjs/ui/styles/shadows.js +123 -0
- package/lib/commonjs/ui/styles/shadows.js.map +1 -0
- package/lib/module/core/index.js +0 -59
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +14 -10
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +8 -23
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +49 -38
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +2 -8
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/hooks/index.js +2 -1
- package/lib/module/ui/hooks/index.js.map +1 -1
- package/lib/module/ui/hooks/useAuthFetch.js +177 -0
- package/lib/module/ui/hooks/useAuthFetch.js.map +1 -0
- package/lib/module/ui/hooks/useFollow.js +10 -29
- package/lib/module/ui/hooks/useFollow.js.map +1 -1
- package/lib/module/ui/hooks/useOxyFollow.js +186 -0
- package/lib/module/ui/hooks/useOxyFollow.js.map +1 -0
- package/lib/module/ui/index.js +12 -2
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +5 -1
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js +37 -19
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +27 -9
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js +2 -8
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +1 -1
- package/lib/module/ui/store/index.js +23 -249
- package/lib/module/ui/store/index.js.map +1 -1
- package/lib/module/ui/store/setupOxyStore.js +59 -0
- package/lib/module/ui/store/setupOxyStore.js.map +1 -0
- package/lib/module/ui/store/slices/authSlice.js +48 -0
- package/lib/module/ui/store/slices/authSlice.js.map +1 -0
- package/lib/module/ui/store/slices/followSlice.js +232 -0
- package/lib/module/ui/store/slices/followSlice.js.map +1 -0
- package/lib/module/ui/store/slices/index.js +11 -0
- package/lib/module/ui/store/slices/index.js.map +1 -0
- package/lib/module/ui/store/slices/types.js +15 -0
- package/lib/module/ui/store/slices/types.js.map +1 -0
- package/lib/module/ui/styles/index.js +1 -0
- package/lib/module/ui/styles/index.js.map +1 -1
- package/lib/module/ui/styles/shadows.js +119 -0
- package/lib/module/ui/styles/shadows.js.map +1 -0
- package/lib/typescript/core/index.d.ts +0 -28
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +3 -5
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/index.d.ts +2 -1
- package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts +33 -0
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useOxyFollow.d.ts +81 -0
- package/lib/typescript/ui/hooks/useOxyFollow.d.ts.map +1 -0
- package/lib/typescript/ui/index.d.ts +3 -1
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/types.d.ts +22 -4
- package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/karma/KarmaRewardsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/store/index.d.ts +19 -58
- package/lib/typescript/ui/store/index.d.ts.map +1 -1
- package/lib/typescript/ui/store/setupOxyStore.d.ts +29 -0
- package/lib/typescript/ui/store/setupOxyStore.d.ts.map +1 -0
- package/lib/typescript/ui/store/slices/authSlice.d.ts +32 -0
- package/lib/typescript/ui/store/slices/authSlice.d.ts.map +1 -0
- package/lib/typescript/ui/store/slices/followSlice.d.ts +120 -0
- package/lib/typescript/ui/store/slices/followSlice.d.ts.map +1 -0
- package/lib/typescript/ui/store/slices/index.d.ts +9 -0
- package/lib/typescript/ui/store/slices/index.d.ts.map +1 -0
- package/lib/typescript/ui/store/slices/types.d.ts +16 -0
- package/lib/typescript/ui/store/slices/types.d.ts.map +1 -0
- package/lib/typescript/ui/styles/index.d.ts +1 -0
- package/lib/typescript/ui/styles/index.d.ts.map +1 -1
- package/lib/typescript/ui/styles/shadows.d.ts +233 -0
- package/lib/typescript/ui/styles/shadows.d.ts.map +1 -0
- package/package.json +14 -15
- package/src/__tests__/ui/hooks/useOxyFollow.test.tsx +92 -0
- package/src/__tests__/ui/store/setupOxyStore.test.ts +50 -0
- package/src/__tests__/validate-structure.js +91 -0
- package/src/__tests__/validation.js +42 -0
- package/src/core/index.ts +0 -66
- package/src/index.ts +36 -4
- package/src/ui/components/FollowButton.tsx +11 -25
- package/src/ui/components/OxyProvider.tsx +48 -33
- package/src/ui/components/OxySignInButton.tsx +2 -6
- package/src/ui/hooks/index.ts +2 -1
- package/src/ui/hooks/useAuthFetch.ts +200 -0
- package/src/ui/hooks/useFollow.ts +10 -30
- package/src/ui/hooks/useOxyFollow.ts +188 -0
- package/src/ui/index.ts +34 -2
- package/src/ui/navigation/types.ts +24 -4
- package/src/ui/screens/AccountCenterScreen.tsx +5 -7
- package/src/ui/screens/AppInfoScreen.tsx +40 -23
- package/src/ui/screens/FileManagementScreen.tsx +268 -248
- package/src/ui/screens/karma/KarmaRewardsScreen.tsx +2 -5
- package/src/ui/store/index.ts +31 -245
- package/src/ui/store/setupOxyStore.ts +58 -0
- package/src/ui/store/slices/authSlice.ts +43 -0
- package/src/ui/store/slices/followSlice.ts +207 -0
- package/src/ui/store/slices/index.ts +31 -0
- package/src/ui/store/slices/types.ts +33 -0
- package/src/ui/styles/index.ts +1 -0
- package/src/ui/styles/shadows.ts +112 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-native';
|
|
2
|
+
import { Provider } from 'react-redux';
|
|
3
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
4
|
+
import { useOxyFollow } from '../../../ui/hooks/useOxyFollow';
|
|
5
|
+
import { setupOxyStore } from '../../../ui/store/setupOxyStore';
|
|
6
|
+
import { OxyContextProvider } from '../../../ui/context/OxyContext';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
// Mock OxyServices
|
|
10
|
+
const mockOxyServices = {
|
|
11
|
+
getFollowStatus: jest.fn(),
|
|
12
|
+
followUser: jest.fn(),
|
|
13
|
+
unfollowUser: jest.fn(),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const createTestStore = () => {
|
|
17
|
+
return configureStore({
|
|
18
|
+
reducer: {
|
|
19
|
+
...setupOxyStore(),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const createWrapper = (store: ReturnType<typeof createTestStore>) => {
|
|
25
|
+
return ({ children }: { children: React.ReactNode }) => (
|
|
26
|
+
<Provider store={store}>
|
|
27
|
+
<OxyContextProvider oxyServices={mockOxyServices}>
|
|
28
|
+
{children}
|
|
29
|
+
</OxyContextProvider>
|
|
30
|
+
</Provider>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe('useOxyFollow', () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
jest.clearAllMocks();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('should work with external store containing Oxy reducers', () => {
|
|
40
|
+
const store = createTestStore();
|
|
41
|
+
const wrapper = createWrapper(store);
|
|
42
|
+
|
|
43
|
+
const { result } = renderHook(() => useOxyFollow('user1'), { wrapper });
|
|
44
|
+
|
|
45
|
+
expect(result.current).toHaveProperty('isFollowing');
|
|
46
|
+
expect(result.current).toHaveProperty('isLoading');
|
|
47
|
+
expect(result.current).toHaveProperty('error');
|
|
48
|
+
expect(result.current).toHaveProperty('toggleFollow');
|
|
49
|
+
expect(result.current).toHaveProperty('setFollowStatus');
|
|
50
|
+
expect(result.current).toHaveProperty('fetchStatus');
|
|
51
|
+
expect(result.current).toHaveProperty('clearError');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('should support multiple users mode', () => {
|
|
55
|
+
const store = createTestStore();
|
|
56
|
+
const wrapper = createWrapper(store);
|
|
57
|
+
|
|
58
|
+
const { result } = renderHook(() => useOxyFollow(['user1', 'user2']), { wrapper });
|
|
59
|
+
|
|
60
|
+
expect(result.current).toHaveProperty('followData');
|
|
61
|
+
expect(result.current).toHaveProperty('toggleFollowForUser');
|
|
62
|
+
expect(result.current).toHaveProperty('setFollowStatusForUser');
|
|
63
|
+
expect(result.current).toHaveProperty('fetchStatusForUser');
|
|
64
|
+
expect(result.current).toHaveProperty('fetchAllStatuses');
|
|
65
|
+
expect(result.current).toHaveProperty('clearErrorForUser');
|
|
66
|
+
expect(result.current).toHaveProperty('isAnyLoading');
|
|
67
|
+
expect(result.current).toHaveProperty('hasAnyError');
|
|
68
|
+
expect(result.current).toHaveProperty('allFollowing');
|
|
69
|
+
expect(result.current).toHaveProperty('allNotFollowing');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('should integrate with custom app reducers', () => {
|
|
73
|
+
const store = configureStore({
|
|
74
|
+
reducer: {
|
|
75
|
+
...setupOxyStore(),
|
|
76
|
+
customApp: (state = { feature: 'enabled' }) => state,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const wrapper = createWrapper(store);
|
|
81
|
+
|
|
82
|
+
const { result } = renderHook(() => useOxyFollow('user1'), { wrapper });
|
|
83
|
+
|
|
84
|
+
// Should still work with Oxy features
|
|
85
|
+
expect(result.current.isFollowing).toBe(false);
|
|
86
|
+
expect(result.current.isLoading).toBe(false);
|
|
87
|
+
|
|
88
|
+
// Custom app state should also be accessible in the store
|
|
89
|
+
const state = store.getState();
|
|
90
|
+
expect(state.customApp.feature).toBe('enabled');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
2
|
+
import { setupOxyStore, oxyReducers } from '../../../ui/store/setupOxyStore';
|
|
3
|
+
import { authSlice, followSlice } from '../../../ui/store/slices';
|
|
4
|
+
|
|
5
|
+
describe('setupOxyStore', () => {
|
|
6
|
+
test('should return all Oxy reducers', () => {
|
|
7
|
+
const reducers = setupOxyStore();
|
|
8
|
+
|
|
9
|
+
expect(reducers).toHaveProperty('auth');
|
|
10
|
+
expect(reducers).toHaveProperty('follow');
|
|
11
|
+
expect(reducers.auth).toBe(authSlice.reducer);
|
|
12
|
+
expect(reducers.follow).toBe(followSlice.reducer);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('should work with configureStore', () => {
|
|
16
|
+
const store = configureStore({
|
|
17
|
+
reducer: {
|
|
18
|
+
...setupOxyStore(),
|
|
19
|
+
customReducer: (state = { test: true }) => state,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const state = store.getState();
|
|
24
|
+
expect(state).toHaveProperty('auth');
|
|
25
|
+
expect(state).toHaveProperty('follow');
|
|
26
|
+
expect(state).toHaveProperty('customReducer');
|
|
27
|
+
expect(state.customReducer.test).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should support tree-shaking with pick method', () => {
|
|
31
|
+
const authOnly = setupOxyStore.pick('auth');
|
|
32
|
+
expect(authOnly).toHaveProperty('auth');
|
|
33
|
+
expect(authOnly).not.toHaveProperty('follow');
|
|
34
|
+
|
|
35
|
+
const followOnly = setupOxyStore.pick('follow');
|
|
36
|
+
expect(followOnly).toHaveProperty('follow');
|
|
37
|
+
expect(followOnly).not.toHaveProperty('auth');
|
|
38
|
+
|
|
39
|
+
const both = setupOxyStore.pick('auth', 'follow');
|
|
40
|
+
expect(both).toHaveProperty('auth');
|
|
41
|
+
expect(both).toHaveProperty('follow');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should export individual reducers', () => {
|
|
45
|
+
expect(oxyReducers).toHaveProperty('auth');
|
|
46
|
+
expect(oxyReducers).toHaveProperty('follow');
|
|
47
|
+
expect(oxyReducers.auth).toBe(authSlice.reducer);
|
|
48
|
+
expect(oxyReducers.follow).toBe(followSlice.reducer);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple file structure validation for the new Redux architecture
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const requiredFiles = [
|
|
9
|
+
'src/ui/store/setupOxyStore.ts',
|
|
10
|
+
'src/ui/store/slices/authSlice.ts',
|
|
11
|
+
'src/ui/store/slices/followSlice.ts',
|
|
12
|
+
'src/ui/store/slices/types.ts',
|
|
13
|
+
'src/ui/store/slices/index.ts',
|
|
14
|
+
'src/ui/hooks/useOxyFollow.ts',
|
|
15
|
+
'docs/redux-integration.md',
|
|
16
|
+
'docs/migration-guide-redux.md',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const requiredExports = [
|
|
20
|
+
'setupOxyStore',
|
|
21
|
+
'authSlice',
|
|
22
|
+
'followSlice',
|
|
23
|
+
'authReducer',
|
|
24
|
+
'followReducer',
|
|
25
|
+
'useOxyFollow',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
console.log('🔍 Validating new Redux architecture...\n');
|
|
29
|
+
|
|
30
|
+
// Check required files exist
|
|
31
|
+
let filesValid = true;
|
|
32
|
+
for (const file of requiredFiles) {
|
|
33
|
+
const fullPath = path.join(__dirname, '..', '..', file);
|
|
34
|
+
if (fs.existsSync(fullPath)) {
|
|
35
|
+
console.log(`✅ ${file}`);
|
|
36
|
+
} else {
|
|
37
|
+
console.log(`❌ ${file} - MISSING`);
|
|
38
|
+
filesValid = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check key exports in files
|
|
43
|
+
const setupOxyStoreFile = path.join(__dirname, '..', 'ui', 'store', 'setupOxyStore.ts');
|
|
44
|
+
if (fs.existsSync(setupOxyStoreFile)) {
|
|
45
|
+
const content = fs.readFileSync(setupOxyStoreFile, 'utf8');
|
|
46
|
+
if (content.includes('export function setupOxyStore')) {
|
|
47
|
+
console.log('✅ setupOxyStore function exported');
|
|
48
|
+
} else {
|
|
49
|
+
console.log('❌ setupOxyStore function not found');
|
|
50
|
+
filesValid = false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const indexFile = path.join(__dirname, '..', 'ui', 'index.ts');
|
|
55
|
+
if (fs.existsSync(indexFile)) {
|
|
56
|
+
const content = fs.readFileSync(indexFile, 'utf8');
|
|
57
|
+
const exportsFound = requiredExports.filter(exp => content.includes(exp));
|
|
58
|
+
console.log(`✅ Exports found in ui/index.ts: ${exportsFound.join(', ')}`);
|
|
59
|
+
|
|
60
|
+
if (exportsFound.length < requiredExports.length) {
|
|
61
|
+
const missing = requiredExports.filter(exp => !exportsFound.includes(exp));
|
|
62
|
+
console.log(`⚠️ Missing exports: ${missing.join(', ')}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check main index file
|
|
67
|
+
const mainIndexFile = path.join(__dirname, '..', 'index.ts');
|
|
68
|
+
if (fs.existsSync(mainIndexFile)) {
|
|
69
|
+
const content = fs.readFileSync(mainIndexFile, 'utf8');
|
|
70
|
+
if (content.includes('setupOxyStore')) {
|
|
71
|
+
console.log('✅ setupOxyStore exported from main index');
|
|
72
|
+
} else {
|
|
73
|
+
console.log('❌ setupOxyStore not exported from main index');
|
|
74
|
+
filesValid = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log('\n' + '='.repeat(50));
|
|
79
|
+
if (filesValid) {
|
|
80
|
+
console.log('🎉 All validations passed! New Redux architecture is properly implemented.');
|
|
81
|
+
console.log('\nKey features:');
|
|
82
|
+
console.log('- ✅ Framework-agnostic Redux integration');
|
|
83
|
+
console.log('- ✅ Tree-shakable with setupOxyStore.pick()');
|
|
84
|
+
console.log('- ✅ Individual slice exports');
|
|
85
|
+
console.log('- ✅ External store support');
|
|
86
|
+
console.log('- ✅ Backward compatibility');
|
|
87
|
+
console.log('- ✅ Comprehensive documentation');
|
|
88
|
+
} else {
|
|
89
|
+
console.log('❌ Some validations failed. Please check the missing files/exports.');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple validation test for the new Redux architecture
|
|
3
|
+
* This test can be run to ensure the refactoring works correctly
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
7
|
+
|
|
8
|
+
// Test that setupOxyStore can be imported and used
|
|
9
|
+
try {
|
|
10
|
+
const { setupOxyStore } = require('../ui/store/setupOxyStore');
|
|
11
|
+
|
|
12
|
+
console.log('✅ setupOxyStore imported successfully');
|
|
13
|
+
|
|
14
|
+
// Test basic usage
|
|
15
|
+
const reducers = setupOxyStore();
|
|
16
|
+
console.log('✅ setupOxyStore() returns reducers:', Object.keys(reducers));
|
|
17
|
+
|
|
18
|
+
// Test tree-shaking
|
|
19
|
+
const authOnly = setupOxyStore.pick('auth');
|
|
20
|
+
console.log('✅ Tree-shaking works:', Object.keys(authOnly));
|
|
21
|
+
|
|
22
|
+
// Test with configureStore
|
|
23
|
+
const store = configureStore({
|
|
24
|
+
reducer: {
|
|
25
|
+
...setupOxyStore(),
|
|
26
|
+
custom: (state = { test: true }) => state,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const state = store.getState();
|
|
31
|
+
console.log('✅ Store integration works:', Object.keys(state));
|
|
32
|
+
|
|
33
|
+
// Test individual exports
|
|
34
|
+
const { authSlice, followSlice } = require('../ui/store/slices');
|
|
35
|
+
console.log('✅ Individual slices exported:', authSlice.name, followSlice.name);
|
|
36
|
+
|
|
37
|
+
console.log('\n🎉 All tests passed! The new Redux architecture is working correctly.');
|
|
38
|
+
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('❌ Test failed:', error);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
package/src/core/index.ts
CHANGED
|
@@ -1676,72 +1676,6 @@ export class OxyServices {
|
|
|
1676
1676
|
throw this.handleError(error);
|
|
1677
1677
|
}
|
|
1678
1678
|
}
|
|
1679
|
-
|
|
1680
|
-
/**
|
|
1681
|
-
* Health check endpoint to verify API connectivity
|
|
1682
|
-
* @returns Health status and basic server info
|
|
1683
|
-
*/
|
|
1684
|
-
async healthCheck(): Promise<{
|
|
1685
|
-
status: string;
|
|
1686
|
-
users?: number;
|
|
1687
|
-
timestamp?: string;
|
|
1688
|
-
[key: string]: any
|
|
1689
|
-
}> {
|
|
1690
|
-
try {
|
|
1691
|
-
const res = await this.client.get('/');
|
|
1692
|
-
return res.data;
|
|
1693
|
-
} catch (error) {
|
|
1694
|
-
throw this.handleError(error);
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
/**
|
|
1699
|
-
* Download file content using authenticated request
|
|
1700
|
-
* @param fileId - The file ID to download
|
|
1701
|
-
* @returns Response object for further processing
|
|
1702
|
-
*/
|
|
1703
|
-
async downloadFileContent(fileId: string): Promise<Response> {
|
|
1704
|
-
try {
|
|
1705
|
-
const downloadUrl = this.getFileDownloadUrl(fileId);
|
|
1706
|
-
const response = await fetch(downloadUrl);
|
|
1707
|
-
|
|
1708
|
-
if (!response.ok) {
|
|
1709
|
-
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
return response;
|
|
1713
|
-
} catch (error) {
|
|
1714
|
-
throw this.handleError(error);
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
/**
|
|
1719
|
-
* Get file content as text using authenticated request
|
|
1720
|
-
* @param fileId - The file ID to get content for
|
|
1721
|
-
* @returns File content as string
|
|
1722
|
-
*/
|
|
1723
|
-
async getFileContentAsText(fileId: string): Promise<string> {
|
|
1724
|
-
try {
|
|
1725
|
-
const response = await this.downloadFileContent(fileId);
|
|
1726
|
-
return await response.text();
|
|
1727
|
-
} catch (error) {
|
|
1728
|
-
throw this.handleError(error);
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
/**
|
|
1733
|
-
* Get file content as blob using authenticated request
|
|
1734
|
-
* @param fileId - The file ID to get content for
|
|
1735
|
-
* @returns File content as blob
|
|
1736
|
-
*/
|
|
1737
|
-
async getFileContentAsBlob(fileId: string): Promise<Blob> {
|
|
1738
|
-
try {
|
|
1739
|
-
const response = await this.downloadFileContent(fileId);
|
|
1740
|
-
return await response.blob();
|
|
1741
|
-
} catch (error) {
|
|
1742
|
-
throw this.handleError(error);
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
1679
|
}
|
|
1746
1680
|
|
|
1747
1681
|
export default OxyServices;
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
OxyProvider,
|
|
19
19
|
OxyContextProvider,
|
|
20
20
|
useOxy,
|
|
21
|
+
useAuthFetch,
|
|
21
22
|
|
|
22
23
|
// Components
|
|
23
24
|
OxySignInButton,
|
|
@@ -39,9 +40,8 @@ import { OxyContextState, OxyContextProviderProps } from './ui/context/OxyContex
|
|
|
39
40
|
import * as Models from './models/interfaces';
|
|
40
41
|
|
|
41
42
|
// ------------- Core Exports -------------
|
|
42
|
-
export
|
|
43
|
-
export
|
|
44
|
-
export * from './core';
|
|
43
|
+
export { OxyCore, OxyServices };
|
|
44
|
+
export default OxyServices; // Default export for backward compatibility
|
|
45
45
|
|
|
46
46
|
// ------------- Utility Exports -------------
|
|
47
47
|
export { DeviceManager } from './utils';
|
|
@@ -57,6 +57,7 @@ export {
|
|
|
57
57
|
OxyProvider,
|
|
58
58
|
OxyContextProvider,
|
|
59
59
|
useOxy,
|
|
60
|
+
useAuthFetch,
|
|
60
61
|
|
|
61
62
|
// Components
|
|
62
63
|
OxySignInButton,
|
|
@@ -68,10 +69,41 @@ export {
|
|
|
68
69
|
useFollow,
|
|
69
70
|
ProfileScreen,
|
|
70
71
|
OxyRouter,
|
|
72
|
+
|
|
73
|
+
// Redux Store - NEW ARCHITECTURE
|
|
74
|
+
setupOxyStore,
|
|
75
|
+
oxyReducers,
|
|
76
|
+
// Individual slices
|
|
77
|
+
authSlice,
|
|
78
|
+
authActions,
|
|
79
|
+
authSelectors,
|
|
80
|
+
authReducer,
|
|
81
|
+
followSlice,
|
|
82
|
+
followActions,
|
|
83
|
+
followSelectors,
|
|
84
|
+
followThunks,
|
|
85
|
+
followReducer,
|
|
86
|
+
// Action creators
|
|
87
|
+
loginStart,
|
|
88
|
+
loginSuccess,
|
|
89
|
+
loginFailure,
|
|
90
|
+
logout,
|
|
91
|
+
setFollowingStatus,
|
|
92
|
+
clearFollowError,
|
|
93
|
+
resetFollowState,
|
|
94
|
+
fetchFollowStatus,
|
|
95
|
+
toggleFollowUser,
|
|
96
|
+
// Types
|
|
97
|
+
type AuthState,
|
|
98
|
+
type FollowState,
|
|
99
|
+
initialAuthState,
|
|
100
|
+
initialFollowState,
|
|
101
|
+
|
|
102
|
+
// Legacy exports (deprecated)
|
|
71
103
|
store,
|
|
72
104
|
type RootState,
|
|
73
105
|
type AppDispatch,
|
|
74
|
-
};
|
|
106
|
+
} from './ui';
|
|
75
107
|
|
|
76
108
|
// ------------- Type Exports -------------
|
|
77
109
|
export { OxyContextState, OxyContextProviderProps };
|
|
@@ -142,31 +142,17 @@ const FollowButton: React.FC<FollowButtonProps> = ({
|
|
|
142
142
|
const dispatch = useDispatch();
|
|
143
143
|
const { oxyServices, isAuthenticated } = useOxy();
|
|
144
144
|
|
|
145
|
-
// Optimized single selector to prevent multiple re-renders
|
|
146
|
-
const followState = useSelector((state: RootState) => {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
isFollowing: state.follow.followingUsers?.[userId] ?? initiallyFollowing ?? false,
|
|
158
|
-
isLoading: state.follow.loadingUsers?.[userId] ?? false,
|
|
159
|
-
error: state.follow.errors?.[userId] ?? null
|
|
160
|
-
};
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// Whether the follow status has been loaded from the store with defensive check
|
|
164
|
-
const isStatusKnown = useSelector((state: RootState) => {
|
|
165
|
-
if (!state.follow?.followingUsers) {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
return Object.prototype.hasOwnProperty.call(state.follow.followingUsers, userId);
|
|
169
|
-
});
|
|
145
|
+
// Optimized single selector to prevent multiple re-renders
|
|
146
|
+
const followState = useSelector((state: RootState) => ({
|
|
147
|
+
isFollowing: state.follow.followingUsers[userId] ?? initiallyFollowing ?? false,
|
|
148
|
+
isLoading: state.follow.loadingUsers[userId] ?? false,
|
|
149
|
+
error: state.follow.errors[userId]
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
// Whether the follow status has been loaded from the store
|
|
153
|
+
const isStatusKnown = useSelector((state: RootState) =>
|
|
154
|
+
Object.prototype.hasOwnProperty.call(state.follow.followingUsers, userId)
|
|
155
|
+
);
|
|
170
156
|
|
|
171
157
|
const { isFollowing, isLoading, error } = followState;
|
|
172
158
|
|
|
@@ -32,55 +32,70 @@ const OxyProvider: React.FC<OxyProviderProps> = (props) => {
|
|
|
32
32
|
onAuthStateChange,
|
|
33
33
|
storageKeyPrefix,
|
|
34
34
|
showInternalToaster = true,
|
|
35
|
+
store: externalStore,
|
|
36
|
+
skipReduxProvider = false,
|
|
35
37
|
...bottomSheetProps
|
|
36
38
|
} = props;
|
|
37
39
|
|
|
38
40
|
// Create internal bottom sheet ref
|
|
39
41
|
const internalBottomSheetRef = useRef<BottomSheetModalRef>(null);
|
|
40
42
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
+
// Determine which store to use
|
|
44
|
+
const storeToUse = externalStore || store;
|
|
45
|
+
|
|
46
|
+
// Helper function to wrap content with Redux Provider if needed
|
|
47
|
+
const wrapWithReduxProvider = (content: React.ReactNode) => {
|
|
48
|
+
if (skipReduxProvider) {
|
|
49
|
+
// App manages Redux Provider externally
|
|
50
|
+
return content;
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
return (
|
|
44
|
-
<Provider store={
|
|
45
|
-
|
|
46
|
-
oxyServices={oxyServices}
|
|
47
|
-
storageKeyPrefix={storageKeyPrefix}
|
|
48
|
-
onAuthStateChange={onAuthStateChange}
|
|
49
|
-
>
|
|
50
|
-
{children}
|
|
51
|
-
</OxyContextProvider>
|
|
54
|
+
<Provider store={storeToUse}>
|
|
55
|
+
{content}
|
|
52
56
|
</Provider>
|
|
53
57
|
);
|
|
54
|
-
}
|
|
58
|
+
};
|
|
55
59
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
// If contextOnly is true, we just provide the context without the bottom sheet UI
|
|
61
|
+
if (contextOnly) {
|
|
62
|
+
return wrapWithReduxProvider(
|
|
59
63
|
<OxyContextProvider
|
|
60
64
|
oxyServices={oxyServices}
|
|
61
65
|
storageKeyPrefix={storageKeyPrefix}
|
|
62
66
|
onAuthStateChange={onAuthStateChange}
|
|
63
|
-
bottomSheetRef={internalBottomSheetRef}
|
|
64
67
|
>
|
|
65
|
-
|
|
66
|
-
<GestureHandlerRootView style={styles.gestureHandlerRoot}>
|
|
67
|
-
<BottomSheetModalProvider>
|
|
68
|
-
<StatusBar translucent backgroundColor="transparent" />
|
|
69
|
-
<SafeAreaProvider>
|
|
70
|
-
<OxyBottomSheet {...bottomSheetProps} bottomSheetRef={internalBottomSheetRef} oxyServices={oxyServices} />
|
|
71
|
-
{children}
|
|
72
|
-
</SafeAreaProvider>
|
|
73
|
-
</BottomSheetModalProvider>
|
|
74
|
-
{/* Global Toaster for app-wide notifications outside of Modal contexts - only show if internal toaster is disabled */}
|
|
75
|
-
{!showInternalToaster && (
|
|
76
|
-
<View style={styles.toasterContainer}>
|
|
77
|
-
<Toaster position="top-center" swipeToDismissDirection="left" offset={15} />
|
|
78
|
-
</View>
|
|
79
|
-
)}
|
|
80
|
-
</GestureHandlerRootView>
|
|
81
|
-
</FontLoader>
|
|
68
|
+
{children}
|
|
82
69
|
</OxyContextProvider>
|
|
83
|
-
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Otherwise, provide both the context and the bottom sheet UI
|
|
74
|
+
return wrapWithReduxProvider(
|
|
75
|
+
<OxyContextProvider
|
|
76
|
+
oxyServices={oxyServices}
|
|
77
|
+
storageKeyPrefix={storageKeyPrefix}
|
|
78
|
+
onAuthStateChange={onAuthStateChange}
|
|
79
|
+
bottomSheetRef={internalBottomSheetRef}
|
|
80
|
+
>
|
|
81
|
+
<FontLoader>
|
|
82
|
+
<GestureHandlerRootView style={styles.gestureHandlerRoot}>
|
|
83
|
+
<BottomSheetModalProvider>
|
|
84
|
+
<StatusBar translucent backgroundColor="transparent" />
|
|
85
|
+
<SafeAreaProvider>
|
|
86
|
+
<OxyBottomSheet {...bottomSheetProps} bottomSheetRef={internalBottomSheetRef} oxyServices={oxyServices} />
|
|
87
|
+
{children}
|
|
88
|
+
</SafeAreaProvider>
|
|
89
|
+
</BottomSheetModalProvider>
|
|
90
|
+
{/* Global Toaster for app-wide notifications outside of Modal contexts - only show if internal toaster is disabled */}
|
|
91
|
+
{!showInternalToaster && (
|
|
92
|
+
<View style={styles.toasterContainer}>
|
|
93
|
+
<Toaster position="top-center" swipeToDismissDirection="left" offset={15} />
|
|
94
|
+
</View>
|
|
95
|
+
)}
|
|
96
|
+
</GestureHandlerRootView>
|
|
97
|
+
</FontLoader>
|
|
98
|
+
</OxyContextProvider>
|
|
84
99
|
);
|
|
85
100
|
};
|
|
86
101
|
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { TouchableOpacity, Text, View, StyleSheet, ViewStyle, TextStyle, StyleProp, Platform } from 'react-native';
|
|
3
3
|
import { useOxy } from '../context/OxyContext';
|
|
4
4
|
import OxyLogo from './OxyLogo';
|
|
5
|
-
import { fontFamilies } from '../styles
|
|
5
|
+
import { fontFamilies, shadows } from '../styles';
|
|
6
6
|
|
|
7
7
|
export interface OxySignInButtonProps {
|
|
8
8
|
/**
|
|
@@ -168,11 +168,7 @@ const styles = StyleSheet.create({
|
|
|
168
168
|
backgroundColor: '#FFFFFF',
|
|
169
169
|
borderWidth: 1,
|
|
170
170
|
borderColor: '#DDDDDD',
|
|
171
|
-
|
|
172
|
-
shadowOffset: { width: 0, height: 2 },
|
|
173
|
-
shadowOpacity: 0.1,
|
|
174
|
-
shadowRadius: 4,
|
|
175
|
-
elevation: 2,
|
|
171
|
+
...shadows.button,
|
|
176
172
|
},
|
|
177
173
|
buttonOutline: {
|
|
178
174
|
backgroundColor: 'transparent',
|
package/src/ui/hooks/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { useFollow } from './
|
|
1
|
+
export { useOxyFollow, useFollow } from './useOxyFollow';
|
|
2
|
+
export { useAuthFetch } from './useAuthFetch';
|