From 0c25cc8e368c5613a4fbfcd2bfd0ad140d92ee53 Mon Sep 17 00:00:00 2001 From: Netcup Gituser Date: Thu, 7 Dec 2023 16:13:05 +0100 Subject: [PATCH] commit --- App.tsx | 118 -------- ios/.netrc | 3 + ios/Podfile | 6 + package-lock.json | 267 ++++++++++++++++ package.json | 4 + src/components/chat/initChatDatabase.ts | 28 ++ src/components/map/DisplayMarkerList.tsx | 55 ++++ src/components/map/EventMarker.tsx | 130 ++++++++ src/components/map/cluster/Cluster.tsx | 88 ++++++ src/components/map/cluster/getData.ts | 191 ++++++++++++ src/configs/appNonSaveVar.ts | 21 +- src/configs/appNonSaveVarReducer.ts | 44 +++ src/configs/appVar.ts | 2 + src/configs/appVarReducer.ts | 3 + src/configs/chat/types.ts | 107 +++++++ src/helper/secureRandom.ts | 1 + src/helper/storage/BigDataManager.ts | 13 +- src/helper/storage/appData.ts | 7 +- src/helper/storage/bdm/encryption.ts | 44 +++ src/helper/storage/bdm/get.ts | 39 +++ src/helper/storage/bdm/getDB.ts | 115 +++++++ src/helper/storage/bdm/init.ts | 54 +++- src/helper/storage/bdm/migration.ts | 54 +++- src/helper/storage/bdm/schemas.ts | 49 +-- src/helper/storage/bdm/schemas/chat.ts | 86 ++++++ .../storage/bdm/schemas/chatRoomInfos.ts | 73 +++++ src/helper/storage/bdm/schemas/users.ts | 93 ++++++ src/helper/storage/bdm/set.ts | 19 ++ src/helper/storage/bdm/types.ts | 44 +++ src/lang/en.ts | 2 +- src/navigation/tabs/main/CalendarTab.tsx | 17 +- src/navigation/tabs/main/MapTab.tsx | 8 +- src/pages/appStart/StartHelper.tsx | 98 ++++-- src/pages/calendar/calendar.tsx | 30 ++ src/pages/map/map.tsx | 140 +++++++++ src/user/MyUserManager.ts | 48 ++- src/user/UserManager.ts | 284 +++++++++++++++++- src/user/types.ts | 12 +- 38 files changed, 2148 insertions(+), 249 deletions(-) delete mode 100644 App.tsx create mode 100644 ios/.netrc create mode 100644 src/components/chat/initChatDatabase.ts create mode 100644 src/components/map/DisplayMarkerList.tsx create mode 100644 src/components/map/EventMarker.tsx create mode 100644 src/components/map/cluster/Cluster.tsx create mode 100644 src/components/map/cluster/getData.ts create mode 100644 src/configs/chat/types.ts create mode 100644 src/helper/secureRandom.ts create mode 100644 src/helper/storage/bdm/encryption.ts create mode 100644 src/helper/storage/bdm/get.ts create mode 100644 src/helper/storage/bdm/getDB.ts create mode 100644 src/helper/storage/bdm/schemas/chat.ts create mode 100644 src/helper/storage/bdm/schemas/chatRoomInfos.ts create mode 100644 src/helper/storage/bdm/schemas/users.ts create mode 100644 src/helper/storage/bdm/set.ts create mode 100644 src/helper/storage/bdm/types.ts create mode 100644 src/pages/calendar/calendar.tsx create mode 100644 src/pages/map/map.tsx diff --git a/App.tsx b/App.tsx deleted file mode 100644 index bf24c33..0000000 --- a/App.tsx +++ /dev/null @@ -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 ( - - - {title} - - - {children} - - - ); -} - -function App(): JSX.Element { - const isDarkMode = useColorScheme() === 'dark'; - - const backgroundStyle = { - backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, - }; - - return ( - - - -
- -
- Edit App.tsx to change this - screen and then come back to see your edits. -
-
- -
-
- -
-
- Read the docs to discover what to do next: -
- -
- - - ); -} - -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; diff --git a/ios/.netrc b/ios/.netrc new file mode 100644 index 0000000..e0e3885 --- /dev/null +++ b/ios/.netrc @@ -0,0 +1,3 @@ +machine api.mapbox.com +login mapbox +password sk.eyJ1IjoidGl0YW5pdW1iYWNoIiwiYSI6ImNscGlkb3I5MTBmbTYycm1oNDRmaWJrNTUifQ.Xp5gptRu3GcyAi8ihoOoIg \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index f1b7038..a013d1a 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -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, diff --git a/package-lock.json b/package-lock.json index e3ad379..e24ae7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index b120742..231743f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/chat/initChatDatabase.ts b/src/components/chat/initChatDatabase.ts new file mode 100644 index 0000000..047e615 --- /dev/null +++ b/src/components/chat/initChatDatabase.ts @@ -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; diff --git a/src/components/map/DisplayMarkerList.tsx b/src/components/map/DisplayMarkerList.tsx new file mode 100644 index 0000000..07ec062 --- /dev/null +++ b/src/components/map/DisplayMarkerList.tsx @@ -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 ( + + {eventMapMarkers.map((marker: PA_Point) => { + return marker.type === 'event' ? ( + + + + ) : ( + + + + ); + })} + + ); +} + +const styles = StyleSheet.create({ + marker: { + padding: 5, + borderRadius: 5, + }, + markerText: { + color: 'white', + fontWeight: 'bold', + }, +}); + +export default DisplayMarkerList; diff --git a/src/components/map/EventMarker.tsx b/src/components/map/EventMarker.tsx new file mode 100644 index 0000000..e364482 --- /dev/null +++ b/src/components/map/EventMarker.tsx @@ -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 ( + + + {avatars.slice(0, 3).map((avatar, index) => { + return ( + + {avatar.alt} + + ); + })} + + {'+ ' + remainingCount + ''} + + + + + {marker.type} + + ); +} + +const styles = StyleSheet.create({ + marker: { + padding: 5, + borderRadius: 5, + }, + markerText: { + color: 'white', + fontWeight: 'bold', + }, +}); diff --git a/src/components/map/cluster/Cluster.tsx b/src/components/map/cluster/Cluster.tsx new file mode 100644 index 0000000..f6600c1 --- /dev/null +++ b/src/components/map/cluster/Cluster.tsx @@ -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 ( + + + + + {number} + + + + + ); +}; + +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; diff --git a/src/components/map/cluster/getData.ts b/src/components/map/cluster/getData.ts new file mode 100644 index 0000000..d6bff35 --- /dev/null +++ b/src/components/map/cluster/getData.ts @@ -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; diff --git a/src/configs/appNonSaveVar.ts b/src/configs/appNonSaveVar.ts index 39a7218..41b18d8 100644 --- a/src/configs/appNonSaveVar.ts +++ b/src/configs/appNonSaveVar.ts @@ -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: [], }; diff --git a/src/configs/appNonSaveVarReducer.ts b/src/configs/appNonSaveVarReducer.ts index 74099bb..06caba7 100644 --- a/src/configs/appNonSaveVarReducer.ts +++ b/src/configs/appNonSaveVarReducer.ts @@ -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) => { state.theme = action.payload; }, + setCachedUser: (state, action: PayloadAction) => { + state.cachedUsers[action.payload.UserId] = action.payload; + }, + removeCachedUser: (state, action: PayloadAction) => { + delete state.cachedUsers[action.payload]; + }, + setSelectedChat: (state, action: PayloadAction) => { + state.selectedChat = action.payload; + }, + setChatEntity: (state, action: PayloadAction) => { + const roomId = action.payload.roomId; + + state.chats[roomId] = action.payload; + + if (state.chatActivity.includes(roomId) === false) + state.chatActivity.unshift(roomId); + }, + changeChatEntity: (state, action: PayloadAction) => { + 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) => { + delete state.chats[action.payload]; + + state.chatActivity = state.chatActivity.filter(function (ele) { + return ele !== action.payload; + }); + }, + setMapMarkers: (state, action: PayloadAction) => { + state.eventMapMarkers = action.payload; + } }, }); diff --git a/src/configs/appVar.ts b/src/configs/appVar.ts index 2bb1c8a..f94db1a 100644 --- a/src/configs/appVar.ts +++ b/src/configs/appVar.ts @@ -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: { diff --git a/src/configs/appVarReducer.ts b/src/configs/appVarReducer.ts index e5bea75..4d98a5c 100644 --- a/src/configs/appVarReducer.ts +++ b/src/configs/appVarReducer.ts @@ -43,6 +43,9 @@ export const appVariablesSlice = createSlice({ setAccount: (state, action: PayloadAction) => { state.preferences.accounts[action.payload.UserId] = action.payload; }, + setDBEK: (state, action: PayloadAction) => { + state.preferences.dbek = action.payload; + }, }, }); diff --git a/src/configs/chat/types.ts b/src/configs/chat/types.ts new file mode 100644 index 0000000..8b5b98d --- /dev/null +++ b/src/configs/chat/types.ts @@ -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; +} diff --git a/src/helper/secureRandom.ts b/src/helper/secureRandom.ts new file mode 100644 index 0000000..e49be8c --- /dev/null +++ b/src/helper/secureRandom.ts @@ -0,0 +1 @@ +export {generateSecureRandom} from 'react-native-securerandom'; diff --git a/src/helper/storage/BigDataManager.ts b/src/helper/storage/BigDataManager.ts index de7f78c..d41d805 100644 --- a/src/helper/storage/BigDataManager.ts +++ b/src/helper/storage/BigDataManager.ts @@ -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; diff --git a/src/helper/storage/appData.ts b/src/helper/storage/appData.ts index 18a451a..ff1838e 100644 --- a/src/helper/storage/appData.ts +++ b/src/helper/storage/appData.ts @@ -1,7 +1,12 @@ import EncryptedStorage from 'react-native-encrypted-storage'; export async function getData(key: string): Promise { - 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 { diff --git a/src/helper/storage/bdm/encryption.ts b/src/helper/storage/bdm/encryption.ts new file mode 100644 index 0000000..fcf175f --- /dev/null +++ b/src/helper/storage/bdm/encryption.ts @@ -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 { + 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; +} diff --git a/src/helper/storage/bdm/get.ts b/src/helper/storage/bdm/get.ts new file mode 100644 index 0000000..a1a5379 --- /dev/null +++ b/src/helper/storage/bdm/get.ts @@ -0,0 +1,39 @@ +import {getDatabase} from './getDB'; +import {databaseConf, mergeDBName, possibleDBKeys} from './types'; + +export interface filterParam { + type: 'name'; +} + +export const getEntry = async , T>( + schema: T2, + key: possibleDBKeys, + suffix?: string, +): Promise => { + const nameObj = {name: schema.details.name, suffix}; + const dbName = mergeDBName(nameObj); + + const realm = await getDatabase(nameObj); + + const val = realm.objectForPrimaryKey( + nameObj.name, + key, + ); + + return val as T; +}; + +export const getAllEntries = async , T>( + schema: T2, + filter?: filterParam, + suffix?: string, +): Promise => { + const nameObj = {name: schema.details.name, suffix}; + const dbName = mergeDBName(nameObj); + + const realm = await getDatabase(nameObj); + + const val = realm.objects(nameObj.name); + + return [...val] as T[]; +}; diff --git a/src/helper/storage/bdm/getDB.ts b/src/helper/storage/bdm/getDB.ts new file mode 100644 index 0000000..98a72e0 --- /dev/null +++ b/src/helper/storage/bdm/getDB.ts @@ -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 { + 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 { + 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"; + } +} diff --git a/src/helper/storage/bdm/init.ts b/src/helper/storage/bdm/init.ts index fb89f74..9fbd988 100644 --- a/src/helper/storage/bdm/init.ts +++ b/src/helper/storage/bdm/init.ts @@ -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 initDatabase = (): Promise => { - 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, - }); - } - resolve(); - }, 1000); +export const initDatabases = (): Promise => { + return new Promise(async (resolve, reject) => { + closeAllDatabases(); + + for (const key in DBSchemas) { + const schema = DBSchemas[key as keyof typeof DBSchemas]; + const name = schema.details.name; + + if (SkipDBSchemas.includes(name)) continue; + + await initDatabase(schema); + } + resolve(); }); }; + +export const initDatabase = async ( + schema: (typeof DBSchemas)[keyof typeof DBSchemas], + fileSuffix?: string, +): Promise => { + 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; +}; diff --git a/src/helper/storage/bdm/migration.ts b/src/helper/storage/bdm/migration.ts index 0ce30e6..f0c9f13 100644 --- a/src/helper/storage/bdm/migration.ts +++ b/src/helper/storage/bdm/migration.ts @@ -1,15 +1,45 @@ import {MigrationCallback} from 'realm'; +import DBSchemas from './schemas'; +import {databaseConf, databaseNames} from './types'; -export const usersDBMigration: 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}`; - } - }*/ +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'); + 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; + }, + 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; + }, }; diff --git a/src/helper/storage/bdm/schemas.ts b/src/helper/storage/bdm/schemas.ts index a6b1276..d836b2d 100644 --- a/src/helper/storage/bdm/schemas.ts +++ b/src/helper/storage/bdm/schemas.ts @@ -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; diff --git a/src/helper/storage/bdm/schemas/chat.ts b/src/helper/storage/bdm/schemas/chat.ts new file mode 100644 index 0000000..42bf2e9 --- /dev/null +++ b/src/helper/storage/bdm/schemas/chat.ts @@ -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 = { + filePath: name, + version: 1, + keys, + migration: () => { + return DBMigration[name](thisSchema); + }, + setEntry: (val: typeof thisSchema.defaultProps, suffix?: string) => { + return setEntry( + thisSchema, + val, + suffix, + ); + }, + getEntry: (key: possibleDBKeys, suffix?: string) => { + return getEntry( + thisSchema, + key, + suffix, + ); + }, + getAllEntries: (filter?: filterParam, suffix?: string) => { + return getAllEntries( + thisSchema, + filter, + suffix, + ); + }, + defaultProps: propsDefault, + details: { + name, + properties: propsType, + primaryKey, + }, +}; + +export default thisSchema; diff --git a/src/helper/storage/bdm/schemas/chatRoomInfos.ts b/src/helper/storage/bdm/schemas/chatRoomInfos.ts new file mode 100644 index 0000000..b7b2b19 --- /dev/null +++ b/src/helper/storage/bdm/schemas/chatRoomInfos.ts @@ -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 = { + filePath: name, + version: 1, + keys, + migration: () => { + return DBMigration[name](thisSchema); + }, + setEntry: (val: typeof thisSchema.defaultProps, suffix?: string) => { + return setEntry( + thisSchema, + val, + suffix, + ); + }, + getEntry: (key: possibleDBKeys, suffix?: string) => { + return getEntry( + thisSchema, + key, + suffix, + ); + }, + getAllEntries: (filter?: filterParam, suffix?: string) => { + return getAllEntries( + thisSchema, + filter, + suffix, + ); + }, + defaultProps: propsDefault, + details: { + name, + properties: propsType, + primaryKey, + }, +}; + +export default thisSchema; diff --git a/src/helper/storage/bdm/schemas/users.ts b/src/helper/storage/bdm/schemas/users.ts new file mode 100644 index 0000000..e1a49a0 --- /dev/null +++ b/src/helper/storage/bdm/schemas/users.ts @@ -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 = { + filePath: name, + version: 1, + keys, + migration: () => { + return DBMigration[name](thisSchema); + }, + setEntry: (val: typeof thisSchema.defaultProps, suffix?: string) => { + return setEntry( + thisSchema, + val, + suffix, + ); + }, + getEntry: (key: possibleDBKeys, suffix?: string) => { + return getEntry( + thisSchema, + key, + suffix, + ); + }, + getAllEntries: (filter?: filterParam, suffix?: string) => { + return getAllEntries( + thisSchema, + filter, + suffix, + ); + }, + defaultProps: propsDefault, + details: { + name, + properties: propsType, + primaryKey, + }, +}; + +export default thisSchema; diff --git a/src/helper/storage/bdm/set.ts b/src/helper/storage/bdm/set.ts new file mode 100644 index 0000000..0e0850f --- /dev/null +++ b/src/helper/storage/bdm/set.ts @@ -0,0 +1,19 @@ +import {getDatabase} from './getDB'; +import {databaseConf, mergeDBName} from './types'; + +import Realm from 'realm'; + +export const setEntry = async , 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); + }); +}; diff --git a/src/helper/storage/bdm/types.ts b/src/helper/storage/bdm/types.ts new file mode 100644 index 0000000..766d1ec --- /dev/null +++ b/src/helper/storage/bdm/types.ts @@ -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 { + filePath: string; + version: number; + migration?: any; + keys: enums; + getEntry: (key: possibleDBKeys, suffix?: string) => Promise; + getAllEntries: ( + filter?: filterParam, + suffix?: string, + ) => Promise; + setEntry: (val: props, suffix?: string) => Promise; + 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; +} diff --git a/src/lang/en.ts b/src/lang/en.ts index 419530a..a7039ad 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -19,7 +19,7 @@ export const lang: LangFormat = { }, calendar: { tabName: 'Calendar', - overview: 'Calendar', + overview: 'Upcomming events', }, map: { tabName: 'Map', diff --git a/src/navigation/tabs/main/CalendarTab.tsx b/src/navigation/tabs/main/CalendarTab.tsx index 5a4d6b8..a09d69c 100644 --- a/src/navigation/tabs/main/CalendarTab.tsx +++ b/src/navigation/tabs/main/CalendarTab.tsx @@ -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; 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() { - Calendar - - ); -} - export default CalendarTab; diff --git a/src/navigation/tabs/main/MapTab.tsx b/src/navigation/tabs/main/MapTab.tsx index 5c19efe..7142a1d 100644 --- a/src/navigation/tabs/main/MapTab.tsx +++ b/src/navigation/tabs/main/MapTab.tsx @@ -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 ( - - Jan dein Part - - ); + return ; } export default MapTab; diff --git a/src/pages/appStart/StartHelper.tsx b/src/pages/appStart/StartHelper.tsx index 9311135..3cfb05d 100644 --- a/src/pages/appStart/StartHelper.tsx +++ b/src/pages/appStart/StartHelper.tsx @@ -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(() => { - BigDataManager.initDatabase() - .then(() => { - console.log('finish'); - setTimeout(() => { - store.dispatch( - appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING), - ); - }, 0); - }) - .catch(err => { - console.error("Database Error! Can't start App :("); +export async function onAppStart() { + await initAppData(); + + await initKey(); + + BigDataManager.initDatabase() + .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, + ); + + 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(actions.loadPreferences(appVar)); - }); + store.dispatch(appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING)); + }) + .catch(err => { + 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 ( - +
state.nonSaveVariables.theme.colors, + ); + + return ( + + Calendar + + + + ); +} diff --git a/src/pages/map/map.tsx b/src/pages/map/map.tsx new file mode 100644 index 0000000..1b57d06 --- /dev/null +++ b/src/pages/map/map.tsx @@ -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(null); + const [mapMarkers, setMapMarkers] = useState([]); // 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 ( + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + page: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + container: { + height: '100%', + width: '100%', + }, + map: { + flex: 1, + }, +}); diff --git a/src/user/MyUserManager.ts b/src/user/MyUserManager.ts index b7132ab..c4b447b 100644 --- a/src/user/MyUserManager.ts +++ b/src/user/MyUserManager.ts @@ -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; diff --git a/src/user/UserManager.ts b/src/user/UserManager.ts index 5a16591..4cc1a7d 100644 --- a/src/user/UserManager.ts +++ b/src/user/UserManager.ts @@ -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 { + 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 { + 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; diff --git a/src/user/types.ts b/src/user/types.ts index 4f65713..aa4ffd7 100644 --- a/src/user/types.ts +++ b/src/user/types.ts @@ -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 { +export interface BasicUserProp { source: SourceProp; url?: string; data?: T1; } +export type ProfilePictureType = BasicUserProp; + export interface ProfilePicture { - lq: BasicUserProp; - hq: BasicUserProp; + lq: ProfilePictureType; //low quality + hq?: ProfilePictureType; //high quality } export interface User { UserId: UserId; ProfilePicture: ProfilePicture; - lastUpdateTimestamp: BasicUserProp; + lastUpdateTimestamp: timestamp; AccountName: BasicUserProp; Username: BasicUserProp; Description: BasicUserProp;