master
Netcup Gituser 2023-12-07 16:13:05 +01:00
parent ec243c5558
commit 0c25cc8e36
38 changed files with 2148 additions and 249 deletions

118
App.tsx
View File

@ -1,118 +0,0 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
import React from 'react';
import type {PropsWithChildren} from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
type SectionProps = PropsWithChildren<{
title: string;
}>;
function Section({children, title}: SectionProps): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
<Text
style={[
styles.sectionTitle,
{
color: isDarkMode ? Colors.white : Colors.black,
},
]}>
{title}
</Text>
<Text
style={[
styles.sectionDescription,
{
color: isDarkMode ? Colors.light : Colors.dark,
},
]}>
{children}
</Text>
</View>
);
}
function App(): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Section title="Step One">
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
</Section>
<Section title="See Your Changes">
<ReloadInstructions />
</Section>
<Section title="Debug">
<DebugInstructions />
</Section>
<Section title="Learn More">
Read the docs to discover what to do next:
</Section>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});
export default App;

3
ios/.netrc Normal file
View File

@ -0,0 +1,3 @@
machine api.mapbox.com
login mapbox
password sk.eyJ1IjoidGl0YW5pdW1iYWNoIiwiYSI6ImNscGlkb3I5MTBmbTYycm1oNDRmaWJrNTUifQ.Xp5gptRu3GcyAi8ihoOoIg

View File

@ -50,7 +50,13 @@ target 'app' do
# Pods for testing
end
pre_install do |installer|
$RNMapboxMaps.pre_install(installer)
... other pre install hooks
end
post_install do |installer|
$RNMapboxMaps.post_install(installer)
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install(
installer,

267
package-lock.json generated
View File

@ -16,14 +16,18 @@
"@react-navigation/native-stack": "^6.9.17",
"@react-spring/native": "^9.7.3",
"@reduxjs/toolkit": "^1.9.7",
"@rnmapbox/maps": "^10.0.15",
"@types/react-native-vector-icons": "^6.4.18",
"geolib": "^3.3.4",
"password-quality-calculator": "^1.0.4",
"react": "18.2.0",
"react-native": "0.72.7",
"react-native-encrypted-storage": "^4.0.3",
"react-native-fs": "^2.20.0",
"react-native-linear-gradient": "^2.8.3",
"react-native-safe-area-context": "^4.7.4",
"react-native-screens": "^3.27.0",
"react-native-securerandom": "^1.0.1",
"react-native-svg": "^13.4.0",
"react-native-user-agent": "^2.3.1",
"react-native-vector-icons": "^10.0.2",
@ -8058,6 +8062,34 @@
}
}
},
"node_modules/@rnmapbox/maps": {
"version": "10.0.15",
"resolved": "https://registry.npmjs.org/@rnmapbox/maps/-/maps-10.0.15.tgz",
"integrity": "sha512-2diBwMF+e7gR6keZ1AEI3xw8U7Cu9+xhv8ce75Bw7flg9aTk+d0KgWv4VDQn4wSphD23EroUgFprVymHvVzZGQ==",
"dependencies": {
"@turf/along": "6.5.0",
"@turf/distance": "6.5.0",
"@turf/helpers": "6.5.0",
"@turf/length": "6.5.0",
"@turf/nearest-point-on-line": "6.5.0",
"@types/geojson": "^7946.0.7",
"debounce": "^1.2.0"
},
"peerDependencies": {
"expo": ">=47.0.0",
"mapbox-gl": "^2.9.0",
"react": ">=16.6.1",
"react-native": ">=0.59.9"
},
"peerDependenciesMeta": {
"expo": {
"optional": true
},
"mapbox-gl": {
"optional": true
}
}
},
"node_modules/@segment/loosely-validate-event": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz",
@ -8121,6 +8153,157 @@
"integrity": "sha512-F7IoHEqf741lut4Z2K+IkWQRvXAhBiZMeY5L7BysG7Z2Z3MlIyFR+AagD8jQ/CqC1vowGnRwfLjeuwIpaeoJxA==",
"dev": true
},
"node_modules/@turf/along": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/along/-/along-6.5.0.tgz",
"integrity": "sha512-LLyWQ0AARqJCmMcIEAXF4GEu8usmd4Kbz3qk1Oy5HoRNpZX47+i5exQtmIWKdqJ1MMhW26fCTXgpsEs5zgJ5gw==",
"dependencies": {
"@turf/bearing": "^6.5.0",
"@turf/destination": "^6.5.0",
"@turf/distance": "^6.5.0",
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/bbox": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz",
"integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==",
"dependencies": {
"@turf/helpers": "^6.5.0",
"@turf/meta": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/bearing": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.5.0.tgz",
"integrity": "sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==",
"dependencies": {
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/destination": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/destination/-/destination-6.5.0.tgz",
"integrity": "sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==",
"dependencies": {
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/distance": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz",
"integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==",
"dependencies": {
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/helpers": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
"integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==",
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/invariant": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz",
"integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==",
"dependencies": {
"@turf/helpers": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/length": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/length/-/length-6.5.0.tgz",
"integrity": "sha512-5pL5/pnw52fck3oRsHDcSGrj9HibvtlrZ0QNy2OcW8qBFDNgZ4jtl6U7eATVoyWPKBHszW3dWETW+iLV7UARig==",
"dependencies": {
"@turf/distance": "^6.5.0",
"@turf/helpers": "^6.5.0",
"@turf/meta": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/line-intersect": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.5.0.tgz",
"integrity": "sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==",
"dependencies": {
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0",
"@turf/line-segment": "^6.5.0",
"@turf/meta": "^6.5.0",
"geojson-rbush": "3.x"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/line-segment": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.5.0.tgz",
"integrity": "sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==",
"dependencies": {
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0",
"@turf/meta": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/meta": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
"integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
"dependencies": {
"@turf/helpers": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@turf/nearest-point-on-line": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz",
"integrity": "sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg==",
"dependencies": {
"@turf/bearing": "^6.5.0",
"@turf/destination": "^6.5.0",
"@turf/distance": "^6.5.0",
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0",
"@turf/line-intersect": "^6.5.0",
"@turf/meta": "^6.5.0"
},
"funding": {
"url": "https://opencollective.com/turf"
}
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@ -8162,6 +8345,11 @@
"@babel/types": "^7.20.7"
}
},
"node_modules/@types/geojson": {
"version": "7946.0.13",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz",
"integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ=="
},
"node_modules/@types/graceful-fs": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@ -9362,6 +9550,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base-64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -10506,6 +10699,11 @@
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
},
"node_modules/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -12763,6 +12961,28 @@
"node": ">=6.9.0"
}
},
"node_modules/geojson-rbush": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-3.2.0.tgz",
"integrity": "sha512-oVltQTXolxvsz1sZnutlSuLDEcQAKYC/uXt9zDzJJ6bu0W+baTI8LZBaTup5afzibEH4N3jlq2p+a152wlBJ7w==",
"dependencies": {
"@turf/bbox": "*",
"@turf/helpers": "6.x",
"@turf/meta": "6.x",
"@types/geojson": "7946.0.8",
"rbush": "^3.0.1"
}
},
"node_modules/geojson-rbush/node_modules/@types/geojson": {
"version": "7946.0.8",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
"integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
},
"node_modules/geolib": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/geolib/-/geolib-3.3.4.tgz",
"integrity": "sha512-EicrlLLL3S42gE9/wde+11uiaYAaeSVDwCUIv2uMIoRBfNJCn8EsSI+6nS3r4TCKDO6+RQNM9ayLq2at+oZQWQ=="
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -18721,6 +18941,11 @@
}
]
},
"node_modules/quickselect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -18753,6 +18978,14 @@
"node": ">= 0.8"
}
},
"node_modules/rbush": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
"integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
"dependencies": {
"quickselect": "^2.0.0"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@ -18894,6 +19127,24 @@
"react-native": "*"
}
},
"node_modules/react-native-fs": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz",
"integrity": "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==",
"dependencies": {
"base-64": "^0.1.0",
"utf8": "^3.0.0"
},
"peerDependencies": {
"react-native": "*",
"react-native-windows": "*"
},
"peerDependenciesMeta": {
"react-native-windows": {
"optional": true
}
}
},
"node_modules/react-native-linear-gradient": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz",
@ -18925,6 +19176,17 @@
"react-native": "*"
}
},
"node_modules/react-native-securerandom": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-1.0.1.tgz",
"integrity": "sha512-ibuDnd3xi17HyD5CkilOXGPFpS9Z1oifjyHFwUl8NMzcQcpruM0ZX8ytr3A4rCeAsaBHjz69r78Xgd6vUswv1Q==",
"dependencies": {
"base64-js": "*"
},
"peerDependencies": {
"react-native": "*"
}
},
"node_modules/react-native-svg": {
"version": "13.4.0",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.4.0.tgz",
@ -21243,6 +21505,11 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/utf8": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
"integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -18,14 +18,18 @@
"@react-navigation/native-stack": "^6.9.17",
"@react-spring/native": "^9.7.3",
"@reduxjs/toolkit": "^1.9.7",
"@rnmapbox/maps": "^10.0.15",
"@types/react-native-vector-icons": "^6.4.18",
"geolib": "^3.3.4",
"password-quality-calculator": "^1.0.4",
"react": "18.2.0",
"react-native": "0.72.7",
"react-native-encrypted-storage": "^4.0.3",
"react-native-fs": "^2.20.0",
"react-native-linear-gradient": "^2.8.3",
"react-native-safe-area-context": "^4.7.4",
"react-native-screens": "^3.27.0",
"react-native-securerandom": "^1.0.1",
"react-native-svg": "^13.4.0",
"react-native-user-agent": "^2.3.1",
"react-native-vector-icons": "^10.0.2",

View File

@ -0,0 +1,28 @@
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
import {initDatabase} from '@helper/storage/bdm/init';
import DBSchemas from '@helper/storage/bdm/schemas';
import BigDataManager from '@helper/storage/BigDataManager';
import {store} from '@redux/store';
import {chatEntity} from '@configs/chat/types';
async function initChatDatabase(chat: chatEntity) {
const keys = BigDataManager.databases.chatRoomInfos.keys;
await initDatabase(DBSchemas.chat, chat.roomId);
await BigDataManager.databases.chatRoomInfos.setEntry({
[keys.RoomId]: chat.roomId,
[keys.initSyncId]: chat.initSyncId,
[keys.syncId]: chat.syncId,
[keys.unreadMessages]: chat.unreadMessages,
[keys.users]: chat.users,
[keys.timestamp]: chat.timestamp,
});
addChatEntity(chat);
}
export function addChatEntity(chat: chatEntity) {
store.dispatch(appNonSaveVarActions.setChatEntity(chat));
}
export default initChatDatabase;

View File

@ -0,0 +1,55 @@
import React from 'react';
import {View, Text, Image, StyleSheet} from 'react-native';
import {PA_Point} from './cluster/getData';
import {MarkerView} from '@rnmapbox/maps';
import {useSelector} from 'react-redux';
import {RootState, store} from '@redux/store';
import CircleNumber from './cluster/Cluster';
import {EventMarker} from './EventMarker';
function DisplayMarkerList(props: {markers: PA_Point[]}) {
const eventMapMarkers = props.markers;
/*const eventMapMarkers = useSelector(
(state: RootState) => state.nonSaveVariables.eventMapMarkers,
);*/
const colors = store.getState().nonSaveVariables.theme.colors;
return (
<React.Fragment>
{eventMapMarkers.map((marker: PA_Point) => {
return marker.type === 'event' ? (
<MarkerView
key={marker.id}
anchor={{x: 0.5, y: 1}}
coordinate={[marker.longitude, marker.latitude]}
allowOverlap={true}>
<EventMarker marker={marker} />
</MarkerView>
) : (
<MarkerView
key={marker.id}
anchor={{x: 0.5, y: 0.5}}
coordinate={[marker.longitude, marker.latitude]}
allowOverlap={true}>
<CircleNumber number={marker.cluster} />
</MarkerView>
);
})}
</React.Fragment>
);
}
const styles = StyleSheet.create({
marker: {
padding: 5,
borderRadius: 5,
},
markerText: {
color: 'white',
fontWeight: 'bold',
},
});
export default DisplayMarkerList;

View File

@ -0,0 +1,130 @@
import React from 'react';
import {Animated} from 'react-native';
import colors from '@configs/colors';
import {View, Image, Text, StyleSheet} from 'react-native';
import {PA_Point} from './cluster/getData';
import {store} from '@redux/store';
import {
Avatar,
AvatarBadge,
AvatarFallbackText,
AvatarGroup,
AvatarImage,
} from '@gluestack-ui/themed';
import tate from '@assets/tate.jpg';
export function EventMarker(props: {marker: PA_Point}) {
const marker = props.marker;
const colors = store.getState().nonSaveVariables.theme.colors;
const fadeAnim = React.useRef(new Animated.Value(0)).current;
const fadeAnim2 = React.useRef(new Animated.Value(0.9)).current;
React.useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
}, [fadeAnim]);
React.useEffect(() => {
Animated.timing(fadeAnim2, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
}, [fadeAnim2]);
const avatars = [
{
src: 'https://image.stern.de/32707724/t/o5/v2/w1440/r1.7778/-/andrew-tate.jpg',
alt: 'Sandeep Srivastva',
color: '$emerald600',
},
{
src: 'https://image.stern.de/32707724/t/o5/v2/w1440/r1.7778/-/andrew-tate.jpg',
alt: 'Arjun Kapoor',
color: '$cyan600',
},
{
src: 'https://image.stern.de/32707724/t/o5/v2/w1440/r1.7778/-/andrew-tate.jpg',
alt: 'Ritik Sharma ',
color: '$indigo600',
},
{
src: 'https://image.stern.de/32707724/t/o5/v2/w1440/r1.7778/-/andrew-tate.jpg',
alt: 'Akhil Sharma',
color: '$gray600',
},
{
src: 'https://image.stern.de/32707724/t/o5/v2/w1440/r1.7778/-/andrew-tate.jpg',
alt: 'Rahul Sharma ',
color: '$red400',
},
];
const extraAvatars = avatars.slice(3);
const remainingCount = extraAvatars.length;
return (
<Animated.View
style={{
...styles.marker,
backgroundColor: colors.backgroundDark400,
opacity: fadeAnim,
transform: [{scale: fadeAnim2}],
}}>
<AvatarGroup>
{avatars.slice(0, 3).map((avatar, index) => {
return (
<Avatar
key={index}
size="sm"
borderColor="$black"
borderWidth="$2"
bg={avatar.color}
sx={{
_dark: {
borderColor: '$black',
},
}}>
<AvatarFallbackText>{avatar.alt}</AvatarFallbackText>
</Avatar>
);
})}
<Avatar
size="sm"
borderColor="$white"
borderWidth="$2"
bg="$gray600"
sx={{
_dark: {
borderColor: '$black',
},
}}>
<AvatarFallbackText>{'+ ' + remainingCount + ''}</AvatarFallbackText>
</Avatar>
</AvatarGroup>
<Image
source={tate}
style={{width: 100, height: 100, alignSelf: 'center'}}
/>
<Text style={styles.markerText}>{marker.type}</Text>
</Animated.View>
);
}
const styles = StyleSheet.create({
marker: {
padding: 5,
borderRadius: 5,
},
markerText: {
color: 'white',
fontWeight: 'bold',
},
});

View File

@ -0,0 +1,88 @@
import React from 'react';
import {View, Text, StyleSheet, Animated} from 'react-native';
import {useSelector} from 'react-redux';
import {RootState, store} from '@redux/store';
const CircleNumber = ({number}: {number: number}) => {
/*const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);*/ //disabled for performance
const colors = store.getState().nonSaveVariables.theme.colors;
const fadeAnim = React.useRef(new Animated.Value(0)).current;
const fadeAnim2 = React.useRef(new Animated.Value(1.2)).current;
React.useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
}, [fadeAnim]);
React.useEffect(() => {
Animated.timing(fadeAnim2, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
}, [fadeAnim2]);
return (
<Animated.View
style={{
...styles.container,
opacity: fadeAnim,
}}>
<Animated.View
style={{
...styles.outerCircle,
backgroundColor: colors.backgroundDark800 + '44',
transform: [{scale: fadeAnim2}],
}}>
<View
style={{
...styles.innerCircle,
backgroundColor: colors.backgroundDark400,
}}>
<Text style={{...styles.text, color: colors.textLight0}}>
{number}
</Text>
</View>
</Animated.View>
</Animated.View>
);
};
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
width: 65 * 1.5,
height: 65 * 1.5,
},
outerCircle: {
width: 65,
height: 65,
borderRadius: 65 / 2,
//borderWidth: 2,
justifyContent: 'center',
alignItems: 'center',
},
innerCircle: {
width: 45,
height: 45,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 15,
},
});
export default CircleNumber;

View File

@ -0,0 +1,191 @@
import {Position} from '@rnmapbox/maps/src/types/Position';
import { getDistance } from 'geolib';
interface PA_Point_Cluster {
latitude: number;
longitude: number;
cluster: number;
type: 'cluster';
id: string;
}
interface PA_Point_Event {
latitude: number;
longitude: number;
type: 'event';
id: number;
}
type PA_Point = PA_Point_Cluster | PA_Point_Event;
export type {PA_Point_Cluster, PA_Point_Event, PA_Point};
const createChecksum = (ids: (number | string)[]) => {
let checksum = 0;
for (const id of ids.sort()) {
const value = typeof id === 'string' ? parseInt(id, 36) : id;
checksum ^= value & 0xFFFF;
}
return checksum;
};
const clusterMarkers = (markers: PA_Point[], threshold: number) => {
const clusters = [];
for (const marker of markers) {
let added = false;
for (const cluster of clusters) {
const [center] = cluster;
const distance = Math.sqrt(
Math.pow(center.latitude - marker.latitude, 2) +
Math.pow(center.longitude - marker.longitude, 2),
);
if (distance < threshold) {
cluster.push(marker);
added = true;
break;
}
}
if (!added) {
clusters.push([marker]);
}
}
// Convert clusters to center points
const centerPoints = clusters.map(cluster => {
let isEvent = 0;
const latitudes = cluster.map(marker => marker.latitude);
const longitudes = cluster.map(marker => marker.longitude);
const centerLatitude =
latitudes.reduce((a, b) => a + b, 0) / latitudes.length;
const centerLongitude =
longitudes.reduce((a, b) => a + b, 0) / longitudes.length;
const sumValues = cluster.reduce((sum, marker) => {
if (marker.type === 'event') {
if(isEvent === 0)
isEvent = 1;
else if(isEvent === 1)
isEvent = -1;
return sum + 1};
isEvent = -1;
marker = marker as PA_Point_Cluster;
return sum + (marker.cluster || 0);
}, 0);
if(isEvent === 1)
{
return {
latitude: centerLatitude,
longitude: centerLongitude,
type: 'event',
id: cluster[0].id,
} as PA_Point;
}
const ids = cluster.map(marker => marker.id);
const idChecksum = createChecksum(ids);
return {
latitude: centerLatitude,
longitude: centerLongitude,
type: 'cluster',
cluster: sumValues,
id: idChecksum.toString(),
} as PA_Point;
});
return centerPoints;
};
/*const calculateThreshold = (bounds: [Position, Position]) => {
const latDiff = bounds[0][0] - bounds[1][0];
const lonDiff = bounds[0][1] - bounds[1][1];
const maxDiff = Math.max(latDiff, lonDiff);
const zoomLevel = Math.floor(Math.log2(360 / maxDiff));
return 1 / Math.pow(2, zoomLevel);
};*/
const calculateRadius = (bounds: [Position, Position]) => {
const distanceInMeters = getDistance(
{ latitude: bounds[0][0], longitude: bounds[0][1] },
{ latitude: bounds[1][0], longitude: bounds[1][1] }
);
return distanceInMeters/600000/(1.5/*android bug fix for roteration*/);
};
function getLocationData(
bounds: [Position, Position], zoomLevel: number
): Promise<{locations: PA_Point[]} | undefined> {
if (bounds === undefined) return Promise.resolve(undefined);
return new Promise(resolve => {
// Simulating an asynchronous operation, such as an API call
setTimeout(() => {
const rawData: {locations: PA_Point[]} = {
locations: [
/*{"latitude": 51.1657, "longitude": 10.4515, "cluster": 27, "type": "cluster", "id": "398742"},
{"latitude": 51.1045, "longitude": 13.2017, "cluster": 19, "type": "cluster", "id": "519274"},
{"latitude": 49.3988, "longitude": 8.6724, "cluster": 42, "type": "cluster", "id": "815926"},
{"latitude": 51.3404, "longitude": 12.3743, "cluster": 5, "type": "cluster", "id": "105473"},
{"latitude": 52.52, "longitude": 13.405, "cluster": 31, "type": "cluster", "id": "267351"},
{"latitude": 48.8566, "longitude": 9.2725, "cluster": 14, "type": "cluster", "id": "845723"},
{"latitude": 53.5511, "longitude": 9.9937, "cluster": 37, "type": "cluster", "id": "921864"},
{"latitude": 50.1109, "longitude": 8.6821, "cluster": 23, "type": "cluster", "id": "467132"},
{"latitude": 51.0493, "longitude": 13.7384, "cluster": 8, "type": "cluster", "id": "842906"},
{"latitude": 49.0069, "longitude": 8.4037, "cluster": 46, "type": "cluster", "id": "346708"},*/
{"latitude": 51.9674, "longitude": 7.6194, "type": "event", "id": 203867},
{"latitude": 50.7753, "longitude": 6.0839, "type": "event", "id": 501298},
{"latitude": 53.0793, "longitude": 8.8017, "type": "event", "id": 763159},
{"latitude": 48.7758, "longitude": 9.1829, "type": "event", "id": 917425},
{"latitude": 52.3759, "longitude": 9.732, "type": "event", "id": 635028},
{"latitude": 51.512, "longitude": 7.4653, "type": "event", "id": 841592},
{"latitude": 49.4875, "longitude": 8.466, "type": "event", "id": 180356},
{"latitude": 51.3611, "longitude": 7.0119, "type": "event", "id": 592874},
{"latitude": 54.3227, "longitude": 10.1356, "type": "event", "id": 384791},
{"latitude": 51.8049, "longitude": 10.3351, "type": "event", "id": 273495},
{"latitude": 50.3213, "longitude": 11.8676, "type": "event", "id": 699318},
{"latitude": 49.8765, "longitude": 6.6695, "type": "event", "id": 159627},
{"latitude": 52.2651, "longitude": 7.3085, "type": "event", "id": 903716},
{"latitude": 53.9638, "longitude": 12.413, "type": "event", "id": 487612},
{"latitude": 50.9321, "longitude": 9.1847, "type": "event", "id": 827464},
{"latitude": 52.4378, "longitude": 13.2847, "type": "event", "id": 931845},
{"latitude": 49.4644, "longitude": 11.0168, "type": "event", "id": 674132},
{"latitude": 51.1983, "longitude": 8.8998, "type": "event", "id": 298473}
],
};
console.log("got data");
const zoomLevelChanged = calculateRadius(bounds);
console.log(zoomLevelChanged);
const data = {
locations: clusterMarkers(rawData.locations, zoomLevelChanged),//calculateThreshold(bounds)
};
console.log("clustered data");
resolve(data);
}, 120); // Simulating a delay of 1 second for the asynchronous operation
});
}
export default getLocationData;

View File

@ -1,10 +1,17 @@
//these variables should not changed by the user and will not be saved in storage
import {chatEntity, roomId} from '@configs/chat/types';
import {User} from '@user/types';
import {UserId} from './types';
import {getVersionByNum, VersionType} from '@helper/version';
import configDarkTheme, {ThemeTokensType} from '@configs/colors';
import { PA_Point } from '@components/map/cluster/getData';
export const APP_VERSION = getVersionByNum(1);
export const AppVarMaxBackups: number = 10;
export const maxCachedUsers = 30;
export enum appStatus {
IS_LOADING,
@ -21,13 +28,23 @@ export enum connectionStatus {
export interface NON_SAVE_VARS {
currentAppVersion: VersionType;
appStatus: appStatus;
connectionStatus: connectionStatus;
theme: ThemeTokensType;
connectionStatus: connectionStatus;
cachedUsers: {[key: UserId]: User};
chats: {[key: roomId]: chatEntity};
chatActivity: roomId[];
selectedChat: roomId | 'none';
eventMapMarkers: PA_Point[];
}
export const non_save_vars: NON_SAVE_VARS = {
currentAppVersion: APP_VERSION,
appStatus: appStatus.IS_LOADING,
connectionStatus: connectionStatus.UNKNOWN,
theme: configDarkTheme.tokens,
connectionStatus: connectionStatus.UNKNOWN,
cachedUsers: {},
chats: {},
chatActivity: [],
selectedChat: 'none',
eventMapMarkers: [],
};

View File

@ -3,6 +3,11 @@ import type {PayloadAction} from '@reduxjs/toolkit';
import {appStatus, non_save_vars} from './appNonSaveVar';
import {ThemeTokensType} from '@configs/colors';
import {chatEntity, roomId} from '@configs/chat/types';
import {User} from '@user/types';
import {UserId} from './types';
import {PA_Point} from '@components/map/cluster/getData';
export const appNonSaveVariablesSlice = createSlice({
name: 'non_save_vars',
initialState: non_save_vars,
@ -13,6 +18,45 @@ export const appNonSaveVariablesSlice = createSlice({
setTheme: (state, action: PayloadAction<ThemeTokensType>) => {
state.theme = action.payload;
},
setCachedUser: (state, action: PayloadAction<User>) => {
state.cachedUsers[action.payload.UserId] = action.payload;
},
removeCachedUser: (state, action: PayloadAction<UserId>) => {
delete state.cachedUsers[action.payload];
},
setSelectedChat: (state, action: PayloadAction<roomId>) => {
state.selectedChat = action.payload;
},
setChatEntity: (state, action: PayloadAction<chatEntity>) => {
const roomId = action.payload.roomId;
state.chats[roomId] = action.payload;
if (state.chatActivity.includes(roomId) === false)
state.chatActivity.unshift(roomId);
},
changeChatEntity: (state, action: PayloadAction<chatEntity>) => {
const roomId = action.payload.roomId;
state.chats[roomId] = action.payload;
if (state.chatActivity.includes(roomId) === false)
state.chatActivity.unshift(roomId);
state.chatActivity.sort(function (x, y) {
return x === roomId ? -1 : y === roomId ? 1 : 0;
});
},
removeChatEntity: (state, action: PayloadAction<roomId>) => {
delete state.chats[action.payload];
state.chatActivity = state.chatActivity.filter(function (ele) {
return ele !== action.payload;
});
},
setMapMarkers: (state, action: PayloadAction<PA_Point[]>) => {
state.eventMapMarkers = action.payload;
}
},
});

View File

@ -47,6 +47,7 @@ export interface RegisterProcess {
}
export interface PREFERENCES_VARS {
dbek: number; //db encryption key
version: VersionType;
theme: ThemeMode;
RegisterProcess: RegisterProcess;
@ -55,6 +56,7 @@ export interface PREFERENCES_VARS {
}
export const preferences_vars_default: PREFERENCES_VARS = {
dbek: -5,
version: APP_VERSION, //version of datatypes in storage
theme: ThemeMode.Dark,
RegisterProcess: {

View File

@ -43,6 +43,9 @@ export const appVariablesSlice = createSlice({
setAccount: (state, action: PayloadAction<MyUserAccount>) => {
state.preferences.accounts[action.payload.UserId] = action.payload;
},
setDBEK: (state, action: PayloadAction<number>) => {
state.preferences.dbek = action.payload;
},
},
});

107
src/configs/chat/types.ts Normal file
View File

@ -0,0 +1,107 @@
import {timestamp, UserId} from '@configs/types';
import DBSchemas from '@helper/storage/bdm/schemas';
import LangFormat from '@lang/default';
import {timeStamp} from 'console';
export enum chatTags {
GROUP = 0,
FRIEND = 1,
GOOD_FRIEND = 2,
}
interface tagUIType {
name: string;
background: string;
textColor?: string;
}
interface tagUITypeFinal extends tagUIType {
textColor: string;
}
const tagUI: {[key in chatTags]: tagUIType} = {
[chatTags.GROUP]: {
name: 'Group',
background: '#37f',
},
[chatTags.FRIEND]: {
name: 'Friend',
background: 'lime.300',
textColor: '#111',
},
[chatTags.GOOD_FRIEND]: {
name: 'Good friend',
background: 'lime.600',
},
};
/*export function getTagUI(tagKey: chatTags, lang?: LangFormat): tagUITypeFinal {
let tag = tagUI[tagKey];
tag.textColor = tag.textColor || '#fff';
if (lang !== undefined && lang.chat.tags.names[tagKey] !== undefined) {
tag.name = lang.chat.tags.names[tagKey];
}
return tag as tagUITypeFinal;
}*/
export type roomId = string;
type syncId = number;
export interface chatEntity {
roomId: roomId;
syncId: syncId;
initSyncId: syncId;
timestamp: timestamp;
users: [UserId, ...UserId[]];
title?: string;
unreadMessages: number;
tags: chatTags[];
}
export function convertDatabaseChat(
chat: typeof DBSchemas.chatRoomInfos.defaultProps,
): chatEntity | undefined {
const keys = DBSchemas.chatRoomInfos.keys;
if (chat[keys.users].length === 0) return undefined;
const newChat: chatEntity = {
initSyncId: chat[keys.initSyncId],
roomId: chat[keys.RoomId],
syncId: chat[keys.syncId],
tags: [],
unreadMessages: chat[keys.unreadMessages],
timestamp: chat[keys.timestamp],
//@ts-ignore
users: [...chat[keys.users]],
//title: chat[keys.title]
};
return newChat;
}
export function convertChatDatabase(
chat: chatEntity,
): typeof DBSchemas.chatRoomInfos.defaultProps {
const keys = DBSchemas.chatRoomInfos.keys;
const newChat: typeof DBSchemas.chatRoomInfos.defaultProps = {
[keys.initSyncId]: chat.initSyncId,
[keys.RoomId]: chat.roomId,
[keys.syncId]: chat.syncId,
[keys.unreadMessages]: chat.unreadMessages,
[keys.timestamp]: chat.timestamp,
//@ts-ignore
[keys.users]: chat.users,
//title: chat[keys.title]
};
return newChat;
}

View File

@ -0,0 +1 @@
export {generateSecureRandom} from 'react-native-securerandom';

View File

@ -1,4 +1,13 @@
import {initDatabase} from './bdm/init';
import {initDatabases} from './bdm/init';
import {setEntry} from './bdm/set';
import {getEntry} from './bdm/get';
import DBSchemas from './bdm/schemas';
import {databaseNames, possibleDBKeys} from './bdm/types';
const BigDataManager = {initDatabase};
const BigDataManager = {
initDatabase: initDatabases,
setEntry,
getEntry,
databases: DBSchemas,
};
export default BigDataManager;

View File

@ -1,7 +1,12 @@
import EncryptedStorage from 'react-native-encrypted-storage';
export async function getData(key: string): Promise<string | null> {
return EncryptedStorage.getItem(key);
return new Promise((resolve, reject) => {
EncryptedStorage.getItem(key).then(value => {
if (value === undefined) resolve(null);
else resolve(value);
});
});
}
export async function setData(key: string, value: string): Promise<void> {

View File

@ -0,0 +1,44 @@
import {appVarActions} from '@configs/appVarReducer';
import {saveVarChanges} from '@helper/appData';
import {store} from '@redux/store';
import {generateSecureRandom} from '@helper/secureRandom';
import {getData, setData} from '../appData';
import {Buffer} from 'buffer';
type encryptType = Uint8Array | undefined;
let dbEncryption: encryptType;
export function initKey(): Promise<Uint8Array> {
return new Promise(async resolve => {
let dbekIndex = store.getState().appVariables.preferences.dbek;
if (dbekIndex === undefined || dbekIndex < 0) {
dbekIndex = Math.round(Math.random() * 1000000);
store.dispatch(appVarActions.setDBEK(dbekIndex));
saveVarChanges();
}
let dbekEncoded = await getData('dbek' + dbekIndex);
if (dbekEncoded === null) {
dbEncryption = await generateSecureRandom(64);
console.log(dbEncryption);
let buf = Buffer.from(dbEncryption).toString('base64');
await setData('dbek' + dbekIndex, buf);
} else {
dbEncryption = new Uint8Array(Buffer.from(dbekEncoded, 'base64'));
}
console.log('dbekIndex', dbekIndex);
resolve(dbEncryption);
});
}
export function getKey(): encryptType {
return dbEncryption;
}

View File

@ -0,0 +1,39 @@
import {getDatabase} from './getDB';
import {databaseConf, mergeDBName, possibleDBKeys} from './types';
export interface filterParam {
type: 'name';
}
export const getEntry = async <T2 extends databaseConf<T, any>, T>(
schema: T2,
key: possibleDBKeys,
suffix?: string,
): Promise<null | T> => {
const nameObj = {name: schema.details.name, suffix};
const dbName = mergeDBName(nameObj);
const realm = await getDatabase(nameObj);
const val = realm.objectForPrimaryKey<typeof schema.details.properties>(
nameObj.name,
key,
);
return val as T;
};
export const getAllEntries = async <T2 extends databaseConf<T, any>, T>(
schema: T2,
filter?: filterParam,
suffix?: string,
): Promise<null | T[]> => {
const nameObj = {name: schema.details.name, suffix};
const dbName = mergeDBName(nameObj);
const realm = await getDatabase(nameObj);
const val = realm.objects<typeof schema.details.properties>(nameObj.name);
return [...val] as T[];
};

View File

@ -0,0 +1,115 @@
import {timestamp} from '@configs/types';
import MyUserManager from '@user/MyUserManager';
import Realm from 'realm';
Realm.flags.THROW_ON_GLOBAL_REALM = true;
import {getKey} from './encryption';
import {
databaseConf,
databaseNames,
databaseNameSuffix,
mergeDBName,
} from './types';
import RNFS from 'react-native-fs';
import {databaseConfType} from './schemas';
const closeTimeout = 5; // in seconds // when DB read/writes is too long in idle it will be closed
type DBType = Realm;
export interface DBObject {
name: string;
schema: databaseConfType;
db: DBType | undefined;
lastUsedTimestamp?: timestamp; // when timeout is undefined then db is closed
}
export type DBs = {[key: string]: DBObject};
export let databases: DBs = {};
function getTime() {
return Math.floor(new Date().getTime() / 1000);
}
setInterval(() => {
const curTime = getTime();
for (let name in databases) {
let dbObj = databases[name];
if (
dbObj.lastUsedTimestamp !== undefined &&
dbObj.lastUsedTimestamp + closeTimeout < curTime
) {
dbObj.db?.close();
dbObj.lastUsedTimestamp = undefined;
//console.log('Database not used. Closing ' + dbObj.name);
}
}
}, 5000);
export function closeAllDatabases() {
for (let name in databases) {
let dbObj = databases[name];
if (dbObj.lastUsedTimestamp !== undefined) {
dbObj.db?.close();
dbObj.lastUsedTimestamp = undefined;
}
}
}
export async function openMyDatabase(
schema: databaseConfType,
nameObj: databaseNameSuffix,
): Promise<DBType> {
const folderPath = MyUserManager.getSelectedUserId();
const path =
folderPath +
'/' +
schema.filePath +
(nameObj.suffix !== undefined ? '_' + nameObj.suffix : '');
const folderExists = await RNFS.exists(
RNFS.DocumentDirectoryPath + '/' + folderPath,
);
if (folderExists === false) {
await RNFS.mkdir(RNFS.DocumentDirectoryPath + '/' + folderPath);
}
console.log('schema.details', schema.details);
const realm = await Realm.open({
schema: [schema.details as any],
schemaVersion: schema.version,
path,
encryptionKey: getKey(),
});
console.log('path', path);
return realm;
}
export async function getDatabase(
_name: databaseNameSuffix,
init?: boolean,
): Promise<DBType> {
const name = mergeDBName(_name);
let dbObj = databases[name];
if (dbObj !== undefined) {
if (dbObj.lastUsedTimestamp !== undefined && dbObj.db !== undefined) {
dbObj.lastUsedTimestamp = getTime();
return dbObj.db;
} else {
dbObj.lastUsedTimestamp = undefined;
const db = await openMyDatabase(dbObj.schema, _name);
dbObj.lastUsedTimestamp = getTime();
dbObj.db = db;
return db;
}
} else {
throw "Database '" + name + "' is not initialized";
}
}

View File

@ -1,18 +1,42 @@
import Realm from 'realm';
import DBSchemas from './schemas';
import {closeAllDatabases, databases, DBObject, getDatabase} from './getDB';
import DBSchemas, {SkipDBSchemas} from './schemas';
import {databaseConf, databaseNameSuffix, mergeDBName} from './types';
export const initDatabases = (): Promise<void> => {
return new Promise(async (resolve, reject) => {
closeAllDatabases();
export const initDatabase = (): Promise<void> => {
return new Promise((resolve, reject) => {
setTimeout(() => {
for (const key in DBSchemas) {
const Schema = DBSchemas[key as keyof typeof DBSchemas];
Realm.open({
schema: [Schema.details],
schemaVersion: Schema.version,
path: Schema.filePath,
});
const schema = DBSchemas[key as keyof typeof DBSchemas];
const name = schema.details.name;
if (SkipDBSchemas.includes(name)) continue;
await initDatabase(schema);
}
resolve();
}, 1000);
});
};
export const initDatabase = async (
schema: (typeof DBSchemas)[keyof typeof DBSchemas],
fileSuffix?: string,
): Promise<string> => {
const _nameObj: databaseNameSuffix = {
name: schema.details.name,
suffix: fileSuffix,
};
console.log('initDatabase', _nameObj);
const name = mergeDBName(_nameObj);
let dbObj: DBObject = {
schema: schema,
name,
db: undefined,
};
databases[name] = dbObj;
await getDatabase(_nameObj, true); // init Database
return name;
};

View File

@ -1,6 +1,10 @@
import {MigrationCallback} from 'realm';
import DBSchemas from './schemas';
import {databaseConf, databaseNames} from './types';
export const usersDBMigration: MigrationCallback = (oldRealm, newRealm) => {
export const DBMigration: {[key in databaseNames]: any} = {
users: (Schema: typeof DBSchemas.users) => {
const callback: MigrationCallback = (oldRealm, newRealm) => {
/*// only apply this change if upgrading to schemaVersion 2
if (oldRealm.schemaVersion < 2) {
const oldObjects = oldRealm.objects('Person');
@ -13,3 +17,29 @@ export const usersDBMigration: MigrationCallback = (oldRealm, newRealm) => {
}
}*/
};
return callback;
},
chat: (Schema: typeof DBSchemas.chat) => {
const callback: MigrationCallback = (oldRealm, newRealm) => {
/*// only apply this change if upgrading to schemaVersion 2
if (oldRealm.schemaVersion < 2) {
const oldObjects = oldRealm.objects('Person');
const newObjects = newRealm.objects('Person');
// loop through all objects and set the fullName property in the new schema
for (const objectIndex in oldObjects) {
const oldObject = oldObjects[objectIndex];
const newObject = newObjects[objectIndex];
newObject.fullName = `${oldObject.firstName} ${oldObject.lastName}`;
}
}*/
};
return callback;
},
chatRoomInfos: (Schema: typeof DBSchemas.chatRoomInfos) => {
const callback: MigrationCallback = (oldRealm, newRealm) => {};
return callback;
},
};

View File

@ -1,46 +1,9 @@
import {usersDBMigration} from './migration';
import chat from './schemas/chat';
import users from './schemas/users';
import chatRoomInfos from './schemas/chatRoomInfos';
type databaseNames = 'users' | 'chat';
const DBSchemas = {users, chat, chatRoomInfos};
export const SkipDBSchemas = [chat.details.name];
interface databaseConf {
filePath: string;
version: number;
migration?: any;
details: {
name: databaseNames;
properties: any;
primaryKey: string;
};
}
const users: databaseConf = {
filePath: 'users',
version: 1,
migration: usersDBMigration,
details: {
name: 'users',
properties: {
UserId: 'string',
AccountName: 'string',
Username: 'string',
},
primaryKey: 'UserId',
},
};
const chat: databaseConf = {
filePath: 'chat',
version: 1,
//migration: chatDBMigration,
details: {
name: 'chat',
properties: {
UserId: 'string',
msg: 'string',
},
primaryKey: 'UserId',
},
};
const DBSchemas = {users, chat};
export type databaseConfType = typeof DBSchemas[keyof typeof DBSchemas];
export default DBSchemas;

View File

@ -0,0 +1,86 @@
import {filterParam, getAllEntries, getEntry} from '../get';
import {DBMigration} from '../migration';
import {setEntry} from '../set';
import {databaseConf, possibleDBKeys} from '../types';
enum keys {
syncID = "a",
UserId = 'b',
data = 'c',
massageType = "d",
created_at = "e",
received_by_server = "f",
received_by_target_clients = "g",
reply = "h",
reactions = "i",
hasRead = "j",
}
const name = 'chat';
const primaryKey: keyof typeof propsDefault = keys.UserId;
const propsType: {[key in keyof typeof propsDefault]: string} = {
[keys.syncID]: "int",
[keys.UserId]: 'string',
[keys.data]: 'data',
[keys.massageType]: "int",
[keys.created_at]: "int",
[keys.received_by_server]: "int",
[keys.received_by_target_clients]: "int",
[keys.reply]: "int",
[keys.reactions]: "i",
[keys.hasRead]: "j",
};
const propsDefault = {
[keys.syncID]: "a",
[keys.UserId]: 'b',
[keys.data]: 'c',
[keys.massageType]: "d",
[keys.created_at]: "e",
[keys.received_by_server]: "f",
[keys.received_by_target_clients]: "g",
[keys.reply]: "h",
[keys.reactions]: "i",
[keys.hasRead]: "j",
};
const thisSchema: databaseConf<typeof propsDefault, typeof keys> = {
filePath: name,
version: 1,
keys,
migration: () => {
return DBMigration[name](thisSchema);
},
setEntry: (val: typeof thisSchema.defaultProps, suffix?: string) => {
return setEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
val,
suffix,
);
},
getEntry: (key: possibleDBKeys, suffix?: string) => {
return getEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
key,
suffix,
);
},
getAllEntries: (filter?: filterParam, suffix?: string) => {
return getAllEntries<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
filter,
suffix,
);
},
defaultProps: propsDefault,
details: {
name,
properties: propsType,
primaryKey,
},
};
export default thisSchema;

View File

@ -0,0 +1,73 @@
import {filterParam, getAllEntries, getEntry} from '../get';
import {DBMigration} from '../migration';
import {setEntry} from '../set';
import {databaseConf, possibleDBKeys} from '../types';
enum keys {
RoomId = 'a',
syncId = 'b',
initSyncId = 'c',
unreadMessages = 'd',
users = 'e',
timestamp = 'f',
}
const name = 'chatRoomInfos';
const primaryKey: keyof typeof propsDefault = keys.RoomId;
const propsType: {[key in keyof typeof propsDefault]: string} = {
[keys.RoomId]: 'string',
[keys.syncId]: 'int',
[keys.initSyncId]: 'int',
[keys.unreadMessages]: 'int',
[keys.users]: 'string[]',
[keys.timestamp]: 'int',
};
const propsDefault = {
[keys.RoomId]: '',
[keys.syncId]: 0,
[keys.initSyncId]: 0,
[keys.unreadMessages]: 0,
[keys.users]: ['none'],
[keys.timestamp]: 0,
};
const thisSchema: databaseConf<typeof propsDefault, typeof keys> = {
filePath: name,
version: 1,
keys,
migration: () => {
return DBMigration[name](thisSchema);
},
setEntry: (val: typeof thisSchema.defaultProps, suffix?: string) => {
return setEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
val,
suffix,
);
},
getEntry: (key: possibleDBKeys, suffix?: string) => {
return getEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
key,
suffix,
);
},
getAllEntries: (filter?: filterParam, suffix?: string) => {
return getAllEntries<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
filter,
suffix,
);
},
defaultProps: propsDefault,
details: {
name,
properties: propsType,
primaryKey,
},
};
export default thisSchema;

View File

@ -0,0 +1,93 @@
import {filterParam, getAllEntries, getEntry} from '../get';
import {DBMigration} from '../migration';
import {setEntry} from '../set';
import {databaseConf, possibleDBKeys} from '../types';
enum keys {
UserId = 'a',
AccountName = 'b',
Username = 'c',
Description = 'd',
FollowersCount = 'e',
FollowingCount = 'f',
lastUpdateTimestamp = 'g',
ProfilePicture = 'h',
ProfilePictureBinaryLQ = 'i',
ProfilePictureBinaryHQ = 'j',
XpLevel = 'k',
XpPoints = 'l',
}
const name = 'users';
const primaryKey: keyof typeof propsDefault = keys.UserId;
const propsType: {[key in keyof typeof propsDefault]: string} = {
[keys.UserId]: 'string',
[keys.AccountName]: 'string',
[keys.Username]: 'string',
[keys.Description]: 'string',
[keys.FollowersCount]: 'int',
[keys.FollowingCount]: 'int',
[keys.lastUpdateTimestamp]: 'int',
[keys.ProfilePicture]: 'string', //URL
[keys.ProfilePictureBinaryLQ]: 'data',
[keys.ProfilePictureBinaryHQ]: 'data',
[keys.XpLevel]: 'int',
[keys.XpPoints]: 'int',
};
const propsDefault = {
[keys.UserId]: '',
[keys.AccountName]: '',
[keys.Username]: '',
[keys.Description]: '',
[keys.FollowersCount]: 0,
[keys.FollowingCount]: 0,
[keys.lastUpdateTimestamp]: 0,
[keys.ProfilePicture]: '', //URL
[keys.ProfilePictureBinaryLQ]: new ArrayBuffer(0),
[keys.ProfilePictureBinaryHQ]: new ArrayBuffer(0),
[keys.XpLevel]: 0,
[keys.XpPoints]: 0,
};
const thisSchema: databaseConf<typeof propsDefault, typeof keys> = {
filePath: name,
version: 1,
keys,
migration: () => {
return DBMigration[name](thisSchema);
},
setEntry: (val: typeof thisSchema.defaultProps, suffix?: string) => {
return setEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
val,
suffix,
);
},
getEntry: (key: possibleDBKeys, suffix?: string) => {
return getEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
key,
suffix,
);
},
getAllEntries: (filter?: filterParam, suffix?: string) => {
return getAllEntries<typeof thisSchema, typeof thisSchema.defaultProps>(
thisSchema,
filter,
suffix,
);
},
defaultProps: propsDefault,
details: {
name,
properties: propsType,
primaryKey,
},
};
export default thisSchema;

View File

@ -0,0 +1,19 @@
import {getDatabase} from './getDB';
import {databaseConf, mergeDBName} from './types';
import Realm from 'realm';
export const setEntry = async <T2 extends databaseConf<T, any>, T>(
schema: T2,
value: T,
suffix?: string,
) => {
const nameObj = {name: schema.details.name, suffix};
const dbName = mergeDBName(nameObj);
const realm = await getDatabase(nameObj);
realm.write(() => {
realm.create(nameObj.name, value as any, Realm.UpdateMode.Modified);
});
};

View File

@ -0,0 +1,44 @@
import MyUserManager from '@user/MyUserManager';
import {filterParam} from './get';
export type databaseNames = 'users' | 'chat' | 'chatRoomInfos';
export type possibleDBKeys = string;
export interface databaseConf<props, enums> {
filePath: string;
version: number;
migration?: any;
keys: enums;
getEntry: (key: possibleDBKeys, suffix?: string) => Promise<props | null>;
getAllEntries: (
filter?: filterParam,
suffix?: string,
) => Promise<props[] | null>;
setEntry: (val: props, suffix?: string) => Promise<void>;
defaultProps: props;
details: {
name: databaseNames;
properties: {[key in keyof props]: string};
primaryKey: keyof props;
};
}
export interface databaseNameSuffix {
name: databaseNames;
suffix?: string;
}
export function mergeDBName(nameObj: databaseNameSuffix, web?: 'web'): string {
if (web === 'web') {
return nameObj.suffix === undefined
? nameObj.name + '-' + MyUserManager.getSelectedUserId()
: nameObj.name +
'-' +
MyUserManager.getSelectedUserId() +
('_' + nameObj.suffix);
}
return nameObj.suffix === undefined
? nameObj.name
: nameObj.name + nameObj.suffix;
}

View File

@ -19,7 +19,7 @@ export const lang: LangFormat = {
},
calendar: {
tabName: 'Calendar',
overview: 'Calendar',
overview: 'Upcomming events',
},
map: {
tabName: 'Map',

View File

@ -1,5 +1,4 @@
import { MyScreenContainer } from '@components/MyScreenContainer';
import { Text } from '@gluestack-ui/themed';
import {CalendarScreen} from '@pages/calendar/calendar';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
@ -20,7 +19,9 @@ export type CalendarScreenNavigationProp =
NativeStackNavigationProp<CalendarStackNavigatorParamList>;
function CalendarTab() {
const lang = useSelector((state: RootState) => state.appVariables.lang.navigation.home.calendar);
const lang = useSelector(
(state: RootState) => state.appVariables.lang.navigation.home.calendar,
);
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
@ -36,7 +37,7 @@ function CalendarTab() {
<CalendarStack.Screen
name="Overview"
options={{
title: lang.tabName,
title: lang.overview,
headerShown: true,
headerShadowVisible: false,
headerStyle: headerStyle,
@ -47,12 +48,4 @@ function CalendarTab() {
);
}
function CalendarScreen() {
return (
<MyScreenContainer>
<Text>Calendar</Text>
</MyScreenContainer>
);
}
export default CalendarTab;

View File

@ -8,6 +8,8 @@ import {RootState} from '@redux/store';
import {View} from 'react-native';
import {useSelector} from 'react-redux';
import {Map} from '@pages/map/map';
export const MapTabName = 'Map';
export type MapStackNavigatorParamList = {
@ -44,11 +46,7 @@ function MapTab() {
}
function MapScreen() {
return (
<MyScreenContainer>
<Text>Jan dein Part</Text>
</MyScreenContainer>
);
return <Map />;
}
export default MapTab;

View File

@ -6,30 +6,89 @@ import {useSelector} from 'react-redux';
import {RootState, store} from '@redux/store';
import {useEffect} from 'react';
import imgSrc from '@img/maimg.png';
import {placeholder} from '@lang/default';
import {initAppData} from '@helper/appData';
import {appStatus} from '@configs/appNonSaveVar';
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
import BigDataManager from '@helper/storage/BigDataManager';
import {initKey} from '@helper/storage/bdm/encryption';
import UserManager from '@user/UserManager';
import MyUserManager from '@user/MyUserManager';
import DBSchemas from '@helper/storage/bdm/schemas';
import {chatTags, convertDatabaseChat} from '@configs/chat/types';
import {addChatEntity} from '@components/chat/initChatDatabase';
import {initDatabase} from '@helper/storage/bdm/init';
const AnimationView = animated(View);
function onAppStart() {
initAppData().then(() => {
export async function onAppStart() {
await initAppData();
await initKey();
BigDataManager.initDatabase()
.then(() => {
console.log('finish');
setTimeout(() => {
store.dispatch(
appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING),
.then(async () => {
const keys = BigDataManager.databases.chatRoomInfos.keys;
const entries =
(await BigDataManager.databases.chatRoomInfos.getAllEntries()) || [];
entries.sort((a, b) =>
a[keys.timestamp] > b[keys.timestamp]
? 1
: b[keys.timestamp] > a[keys.timestamp]
? -1
: 0,
);
}, 0);
for (let i = 0; i < entries.length; i++) {
console.log(entries[i]);
const chat = convertDatabaseChat(entries[i]);
if (chat === undefined) continue;
await initDatabase(DBSchemas.chat, chat.roomId);
addChatEntity(chat);
/*if (chat.roomId === 'test') {
const chatDBKeys = BigDataManager.databases.chat.keys;
for (let i = 0; i < 10; i++) {
await BigDataManager.databases.chat.setEntry(
{
[chatDBKeys.UserId]: MyUserManager.getSelectedUserId(),
[chatDBKeys.data]: 'heyho',
},
chat.roomId,
);
}
}*/
}
console.log('finish');
const usrDBKeys = BigDataManager.databases.users.keys;
await BigDataManager.databases.users.setEntry({
[usrDBKeys.UserId]: 'test',
[usrDBKeys.AccountName]: '#845613',
[usrDBKeys.Username]: 'TestGroupVirtual',
[usrDBKeys.Description]: 'This is a test account that is not real. ^^',
[usrDBKeys.FollowersCount]: 2,
[usrDBKeys.FollowingCount]: 24,
[usrDBKeys.lastUpdateTimestamp]: 412341234,
[usrDBKeys.ProfilePicture]: '',
[usrDBKeys.ProfilePictureBinaryHQ]: new ArrayBuffer(0),
[usrDBKeys.ProfilePictureBinaryLQ]: new ArrayBuffer(0),
[usrDBKeys.XpLevel]: 0,
[usrDBKeys.XpPoints]: 0,
});
store.dispatch(appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING));
})
.catch(err => {
console.error("Database Error! Can't start App :(");
console.error("Database Error! Can't start App :(", err);
});
//store.dispatch(actions.loadPreferences(appVar));
});
}
function StartHelper() {
@ -72,12 +131,17 @@ function StartHelper() {
});
}, []);
useEffect(onAppStart, []);
useEffect(() => {
(async () => {
await onAppStart();
})();
}, []);
if (currentAppStatus === appStatus.APP_RUNNING) return null;
return (
<SafeAreaView style={[{flex: 1, backgroundColor: currentTheme.backgroundDark400}]}>
<SafeAreaView
style={[{flex: 1, backgroundColor: currentTheme.backgroundDark400}]}>
<Center height={'100%'}>
<AnimationView
style={{

View File

@ -0,0 +1,30 @@
import {MyScreenContainer} from '@components/MyScreenContainer';
import {Text} from '@gluestack-ui/themed';
import {RootState} from '@redux/store';
import {View} from 'react-native';
import {useSelector} from 'react-redux';
const events = [
{
title: 'Bootshaus',
description: '01.12.2023 20:00 Uhr',
},
{
title: 'Bootshaus',
description: '01.12.2023 20:00 Uhr',
},
];
export function CalendarScreen() {
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
return (
<MyScreenContainer>
<Text>Calendar</Text>
<View style={{}}></View>
</MyScreenContainer>
);
}

140
src/pages/map/map.tsx Normal file
View File

@ -0,0 +1,140 @@
import {StyleSheet, View, Text, Image} from 'react-native';
import Mapbox, {MarkerView} from '@rnmapbox/maps';
import React, {useState} from 'react'; // Add useState import
import DisplayMarkerList from '@components/map/DisplayMarkerList';
import getLocationData, {PA_Point} from '@components/map/cluster/getData';
import {store} from '@redux/store';
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
import {Position} from '@rnmapbox/maps/src/types/Position';
import {Dimensions} from 'react-native';
Mapbox.setAccessToken(
'pk.eyJ1IjoidGl0YW5pdW1iYWNoIiwiYSI6ImNscGgzZGJxMDAwbHQyaXA2N3BtOWUxbWkifQ.x-f8JJxwQHWmPFI3P6Qn-w',
);
let lastCameraChange = 0;
let isRerenderData = 0;
export const Map = () => {
const mapRef = React.useRef<Mapbox.MapView | null>(null);
const [mapMarkers, setMapMarkers] = useState<PA_Point[]>([]); // Add useState for visibleBounds
const [lastDataRerender, setLastDataRerender] = useState(0); // Add useState for visibleBounds
const getVisibleBounds = async () => {
// return when lastDataRerender is 300ms ago
const now = Date.now();
if (now - lastDataRerender < 2000) return;
isRerenderData = now;
console.log('----1');
if (mapRef.current) {
const visibleBounds = await (
mapRef.current as Mapbox.MapView
).getVisibleBounds();
/* const deviceWidth = Dimensions.get('window').width;
const deviceHeight = Dimensions.get('window').height;
const visibleBounds: [Position, Position] = [
await mapRef.current.getCoordinateFromView([100, 100]),
await mapRef.current.getCoordinateFromView([100, 200]),
];
//log visibleBounds
console.log(visibleBounds);
*/
//const zoomLevel = await (mapRef.current as Mapbox.MapView).getZoom();
console.log('----2', Date.now() - now + 'ms');
//console.log(visibleBounds);
let markerData = (await getLocationData(visibleBounds, 0))?.locations;
console.log('----3', Date.now() - now + 'ms');
//store.dispatch(appNonSaveVarActions.setMapMarkers(markerData || []));
setMapMarkers(markerData || []);
console.log('----4', Date.now() - now + 'ms');
}
isRerenderData = 0;
};
function onCameraChanged() {
const now = Date.now();
if (now - lastCameraChange > 500) {
lastCameraChange = now;
setTimeout(getVisibleBounds, 500);
console.log('------------5', Date.now() - now + 'ms');
}
}
function onMapMoveFinished() {
getVisibleBounds();
}
React.useEffect(() => {
let intervalId: NodeJS.Timeout;
if (mapRef.current !== null) {
}
const executeInterval = async () => {
await getVisibleBounds();
intervalId = setTimeout(executeInterval, 50000);
};
executeInterval();
return () => {
clearInterval(intervalId);
};
}, []);
return (
<View style={styles.page}>
<View style={styles.container}>
<Mapbox.MapView
ref={mapRef}
onMapIdle={onMapMoveFinished}
onCameraChanged={onCameraChanged}
style={styles.map}
compassEnabled={true}
scaleBarEnabled={false}
//attributionPosition={{bottom: 75, left: 100}}
//logoPosition={{bottom: 75, left: 10}}
attributionPosition={{top: 10, left: 100}}
logoPosition={{top: 10, left: 10}}
styleURL="mapbox://styles/titaniumbach/clpij5uoo00o301pg2dj23j0m"
projection="globe">
<DisplayMarkerList markers={mapMarkers} />
</Mapbox.MapView>
</View>
</View>
);
};
const styles = StyleSheet.create({
page: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
container: {
height: '100%',
width: '100%',
},
map: {
flex: 1,
},
});

View File

@ -9,7 +9,9 @@ import {
} from '@configs/types';
import {saveVarChanges} from '@helper/appData';
import {apiBackendRequest, makeRequest} from '@helper/request';
import {store} from '@redux/store';
import BigDataManager from '@helper/storage/BigDataManager';
import {RootState, store} from '@redux/store';
import {useSelector} from 'react-redux';
import {MyUserAccount, createUserProp, SourceProp} from './types';
function createNewMyUser(
@ -33,13 +35,9 @@ function createNewMyUser(
EMail,
SessionId,
WebSocketSessionId,
lastUpdateTimestamp: createUserProp(
SourceProp.offline,
Math.floor(new Date().getTime() / 1000),
),
lastUpdateTimestamp: Math.floor(new Date().getTime() / 1000),
ProfilePicture: {
hq: createUserProp(SourceProp.offline),
lq: createUserProp(SourceProp.offline),
lq: createUserProp(SourceProp.online),
},
userSettings: {
lang: store.getState().appVariables.lang.details.langCode,
@ -47,9 +45,8 @@ function createNewMyUser(
},
};
console.log('SessionId', SessionId);
createMyUser(user);
BigDataManager.initDatabase();
makeRequest({
path: apiBackendRequest.APP_START,
@ -70,7 +67,6 @@ function createNewMyUser(
})
.then(resp => {
let user = {...getMyUser(UserId)};
console.log(user);
user.AccountName = createUserProp(
SourceProp.offline,
resp.response.AccountName,
@ -125,6 +121,27 @@ function setMyUser(user: MyUserAccount) {
saveVarChanges();
}
function logoutMyUser() {
store.dispatch(appVarActions.setCurrentAccount('none'));
saveVarChanges();
}
function getSelectedUserId(): UserId {
return store.getState().appVariables.preferences.selectedAccount;
}
function getSelectedMyUserSelector(): MyUserAccount | undefined {
const myUserId = useSelector(
(state: RootState) => state.appVariables.preferences.selectedAccount,
);
const myUser = useSelector(
(state: RootState) => state.appVariables.preferences.accounts[myUserId],
);
return myUser;
}
function getSessionId(userId?: UserId): XAuthorization | undefined {
const preferences = store.getState().appVariables.preferences;
let user = preferences.accounts[userId || preferences.selectedAccount];
@ -133,11 +150,14 @@ function getSessionId(userId?: UserId): XAuthorization | undefined {
let SessionId = user.SessionId;
console.log(userId || preferences.selectedAccount);
console.log(preferences.accounts[userId || preferences.selectedAccount]);
return SessionId;
}
const MyUserManager = {createNewMyUser, getSessionId};
const MyUserManager = {
createNewMyUser,
getSessionId,
getSelectedUserId,
logoutMyUser,
getSelectedMyUserSelector,
};
export default MyUserManager;

View File

@ -1,5 +1,285 @@
import {maxCachedUsers} from '@configs/appNonSaveVar';
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
import {UserId, XAuthorization} from '@configs/types';
import {store} from '@redux/store';
import {makeRequest, apiBackendRequest} from '@helper/request';
import BigDataManager from '@helper/storage/BigDataManager';
import {RootState, store} from '@redux/store';
import {useSelector} from 'react-redux';
import {
BasicUserProp,
createUserProp,
ProfilePicture,
ProfilePictureType,
SourceProp,
User,
} from './types';
const UserManager = {};
let cachedUserList: UserId[] = [];
async function getUser(
UserId: UserId,
save?: boolean,
): Promise<User | undefined> {
if (UserId === 'none') {
return undefined;
}
let user: User | undefined;
let state = store.getState();
let userIsInCache = false;
{
const usr = state.nonSaveVariables.cachedUsers[UserId];
if (usr !== undefined) {
user = usr;
userIsInCache = true;
}
}
if (UserId === state.appVariables.preferences.selectedAccount) {
const usr = state.appVariables.preferences.accounts[UserId];
if (usr !== undefined) {
user = {
AccountName: usr.AccountName,
Description: usr.Description,
FollowersCount: usr.FollowersCount,
FollowingCount: usr.FollowingCount,
lastUpdateTimestamp: usr.lastUpdateTimestamp,
ProfilePicture: usr.ProfilePicture,
UserId,
Username: usr.Username,
XpLevel: usr.XpLevel,
XpPoints: usr.XpPoints,
};
}
}
if (user === undefined) {
const usrDBKeys = BigDataManager.databases.users.keys;
const usr = await BigDataManager.databases.users.getEntry(UserId);
if (usr !== undefined && usr !== null) {
let ProfilePicture = {
lq:
usr[usrDBKeys.ProfilePictureBinaryLQ].byteLength !== 0
? createUserProp(
SourceProp.offline,
new Blob([usr[usrDBKeys.ProfilePictureBinaryLQ]]),
)
: createUserProp(
SourceProp.online,
undefined,
usr[usrDBKeys.ProfilePicture],
),
hq:
usr[usrDBKeys.ProfilePictureBinaryHQ].byteLength !== 0
? createUserProp(
SourceProp.offline,
new Blob([usr[usrDBKeys.ProfilePictureBinaryHQ]]),
)
: createUserProp(
SourceProp.online,
undefined,
usr[usrDBKeys.ProfilePicture],
),
};
user = {
AccountName: createUserProp(
SourceProp.offline,
usr[usrDBKeys.AccountName],
),
Description: createUserProp(
SourceProp.offline,
usr[usrDBKeys.Description],
),
FollowersCount: createUserProp(
SourceProp.offline,
usr[usrDBKeys.FollowersCount],
),
FollowingCount: createUserProp(
SourceProp.offline,
usr[usrDBKeys.FollowingCount],
),
lastUpdateTimestamp: usr[usrDBKeys.lastUpdateTimestamp],
ProfilePicture,
UserId,
Username: createUserProp(SourceProp.offline, usr[usrDBKeys.Username]),
XpLevel: createUserProp(SourceProp.offline, usr[usrDBKeys.XpLevel]),
XpPoints: createUserProp(SourceProp.offline, usr[usrDBKeys.XpPoints]),
};
}
}
if (user === undefined) {
try {
const resp = await makeRequest({
path: apiBackendRequest.GET_USER_PROFILE,
requestGET: {':userId': UserId},
response: {
AccountName: '',
Description: '',
FollowersCount: 0,
FollowingCount: 0,
Username: '',
XpLevel: 0,
XpPoints: 0,
AvatarUrl: '',
},
});
user = {
AccountName: createUserProp(
SourceProp.cached,
resp.response.AccountName,
),
Description: createUserProp(
SourceProp.cached,
resp.response.Description,
),
FollowersCount: createUserProp(
SourceProp.cached,
resp.response.FollowersCount,
),
FollowingCount: createUserProp(
SourceProp.cached,
resp.response.FollowingCount,
),
lastUpdateTimestamp: Math.floor(new Date().getTime() / 1000),
ProfilePicture: {
lq: createUserProp(
SourceProp.online,
undefined,
resp.response.AvatarUrl,
),
},
UserId,
Username: createUserProp(SourceProp.offline, resp.response.Username),
XpLevel: createUserProp(SourceProp.cached, resp.response.XpLevel),
XpPoints: createUserProp(SourceProp.cached, resp.response.XpPoints),
};
//BigDataManager.setEntry('users', user);
} catch (error: any) {
console.error(error.status);
}
}
if (userIsInCache === false && user !== undefined) {
console.log('save in cache');
store.dispatch(appNonSaveVarActions.setCachedUser(user));
cachedUserList.push(user.UserId);
if (cachedUserList.length > maxCachedUsers) {
let usrId = cachedUserList[0];
cachedUserList.shift();
console.log('usrId', usrId);
store.dispatch(appNonSaveVarActions.removeCachedUser(usrId));
}
}
return user;
}
enum GetParam {
CACHE = 0,
SAVE,
}
let getUserList: {[key: UserId]: GetParam} = {};
async function refreshUsers() {
for (let UserId in getUserList) {
const param = getUserList[UserId];
delete getUserList[UserId];
await getUser(UserId);
}
}
setInterval(refreshUsers, 500);
function addUserToGetQueue(UserId: UserId, param: GetParam) {
if (getUserList[UserId] === undefined) {
getUserList[UserId] = param;
} else if (getUserList[UserId] < param) {
getUserList[UserId] = param;
}
}
function getUserSelector(UserId: UserId) {
addUserToGetQueue(UserId, GetParam.CACHE);
const myUser = useSelector(
(state: RootState) => state.nonSaveVariables.cachedUsers[UserId],
);
if (myUser === undefined) {
return initUndefinedUser(UserId);
}
return myUser;
}
function getUserSelectorPicture(UserId: UserId): ProfilePicture {
addUserToGetQueue(UserId, GetParam.CACHE);
const myUser = useSelector(
(state: RootState) =>
state.nonSaveVariables.cachedUsers[UserId]?.ProfilePicture,
);
if (myUser === undefined) {
return {
lq: createUserProp(SourceProp.online),
hq: createUserProp(SourceProp.online),
};
}
return myUser;
}
function getUserSelectorAccountName(UserId: UserId): BasicUserProp<string> {
addUserToGetQueue(UserId, GetParam.CACHE);
const myUser = useSelector(
(state: RootState) =>
state.nonSaveVariables.cachedUsers[UserId]?.AccountName,
);
if (myUser === undefined) {
return createUserProp(SourceProp.online);
}
return myUser;
}
function initUndefinedUser(UserId: UserId): User {
return {
AccountName: createUserProp(SourceProp.online),
Description: createUserProp(SourceProp.online),
FollowersCount: createUserProp(SourceProp.online),
FollowingCount: createUserProp(SourceProp.online),
lastUpdateTimestamp: 0,
ProfilePicture: {
lq: createUserProp(SourceProp.online),
hq: createUserProp(SourceProp.online),
},
UserId,
Username: createUserProp(SourceProp.online),
XpLevel: createUserProp(SourceProp.online),
XpPoints: createUserProp(SourceProp.online),
};
}
const UserManager = {
getUser,
getUserSelector,
getUserSelectorPicture,
getUserSelectorAccountName,
initUndefinedUser,
};
export default UserManager;

View File

@ -1,4 +1,4 @@
import {ThemeMode} from '@configs/appVar';
import {ThemeMode} from '@configs/colors';
import {
AccountName,
EMail,
@ -16,22 +16,24 @@ export enum SourceProp {
cached = 1,
}
interface BasicUserProp<T1> {
export interface BasicUserProp<T1> {
source: SourceProp;
url?: string;
data?: T1;
}
export type ProfilePictureType = BasicUserProp<Blob | undefined>;
export interface ProfilePicture {
lq: BasicUserProp<string>;
hq: BasicUserProp<string>;
lq: ProfilePictureType; //low quality
hq?: ProfilePictureType; //high quality
}
export interface User {
UserId: UserId;
ProfilePicture: ProfilePicture;
lastUpdateTimestamp: BasicUserProp<timestamp>;
lastUpdateTimestamp: timestamp;
AccountName: BasicUserProp<AccountName>;
Username: BasicUserProp<Username>;
Description: BasicUserProp<string>;