From 58e907258b5af66984192322bc00f0e6302dc853 Mon Sep 17 00:00:00 2001 From: Devin Godage Date: Wed, 6 May 2026 00:43:49 +0530 Subject: [PATCH 1/6] chore(license-updates): Add license plist files for react-native-is-edge-to-edge@1.3.1 and react-native-legal@1.6.3, remove outdated vector icons and supports-color@5.5.0 plist files, and update xml-formatter to version 3.7.0 --- .../libraries/@babel_code-frame@7.10.4.json | 1 - .../libraries/@babel_highlight@7.25.9.json | 1 - ....0.json => @callstack_licenses@0.3.1.json} | 2 +- ....json => @expo_config-plugins@55.0.8.json} | 2 +- .../libraries/@expo_config-types@54.0.10.json | 1 - .../libraries/@expo_config-types@55.0.5.json | 1 + .../libraries/@expo_json-file@10.0.13.json | 1 + .../libraries/@expo_json-file@10.0.8.json | 1 - ...list@0.4.8.json => @expo_plist@0.5.2.json} | 2 +- ...0.8.12.json => @xmldom_xmldom@0.8.13.json} | 2 +- .../config/libraries/ansi-styles@3.2.1.json | 1 - android/config/libraries/chalk@2.4.2.json | 1 - .../config/libraries/color-convert@1.9.3.json | 1 - .../config/libraries/color-name@1.1.3.json | 1 - .../libraries/escape-string-regexp@1.0.5.json | 1 - android/config/libraries/has-flag@3.0.0.json | 1 - ...7.0.2.json => node-html-parser@7.1.0.json} | 2 +- ...son => react-native-bootsplash@7.3.1.json} | 2 +- .../react-native-is-edge-to-edge@1.3.1.json | 1 + ...6.0.json => react-native-legal@1.6.3.json} | 2 +- .../libraries/supports-color@5.5.0.json | 1 - ...er@3.6.7.json => xml-formatter@3.7.0.json} | 2 +- ios/Podfile.lock | 150 ++++----- ...om.mono0926.LicensePlist.latest_result.txt | 186 +---------- .../com.mono0926.LicensePlist.plist | 292 ++---------------- .../@babel_code-frame@7.10.4.plist | 39 --- .../@babel_highlight@7.25.9.plist | 39 --- ....plist => @callstack_licenses@0.3.1.plist} | 0 ...list => @expo_config-plugins@55.0.8.plist} | 0 ....plist => @expo_config-types@55.0.5.plist} | 0 ....8.plist => @expo_json-file@10.0.13.plist} | 0 ...st@0.4.8.plist => @expo_plist@0.5.2.plist} | 0 ...8.12.plist => @xmldom_xmldom@0.8.13.plist} | 0 .../com.mono0926.LicensePlist/HtmlToPdf.plist | 37 --- .../RNBootSplash.plist | 38 --- .../RNGestureHandler.plist | 38 --- .../RNLocalize.plist | 38 --- .../RNReanimated.plist | 38 --- .../com.mono0926.LicensePlist/RNSVG.plist | 38 --- .../com.mono0926.LicensePlist/RNScreens.plist | 38 --- .../com.mono0926.LicensePlist/RNShare.plist | 39 --- .../RNWorklets.plist | 37 --- .../React-Core.plist | 38 --- .../com.mono0926.LicensePlist/React.plist | 38 --- .../ReactNativeLegal.plist | 38 --- .../ansi-styles@3.2.1.plist | 26 -- .../chalk@2.4.2.plist | 26 -- .../color-convert@1.9.3.plist | 38 --- .../color-name@1.1.3.plist | 24 -- .../escape-string-regexp@1.0.5.plist | 38 --- .../has-flag@3.0.0.plist | 26 -- ...0.2.plist => node-html-parser@7.1.0.plist} | 0 ...st => react-native-bootsplash@7.3.1.plist} | 0 ... react-native-is-edge-to-edge@1.3.1.plist} | 4 +- ...0.plist => react-native-legal@1.6.3.plist} | 0 .../react-native-unistyles.plist | 37 --- ...react-native-vector-icons-ant-design.plist | 37 --- .../react-native-vector-icons-feather.plist | 37 --- ...act-native-vector-icons-fontawesome6.plist | 37 --- ...react-native-vector-icons-foundation.plist | 37 --- .../react-native-vector-icons-ionicons.plist | 37 --- .../react-native-vector-icons-lucide.plist | 37 --- ...e-vector-icons-material-design-icons.plist | 37 --- .../react-native-vector-icons-octicons.plist | 37 --- .../supports-color@5.5.0.plist | 26 -- ...@3.6.7.plist => xml-formatter@3.7.0.plist} | 0 ios/TipCalculator.xcodeproj/project.pbxproj | 14 +- ios/license_plist.yml | 143 ++++----- 68 files changed, 205 insertions(+), 1684 deletions(-) delete mode 100644 android/config/libraries/@babel_code-frame@7.10.4.json delete mode 100644 android/config/libraries/@babel_highlight@7.25.9.json rename android/config/libraries/{@callstack_licenses@0.3.0.json => @callstack_licenses@0.3.1.json} (77%) rename android/config/libraries/{@expo_config-plugins@54.0.4.json => @expo_config-plugins@55.0.8.json} (67%) delete mode 100644 android/config/libraries/@expo_config-types@54.0.10.json create mode 100644 android/config/libraries/@expo_config-types@55.0.5.json create mode 100644 android/config/libraries/@expo_json-file@10.0.13.json delete mode 100644 android/config/libraries/@expo_json-file@10.0.8.json rename android/config/libraries/{@expo_plist@0.4.8.json => @expo_plist@0.5.2.json} (67%) rename android/config/libraries/{@xmldom_xmldom@0.8.12.json => @xmldom_xmldom@0.8.13.json} (71%) delete mode 100644 android/config/libraries/ansi-styles@3.2.1.json delete mode 100644 android/config/libraries/chalk@2.4.2.json delete mode 100644 android/config/libraries/color-convert@1.9.3.json delete mode 100644 android/config/libraries/color-name@1.1.3.json delete mode 100644 android/config/libraries/escape-string-regexp@1.0.5.json delete mode 100644 android/config/libraries/has-flag@3.0.0.json rename android/config/libraries/{node-html-parser@7.0.2.json => node-html-parser@7.1.0.json} (73%) rename android/config/libraries/{react-native-bootsplash@7.1.0.json => react-native-bootsplash@7.3.1.json} (73%) create mode 100644 android/config/libraries/react-native-is-edge-to-edge@1.3.1.json rename android/config/libraries/{react-native-legal@1.6.0.json => react-native-legal@1.6.3.json} (75%) delete mode 100644 android/config/libraries/supports-color@5.5.0.json rename android/config/libraries/{xml-formatter@3.6.7.json => xml-formatter@3.7.0.json} (74%) delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/@babel_code-frame@7.10.4.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/@babel_highlight@7.25.9.plist rename ios/Settings.bundle/com.mono0926.LicensePlist/{@callstack_licenses@0.3.0.plist => @callstack_licenses@0.3.1.plist} (100%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{@expo_config-plugins@54.0.4.plist => @expo_config-plugins@55.0.8.plist} (100%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{@expo_config-types@54.0.10.plist => @expo_config-types@55.0.5.plist} (100%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{@expo_json-file@10.0.8.plist => @expo_json-file@10.0.13.plist} (100%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{@expo_plist@0.4.8.plist => @expo_plist@0.5.2.plist} (100%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{@xmldom_xmldom@0.8.12.plist => @xmldom_xmldom@0.8.13.plist} (100%) delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/HtmlToPdf.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNBootSplash.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNGestureHandler.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNLocalize.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNReanimated.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNSVG.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNScreens.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNShare.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/RNWorklets.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/React-Core.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/React.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/ReactNativeLegal.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/ansi-styles@3.2.1.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/chalk@2.4.2.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/color-convert@1.9.3.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/color-name@1.1.3.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/escape-string-regexp@1.0.5.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/has-flag@3.0.0.plist rename ios/Settings.bundle/com.mono0926.LicensePlist/{node-html-parser@7.0.2.plist => node-html-parser@7.1.0.plist} (100%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{react-native-bootsplash@7.1.0.plist => react-native-bootsplash@7.3.1.plist} (100%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{react-native-safe-area-context.plist => react-native-is-edge-to-edge@1.3.1.plist} (95%) rename ios/Settings.bundle/com.mono0926.LicensePlist/{react-native-legal@1.6.0.plist => react-native-legal@1.6.3.plist} (100%) delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-unistyles.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ant-design.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-feather.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-fontawesome6.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-foundation.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ionicons.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-lucide.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-material-design-icons.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-octicons.plist delete mode 100644 ios/Settings.bundle/com.mono0926.LicensePlist/supports-color@5.5.0.plist rename ios/Settings.bundle/com.mono0926.LicensePlist/{xml-formatter@3.6.7.plist => xml-formatter@3.7.0.plist} (100%) diff --git a/android/config/libraries/@babel_code-frame@7.10.4.json b/android/config/libraries/@babel_code-frame@7.10.4.json deleted file mode 100644 index a8a11448..00000000 --- a/android/config/libraries/@babel_code-frame@7.10.4.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"7.10.4","description":"Generate errors that contain a code frame that point to source locations.","developers":[{"name":"Sebastian McKenzie ","organisationUrl":""}],"licenses":["MIT_29844c3773154ee8b2e579050c77793e74261da427b77cf5ea7b010de3f167d60d9aaec8165b25a41065477508fb3be56c47a6ce8c0e61e2a297d6b4664398c5"],"name":"@babel/code-frame","tag":"","uniqueId":"@babel_code-frame@7.10.4","website":"https://github.com/babel/babel"} \ No newline at end of file diff --git a/android/config/libraries/@babel_highlight@7.25.9.json b/android/config/libraries/@babel_highlight@7.25.9.json deleted file mode 100644 index ed2776b8..00000000 --- a/android/config/libraries/@babel_highlight@7.25.9.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"7.25.9","description":"Syntax highlight JavaScript strings for output in terminals.","developers":[{"name":"The Babel Team (https://babel.dev/team)","organisationUrl":""}],"licenses":["MIT_29844c3773154ee8b2e579050c77793e74261da427b77cf5ea7b010de3f167d60d9aaec8165b25a41065477508fb3be56c47a6ce8c0e61e2a297d6b4664398c5"],"name":"@babel/highlight","tag":"","uniqueId":"@babel_highlight@7.25.9","website":"https://github.com/babel/babel"} \ No newline at end of file diff --git a/android/config/libraries/@callstack_licenses@0.3.0.json b/android/config/libraries/@callstack_licenses@0.3.1.json similarity index 77% rename from android/config/libraries/@callstack_licenses@0.3.0.json rename to android/config/libraries/@callstack_licenses@0.3.1.json index e13507ea..8cbedc85 100644 --- a/android/config/libraries/@callstack_licenses@0.3.0.json +++ b/android/config/libraries/@callstack_licenses@0.3.1.json @@ -1 +1 @@ -{"artifactVersion":"0.3.0","description":"API package that exposes functionalities for programmatic scanning of licenses in Node.js projects.","developers":[{"name":"Mateusz Mędrek (https://github.com/mateusz1913)","organisationUrl":""}],"licenses":["MIT_a7a37cc7a016a6c0ab07dab285d42eec347265002608d942ddff2203381875790621a0c35e6261921f1059784153a72767a0b3eb3e6225873d5358e2c8df8523"],"name":"@callstack/licenses","tag":"","uniqueId":"@callstack_licenses@0.3.0","website":"https://github.com/callstackincubator/react-native-legal"} \ No newline at end of file +{"artifactVersion":"0.3.1","description":"API package that exposes functionalities for programmatic scanning of licenses in Node.js projects.","developers":[{"name":"Mateusz Mędrek (https://github.com/mateusz1913)","organisationUrl":""}],"licenses":["MIT_a7a37cc7a016a6c0ab07dab285d42eec347265002608d942ddff2203381875790621a0c35e6261921f1059784153a72767a0b3eb3e6225873d5358e2c8df8523"],"name":"@callstack/licenses","tag":"","uniqueId":"@callstack_licenses@0.3.1","website":"https://github.com/callstackincubator/react-native-legal"} \ No newline at end of file diff --git a/android/config/libraries/@expo_config-plugins@54.0.4.json b/android/config/libraries/@expo_config-plugins@55.0.8.json similarity index 67% rename from android/config/libraries/@expo_config-plugins@54.0.4.json rename to android/config/libraries/@expo_config-plugins@55.0.8.json index d315083b..c854f780 100644 --- a/android/config/libraries/@expo_config-plugins@54.0.4.json +++ b/android/config/libraries/@expo_config-plugins@55.0.8.json @@ -1 +1 @@ -{"artifactVersion":"54.0.4","description":"A library for Expo config plugins","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_de05e3e50e0f51de88a968f0a13f2503f3ea5feeed361fc4f7c49b5929c8ad6cd859c4ae1dc955df847d5e3c71772cfd181ba655f05daf3fddbc3a2b41d717bb"],"name":"@expo/config-plugins","tag":"","uniqueId":"@expo_config-plugins@54.0.4","website":"https://github.com/expo/expo"} \ No newline at end of file +{"artifactVersion":"55.0.8","description":"A library for Expo config plugins","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_de05e3e50e0f51de88a968f0a13f2503f3ea5feeed361fc4f7c49b5929c8ad6cd859c4ae1dc955df847d5e3c71772cfd181ba655f05daf3fddbc3a2b41d717bb"],"name":"@expo/config-plugins","tag":"","uniqueId":"@expo_config-plugins@55.0.8","website":"https://github.com/expo/expo"} \ No newline at end of file diff --git a/android/config/libraries/@expo_config-types@54.0.10.json b/android/config/libraries/@expo_config-types@54.0.10.json deleted file mode 100644 index df901ee6..00000000 --- a/android/config/libraries/@expo_config-types@54.0.10.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"54.0.10","description":"Types for the Expo config object app.config.ts","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_e515d90c7c6764495b4780471b2030d482e1c95b1eb181f14a52a372222c5cd2ce19a16527193946a39fcef06419d52223d66a895e874a0bbcab4a8020206489"],"name":"@expo/config-types","tag":"","uniqueId":"@expo_config-types@54.0.10","website":"https://github.com/expo/expo"} \ No newline at end of file diff --git a/android/config/libraries/@expo_config-types@55.0.5.json b/android/config/libraries/@expo_config-types@55.0.5.json new file mode 100644 index 00000000..8a02129f --- /dev/null +++ b/android/config/libraries/@expo_config-types@55.0.5.json @@ -0,0 +1 @@ +{"artifactVersion":"55.0.5","description":"Types for the Expo config object app.config.ts","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_e515d90c7c6764495b4780471b2030d482e1c95b1eb181f14a52a372222c5cd2ce19a16527193946a39fcef06419d52223d66a895e874a0bbcab4a8020206489"],"name":"@expo/config-types","tag":"","uniqueId":"@expo_config-types@55.0.5","website":"https://github.com/expo/expo"} \ No newline at end of file diff --git a/android/config/libraries/@expo_json-file@10.0.13.json b/android/config/libraries/@expo_json-file@10.0.13.json new file mode 100644 index 00000000..a1f51cbd --- /dev/null +++ b/android/config/libraries/@expo_json-file@10.0.13.json @@ -0,0 +1 @@ +{"artifactVersion":"10.0.13","description":"A module for reading, writing, and manipulating JSON files","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_de05e3e50e0f51de88a968f0a13f2503f3ea5feeed361fc4f7c49b5929c8ad6cd859c4ae1dc955df847d5e3c71772cfd181ba655f05daf3fddbc3a2b41d717bb"],"name":"@expo/json-file","tag":"","uniqueId":"@expo_json-file@10.0.13","website":"https://github.com/expo/expo"} \ No newline at end of file diff --git a/android/config/libraries/@expo_json-file@10.0.8.json b/android/config/libraries/@expo_json-file@10.0.8.json deleted file mode 100644 index 6489d6b7..00000000 --- a/android/config/libraries/@expo_json-file@10.0.8.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"10.0.8","description":"A module for reading, writing, and manipulating JSON files","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_de05e3e50e0f51de88a968f0a13f2503f3ea5feeed361fc4f7c49b5929c8ad6cd859c4ae1dc955df847d5e3c71772cfd181ba655f05daf3fddbc3a2b41d717bb"],"name":"@expo/json-file","tag":"","uniqueId":"@expo_json-file@10.0.8","website":"https://github.com/expo/expo"} \ No newline at end of file diff --git a/android/config/libraries/@expo_plist@0.4.8.json b/android/config/libraries/@expo_plist@0.5.2.json similarity index 67% rename from android/config/libraries/@expo_plist@0.4.8.json rename to android/config/libraries/@expo_plist@0.5.2.json index 0b5ae519..b03f4b95 100644 --- a/android/config/libraries/@expo_plist@0.4.8.json +++ b/android/config/libraries/@expo_plist@0.5.2.json @@ -1 +1 @@ -{"artifactVersion":"0.4.8","description":"Mac OS X Plist parser/builder for Node.js and browsers","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_830462f6b7b459f3f5b35b61e71e234c35af8de024890ca33d2292845d00c6b36528e595f1ac43487342656ab30110375daef1f7281cb8d5e2339449dc4e3572"],"name":"@expo/plist","tag":"","uniqueId":"@expo_plist@0.4.8","website":"https://github.com/expo/expo"} \ No newline at end of file +{"artifactVersion":"0.5.2","description":"Mac OS X Plist parser/builder for Node.js and browsers","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_830462f6b7b459f3f5b35b61e71e234c35af8de024890ca33d2292845d00c6b36528e595f1ac43487342656ab30110375daef1f7281cb8d5e2339449dc4e3572"],"name":"@expo/plist","tag":"","uniqueId":"@expo_plist@0.5.2","website":"https://github.com/expo/expo"} \ No newline at end of file diff --git a/android/config/libraries/@xmldom_xmldom@0.8.12.json b/android/config/libraries/@xmldom_xmldom@0.8.13.json similarity index 71% rename from android/config/libraries/@xmldom_xmldom@0.8.12.json rename to android/config/libraries/@xmldom_xmldom@0.8.13.json index 202b26cc..139463c7 100644 --- a/android/config/libraries/@xmldom_xmldom@0.8.12.json +++ b/android/config/libraries/@xmldom_xmldom@0.8.13.json @@ -1 +1 @@ -{"artifactVersion":"0.8.12","description":"A pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module.","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_c668991813bcd4dec5ca5b000e8aecd7656e64ed6953939ba82b0a33d206b608d6bb7f5e45feb9fe5f4f53e8ffe5c3d8c83aec2736a15295caf19b7d1f095856"],"name":"@xmldom/xmldom","tag":"","uniqueId":"@xmldom_xmldom@0.8.12","website":"https://github.com/xmldom/xmldom"} \ No newline at end of file +{"artifactVersion":"0.8.13","description":"A pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module.","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_c668991813bcd4dec5ca5b000e8aecd7656e64ed6953939ba82b0a33d206b608d6bb7f5e45feb9fe5f4f53e8ffe5c3d8c83aec2736a15295caf19b7d1f095856"],"name":"@xmldom/xmldom","tag":"","uniqueId":"@xmldom_xmldom@0.8.13","website":"https://github.com/xmldom/xmldom"} \ No newline at end of file diff --git a/android/config/libraries/ansi-styles@3.2.1.json b/android/config/libraries/ansi-styles@3.2.1.json deleted file mode 100644 index c13a8192..00000000 --- a/android/config/libraries/ansi-styles@3.2.1.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"3.2.1","description":"ANSI escape codes for styling strings in the terminal","developers":[{"name":"Sindre Sorhus","organisationUrl":""}],"licenses":["MIT_9c8b2def76ae5ffe4d636166bf9635d7abd69cdac4bf819a2145f7969646d39ae95c96364bc117f9fa544b98518c294233455d4f665af430c75d70798dd4ab13"],"name":"ansi-styles","tag":"","uniqueId":"ansi-styles@3.2.1","website":"chalk/ansi-styles"} \ No newline at end of file diff --git a/android/config/libraries/chalk@2.4.2.json b/android/config/libraries/chalk@2.4.2.json deleted file mode 100644 index f4dfdc8a..00000000 --- a/android/config/libraries/chalk@2.4.2.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"2.4.2","description":"Terminal string styling done right","developers":[{"name":"","organisationUrl":""}],"licenses":["MIT_9c8b2def76ae5ffe4d636166bf9635d7abd69cdac4bf819a2145f7969646d39ae95c96364bc117f9fa544b98518c294233455d4f665af430c75d70798dd4ab13"],"name":"chalk","tag":"","uniqueId":"chalk@2.4.2","website":"chalk/chalk"} \ No newline at end of file diff --git a/android/config/libraries/color-convert@1.9.3.json b/android/config/libraries/color-convert@1.9.3.json deleted file mode 100644 index 412d58c6..00000000 --- a/android/config/libraries/color-convert@1.9.3.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"1.9.3","description":"Plain color conversion functions","developers":[{"name":"Heather Arthur ","organisationUrl":""}],"licenses":["MIT_449fbdf7888a5b9088b5f84aa6d1a42cf951782a062079f63fe5e1e797e709ed4737c3e19300d0a98a01013431e73652c5b81438913ba952ff1fb63bce460e5b"],"name":"color-convert","tag":"","uniqueId":"color-convert@1.9.3","website":"Qix-/color-convert"} \ No newline at end of file diff --git a/android/config/libraries/color-name@1.1.3.json b/android/config/libraries/color-name@1.1.3.json deleted file mode 100644 index d79df8f0..00000000 --- a/android/config/libraries/color-name@1.1.3.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"1.1.3","description":"A list of color names and its values","developers":[{"name":"DY ","organisationUrl":""}],"licenses":["MIT_fc1d65352c114c7594c9bedf5be432ba39d426feaf50bf8f7c52d32781323c84bfc9a68531aefb558c97ebe46e712e1d35d860ba1e1a6ab48b4a79b894092540"],"name":"color-name","tag":"","uniqueId":"color-name@1.1.3","website":"https://github.com/dfcreative/color-name"} \ No newline at end of file diff --git a/android/config/libraries/escape-string-regexp@1.0.5.json b/android/config/libraries/escape-string-regexp@1.0.5.json deleted file mode 100644 index 9f46d639..00000000 --- a/android/config/libraries/escape-string-regexp@1.0.5.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"1.0.5","description":"Escape RegExp special characters","developers":[{"name":"Sindre Sorhus","organisationUrl":""}],"licenses":["MIT_ae79e7a4209a451aef6b78f7b0b88170e7a22335126ac345522bf4eafe0818da5865aae1507c5dc0224ef854548c721df9a84371822f36d50cbcd97fa946eee9"],"name":"escape-string-regexp","tag":"","uniqueId":"escape-string-regexp@1.0.5","website":"sindresorhus/escape-string-regexp"} \ No newline at end of file diff --git a/android/config/libraries/has-flag@3.0.0.json b/android/config/libraries/has-flag@3.0.0.json deleted file mode 100644 index 80f955bf..00000000 --- a/android/config/libraries/has-flag@3.0.0.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"3.0.0","description":"Check if argv has a specific flag","developers":[{"name":"Sindre Sorhus","organisationUrl":""}],"licenses":["MIT_9c8b2def76ae5ffe4d636166bf9635d7abd69cdac4bf819a2145f7969646d39ae95c96364bc117f9fa544b98518c294233455d4f665af430c75d70798dd4ab13"],"name":"has-flag","tag":"","uniqueId":"has-flag@3.0.0","website":"sindresorhus/has-flag"} \ No newline at end of file diff --git a/android/config/libraries/node-html-parser@7.0.2.json b/android/config/libraries/node-html-parser@7.1.0.json similarity index 73% rename from android/config/libraries/node-html-parser@7.0.2.json rename to android/config/libraries/node-html-parser@7.1.0.json index 8d50926b..3c480ab4 100644 --- a/android/config/libraries/node-html-parser@7.0.2.json +++ b/android/config/libraries/node-html-parser@7.1.0.json @@ -1 +1 @@ -{"artifactVersion":"7.0.2","description":"A very fast HTML parser, generating a simplified DOM, with basic element query support.","developers":[{"name":"Xiaoyi Shi ","organisationUrl":""}],"licenses":["MIT_53c40295c1d79d9ccd9e07482a5bdb0c3b471a1e1402d9b100511ec4a04041f9ffce13ae1c27289758e2e22dfd0471ddb75034c9813f0755b274155ddb033927"],"name":"node-html-parser","tag":"","uniqueId":"node-html-parser@7.0.2","website":"https://github.com/taoqf/node-fast-html-parser"} \ No newline at end of file +{"artifactVersion":"7.1.0","description":"A very fast HTML parser, generating a simplified DOM, with basic element query support.","developers":[{"name":"Xiaoyi Shi ","organisationUrl":""}],"licenses":["MIT_53c40295c1d79d9ccd9e07482a5bdb0c3b471a1e1402d9b100511ec4a04041f9ffce13ae1c27289758e2e22dfd0471ddb75034c9813f0755b274155ddb033927"],"name":"node-html-parser","tag":"","uniqueId":"node-html-parser@7.1.0","website":"https://github.com/taoqf/node-fast-html-parser"} \ No newline at end of file diff --git a/android/config/libraries/react-native-bootsplash@7.1.0.json b/android/config/libraries/react-native-bootsplash@7.3.1.json similarity index 73% rename from android/config/libraries/react-native-bootsplash@7.1.0.json rename to android/config/libraries/react-native-bootsplash@7.3.1.json index 489427f6..8516d6e0 100644 --- a/android/config/libraries/react-native-bootsplash@7.1.0.json +++ b/android/config/libraries/react-native-bootsplash@7.3.1.json @@ -1 +1 @@ -{"artifactVersion":"7.1.0","description":"Display a bootsplash on your app starts. Hide it when you want.","developers":[{"name":"Mathieu Acthernoene ","organisationUrl":""}],"licenses":["MIT_efb95511deaf54a1021788b4cb879dfba4632acf787386c16bf60a93e1c2cc8d68a34ba3f9181c8b5b8a75cfe97ccbf4553b073a7fc6ed8ac6c77ea33fa09cba"],"name":"react-native-bootsplash","tag":"","uniqueId":"react-native-bootsplash@7.1.0","website":"https://github.com/zoontek/react-native-bootsplash"} \ No newline at end of file +{"artifactVersion":"7.3.1","description":"Display a bootsplash on your app starts. Hide it when you want.","developers":[{"name":"Mathieu Acthernoene ","organisationUrl":""}],"licenses":["MIT_efb95511deaf54a1021788b4cb879dfba4632acf787386c16bf60a93e1c2cc8d68a34ba3f9181c8b5b8a75cfe97ccbf4553b073a7fc6ed8ac6c77ea33fa09cba"],"name":"react-native-bootsplash","tag":"","uniqueId":"react-native-bootsplash@7.3.1","website":"https://github.com/zoontek/react-native-bootsplash"} \ No newline at end of file diff --git a/android/config/libraries/react-native-is-edge-to-edge@1.3.1.json b/android/config/libraries/react-native-is-edge-to-edge@1.3.1.json new file mode 100644 index 00000000..57894723 --- /dev/null +++ b/android/config/libraries/react-native-is-edge-to-edge@1.3.1.json @@ -0,0 +1 @@ +{"artifactVersion":"1.3.1","description":"Detect react-native-edge-to-edge package install","developers":[{"name":"Mathieu Acthernoene ","organisationUrl":""}],"licenses":["MIT_73e8938d5ee354c090249327c45b4d8284dc2575516d48b6f0e2ab083c8187a04ebaaadc1b30ee34c8d1f15b1fcf87ad8c8baf264dd7eadc8a079a3ea0c9751a"],"name":"react-native-is-edge-to-edge","tag":"","uniqueId":"react-native-is-edge-to-edge@1.3.1","website":"https://github.com/zoontek/react-native-edge-to-edge"} \ No newline at end of file diff --git a/android/config/libraries/react-native-legal@1.6.0.json b/android/config/libraries/react-native-legal@1.6.3.json similarity index 75% rename from android/config/libraries/react-native-legal@1.6.0.json rename to android/config/libraries/react-native-legal@1.6.3.json index a0fc91d0..eda13796 100644 --- a/android/config/libraries/react-native-legal@1.6.0.json +++ b/android/config/libraries/react-native-legal@1.6.3.json @@ -1 +1 @@ -{"artifactVersion":"1.6.0","description":"Acknowledge OSS libraries used in your React Native app","developers":[{"name":"Mateusz Mędrek (https://github.com/mateusz1913)","organisationUrl":""}],"licenses":["MIT_a7a37cc7a016a6c0ab07dab285d42eec347265002608d942ddff2203381875790621a0c35e6261921f1059784153a72767a0b3eb3e6225873d5358e2c8df8523"],"name":"react-native-legal","tag":"","uniqueId":"react-native-legal@1.6.0","website":"https://github.com/callstackincubator/react-native-legal"} \ No newline at end of file +{"artifactVersion":"1.6.3","description":"Acknowledge OSS libraries used in your React Native app","developers":[{"name":"Mateusz Mędrek (https://github.com/mateusz1913)","organisationUrl":""}],"licenses":["MIT_a7a37cc7a016a6c0ab07dab285d42eec347265002608d942ddff2203381875790621a0c35e6261921f1059784153a72767a0b3eb3e6225873d5358e2c8df8523"],"name":"react-native-legal","tag":"","uniqueId":"react-native-legal@1.6.3","website":"https://github.com/callstackincubator/react-native-legal"} \ No newline at end of file diff --git a/android/config/libraries/supports-color@5.5.0.json b/android/config/libraries/supports-color@5.5.0.json deleted file mode 100644 index c1938965..00000000 --- a/android/config/libraries/supports-color@5.5.0.json +++ /dev/null @@ -1 +0,0 @@ -{"artifactVersion":"5.5.0","description":"Detect whether a terminal supports color","developers":[{"name":"Sindre Sorhus","organisationUrl":""}],"licenses":["MIT_9c8b2def76ae5ffe4d636166bf9635d7abd69cdac4bf819a2145f7969646d39ae95c96364bc117f9fa544b98518c294233455d4f665af430c75d70798dd4ab13"],"name":"supports-color","tag":"","uniqueId":"supports-color@5.5.0","website":"chalk/supports-color"} \ No newline at end of file diff --git a/android/config/libraries/xml-formatter@3.6.7.json b/android/config/libraries/xml-formatter@3.7.0.json similarity index 74% rename from android/config/libraries/xml-formatter@3.6.7.json rename to android/config/libraries/xml-formatter@3.7.0.json index 8efcf3c3..3b35df8a 100644 --- a/android/config/libraries/xml-formatter@3.6.7.json +++ b/android/config/libraries/xml-formatter@3.7.0.json @@ -1 +1 @@ -{"artifactVersion":"3.6.7","description":"Converts a XML string into a human readable format (pretty print) while respecting the xml:space attribute","developers":[{"name":"Chris Bottin ","organisationUrl":""}],"licenses":["MIT_e66392fa78dbc6767e82d57bf38f41429ed48dab3ef19f72e17bf35b8bd06031d6b19ff05862f6045c16cc78b1c65fa3eb35cb00045bc3c3eedb74409f05d3d9"],"name":"xml-formatter","tag":"","uniqueId":"xml-formatter@3.6.7","website":"https://github.com/chrisbottin/xml-formatter"} \ No newline at end of file +{"artifactVersion":"3.7.0","description":"Converts a XML string into a human readable format (pretty print) while respecting the xml:space attribute","developers":[{"name":"Chris Bottin ","organisationUrl":""}],"licenses":["MIT_e66392fa78dbc6767e82d57bf38f41429ed48dab3ef19f72e17bf35b8bd06031d6b19ff05862f6045c16cc78b1c65fa3eb35cb00045bc3c3eedb74409f05d3d9"],"name":"xml-formatter","tag":"","uniqueId":"xml-formatter@3.7.0","website":"https://github.com/chrisbottin/xml-formatter"} \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2f1f3869..f08ff225 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1929,7 +1929,7 @@ PODS: - React-utils (= 0.84.1) - ReactNativeDependencies - ReactNativeDependencies (0.84.1) - - ReactNativeLegal (1.6.0): + - ReactNativeLegal (1.6.3): - hermes-engine - LicensePlist - RCTRequired @@ -1952,7 +1952,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNBootSplash (7.1.0): + - RNBootSplash (7.3.1): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2572,49 +2572,49 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - AsyncStorage: b7e9d8feded2a33e90a600565c35fcf9fcc37543 + AsyncStorage: 0a927dc82ea8eaa0350779b37d73b11d070ea677 FBLazyVector: e97c19a5a442429d1988f182a1940fb08df514da hermes-engine: 014cd8069fbffe88457fa5156ccb3e7c467a8b9d - HtmlToPdf: 9721ff8ee35b34262c801bfd04b7c0d877d589e6 + HtmlToPdf: 3ab07d86b15b6340a80b0d9e6539fe4db6d87f1e LicensePlist: 74682f92f6028b02dce8d84b9b9e65350c971571 RCTDeprecation: af44b104091a34482596cd9bd7e8d90c4e9b4bd7 RCTRequired: bb77b070f75f53398ce43c0aaaa58337cebe2bf6 RCTSwiftUI: afc0a0a635860da1040a0b894bfd529da06d7810 - RCTSwiftUIWrapper: 3197c020094f3b2151bb2d1223f7276787be8166 + RCTSwiftUIWrapper: cbb32eb90f09bd42ea9ed1eecd51fef3294da673 RCTTypeSafety: d13e192a37f151ce354641184bf4239844a3be17 React: 1ba7d364ade7d883a1ec055bfc3606f35fdee17b React-callinvoker: bc2a26f8d84fb01f003fc6de6c9337b64715f95b - React-Core: 7840d3a80b43a95c5e80ef75146bd70925ebab0f + React-Core: bdaa87b276ca31877632a982ecf7c36f8c826414 React-Core-prebuilt: a673ad864e6a33637d443c2e62370f2b35c88262 - React-CoreModules: 2eb010400b63b89e53a324ffb3c112e4c7c3ce42 - React-cxxreact: a558e92199d26f145afa9e62c4233cf8e7950efe + React-CoreModules: b24989f62d56390ae08ca4f65e6f38fe6802de42 + React-cxxreact: 1a2dfcbc18a6b610664dba152adf327f063a0d12 React-debug: 755200a6e7f5e6e0a40ff8d215493d43cce285fc - React-defaultsnativemodule: bb85b1bdd9b4b82650cfa92998567fcfdb030145 - React-domnativemodule: ffdba8ba4323387e821d8298be2013516036df87 - React-Fabric: 8705ba7f14acf5b1df474d1af4b0191c69ac4690 - React-FabricComponents: 5a1b5007fe8c5d5f043e45c335ebef2af0717fb2 - React-FabricImage: e96eea6bb65b501cf5db3ce4ad057a97dea1dd69 - React-featureflags: 410f6c383eb94019f63f105374d738169df291ae - React-featureflagsnativemodule: 5d6d7931ec5d4576639661c0f79169a0ae383023 - React-graphics: b9b69adbe79d6944838aa304c849d78f977e9f21 - React-hermes: 666c66bbc856b46dfa4b132f1c9efaa37ad419a1 - React-idlecallbacksnativemodule: 785d307b9236ec9fb4ec0bcf99b34e8539d2d76e - React-ImageManager: daeef8b1c19803c71a9233f21f7f235cd3ec294e - React-intersectionobservernativemodule: c17189d2350205012682aefe566f7ebaa4f980e3 - React-jserrorhandler: 431377b3d1783127bc394986fe8d932bcb8982fe - React-jsi: 33db13b95bb53827b03d9fb0f567d12b63dfc8ef - React-jsiexecutor: 49de4d48a7c4f283eaa865f7a4f3919f2c39be1c - React-jsinspector: 3ec7dd478806a0eb4ec6ab394e51a395e8f895ba - React-jsinspectorcdp: bcb79a666959b1a3e8aac3ff8209d05c719aaa2a - React-jsinspectornetwork: be3b81a6342b56c74dd0bdd58bf3f62f978c1472 - React-jsinspectortracing: 293251deadf7c255da593bf1d9d337a645abdfdc - React-jsitooling: 6f729cdb85ff0c8294709ec1903e1f0c59662331 - React-jsitracing: be95d903cc9440ab89a704c999dd7db6675dc47f - React-logger: b5521614afb8690420146dfc61a1447bb5d65419 - React-Mapbuffer: f4ee8c62e0ef8359d139124f35a471518a172cd3 - React-microtasksnativemodule: d1956f0eec54c619b63a379520fb4c618a55ccb9 - react-native-safe-area-context: ae7587b95fb580d1800c5b0b2a7bd48c2868e67a - react-native-unistyles: 6f9b91084f4d0103662be9973ff6f70bda9f1056 + React-defaultsnativemodule: 027cad46a2847719b5d3d20dd915463b06a5d4d1 + React-domnativemodule: 5ddfc6b3b73b48a31dfa12f52d6b62527f6f260c + React-Fabric: 6ffcc768e2378e84ed428069c7e2d270ee78f2bf + React-FabricComponents: ee6614287222dd4f04fdb1263d1ae6eb7fe952c6 + React-FabricImage: ab05740a08ad9e23e4e1701e9c354e9a9b048063 + React-featureflags: a8b0c8d9a93b5903f7620408659de160d95e4efe + React-featureflagsnativemodule: 0f0fe1a044829f31d7565a4bdfded376fbcfdfc1 + React-graphics: c497dd295c88729525a4752d524d2d783aa205d4 + React-hermes: c2bde95033e6df1599b5c1b6d7e45736a8aa5cba + React-idlecallbacksnativemodule: 6ceacabe93be052bbe822fb018602f63a8e280e2 + React-ImageManager: 820fe1d55add59ec053099a0c5abe830ecd6c699 + React-intersectionobservernativemodule: f84958aaf662f95f837dc4d26cbb5e7dcc4b8f09 + React-jserrorhandler: 390c6c46e2f639b5ba104385d7fba848396347e8 + React-jsi: 382de7964299bbf878458006a14f52cb66a36cfc + React-jsiexecutor: b781400a9becfb24e36ac063dccb42a52dcb44ca + React-jsinspector: 0644f32cc9b09eae2bc845ceb58d03420ae70821 + React-jsinspectorcdp: 96677569865afe25c737889e02d635db26131d9f + React-jsinspectornetwork: 28c7cac2e92b1739561dcffd07f5554e54050a85 + React-jsinspectortracing: 58ee96f9580a143011f8b914ad6927b5116461a7 + React-jsitooling: bc79639489d610c35731dd26e8e54c37e078996d + React-jsitracing: 1bb9fae4f2ccf891255a419cdfc13372d07ef4a5 + React-logger: 517377b1d2ba7ac722d47fb2183b98de86632063 + React-Mapbuffer: 45e088dfb58dc326ae20cca1814d3726553c4cad + React-microtasksnativemodule: ab9d1a05fe1f58ea44a97d307ef1b53463f45a3f + react-native-safe-area-context: 29044d05d61f2c60d0828c373bd0ebe17eed58d0 + react-native-unistyles: 3411337a7aac426fe5ddba347c25a4c135561cb0 react-native-vector-icons-ant-design: e88c25686a4e778a56823f7ecb4e014fde1713c5 react-native-vector-icons-feather: b669f5bc4981f53aaa25d0e0135ae9a7d5c1bbd5 react-native-vector-icons-fontawesome6: 7464629872b702957385275b62cff2f6c28199cc @@ -2623,51 +2623,51 @@ SPEC CHECKSUMS: react-native-vector-icons-lucide: e3f2087c3328f7a8b5a4ae8d94727b655430caa8 react-native-vector-icons-material-design-icons: 06451bebfd9affaecdd541230f23c6cb077e7063 react-native-vector-icons-octicons: f16562ca7c486f918bc721eb66d9b5a91144ed4d - React-NativeModulesApple: 5ba0903927f6b8d335a091700e9fda143980f819 - React-networking: 3a4b7f9ed2b2d1c0441beacb79674323a24bcca6 + React-NativeModulesApple: b94faa2dce6d8c0a9d722ed7ee27b996d28b62d1 + React-networking: e409d8fb062162da6293e98b77f8d80cf4430e07 React-oscompat: ff26abf0ae3e3fdbe47b44224571e3fc7226a573 - React-perflogger: a86b2146936f2ffa80188425c6e8892729e2ad01 - React-performancecdpmetrics: 65b699c3e52c0a1d978d9cdf15e60c62e335b900 - React-performancetimeline: b44f82caa563e46068d02d9cf5b0d2b84bdc7a6a + React-perflogger: 757c8c725cc20e94eba406885047f03cf83044fb + React-performancecdpmetrics: fec7e28b711c95ccb6fc7e3bb16572d88bcf27ae + React-performancetimeline: 4c6102f19df01db35c37a3e63a058cfbf1a056d9 React-RCTActionSheet: fc1d5d419856868e7f8c13c14591ed63dadef43a - React-RCTAnimation: 2a1e7eeb55b71e8524db296fa31e46eeaa2d0da4 - React-RCTAppDelegate: 317c1102a2d0bcc07567d8de58d9147102080b0e - React-RCTBlob: c82bbf96b2d1389550c05fb9739d4e85d471052e - React-RCTFabric: d82ad8120ba70fe63207e261b9e33d9b6077e2de - React-RCTFBReactNativeSpec: 4d33b5f3b339e9fa902168e8a1d62c458dda16e9 - React-RCTImage: f959463e53ea731ab267e371eea1b92fccf27634 - React-RCTLinking: 2a857113b8059ac4f0a8ae89f8aa312837b955d0 - React-RCTNetwork: dc026bf25f6457249349be8cf8bd5fdf84cdfb14 - React-RCTRuntime: b0630731fd864064066301e1e31406fea606d5f9 - React-RCTSettings: e159e19475df2e92fd3b4ddcfe29f6cc12cf0ee4 - React-RCTText: 7356cc84c2ed79635931891c176f72e0289dc75d - React-RCTVibration: 77621175b67b22e655ce4b29d1cda8498f2033e7 + React-RCTAnimation: 1ce166ec15ab1f8eca8ebaae7f8f709d9be6958c + React-RCTAppDelegate: c752d93f597168a9a4d5678e9354bbb8d84df6d1 + React-RCTBlob: 147d41ee9f80cf27fe9b2f7adc1d6d24f68ec3fc + React-RCTFabric: 712c4ad749a43712609011d178234c90a17cde12 + React-RCTFBReactNativeSpec: 032ea8783dc27290ec6b9af9d8df5351847539a2 + React-RCTImage: fd39f1c478f1e43357bc72c2dbdc2454aafe4035 + React-RCTLinking: 02ca1c83536dab08130f5db4852f293c53885dd6 + React-RCTNetwork: 85dc64c530e4b0be7436f9a15b03caba24e9a3a1 + React-RCTRuntime: c75950caa80e6884cbf0417d8738992256890508 + React-RCTSettings: df5da31865cc1bab7ef5314e65ca18f6b538d71d + React-RCTText: 41587e426883c9a83fd8eb0c57fe328aad4ed57a + React-RCTVibration: 8ca2f9839c53416dffb584adb94501431ba7f96e React-rendererconsistency: e91aba4bb482dac127ad955dba6333a8af629c5b - React-renderercss: 7cc41efaecf557d7b70edaa08fad5ace79f714f6 - React-rendererdebug: 0f004cbed7b4c27327423be47209770830bf3c6d - React-RuntimeApple: 6f4ff8e2d8b05cb3ceabf57e494c04da8751f009 - React-RuntimeCore: 25be9c7025eabb524cd00dbb6ce56d6b122e3d92 - React-runtimeexecutor: 84d394b9f0a8fc7dab8a98bf88a43228bb04dac2 - React-RuntimeHermes: 2bed5b2d2419945cc5c2f6d627a1b46ce3a0f66a - React-runtimescheduler: 23b092dbcd3088f9c947551c23366dd00254caf3 - React-timing: 2ab9ccd4b41aa171090c16f664f6c5bfb2fd0ddc - React-utils: 8d888b379f0808bfabaea03d85f9e8dd9b8548da - React-webperformancenativemodule: c10016db7f1bb1153060d4aa9f7dbde2c88c845d - ReactAppDependencyProvider: e96e93b493d8d86eeaee3e590ba0be53f6abe46f - ReactCodegen: 797de5178718324c6eba3327b07f9a423fbd5787 - ReactCommon: 07572bf9e687c8a52fbe4a3641e9e3a1a477c78e + React-renderercss: 1f15a79f3cc3c9416902b8f70266408116d93bd0 + React-rendererdebug: 77dcf1490ee5c0ce141d2b1eaceed02aa0996826 + React-RuntimeApple: 1074835708500a69770b713f718400137f30ce7a + React-RuntimeCore: 148db945742d7ce2985cc35b8ddc61edfdb46e6d + React-runtimeexecutor: 5742146dac0f8de9c21f5f703993df249c046d0d + React-RuntimeHermes: a5bb378bea92d526341a65afa945a38c9bc787b2 + React-runtimescheduler: 91838dd32460920ed1b4da68590a2684b784aacc + React-timing: 9c0e2b1532317148fa0487bbc3833c1f348981a0 + React-utils: 2f8dd43fed5c6d881ac5971666bbb34cc4a03fa1 + React-webperformancenativemodule: afbee7a9fd0b5bf92f6765eb41767f865b293bcc + ReactAppDependencyProvider: 26bbf1e26768d08dd965a2b5e372e53f67b21fee + ReactCodegen: 4fa1b291ad275013e39212f2463c46480fb53afe + ReactCommon: 309419492d417c4cbb87af06f67735afa40ecb9d ReactNativeDependencies: 1f9690e648fd74ac1ca3b399048069deaa3c6b79 - ReactNativeLegal: 12adb20d6a0c97e20af57850f112a092b2028764 - RNBootSplash: 59b082697b36dd600dcd75ffbbf8ac22f3eb94ec - RNGestureHandler: 07de6f059e0ee5744ae9a56feb07ee345338cc31 - RNLocalize: 1e6eb4290e89502c3d2193d82dfb7de14a9e672b - RNReanimated: 66fa99647173f254f731e6aa59a0a2cc8c838ac1 - RNScreens: 6cb648bdad8fe9bee9259fe144df95b6d1d5b707 - RNShare: f7f766ccd13e8626b33e67c5172bd52469f04306 - RNSVG: 507bf2685de6b3d49449efd4aae7e7471bb9c433 - RNWorklets: 30f9a363d681c776a5f9d74f6d21a6d1bb7bfe80 - Yoga: c0b3f2c7e8d3e327e450223a2414ca3fa296b9a2 + ReactNativeLegal: 66fb9588ca5c0054e6d508962fddda49a1378821 + RNBootSplash: 5df1f73f05b634addad47e621c669b14eec147a9 + RNGestureHandler: 6d378fd1aa991c7ab62a4215ee6cc417895a6954 + RNLocalize: 0454ff57ecee161c1bf5804d26ef7f0d176346e2 + RNReanimated: 0cb8806c6ef2b0f6404e1d0f3ce8ac8177984cf5 + RNScreens: 088d923c4327c63c9f8c942cae17a9d038f47d97 + RNShare: 066c683257d5876d1e7e87275061f2f86c1080b9 + RNSVG: 13970bfde0ea9c9e10e01ab0d7b4a6cde11fca1b + RNWorklets: 0373848914c7751c4e0ca573ebbe38f489026a62 + Yoga: 7c1c3b93e408ac46c7ed64b5641ca7161747378d PODFILE CHECKSUM: 394f07bb09fa68d829a5f1fb8c9b738e6b305e27 -COCOAPODS: 1.16.2 +COCOAPODS: 1.14.3 diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt b/ios/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt index e8f34df2..9fab81da 100644 --- a/ios/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt +++ b/ios/Settings.bundle/com.mono0926.LicensePlist.latest_result.txt @@ -4,143 +4,16 @@ body: MIT License Copyrig… version: 250829098.0.9 -name: HtmlToPdf, nameSpecified: -body: MIT License - -Copyrig… -version: 1.3.0 - name: LicensePlist, nameSpecified: body: MIT License Copyrig… version: 3.27.2 -name: React, nameSpecified: -body: MIT License - -Copyrig… -version: 0.84.1 - -name: React-Core, nameSpecified: -body: MIT License - -Copyrig… -version: 0.84.1 - -name: react-native-safe-area-context, nameSpecified: -body: MIT License - -Copyrig… -version: 5.7.0 - -name: react-native-unistyles, nameSpecified: -body: MIT License - -Copyrig… -version: 2.43.0 - -name: react-native-vector-icons-ant-design, nameSpecified: -body: MIT License - -Copyrig… -version: 12.4.1 - -name: react-native-vector-icons-feather, nameSpecified: -body: MIT License - -Copyrig… -version: 12.4.1 - -name: react-native-vector-icons-fontawesome6, nameSpecified: -body: MIT License - -Copyrig… -version: 12.3.1 - -name: react-native-vector-icons-foundation, nameSpecified: -body: MIT License - -Copyrig… -version: 12.4.1 - -name: react-native-vector-icons-ionicons, nameSpecified: -body: MIT License - -Copyrig… -version: 12.4.1 - -name: react-native-vector-icons-lucide, nameSpecified: -body: MIT License - -Copyrig… -version: 12.4.1 - -name: react-native-vector-icons-material-design-icons, nameSpecified: -body: MIT License - -Copyrig… -version: 12.4.1 - -name: react-native-vector-icons-octicons, nameSpecified: -body: MIT License - -Copyrig… -version: 20.4.1 - -name: ReactNativeLegal, nameSpecified: -body: MIT License - -Copyrig… -version: 1.6.0 - -name: RNBootSplash, nameSpecified: -body: MIT License - -Copyrig… -version: 7.1.0 - -name: RNGestureHandler, nameSpecified: -body: The MIT License (MIT… -version: 2.30.0 - -name: RNLocalize, nameSpecified: -body: MIT License - -Copyrig… -version: 3.7.0 - -name: RNReanimated, nameSpecified: -body: The MIT License (MIT… -version: 4.2.2 - -name: RNScreens, nameSpecified: -body: The MIT License (MIT… -version: 4.24.0 - -name: RNShare, nameSpecified: -body: The MIT License (MIT… -version: 12.0.11 - -name: RNSVG, nameSpecified: -body: The MIT License (MIT… -version: 15.15.3 - -name: RNWorklets, nameSpecified: -body: MIT License - -Copyrig… -version: 0.7.4 - name: @ampproject_remapping@2.3.0, nameSpecified: @ampproject/remapping, version: 2.3.0 body: … -name: @babel_code-frame@7.10.4, nameSpecified: @babel/code-frame, version: 7.10.4 -body: MIT License - -Copyrig… - name: @babel_code-frame@7.29.0, nameSpecified: @babel/code-frame, version: 7.29.0 body: MIT License @@ -241,11 +114,6 @@ body: MIT License Copyrig… -name: @babel_highlight@7.25.9, nameSpecified: @babel/highlight, version: 7.25.9 -body: MIT License - -Copyrig… - name: @babel_parser@7.29.0, nameSpecified: @babel/parser, version: 7.29.0 body: Copyright (C) 2012-2… @@ -409,7 +277,7 @@ body: MIT License Copyrig… -name: @callstack_licenses@0.3.0, nameSpecified: @callstack/licenses, version: 0.3.0 +name: @callstack_licenses@0.3.1, nameSpecified: @callstack/licenses, version: 0.3.1 body: MIT License Copyrig… @@ -417,16 +285,16 @@ Copyrig… name: @egjs_hammerjs@2.0.17, nameSpecified: @egjs/hammerjs, version: 2.0.17 body: The MIT License (MIT… -name: @expo_config-plugins@54.0.4, nameSpecified: @expo/config-plugins, version: 54.0.4 +name: @expo_config-plugins@55.0.8, nameSpecified: @expo/config-plugins, version: 55.0.8 body: The MIT License (MIT… -name: @expo_config-types@54.0.10, nameSpecified: @expo/config-types, version: 54.0.10 +name: @expo_config-types@55.0.5, nameSpecified: @expo/config-types, version: 55.0.5 body: The MIT License (MIT… -name: @expo_json-file@10.0.8, nameSpecified: @expo/json-file, version: 10.0.8 +name: @expo_json-file@10.0.13, nameSpecified: @expo/json-file, version: 10.0.13 body: The MIT License (MIT… -name: @expo_plist@0.4.8, nameSpecified: @expo/plist, version: 0.4.8 +name: @expo_plist@0.5.2, nameSpecified: @expo/plist, version: 0.5.2 body: MIT… name: @expo_sdk-runtime-versions@1.0.0, nameSpecified: @expo/sdk-runtime-versions, version: 1.0.0 @@ -707,7 +575,7 @@ body: MIT License … -name: @xmldom_xmldom@0.8.12, nameSpecified: @xmldom/xmldom, version: 0.8.12 +name: @xmldom_xmldom@0.8.13, nameSpecified: @xmldom/xmldom, version: 0.8.13 body: Copyright 2019 - pre… name: abort-controller@3.0.0, nameSpecified: abort-controller, version: 3.0.0 @@ -738,11 +606,6 @@ body: MIT License Copyrig… -name: ansi-styles@3.2.1, nameSpecified: ansi-styles, version: 3.2.1 -body: MIT License - -Copyrig… - name: ansi-styles@4.3.0, nameSpecified: ansi-styles, version: 4.3.0 body: MIT License @@ -859,11 +722,6 @@ Copyrig… name: caniuse-lite@1.0.30001695, nameSpecified: caniuse-lite, version: 1.0.30001695 body: Attribution 4.0 Inte… -name: chalk@2.4.2, nameSpecified: chalk, version: 2.4.2 -body: MIT License - -Copyrig… - name: chalk@4.1.2, nameSpecified: chalk, version: 4.1.2 body: MIT License @@ -886,15 +744,9 @@ body: The MIT License (MIT… name: cliui@8.0.1, nameSpecified: cliui, version: 8.0.1 body: ISC… -name: color-convert@1.9.3, nameSpecified: color-convert, version: 1.9.3 -body: Copyright (c) 2011-2… - name: color-convert@2.0.1, nameSpecified: color-convert, version: 2.0.1 body: Copyright (c) 2011-2… -name: color-name@1.1.3, nameSpecified: color-name, version: 1.1.3 -body: The MIT License (MIT… - name: color-name@1.1.4, nameSpecified: color-name, version: 1.1.4 body: The MIT License (MIT… @@ -1022,9 +874,6 @@ body: (The MIT License) C… -name: escape-string-regexp@1.0.5, nameSpecified: escape-string-regexp, version: 1.0.5 -body: The MIT License (MIT… - name: escape-string-regexp@2.0.0, nameSpecified: escape-string-regexp, version: 2.0.0 body: MIT License @@ -1146,11 +995,6 @@ body: The ISC License Cop… -name: has-flag@3.0.0, nameSpecified: has-flag, version: 3.0.0 -body: MIT License - -Copyrig… - name: has-flag@4.0.0, nameSpecified: has-flag, version: 4.0.0 body: MIT License @@ -1466,7 +1310,7 @@ body: (The MIT License) C… -name: node-html-parser@7.0.2, nameSpecified: node-html-parser, version: 7.0.2 +name: node-html-parser@7.1.0, nameSpecified: node-html-parser, version: 7.1.0 body: Copyright 2019 Tao Q… name: node-int64@0.4.0, nameSpecified: node-int64, version: 0.4.0 @@ -1618,7 +1462,7 @@ body: MIT License Copyrig… -name: react-native-bootsplash@7.1.0, nameSpecified: react-native-bootsplash, version: 7.1.0 +name: react-native-bootsplash@7.3.1, nameSpecified: react-native-bootsplash, version: 7.3.1 body: MIT License Copyrig… @@ -1641,7 +1485,12 @@ body: MIT License Copyrig… -name: react-native-legal@1.6.0, nameSpecified: react-native-legal, version: 1.6.0 +name: react-native-is-edge-to-edge@1.3.1, nameSpecified: react-native-is-edge-to-edge, version: 1.3.1 +body: MIT License + +Copyrig… + +name: react-native-legal@1.6.3, nameSpecified: react-native-legal, version: 1.6.3 body: MIT License Copyrig… @@ -1873,11 +1722,6 @@ body: MIT License Copyrig… -name: supports-color@5.5.0, nameSpecified: supports-color, version: 5.5.0 -body: MIT License - -Copyrig… - name: supports-color@7.2.0, nameSpecified: supports-color, version: 7.2.0 body: MIT License @@ -2016,7 +1860,7 @@ name: xcode@3.0.1, nameSpecified: xcode, version: 3.0.1 body: … -name: xml-formatter@3.6.7, nameSpecified: xml-formatter, version: 3.6.7 +name: xml-formatter@3.7.0, nameSpecified: xml-formatter, version: 3.7.0 body: The MIT License (MIT… name: xml-parser-xo@4.1.5, nameSpecified: xml-parser-xo, version: 4.1.5 diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist.plist b/ios/Settings.bundle/com.mono0926.LicensePlist.plist index 003d8de6..fcba31ff 100644 --- a/ios/Settings.bundle/com.mono0926.LicensePlist.plist +++ b/ios/Settings.bundle/com.mono0926.LicensePlist.plist @@ -18,14 +18,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/@babel_code-frame@7.10.4 - Title - @babel/code-frame (7.10.4) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/@babel_code-frame@7.29.0 @@ -186,14 +178,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/@babel_highlight@7.25.9 - Title - @babel/highlight (7.25.9) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/@babel_parser@7.29.0 @@ -460,9 +444,9 @@ File - com.mono0926.LicensePlist/@callstack_licenses@0.3.0 + com.mono0926.LicensePlist/@callstack_licenses@0.3.1 Title - @callstack/licenses (0.3.0) + @callstack/licenses (0.3.1) Type PSChildPaneSpecifier @@ -476,33 +460,33 @@ File - com.mono0926.LicensePlist/@expo_config-plugins@54.0.4 + com.mono0926.LicensePlist/@expo_config-plugins@55.0.8 Title - @expo/config-plugins (54.0.4) + @expo/config-plugins (55.0.8) Type PSChildPaneSpecifier File - com.mono0926.LicensePlist/@expo_config-types@54.0.10 + com.mono0926.LicensePlist/@expo_config-types@55.0.5 Title - @expo/config-types (54.0.10) + @expo/config-types (55.0.5) Type PSChildPaneSpecifier File - com.mono0926.LicensePlist/@expo_json-file@10.0.8 + com.mono0926.LicensePlist/@expo_json-file@10.0.13 Title - @expo/json-file (10.0.8) + @expo/json-file (10.0.13) Type PSChildPaneSpecifier File - com.mono0926.LicensePlist/@expo_plist@0.4.8 + com.mono0926.LicensePlist/@expo_plist@0.5.2 Title - @expo/plist (0.4.8) + @expo/plist (0.5.2) Type PSChildPaneSpecifier @@ -1028,9 +1012,9 @@ File - com.mono0926.LicensePlist/@xmldom_xmldom@0.8.12 + com.mono0926.LicensePlist/@xmldom_xmldom@0.8.13 Title - @xmldom/xmldom (0.8.12) + @xmldom/xmldom (0.8.13) Type PSChildPaneSpecifier @@ -1082,14 +1066,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/ansi-styles@3.2.1 - Title - ansi-styles (3.2.1) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/ansi-styles@4.3.0 @@ -1306,14 +1282,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/chalk@2.4.2 - Title - chalk (2.4.2) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/chalk@4.1.2 @@ -1362,14 +1330,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/color-convert@1.9.3 - Title - color-convert (1.9.3) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/color-convert@2.0.1 @@ -1378,14 +1338,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/color-name@1.1.3 - Title - color-name (1.1.3) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/color-name@1.1.4 @@ -1650,14 +1602,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/escape-string-regexp@1.0.5 - Title - escape-string-regexp (1.0.5) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/escape-string-regexp@2.0.0 @@ -1898,14 +1842,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/has-flag@3.0.0 - Title - has-flag (3.0.0) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/has-flag@4.0.0 @@ -1986,14 +1922,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/HtmlToPdf - Title - HtmlToPdf (1.3.0) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/http-errors@2.0.1 @@ -2572,9 +2500,9 @@ File - com.mono0926.LicensePlist/node-html-parser@7.0.2 + com.mono0926.LicensePlist/node-html-parser@7.1.0 Title - node-html-parser (7.0.2) + node-html-parser (7.1.0) Type PSChildPaneSpecifier @@ -2818,22 +2746,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/React - Title - React (0.84.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/React-Core - Title - React-Core (0.84.1) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/react-devtools-core@6.1.5 @@ -2884,9 +2796,9 @@ File - com.mono0926.LicensePlist/react-native-bootsplash@7.1.0 + com.mono0926.LicensePlist/react-native-bootsplash@7.3.1 Title - react-native-bootsplash (7.1.0) + react-native-bootsplash (7.3.1) Type PSChildPaneSpecifier @@ -2924,33 +2836,33 @@ File - com.mono0926.LicensePlist/react-native-legal@1.6.0 + com.mono0926.LicensePlist/react-native-is-edge-to-edge@1.3.1 Title - react-native-legal (1.6.0) + react-native-is-edge-to-edge (1.3.1) Type PSChildPaneSpecifier File - com.mono0926.LicensePlist/react-native-localize@3.7.0 + com.mono0926.LicensePlist/react-native-legal@1.6.3 Title - react-native-localize (3.7.0) + react-native-legal (1.6.3) Type PSChildPaneSpecifier File - com.mono0926.LicensePlist/react-native-reanimated@4.2.2 + com.mono0926.LicensePlist/react-native-localize@3.7.0 Title - react-native-reanimated (4.2.2) + react-native-localize (3.7.0) Type PSChildPaneSpecifier File - com.mono0926.LicensePlist/react-native-safe-area-context + com.mono0926.LicensePlist/react-native-reanimated@4.2.2 Title - react-native-safe-area-context (5.7.0) + react-native-reanimated (4.2.2) Type PSChildPaneSpecifier @@ -2994,14 +2906,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/react-native-unistyles - Title - react-native-unistyles (2.43.0) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/react-native-unistyles@2.43.0 @@ -3010,70 +2914,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/react-native-vector-icons-ant-design - Title - react-native-vector-icons-ant-design (12.4.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/react-native-vector-icons-feather - Title - react-native-vector-icons-feather (12.4.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/react-native-vector-icons-fontawesome6 - Title - react-native-vector-icons-fontawesome6 (12.3.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/react-native-vector-icons-foundation - Title - react-native-vector-icons-foundation (12.4.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/react-native-vector-icons-ionicons - Title - react-native-vector-icons-ionicons (12.4.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/react-native-vector-icons-lucide - Title - react-native-vector-icons-lucide (12.4.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/react-native-vector-icons-material-design-icons - Title - react-native-vector-icons-material-design-icons (12.4.1) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/react-native-vector-icons-octicons - Title - react-native-vector-icons-octicons (20.4.1) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/react-native-worklets@0.7.4 @@ -3106,14 +2946,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/ReactNativeLegal - Title - ReactNativeLegal (1.6.0) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/regenerate-unicode-properties@10.2.2 @@ -3194,70 +3026,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/RNBootSplash - Title - RNBootSplash (7.1.0) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/RNGestureHandler - Title - RNGestureHandler (2.30.0) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/RNLocalize - Title - RNLocalize (3.7.0) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/RNReanimated - Title - RNReanimated (4.2.2) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/RNScreens - Title - RNScreens (4.24.0) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/RNShare - Title - RNShare (12.0.11) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/RNSVG - Title - RNSVG (15.15.3) - Type - PSChildPaneSpecifier - - - File - com.mono0926.LicensePlist/RNWorklets - Title - RNWorklets (0.7.4) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/run-parallel@1.2.0 @@ -3530,14 +3298,6 @@ Type PSChildPaneSpecifier - - File - com.mono0926.LicensePlist/supports-color@5.5.0 - Title - supports-color (5.5.0) - Type - PSChildPaneSpecifier - File com.mono0926.LicensePlist/supports-color@7.2.0 @@ -3820,9 +3580,9 @@ File - com.mono0926.LicensePlist/xml-formatter@3.6.7 + com.mono0926.LicensePlist/xml-formatter@3.7.0 Title - xml-formatter (3.6.7) + xml-formatter (3.7.0) Type PSChildPaneSpecifier diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@babel_code-frame@7.10.4.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@babel_code-frame@7.10.4.plist deleted file mode 100644 index e3c0d9dc..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/@babel_code-frame@7.10.4.plist +++ /dev/null @@ -1,39 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2014-present Sebastian McKenzie and other contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@babel_highlight@7.25.9.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@babel_highlight@7.25.9.plist deleted file mode 100644 index e3c0d9dc..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/@babel_highlight@7.25.9.plist +++ /dev/null @@ -1,39 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2014-present Sebastian McKenzie and other contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@callstack_licenses@0.3.0.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@callstack_licenses@0.3.1.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/@callstack_licenses@0.3.0.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/@callstack_licenses@0.3.1.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-plugins@54.0.4.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-plugins@55.0.8.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-plugins@54.0.4.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-plugins@55.0.8.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-types@54.0.10.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-types@55.0.5.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-types@54.0.10.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/@expo_config-types@55.0.5.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_json-file@10.0.8.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_json-file@10.0.13.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/@expo_json-file@10.0.8.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/@expo_json-file@10.0.13.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_plist@0.4.8.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@expo_plist@0.5.2.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/@expo_plist@0.4.8.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/@expo_plist@0.5.2.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/@xmldom_xmldom@0.8.12.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/@xmldom_xmldom@0.8.13.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/@xmldom_xmldom@0.8.12.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/@xmldom_xmldom@0.8.13.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/HtmlToPdf.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/HtmlToPdf.plist deleted file mode 100644 index 31b817b3..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/HtmlToPdf.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2025 Christopher Dro -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNBootSplash.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNBootSplash.plist deleted file mode 100644 index 423a82b2..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNBootSplash.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2021 Mathieu Acthernoene - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNGestureHandler.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNGestureHandler.plist deleted file mode 100644 index fab2239f..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNGestureHandler.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) - -Copyright (c) 2016 Software Mansion <swmansion.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNLocalize.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNLocalize.plist deleted file mode 100644 index c55429b6..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNLocalize.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2017-present, Mathieu Acthernoene - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNReanimated.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNReanimated.plist deleted file mode 100644 index fab2239f..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNReanimated.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) - -Copyright (c) 2016 Software Mansion <swmansion.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNSVG.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNSVG.plist deleted file mode 100644 index 3f222a94..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNSVG.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) - -Copyright (c) [2015-2016] [Horcrux] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNScreens.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNScreens.plist deleted file mode 100644 index f5a02f3a..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNScreens.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) - -Copyright (c) 2018 Software Mansion <swmansion.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNShare.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNShare.plist deleted file mode 100644 index 32bc6463..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNShare.plist +++ /dev/null @@ -1,39 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) - -Copyright (c) 2015 Esteban Fuentealba 🇨🇱, Mateus Andrade 🇧🇷, Mike Hardy 🇪🇨, João Marins 🇧🇷 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/RNWorklets.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/RNWorklets.plist deleted file mode 100644 index 5cf9d9bf..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/RNWorklets.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2024 nobody -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/React-Core.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/React-Core.plist deleted file mode 100644 index 0e14fabb..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/React-Core.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) Meta Platforms, Inc. and affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/React.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/React.plist deleted file mode 100644 index 0e14fabb..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/React.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) Meta Platforms, Inc. and affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/ReactNativeLegal.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/ReactNativeLegal.plist deleted file mode 100644 index 649a81c0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/ReactNativeLegal.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2023 Callstack - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/ansi-styles@3.2.1.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/ansi-styles@3.2.1.plist deleted file mode 100644 index 33e092a4..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/ansi-styles@3.2.1.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/chalk@2.4.2.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/chalk@2.4.2.plist deleted file mode 100644 index 33e092a4..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/chalk@2.4.2.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/color-convert@1.9.3.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/color-convert@1.9.3.plist deleted file mode 100644 index 59f5c87a..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/color-convert@1.9.3.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com> - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/color-name@1.1.3.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/color-name@1.1.3.plist deleted file mode 100644 index 69e1439c..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/color-name@1.1.3.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) -Copyright (c) 2015 Dmitry Ivanov - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/escape-string-regexp@1.0.5.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/escape-string-regexp@1.0.5.plist deleted file mode 100644 index b1233c4a..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/escape-string-regexp@1.0.5.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - The MIT License (MIT) - -Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/has-flag@3.0.0.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/has-flag@3.0.0.plist deleted file mode 100644 index 33e092a4..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/has-flag@3.0.0.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/node-html-parser@7.0.2.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/node-html-parser@7.1.0.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/node-html-parser@7.0.2.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/node-html-parser@7.1.0.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-bootsplash@7.1.0.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-bootsplash@7.3.1.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/react-native-bootsplash@7.1.0.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/react-native-bootsplash@7.3.1.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-safe-area-context.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-is-edge-to-edge@1.3.1.plist similarity index 95% rename from ios/Settings.bundle/com.mono0926.LicensePlist/react-native-safe-area-context.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/react-native-is-edge-to-edge@1.3.1.plist index 177a1c68..8d4acd85 100644 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-safe-area-context.plist +++ b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-is-edge-to-edge@1.3.1.plist @@ -8,7 +8,7 @@ FooterText MIT License -Copyright (c) 2019 Th3rd Wave +Copyright (c) 2024 Mathieu Acthernoene Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License - MIT + unknown Type PSGroupSpecifier diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-legal@1.6.0.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-legal@1.6.3.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/react-native-legal@1.6.0.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/react-native-legal@1.6.3.plist diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-unistyles.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-unistyles.plist deleted file mode 100644 index 6905aab1..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-unistyles.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2023-2024 Jacek Pudysz -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ant-design.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ant-design.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ant-design.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-feather.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-feather.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-feather.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-fontawesome6.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-fontawesome6.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-fontawesome6.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-foundation.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-foundation.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-foundation.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ionicons.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ionicons.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-ionicons.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-lucide.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-lucide.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-lucide.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-material-design-icons.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-material-design-icons.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-material-design-icons.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-octicons.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-octicons.plist deleted file mode 100644 index 52af15e0..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/react-native-vector-icons-octicons.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) 2015 Joel Arvidsson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - License - MIT - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/supports-color@5.5.0.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/supports-color@5.5.0.plist deleted file mode 100644 index 33e092a4..00000000 --- a/ios/Settings.bundle/com.mono0926.LicensePlist/supports-color@5.5.0.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - MIT License - -Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - License - unknown - Type - PSGroupSpecifier - - - - diff --git a/ios/Settings.bundle/com.mono0926.LicensePlist/xml-formatter@3.6.7.plist b/ios/Settings.bundle/com.mono0926.LicensePlist/xml-formatter@3.7.0.plist similarity index 100% rename from ios/Settings.bundle/com.mono0926.LicensePlist/xml-formatter@3.6.7.plist rename to ios/Settings.bundle/com.mono0926.LicensePlist/xml-formatter@3.7.0.plist diff --git a/ios/TipCalculator.xcodeproj/project.pbxproj b/ios/TipCalculator.xcodeproj/project.pbxproj index 4dd39399..6a653336 100644 --- a/ios/TipCalculator.xcodeproj/project.pbxproj +++ b/ios/TipCalculator.xcodeproj/project.pbxproj @@ -599,7 +599,10 @@ ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = "$(inherited)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-DRCT_REMOVE_LEGACY_ARCH=1", + ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", @@ -607,6 +610,7 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", + "-DRCT_REMOVE_LEGACY_ARCH=1", ); OTHER_LDFLAGS = ( "$(inherited)", @@ -615,6 +619,7 @@ REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_ENABLE_EXPLICIT_MODULES = NO; USE_HERMES = true; }; name = Debug; @@ -676,7 +681,10 @@ "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CFLAGS = "$(inherited)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-DRCT_REMOVE_LEGACY_ARCH=1", + ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", @@ -684,6 +692,7 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", + "-DRCT_REMOVE_LEGACY_ARCH=1", ); OTHER_LDFLAGS = ( "$(inherited)", @@ -691,6 +700,7 @@ ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; + SWIFT_ENABLE_EXPLICIT_MODULES = NO; USE_HERMES = true; VALIDATE_PRODUCT = YES; }; diff --git a/ios/license_plist.yml b/ios/license_plist.yml index d4dcfcfe..a6aa6801 100644 --- a/ios/license_plist.yml +++ b/ios/license_plist.yml @@ -14,7 +14,7 @@ rename: "unicorn-magic@0.1.0": unicorn-magic "picocolors@1.1.1": picocolors "plist@3.1.0": plist - "@xmldom_xmldom@0.8.12": '@xmldom/xmldom' + "@xmldom_xmldom@0.8.13": '@xmldom/xmldom' "base64-js@1.5.1": base64-js "xmlbuilder@15.1.1": xmlbuilder "@react-native-vector-icons_feather@12.4.1": '@react-native-vector-icons/feather' @@ -344,20 +344,11 @@ rename: "regenerator-runtime@0.13.11": regenerator-runtime "scheduler@0.27.0": scheduler "whatwg-fetch@3.6.20": whatwg-fetch - "react-native-bootsplash@7.1.0": react-native-bootsplash - "@expo_config-plugins@54.0.4": '@expo/config-plugins' - "@expo_config-types@54.0.10": '@expo/config-types' - "@expo_json-file@10.0.8": '@expo/json-file' - "@babel_code-frame@7.10.4": '@babel/code-frame' - "@babel_highlight@7.25.9": '@babel/highlight' - "chalk@2.4.2": chalk - "ansi-styles@3.2.1": ansi-styles - "color-convert@1.9.3": color-convert - "color-name@1.1.3": color-name - "escape-string-regexp@1.0.5": escape-string-regexp - "supports-color@5.5.0": supports-color - "has-flag@3.0.0": has-flag - "@expo_plist@0.4.8": '@expo/plist' + "react-native-bootsplash@7.3.1": react-native-bootsplash + "@expo_config-plugins@55.0.8": '@expo/config-plugins' + "@expo_config-types@55.0.5": '@expo/config-types' + "@expo_json-file@10.0.13": '@expo/json-file' + "@expo_plist@0.5.2": '@expo/plist' "@expo_sdk-runtime-versions@1.0.0": '@expo/sdk-runtime-versions' "getenv@2.0.0": getenv "glob@13.0.0": glob @@ -392,7 +383,7 @@ rename: "is-glob@4.0.3": is-glob "is-extglob@2.1.1": is-extglob "merge2@1.4.1": merge2 - "node-html-parser@7.0.2": node-html-parser + "node-html-parser@7.1.0": node-html-parser "css-select@5.1.0": css-select "boolbase@1.0.0": boolbase "css-what@6.1.0": css-what @@ -404,14 +395,14 @@ rename: "nth-check@2.1.1": nth-check "he@1.2.0": he "prettier@3.8.1": prettier - "react-native-is-edge-to-edge@1.2.1": react-native-is-edge-to-edge + "react-native-is-edge-to-edge@1.3.1": react-native-is-edge-to-edge "sharp@0.34.5": sharp "@img_colour@1.0.0": '@img/colour' "detect-libc@2.1.2": detect-libc "@img_sharp-darwin-arm64@0.34.5": '@img/sharp-darwin-arm64' "@img_sharp-libvips-darwin-arm64@1.2.4": '@img/sharp-libvips-darwin-arm64' "ts-dedent@2.2.0": ts-dedent - "xml-formatter@3.6.7": xml-formatter + "xml-formatter@3.7.0": xml-formatter "xml-parser-xo@4.1.5": xml-parser-xo "react-native-gesture-handler@2.30.0": react-native-gesture-handler "@egjs_hammerjs@2.0.17": '@egjs/hammerjs' @@ -419,11 +410,12 @@ rename: "hoist-non-react-statics@3.3.2": hoist-non-react-statics "react-is@16.13.1": react-is "react-native-html-to-pdf@1.3.0": react-native-html-to-pdf - "react-native-legal@1.6.0": react-native-legal - "@callstack_licenses@0.3.0": '@callstack/licenses' + "react-native-legal@1.6.3": react-native-legal + "@callstack_licenses@0.3.1": '@callstack/licenses' "xml2js@0.6.2": xml2js "react-native-localize@3.7.0": react-native-localize "react-native-reanimated@4.2.2": react-native-reanimated + "react-native-is-edge-to-edge@1.2.1": react-native-is-edge-to-edge "react-native-safe-area-context@5.7.0": react-native-safe-area-context "react-native-screens@4.24.0": react-native-screens "react-freeze@1.0.4": react-freeze @@ -520,8 +512,8 @@ manual: version: '3.1.0' source: https://github.com/TooTallNate/node-plist file: '../node_modules/plist/LICENSE' - - name: '@xmldom_xmldom@0.8.12' - version: '0.8.12' + - name: '@xmldom_xmldom@0.8.13' + version: '0.8.13' source: https://github.com/xmldom/xmldom file: '../node_modules/@xmldom/xmldom/LICENSE' - name: base64-js@1.5.1 @@ -1839,60 +1831,24 @@ manual: version: '3.6.20' source: github/fetch file: '../node_modules/whatwg-fetch/LICENSE' - - name: react-native-bootsplash@7.1.0 - version: '7.1.0' + - name: react-native-bootsplash@7.3.1 + version: '7.3.1' source: https://github.com/zoontek/react-native-bootsplash file: '../node_modules/react-native-bootsplash/LICENSE' - - name: '@expo_config-plugins@54.0.4' - version: '54.0.4' + - name: '@expo_config-plugins@55.0.8' + version: '55.0.8' source: https://github.com/expo/expo file: '../node_modules/@expo/config-plugins/LICENSE' - - name: '@expo_config-types@54.0.10' - version: '54.0.10' + - name: '@expo_config-types@55.0.5' + version: '55.0.5' source: https://github.com/expo/expo file: '../node_modules/@expo/config-types/LICENSE' - - name: '@expo_json-file@10.0.8' - version: '10.0.8' + - name: '@expo_json-file@10.0.13' + version: '10.0.13' source: https://github.com/expo/expo file: '../node_modules/@expo/json-file/LICENSE' - - name: '@babel_code-frame@7.10.4' - version: '7.10.4' - source: https://github.com/babel/babel - file: '../node_modules/@expo/json-file/node_modules/@babel/code-frame/LICENSE' - - name: '@babel_highlight@7.25.9' - version: '7.25.9' - source: https://github.com/babel/babel - file: '../node_modules/@babel/highlight/LICENSE' - - name: chalk@2.4.2 - version: '2.4.2' - source: chalk/chalk - file: '../node_modules/@babel/highlight/node_modules/chalk/license' - - name: ansi-styles@3.2.1 - version: '3.2.1' - source: chalk/ansi-styles - file: '../node_modules/@babel/highlight/node_modules/ansi-styles/license' - - name: color-convert@1.9.3 - version: '1.9.3' - source: Qix-/color-convert - file: '../node_modules/@babel/highlight/node_modules/color-convert/LICENSE' - - name: color-name@1.1.3 - version: '1.1.3' - source: https://github.com/dfcreative/color-name - file: '../node_modules/@babel/highlight/node_modules/color-name/LICENSE' - - name: escape-string-regexp@1.0.5 - version: '1.0.5' - source: sindresorhus/escape-string-regexp - file: '../node_modules/@babel/highlight/node_modules/escape-string-regexp/license' - - name: supports-color@5.5.0 - version: '5.5.0' - source: chalk/supports-color - file: '../node_modules/@babel/highlight/node_modules/supports-color/license' - - name: has-flag@3.0.0 - version: '3.0.0' - source: sindresorhus/has-flag - file: '../node_modules/@babel/highlight/node_modules/has-flag/license' - - name: '@expo_plist@0.4.8' - version: '0.4.8' + - name: '@expo_plist@0.5.2' + version: '0.5.2' source: https://github.com/expo/expo body: MIT - name: '@expo_sdk-runtime-versions@1.0.0' @@ -2030,8 +1986,8 @@ manual: version: '1.4.1' source: https://github.com/teambition/merge2 file: '../node_modules/merge2/LICENSE' - - name: node-html-parser@7.0.2 - version: '7.0.2' + - name: node-html-parser@7.1.0 + version: '7.1.0' source: https://github.com/taoqf/node-fast-html-parser file: '../node_modules/node-html-parser/LICENSE' - name: css-select@5.1.0 @@ -2078,10 +2034,10 @@ manual: version: '3.8.1' source: prettier/prettier file: '../node_modules/react-native-bootsplash/node_modules/prettier/LICENSE' - - name: react-native-is-edge-to-edge@1.2.1 - version: '1.2.1' + - name: react-native-is-edge-to-edge@1.3.1 + version: '1.3.1' source: https://github.com/zoontek/react-native-edge-to-edge - file: '../node_modules/react-native-is-edge-to-edge/LICENSE' + file: '../node_modules/react-native-bootsplash/node_modules/react-native-is-edge-to-edge/LICENSE' - name: sharp@0.34.5 version: '0.34.5' source: https://github.com/lovell/sharp @@ -2106,8 +2062,8 @@ manual: version: '2.2.0' source: https://github.com/tamino-martinius/node-ts-dedent file: '../node_modules/ts-dedent/LICENSE' - - name: xml-formatter@3.6.7 - version: '3.6.7' + - name: xml-formatter@3.7.0 + version: '3.7.0' source: https://github.com/chrisbottin/xml-formatter file: '../node_modules/xml-formatter/LICENSE' - name: xml-parser-xo@4.1.5 @@ -2138,12 +2094,12 @@ manual: version: '1.3.0' source: https://github.com/christopherdro/react-native-html-to-pdf file: '../node_modules/react-native-html-to-pdf/LICENSE' - - name: react-native-legal@1.6.0 - version: '1.6.0' + - name: react-native-legal@1.6.3 + version: '1.6.3' source: https://github.com/callstackincubator/react-native-legal file: '../node_modules/react-native-legal/LICENSE' - - name: '@callstack_licenses@0.3.0' - version: '0.3.0' + - name: '@callstack_licenses@0.3.1' + version: '0.3.1' source: https://github.com/callstackincubator/react-native-legal file: '../node_modules/@callstack/licenses/LICENSE' - name: xml2js@0.6.2 @@ -2158,6 +2114,10 @@ manual: version: '4.2.2' source: https://github.com/software-mansion/react-native-reanimated file: '../node_modules/react-native-reanimated/LICENSE' + - name: react-native-is-edge-to-edge@1.2.1 + version: '1.2.1' + source: https://github.com/zoontek/react-native-edge-to-edge + file: '../node_modules/react-native-is-edge-to-edge/LICENSE' - name: react-native-safe-area-context@5.7.0 version: '5.7.0' source: https://github.com/AppAndFlow/react-native-safe-area-context @@ -2314,4 +2274,29 @@ manual: version: '7.28.6' source: https://github.com/babel/babel file: '../node_modules/@babel/plugin-syntax-typescript/LICENSE' +exclude: + - name: AsyncStorage + - name: react-native-vector-icons-ant-design + - name: react-native-vector-icons-feather + - name: react-native-vector-icons-fontawesome6 + - name: react-native-vector-icons-foundation + - name: react-native-vector-icons-ionicons + - name: react-native-vector-icons-lucide + - name: react-native-vector-icons-material-design-icons + - name: react-native-vector-icons-octicons + - name: React-Core-prebuilt + - name: React-Core + - name: React + - name: RNBootSplash + - name: RNGestureHandler + - name: HtmlToPdf + - name: ReactNativeLegal + - name: RNLocalize + - name: RNReanimated + - name: react-native-safe-area-context + - name: RNScreens + - name: RNShare + - name: RNSVG + - name: react-native-unistyles + - name: RNWorklets # END Generated NPM license entries \ No newline at end of file From 78414917346dd660551fb3a406a003069350f488 Mon Sep 17 00:00:00 2001 From: Devin Godage Date: Wed, 13 May 2026 01:02:05 +0530 Subject: [PATCH 2/6] feat: Implement preset duplication and management features in CustomSplitScreen, including hooks for saving, updating, and deleting presets --- .../StyledCustomSplitFooter/index.tsx | 130 +++ .../StyledCustomSplitPersonCard/index.tsx | 179 ++++ .../StyledCustomSplitPresetCard/index.tsx | 118 +++ app/components/index.ts | 3 + app/hooks/index.ts | 8 + app/hooks/useCustomSplitPeople.ts | 97 ++ app/hooks/useCustomSplitValidation.ts | 116 +++ app/hooks/usePresetDuplication.ts | 163 ++++ app/hooks/useSplitPresets.ts | 109 +++ app/screens/TipScreens/CustomSplitScreen.tsx | 855 +++--------------- tsconfig.json | 2 + 11 files changed, 1057 insertions(+), 723 deletions(-) create mode 100644 app/components/StyledCustomSplitFooter/index.tsx create mode 100644 app/components/StyledCustomSplitPersonCard/index.tsx create mode 100644 app/components/StyledCustomSplitPresetCard/index.tsx create mode 100644 app/hooks/useCustomSplitPeople.ts create mode 100644 app/hooks/useCustomSplitValidation.ts create mode 100644 app/hooks/usePresetDuplication.ts create mode 100644 app/hooks/useSplitPresets.ts diff --git a/app/components/StyledCustomSplitFooter/index.tsx b/app/components/StyledCustomSplitFooter/index.tsx new file mode 100644 index 00000000..5914ca08 --- /dev/null +++ b/app/components/StyledCustomSplitFooter/index.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { View, Text, Pressable } from 'react-native'; +import { StyledIcons } from '@components'; +import { IndividualSplit } from '@/context/types'; + +interface ValidationStatus { + status: 'complete' | 'under' | 'over'; + totalAllocatedPercentage: number; + fixedTotal: number; + percentageTotal: number; + remainderCount: number; + remainingPercentage: number; +} + +interface StyledCustomSplitFooterProps { + validation: ValidationStatus; + canSave: boolean; + isCustomSplitActive: boolean; + activePresetId: string | null; + onSave: () => void; + onClearCustomSplit: () => void; + onSaveAsNew: () => void; + onUpdatePreset: () => void; + people: IndividualSplit[]; + getValidationIcon: () => { name: any; color: string }; + getValidationText: () => string; + t: (key: string, options?: any) => string; + theme: any; + styles: any; +} + +const StyledCustomSplitFooter = ({ + validation, + canSave, + isCustomSplitActive, + activePresetId, + onSave, + onClearCustomSplit, + onSaveAsNew, + onUpdatePreset, + people, + getValidationIcon, + getValidationText, + t, + theme, + styles, +}: StyledCustomSplitFooterProps) => { + const validationIcon = getValidationIcon(); + + return ( + + {/* Display clear button if custom split is currently active */} + {isCustomSplitActive && ( + + + + {t('screens.customSplit.clearActiveSplit')} + + + )} + + {/* Validation Row */} + + + + {getValidationText()} + + + + {/* Breakdown Row */} + {validation.status === 'complete' && ( + + {validation.fixedTotal > 0 && ( + + {validation.fixedTotal.toFixed(2)} {t('screens.customSplit.fixed').toLowerCase()} + + )} + {validation.percentageTotal > 0 && ( + + {validation.percentageTotal.toFixed(1)}% {t('screens.customSplit.allocated')} + + )} + {validation.remainderCount > 0 && ( + + {validation.remainderCount} {t('screens.customSplit.remainder').toLowerCase()} + + )} + + )} + + {/* Save Button */} + + + {t('screens.customSplit.saveButton')} + + + + {/* Footer Button Row */} + + + + {activePresetId + ? t('screens.customSplit.updatePreset') + : t('screens.customSplit.saveAsNewPreset')} + + + + + ); +}; + +export default React.memo(StyledCustomSplitFooter); diff --git a/app/components/StyledCustomSplitPersonCard/index.tsx b/app/components/StyledCustomSplitPersonCard/index.tsx new file mode 100644 index 00000000..157fdd2e --- /dev/null +++ b/app/components/StyledCustomSplitPersonCard/index.tsx @@ -0,0 +1,179 @@ +import React, { useCallback } from 'react'; +import { View, Text, TextInput, Pressable } from 'react-native'; +import Animated, { FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated'; +import { StyledIcons } from '@components'; +import { IndividualSplit } from '@/context/types'; + +const MIN_PEOPLE = 2; + +// Allocation type button component +const AllocationTypeButton = ({ + label, + isActive, + onPress, + theme, + styles, +}: { + label: string; + isActive: boolean; + onPress: () => void; + theme: any; + styles: any; +}) => ( + + + {label} + + +); + +interface PersonCardProps { + person: IndividualSplit; + index: number; + totalPeople: number; + currencySymbol: string; + onUpdate: (id: string, updates: Partial) => void; + onRemove: (id: string) => void; + t: (key: string, options?: any) => string; + theme: any; + styles: any; +} + +const PersonCard = ({ + person, + index, + totalPeople, + currencySymbol, + onUpdate, + onRemove, + t, + theme, + styles, +}: PersonCardProps) => { + const canRemove = totalPeople > MIN_PEOPLE; + const placeholderName = `${t('screens.customSplit.personDefault', { number: index + 1 })} - ${t( + 'screens.customSplit.namePlaceholder', + )}`; + + const handleAllocationTypeChange = useCallback( + (type: IndividualSplit['allocationType']) => { + onUpdate(person.id, { + allocationType: type, + value: undefined, + calculatedAmount: undefined, + }); + }, + [person.id, onUpdate], + ); + + const handleValueChange = useCallback( + (text: string) => { + const cleaned = text.replace(/[^0-9.]/g, ''); + // Prevent multiple decimal points + const parts = cleaned.split('.'); + const sanitized = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : cleaned; + const numValue = parseFloat(sanitized); + onUpdate(person.id, { value: isNaN(numValue) ? undefined : numValue }); + }, + [person.id, onUpdate], + ); + + return ( + + {/* Card Header: Name + Delete */} + + onUpdate(person.id, { name: text })} + placeholder={placeholderName} + placeholderTextColor={theme.utils.hexToRGBA(theme.colors.card_typography, 0.4)} + maxLength={20} + /> + {canRemove && ( + onRemove(person.id)} hitSlop={8}> + + + )} + + + {/* Allocation Type Selector */} + + handleAllocationTypeChange('fixed')} + theme={theme} + styles={styles} + /> + handleAllocationTypeChange('percentage')} + theme={theme} + styles={styles} + /> + handleAllocationTypeChange('remainder')} + theme={theme} + styles={styles} + /> + + + {/* Value Input (hidden for remainder type) */} + {person.allocationType !== 'remainder' && ( + + + {person.allocationType === 'fixed' ? currencySymbol : ''} + + + + {person.allocationType === 'percentage' ? '%' : ''} + + + )} + + {/* Remainder indicator */} + {person.allocationType === 'remainder' && ( + + + {t('screens.customSplit.remainder')} + + )} + + ); +}; + +export default React.memo(PersonCard); diff --git a/app/components/StyledCustomSplitPresetCard/index.tsx b/app/components/StyledCustomSplitPresetCard/index.tsx new file mode 100644 index 00000000..c1f24c3a --- /dev/null +++ b/app/components/StyledCustomSplitPresetCard/index.tsx @@ -0,0 +1,118 @@ +import React, { useEffect } from 'react'; +import { View, Text, Pressable } from 'react-native'; +import Animated, { + useSharedValue, + useAnimatedStyle, + withRepeat, + withSequence, + withTiming, + Easing, +} from 'react-native-reanimated'; +import { StyledIcons } from '@components'; +import { SavedSplitPreset } from '@/context/types'; + +interface ShakingPresetCardProps { + preset: SavedSplitPreset; + isActive: boolean; + isDeleteMode: boolean; + onPress: () => void; + onLongPress: () => void; + onDelete: (id: string) => void; + getPresetSummary: (preset: SavedSplitPreset) => string; + t: (key: string, options?: any) => string; + theme: any; + styles: any; +} + +const ShakingPresetCard = ({ + preset, + isActive, + isDeleteMode, + onPress, + onLongPress, + onDelete, + getPresetSummary, + t, + theme, + styles, +}: ShakingPresetCardProps) => { + const rotation = useSharedValue(0); + + useEffect(() => { + if (isDeleteMode) { + rotation.value = withRepeat( + withSequence( + withTiming(-2, { duration: 80, easing: Easing.linear }), + withTiming(2, { duration: 80, easing: Easing.linear }), + withTiming(-2, { duration: 80, easing: Easing.linear }), + withTiming(0, { duration: 80, easing: Easing.linear }), + ), + -1, + false, + ); + } else { + rotation.value = withTiming(0, { duration: 100 }); + } + }, [isDeleteMode, rotation]); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ rotateZ: `${rotation.value}deg` }], + })); + + return ( + + + + {preset.name} + + + {t('screens.customSplit.presetPeople', { + count: preset.customSplits.length, + })} + + + {getPresetSummary(preset)} + + + {isDeleteMode && ( + onDelete(preset.id)} + hitSlop={8} + accessibilityRole="button" + accessibilityLabel={t('screens.customSplit.deletePresetAccessibilityLabel', { + name: preset.name, + defaultValue: `Delete preset ${preset.name}`, + })} + > + + + )} + + ); +}; + +export default React.memo(ShakingPresetCard); diff --git a/app/components/index.ts b/app/components/index.ts index 21e7f33b..122f5331 100644 --- a/app/components/index.ts +++ b/app/components/index.ts @@ -21,6 +21,9 @@ export { StyledSharePreviewModal } from './StyledSharePreviewModal'; export { StyledAlert } from './StyledAlert'; export { StyledSavedTipsList } from './StyledSavedTipsList'; export { StyledFilterCapsule } from './StyledFilterCapsule'; +export { default as StyledCustomSplitPersonCard } from './StyledCustomSplitPersonCard'; +export { default as StyledCustomSplitPresetCard } from './StyledCustomSplitPresetCard'; +export { default as StyledCustomSplitFooter } from './StyledCustomSplitFooter'; // types should export seperately export type { IconTypeMap, StyledIconTypesKey, BaseIconProps, StyledIconsProps } from './StyledIcons'; diff --git a/app/hooks/index.ts b/app/hooks/index.ts index 0fe51c65..b74497ee 100644 --- a/app/hooks/index.ts +++ b/app/hooks/index.ts @@ -35,6 +35,10 @@ import { useModalEntrance, useBottomSheetEntrance, } from './useAnimations'; +import { useCustomSplitPeople } from './useCustomSplitPeople'; +import { useCustomSplitValidation } from './useCustomSplitValidation'; +import { useSplitPresets } from './useSplitPresets'; +import { usePresetDuplication } from './usePresetDuplication'; export { convertToTwoDecimalPoints, @@ -67,6 +71,10 @@ export { useVisibilityAnimation, useModalEntrance, useBottomSheetEntrance, + useCustomSplitPeople, + useCustomSplitValidation, + useSplitPresets, + usePresetDuplication, }; export type { BillCalculationType, diff --git a/app/hooks/useCustomSplitPeople.ts b/app/hooks/useCustomSplitPeople.ts new file mode 100644 index 00000000..aa6cdc1a --- /dev/null +++ b/app/hooks/useCustomSplitPeople.ts @@ -0,0 +1,97 @@ +import { useState, useCallback, useEffect } from 'react'; +import { useRoute, RouteProp } from '@react-navigation/native'; +import { useAppContext } from '@/context/AppContext'; +import { IndividualSplit } from '@/context/types'; + +const MIN_PEOPLE = 2; +const MAX_PEOPLE = 15; + +const generateId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`; + +const createDefaultPerson = (index: number): IndividualSplit => ({ + id: generateId(), + name: '', + allocationType: 'remainder', + value: undefined, + calculatedAmount: undefined, +}); + +type CustomSplitRouteParams = { + CustomSplitScreen: { + totalBill: number; + tipPercentage: number; + currencySymbol: string; + presetId?: string; + }; +}; + +export const useCustomSplitPeople = () => { + const { state } = useAppContext(); + const route = useRoute>(); + const { presetId } = route.params || {}; + + const [hasLoadedPresetFromRoute, setHasLoadedPresetFromRoute] = useState(false); + + // Resolve initial people from preset if presetId is provided + const getInitialPeople = (): IndividualSplit[] => { + if (presetId) { + const preset = state.savedSplitPresets.find(p => p.id === presetId); + if (preset) { + return preset.customSplits.map(split => ({ + ...split, + calculatedAmount: undefined, + })); + } + } + return [createDefaultPerson(0), createDefaultPerson(1)]; + }; + + // Initialize with 2 default people or loaded preset + const [people, setPeople] = useState(getInitialPeople); + + // Sync preset loading when presets become available (after async persist load) + useEffect(() => { + if (presetId && !hasLoadedPresetFromRoute && state.savedSplitPresets.length > 0) { + const preset = state.savedSplitPresets.find(p => p.id === presetId); + if (preset) { + const loadedPeople = preset.customSplits.map(split => ({ + ...split, + calculatedAmount: undefined, + })); + setPeople(loadedPeople); + setHasLoadedPresetFromRoute(true); + } + } + }, [presetId, state.savedSplitPresets, hasLoadedPresetFromRoute]); + + const handleUpdatePerson = useCallback((id: string, updates: Partial) => { + setPeople(prev => prev.map(person => (person.id === id ? { ...person, ...updates } : person))); + }, []); + + const handleRemovePerson = useCallback((id: string) => { + setPeople(prev => { + if (prev.length <= MIN_PEOPLE) return prev; + return prev.filter(person => person.id !== id); + }); + }, []); + + const handleAddPerson = useCallback(() => { + setPeople(prev => { + if (prev.length >= MAX_PEOPLE) return prev; + return [...prev, createDefaultPerson(prev.length)]; + }); + }, []); + + return { + people, + setPeople, + handleUpdatePerson, + handleRemovePerson, + handleAddPerson, + hasLoadedPresetFromRoute, + setHasLoadedPresetFromRoute, + MIN_PEOPLE, + MAX_PEOPLE, + createDefaultPerson, + }; +}; diff --git a/app/hooks/useCustomSplitValidation.ts b/app/hooks/useCustomSplitValidation.ts new file mode 100644 index 00000000..bebf4767 --- /dev/null +++ b/app/hooks/useCustomSplitValidation.ts @@ -0,0 +1,116 @@ +import { useMemo } from 'react'; +import { IndividualSplit } from '@/context/types'; +import { toFixedWithoutRounding } from '@hooks'; + +export const useCustomSplitValidation = ( + people: IndividualSplit[], + totalBill: number, + tipPercentage: number, + theme: any, + t: (key: string, options?: any) => string, +) => { + // Calculate overall total (bill + tip) + const overallTotal = useMemo(() => { + const tip = (tipPercentage / 100) * totalBill; + return totalBill + tip; + }, [totalBill, tipPercentage]); + + // Validation computation + const validation = useMemo(() => { + let fixedTotal = 0; + let percentageTotal = 0; + let remainderCount = 0; + + people.forEach(person => { + switch (person.allocationType) { + case 'fixed': + fixedTotal += person.value || 0; + break; + case 'percentage': + percentageTotal += person.value || 0; + break; + case 'remainder': + remainderCount++; + break; + } + }); + + // Convert fixed to percentage of overallTotal for unified comparison + const fixedPercentage = overallTotal > 0 ? (fixedTotal / overallTotal) * 100 : 0; + const totalAllocatedPercentage = fixedPercentage + percentageTotal; + + // Remainder people get the leftover + const remainingPercentage = 100 - totalAllocatedPercentage; + + // Determine status + let status: 'complete' | 'under' | 'over'; + const tolerance = 0.01; + + if (remainderCount > 0) { + // With remainder people, they absorb the leftover + if (totalAllocatedPercentage > 100 + tolerance) { + status = 'over'; + } else if (remainingPercentage < -tolerance) { + status = 'over'; + } else { + status = 'complete'; + } + } else { + // No remainder people — must sum to exactly 100% + if (Math.abs(totalAllocatedPercentage - 100) <= tolerance) { + status = 'complete'; + } else if (totalAllocatedPercentage < 100) { + status = 'under'; + } else { + status = 'over'; + } + } + + return { + status, + totalAllocatedPercentage, + fixedTotal, + percentageTotal, + remainderCount, + remainingPercentage: Math.max(0, remainingPercentage), + }; + }, [people, overallTotal]); + + const canSave = + validation.status === 'complete' && people.length >= 2 && overallTotal > 0; + + // Validation status display + const getValidationIcon = () => { + switch (validation.status) { + case 'complete': + return { name: 'check-circle' as const, color: theme.colors.success }; + case 'under': + return { name: 'alert-circle' as const, color: theme.colors.error_toast }; + case 'over': + return { name: 'alert' as const, color: theme.colors.warning }; + } + }; + + const getValidationText = () => { + switch (validation.status) { + case 'complete': + return t('screens.customSplit.validationComplete'); + case 'under': { + const remaining = toFixedWithoutRounding(100 - validation.totalAllocatedPercentage, 1); + return t('screens.customSplit.validationUnder', { remaining: `${remaining}%` }); + } + case 'over': { + const excess = toFixedWithoutRounding(validation.totalAllocatedPercentage - 100, 1); + return t('screens.customSplit.validationOver', { excess: `${excess}%` }); + } + } + }; + + return { + overallTotal, + validation, + canSave, + getValidationIcon, + getValidationText, + }; +}; diff --git a/app/hooks/usePresetDuplication.ts b/app/hooks/usePresetDuplication.ts new file mode 100644 index 00000000..cb5548b9 --- /dev/null +++ b/app/hooks/usePresetDuplication.ts @@ -0,0 +1,163 @@ +import { useState, useCallback } from 'react'; +import Toast from 'react-native-toast-message'; +import { useAppContext } from '@/context/AppContext'; +import { IndividualSplit, SavedSplitPreset } from '@/context/types'; + +const generateId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`; + +export const usePresetDuplication = () => { + const { state, dispatch } = useAppContext(); + + const [isPresetNameModalVisible, setIsPresetNameModalVisible] = useState(false); + const [presetNameInput, setPresetNameInput] = useState(''); + const [isDeletePresetVisible, setIsDeletePresetVisible] = useState(false); + const [presetToDelete, setPresetToDelete] = useState(null); + const [duplicateAlert, setDuplicateAlert] = useState<{ + type: 'name' | 'config' | 'both'; + preset: SavedSplitPreset; + } | null>(null); + + // Check for duplicate presets (name match, config match, or both) + const findDuplicatePreset = useCallback( + ( + name: string, + splits: IndividualSplit[], + ): { type: 'name' | 'config' | 'both'; preset: SavedSplitPreset } | null => { + const lowerName = name.toLowerCase(); + + const isSameConfig = (a: IndividualSplit[], b: IndividualSplit[]) => { + if (a.length !== b.length) return false; + return a.every( + (split, i) => + split.allocationType === b[i].allocationType && + split.value === b[i].value && + split.name === b[i].name, + ); + }; + + for (const existing of state.savedSplitPresets) { + const nameMatch = existing.name.toLowerCase() === lowerName; + const configMatch = isSameConfig(splits, existing.customSplits); + + if (nameMatch && configMatch) return { type: 'both', preset: existing }; + if (nameMatch) return { type: 'name', preset: existing }; + if (configMatch) return { type: 'config', preset: existing }; + } + + return null; + }, + [state.savedSplitPresets], + ); + + // Get named people (fills in default names for unnamed) + const getNamedPeople = useCallback( + (people: IndividualSplit[], t: (key: string, options?: any) => string) => { + return people.map((person, index) => ({ + ...person, + name: person.name.trim() || t('screens.customSplit.personDefault', { number: index + 1 }), + calculatedAmount: undefined, + })); + }, + [], + ); + + // Save as new preset + const handleSavePreset = useCallback( + ( + people: IndividualSplit[], + t: (key: string, options?: any) => string, + onClose: () => void, + ) => { + const trimmedName = presetNameInput.trim(); + if (!trimmedName) return; + + const namedPeople = getNamedPeople(people, t); + + // Check for duplicates before saving + const duplicate = findDuplicatePreset(trimmedName, namedPeople); + if (duplicate) { + setIsPresetNameModalVisible(false); + setDuplicateAlert(duplicate); + return; + } + + const now = Date.now(); + + const newPreset: SavedSplitPreset = { + id: generateId(), + name: trimmedName, + createdAt: now, + updatedAt: now, + customSplits: namedPeople, + }; + + dispatch({ type: 'SAVE_SPLIT_PRESET', payload: newPreset }); + setIsPresetNameModalVisible(false); + setPresetNameInput(''); + Toast.show({ type: 'success', text1: t('screens.customSplit.presetSaved') }); + onClose(); + }, + [presetNameInput, getNamedPeople, findDuplicatePreset, dispatch], + ); + + // Update existing preset + const handleUpdatePreset = useCallback( + ( + people: IndividualSplit[], + activePresetId: string | null, + t: (key: string, options?: any) => string, + ) => { + if (!activePresetId) return; + + const existingPreset = state.savedSplitPresets.find(p => p.id === activePresetId); + if (!existingPreset) return; + + const namedPeople = getNamedPeople(people, t); + + const updatedPreset: SavedSplitPreset = { + ...existingPreset, + updatedAt: Date.now(), + customSplits: namedPeople, + }; + + dispatch({ type: 'UPDATE_SPLIT_PRESET', payload: updatedPreset }); + Toast.show({ type: 'success', text1: t('screens.customSplit.presetUpdated') }); + }, + [state.savedSplitPresets, getNamedPeople, dispatch], + ); + + // Delete a preset + const handleDeletePreset = useCallback( + (t: (key: string, options?: any) => string, onDone?: () => void) => { + if (!presetToDelete) return; + // Check if this is the last preset before dispatching + const remainingPresetsCount = state.savedSplitPresets.filter( + p => p.id !== presetToDelete, + ).length; + dispatch({ type: 'DELETE_SPLIT_PRESET', payload: presetToDelete }); + setPresetToDelete(null); + setIsDeletePresetVisible(false); + Toast.show({ type: 'success', text1: t('screens.customSplit.presetDeleted') }); + if (onDone) onDone(); + }, + [presetToDelete, dispatch, state.savedSplitPresets], + ); + + return { + isPresetNameModalVisible, + setIsPresetNameModalVisible, + presetNameInput, + setPresetNameInput, + isDeletePresetVisible, + setIsDeletePresetVisible, + presetToDelete, + setPresetToDelete, + duplicateAlert, + setDuplicateAlert, + findDuplicatePreset, + getNamedPeople, + handleSavePreset, + handleUpdatePreset, + handleDeletePreset, + }; +}; diff --git a/app/hooks/useSplitPresets.ts b/app/hooks/useSplitPresets.ts new file mode 100644 index 00000000..d95ac05f --- /dev/null +++ b/app/hooks/useSplitPresets.ts @@ -0,0 +1,109 @@ +import { useState, useCallback, useEffect } from 'react'; +import { useAppContext } from '@/context/AppContext'; +import { IndividualSplit, SavedSplitPreset } from '@/context/types'; + +export const useSplitPresets = () => { + const { state } = useAppContext(); + + const [activePresetId, setActivePresetId] = useState(null); + const [isPresetsExpanded, setIsPresetsExpanded] = useState(true); + const [isPresetDeleteMode, setIsPresetDeleteMode] = useState(false); + + // Check if the current activeSplitConfig matches any saved preset + useEffect(() => { + if ( + state.activeSplitConfig?.type === 'custom' && + state.activeSplitConfig?.customSplits && + state.savedSplitPresets.length > 0 + ) { + const currentSplits = state.activeSplitConfig.customSplits; + + // Find a preset that matches the current config + const matchingPreset = state.savedSplitPresets.find(preset => { + if (preset.customSplits.length !== currentSplits.length) return false; + + return preset.customSplits.every((split, index) => { + const current = currentSplits[index]; + return ( + split.allocationType === current.allocationType && + split.value === current.value && + split.name === current.name + ); + }); + }); + + if (matchingPreset) { + setActivePresetId(matchingPreset.id); + } + } + }, [state.activeSplitConfig, state.savedSplitPresets]); + + // Load a preset into the editor + const handleLoadPreset = useCallback( + (preset: SavedSplitPreset, onLoadPeople: (people: IndividualSplit[]) => void) => { + if (isPresetDeleteMode) return; + const loadedPeople = preset.customSplits.map(split => ({ + ...split, + calculatedAmount: undefined, + })); + onLoadPeople(loadedPeople); + setActivePresetId(preset.id); + }, + [isPresetDeleteMode], + ); + + // Toggle preset selection: deselect if already active, load if not + const handlePresetPress = useCallback( + (preset: SavedSplitPreset, onLoadPeople: (people: IndividualSplit[]) => void) => { + if (isPresetDeleteMode) return; + if (activePresetId === preset.id) { + // Deselect the preset + setActivePresetId(null); + } else { + // Load the preset + handleLoadPreset(preset, onLoadPeople); + } + }, + [activePresetId, isPresetDeleteMode, handleLoadPreset], + ); + + // Clear active preset and reset to blank state + const handleClearPreset = useCallback( + (onClearPeople: () => void) => { + setActivePresetId(null); + onClearPeople(); + }, + [], + ); + + // Handle long press on preset card — enter delete mode + const handlePresetLongPress = useCallback(() => { + setIsPresetDeleteMode(true); + }, []); + + // Handle delete button press on a preset card + const handlePresetDeletePress = useCallback((id: string) => { + return id; // Return ID to be used in delete confirmation + }, []); + + // Exit delete mode when all presets are deleted + useEffect(() => { + if (isPresetDeleteMode && state.savedSplitPresets.length === 0) { + setIsPresetDeleteMode(false); + } + }, [state.savedSplitPresets.length, isPresetDeleteMode]); + + return { + activePresetId, + setActivePresetId, + isPresetsExpanded, + setIsPresetsExpanded, + isPresetDeleteMode, + setIsPresetDeleteMode, + handleLoadPreset, + handlePresetPress, + handleClearPreset, + handlePresetLongPress, + handlePresetDeletePress, + }; +}; diff --git a/app/screens/TipScreens/CustomSplitScreen.tsx b/app/screens/TipScreens/CustomSplitScreen.tsx index bfb5302e..2402597a 100644 --- a/app/screens/TipScreens/CustomSplitScreen.tsx +++ b/app/screens/TipScreens/CustomSplitScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useMemo, useEffect } from 'react'; +import React, { useState, useCallback } from 'react'; import { View, Text, @@ -11,22 +11,24 @@ import { import { useTranslation } from 'react-i18next'; import { useNavigation, useRoute, RouteProp } from '@react-navigation/native'; import { UnistylesRuntime, createStyleSheet, useStyles } from 'react-native-unistyles'; -import Animated, { - FadeIn, - FadeOut, - LinearTransition, - useSharedValue, - useAnimatedStyle, - withRepeat, - withSequence, - withTiming, - Easing, -} from 'react-native-reanimated'; import Toast from 'react-native-toast-message'; -import { StyledHeader, StyledIcons, StyledAlert } from '@components'; +import { + StyledHeader, + StyledIcons, + StyledAlert, + StyledCustomSplitPersonCard, + StyledCustomSplitPresetCard, +} from '@components'; import { useAppContext } from '@/context/AppContext'; -import { IndividualSplit, SavedSplitPreset } from '@/context/types'; -import { toFixedWithoutRounding } from '@hooks'; +import { SavedSplitPreset } from '@/context/types'; +import { + toFixedWithoutRounding, + useCustomSplitPeople, + useCustomSplitValidation, + useSplitPresets, + usePresetDuplication, +} from '@hooks'; +import { IndividualSplit } from '@/context/types'; type CustomSplitRouteParams = { CustomSplitScreen: { @@ -40,284 +42,6 @@ type CustomSplitRouteParams = { const MIN_PEOPLE = 2; const MAX_PEOPLE = 15; -const generateId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`; - -const createDefaultPerson = (index: number): IndividualSplit => ({ - id: generateId(), - name: '', - allocationType: 'remainder', - value: undefined, - calculatedAmount: undefined, -}); - -// Allocation type button component -const AllocationTypeButton = ({ - label, - isActive, - onPress, - theme, - styles, -}: { - label: string; - isActive: boolean; - onPress: () => void; - theme: any; - styles: any; -}) => ( - - - {label} - - -); - -// Person card component -const PersonCard = ({ - person, - index, - totalPeople, - currencySymbol, - onUpdate, - onRemove, - t, - theme, - styles, -}: { - person: IndividualSplit; - index: number; - totalPeople: number; - currencySymbol: string; - onUpdate: (id: string, updates: Partial) => void; - onRemove: (id: string) => void; - t: (key: string, options?: any) => string; - theme: any; - styles: any; -}) => { - const canRemove = totalPeople > MIN_PEOPLE; - const placeholderName = `${t('screens.customSplit.personDefault', { number: index + 1 })} - ${t( - 'screens.customSplit.namePlaceholder', - )}`; - - const handleAllocationTypeChange = (type: IndividualSplit['allocationType']) => { - onUpdate(person.id, { - allocationType: type, - value: undefined, - calculatedAmount: undefined, - }); - }; - - const handleValueChange = (text: string) => { - const cleaned = text.replace(/[^0-9.]/g, ''); - // Prevent multiple decimal points - const parts = cleaned.split('.'); - const sanitized = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : cleaned; - const numValue = parseFloat(sanitized); - onUpdate(person.id, { value: isNaN(numValue) ? undefined : numValue }); - }; - - return ( - - {/* Card Header: Name + Delete */} - - onUpdate(person.id, { name: text })} - placeholder={placeholderName} - placeholderTextColor={theme.utils.hexToRGBA(theme.colors.card_typography, 0.4)} - maxLength={20} - /> - {canRemove && ( - onRemove(person.id)} hitSlop={8}> - - - )} - - - {/* Allocation Type Selector */} - - handleAllocationTypeChange('fixed')} - theme={theme} - styles={styles} - /> - handleAllocationTypeChange('percentage')} - theme={theme} - styles={styles} - /> - handleAllocationTypeChange('remainder')} - theme={theme} - styles={styles} - /> - - - {/* Value Input (hidden for remainder type) */} - {person.allocationType !== 'remainder' && ( - - - {person.allocationType === 'fixed' ? currencySymbol : ''} - - - - {person.allocationType === 'percentage' ? '%' : ''} - - - )} - - {/* Remainder indicator */} - {person.allocationType === 'remainder' && ( - - - {t('screens.customSplit.remainder')} - - )} - - ); -}; - -const MemoizedPersonCard = React.memo(PersonCard); - -// Shaking preset card component for iOS-style delete mode -const ShakingPresetCard = ({ - preset, - isActive, - isDeleteMode, - onPress, - onLongPress, - onDelete, - getPresetSummary, - t, - theme, - styles, -}: { - preset: SavedSplitPreset; - isActive: boolean; - isDeleteMode: boolean; - onPress: () => void; - onLongPress: () => void; - onDelete: (id: string) => void; - getPresetSummary: (preset: SavedSplitPreset) => string; - t: (key: string, options?: any) => string; - theme: any; - styles: any; -}) => { - const rotation = useSharedValue(0); - - useEffect(() => { - if (isDeleteMode) { - rotation.value = withRepeat( - withSequence( - withTiming(-2, { duration: 80, easing: Easing.linear }), - withTiming(2, { duration: 80, easing: Easing.linear }), - withTiming(-2, { duration: 80, easing: Easing.linear }), - withTiming(0, { duration: 80, easing: Easing.linear }), - ), - -1, - false, - ); - } else { - rotation.value = withTiming(0, { duration: 100 }); - } - }, [isDeleteMode, rotation]); - - const animatedStyle = useAnimatedStyle(() => ({ - transform: [{ rotateZ: `${rotation.value}deg` }], - })); - - return ( - - - - {preset.name} - - - {t('screens.customSplit.presetPeople', { - count: preset.customSplits.length, - })} - - - {getPresetSummary(preset)} - - - {isDeleteMode && ( - onDelete(preset.id)} - hitSlop={8} - accessibilityRole="button" - accessibilityLabel={t('screens.customSplit.deletePresetAccessibilityLabel', { - name: preset.name, - defaultValue: `Delete preset ${preset.name}`, - })} - > - - - )} - - ); -}; - const CustomSplitScreen = () => { const { styles, theme } = useStyles(stylesheet); const { t } = useTranslation(); @@ -325,181 +49,74 @@ const CustomSplitScreen = () => { const { state, dispatch } = useAppContext(); const route = useRoute>(); - const { totalBill = 0, tipPercentage = 0, currencySymbol = '$', presetId } = route.params || {}; + const { totalBill = 0, tipPercentage = 0, currencySymbol = '$' } = route.params || {}; + + // Import hooks + const { + people, + setPeople, + handleUpdatePerson, + handleRemovePerson, + handleAddPerson, + createDefaultPerson, + } = useCustomSplitPeople(); + + const { overallTotal, validation, canSave, getValidationIcon, getValidationText } = + useCustomSplitValidation(people, totalBill, tipPercentage, theme, t); + + const { + activePresetId, + setActivePresetId, + isPresetsExpanded, + setIsPresetsExpanded, + isPresetDeleteMode, + setIsPresetDeleteMode, + handleLoadPreset: hLoadPreset, + handlePresetPress, + handleClearPreset, + handlePresetLongPress, + } = useSplitPresets(); + + const { + isPresetNameModalVisible, + setIsPresetNameModalVisible, + presetNameInput, + setPresetNameInput, + isDeletePresetVisible, + setIsDeletePresetVisible, + presetToDelete, + setPresetToDelete, + duplicateAlert, + setDuplicateAlert, + handleSavePreset: hSavePreset, + handleUpdatePreset: hUpdatePreset, + handleDeletePreset: hDeletePreset, + getNamedPeople, + } = usePresetDuplication(); const [isInfoVisible, setIsInfoVisible] = useState(false); - const [activePresetId, setActivePresetId] = useState(null); - const [hasLoadedPresetFromRoute, setHasLoadedPresetFromRoute] = useState(false); - const [isPresetNameModalVisible, setIsPresetNameModalVisible] = useState(false); - const [presetNameInput, setPresetNameInput] = useState(''); - const [isDeletePresetVisible, setIsDeletePresetVisible] = useState(false); - const [presetToDelete, setPresetToDelete] = useState(null); - const [isPresetsExpanded, setIsPresetsExpanded] = useState(true); - const [isPresetDeleteMode, setIsPresetDeleteMode] = useState(false); - const [duplicateAlert, setDuplicateAlert] = useState<{ - type: 'name' | 'config' | 'both'; - preset: SavedSplitPreset; - } | null>(null); - - // Resolve initial people from preset if presetId is provided - const getInitialPeople = (): IndividualSplit[] => { - if (presetId) { - const preset = state.savedSplitPresets.find(p => p.id === presetId); - if (preset) { - return preset.customSplits.map(split => ({ - ...split, - calculatedAmount: undefined, - })); - } - } - return [createDefaultPerson(0), createDefaultPerson(1)]; - }; - - // Initialize with 2 default people or loaded preset - const [people, setPeople] = useState(getInitialPeople); - // Sync preset loading when presets become available (after async persist load) - useEffect(() => { - if (presetId && !hasLoadedPresetFromRoute && state.savedSplitPresets.length > 0) { - const preset = state.savedSplitPresets.find(p => p.id === presetId); - if (preset) { - const loadedPeople = preset.customSplits.map(split => ({ - ...split, - calculatedAmount: undefined, - })); - setPeople(loadedPeople); - setActivePresetId(presetId); - setHasLoadedPresetFromRoute(true); - } - } - }, [presetId, state.savedSplitPresets, hasLoadedPresetFromRoute]); - - // Check if the current activeSplitConfig matches any saved preset - useEffect(() => { - if ( - state.activeSplitConfig?.type === 'custom' && - state.activeSplitConfig?.customSplits && - state.savedSplitPresets.length > 0 && - !presetId // Only do this if presetId wasn't explicitly provided - ) { - const currentSplits = state.activeSplitConfig.customSplits; - - // Find a preset that matches the current config - const matchingPreset = state.savedSplitPresets.find(preset => { - if (preset.customSplits.length !== currentSplits.length) return false; - - return preset.customSplits.every((split, index) => { - const current = currentSplits[index]; - return ( - split.allocationType === current.allocationType && - split.value === current.value && - split.name === current.name - ); - }); - }); - - if (matchingPreset) { - setActivePresetId(matchingPreset.id); - } - } - }, [state.activeSplitConfig, state.savedSplitPresets, presetId]); - - const handleUpdatePerson = useCallback((id: string, updates: Partial) => { - setPeople(prev => prev.map(person => (person.id === id ? { ...person, ...updates } : person))); - }, []); - - const handleRemovePerson = useCallback((id: string) => { - setPeople(prev => { - if (prev.length <= MIN_PEOPLE) return prev; - return prev.filter(person => person.id !== id); - }); - }, []); - - const handleAddPerson = useCallback(() => { - setPeople(prev => { - if (prev.length >= MAX_PEOPLE) return prev; - return [...prev, createDefaultPerson(prev.length)]; - }); - }, []); - - // Calculate overall total (bill + tip) - const overallTotal = useMemo(() => { - const tip = (tipPercentage / 100) * totalBill; - return totalBill + tip; - }, [totalBill, tipPercentage]); - - // Validation computation - const validation = useMemo(() => { - let fixedTotal = 0; - let percentageTotal = 0; - let remainderCount = 0; - - people.forEach(person => { - switch (person.allocationType) { - case 'fixed': - fixedTotal += person.value || 0; - break; - case 'percentage': - percentageTotal += person.value || 0; - break; - case 'remainder': - remainderCount++; - break; - } - }); - - // Convert fixed to percentage of overallTotal for unified comparison - const fixedPercentage = overallTotal > 0 ? (fixedTotal / overallTotal) * 100 : 0; - const totalAllocatedPercentage = fixedPercentage + percentageTotal; - - // Remainder people get the leftover - const remainingPercentage = 100 - totalAllocatedPercentage; - - // Determine status - let status: 'complete' | 'under' | 'over'; - const tolerance = 0.01; - - if (remainderCount > 0) { - // With remainder people, they absorb the leftover - if (totalAllocatedPercentage > 100 + tolerance) { - status = 'over'; - } else if (remainingPercentage < -tolerance) { - status = 'over'; - } else { - status = 'complete'; - } - } else { - // No remainder people — must sum to exactly 100% - if (Math.abs(totalAllocatedPercentage - 100) <= tolerance) { - status = 'complete'; - } else if (totalAllocatedPercentage < 100) { - status = 'under'; - } else { - status = 'over'; - } - } - - return { - status, - totalAllocatedPercentage, - fixedTotal, - percentageTotal, - remainderCount, - remainingPercentage: Math.max(0, remainingPercentage), - }; - }, [people, overallTotal]); + // Wrapper for handleLoadPreset to pass people update + const handleLoadPreset = useCallback( + (preset: SavedSplitPreset) => { + hLoadPreset(preset, setPeople); + }, + [hLoadPreset], + ); - const canSave = - validation.status === 'complete' && people.length >= MIN_PEOPLE && overallTotal > 0; + // Wrapper for handlePresetPress + const handlePresetPressWrapper = useCallback( + (preset: SavedSplitPreset) => { + handlePresetPress(preset, setPeople); + }, + [handlePresetPress], + ); + // Main save handler const handleSave = useCallback(() => { if (!canSave) return; - // Set names to defaults for unnamed people - const namedPeople = people.map((person, index) => ({ - ...person, - name: person.name.trim() || t('screens.customSplit.personDefault', { number: index + 1 }), - })); + const namedPeople = getNamedPeople(people, t); dispatch({ type: 'SET_ACTIVE_SPLIT_CONFIG', @@ -510,183 +127,16 @@ const CustomSplitScreen = () => { }); navigation.goBack(); - }, [canSave, people, dispatch, navigation, t]); + }, [canSave, people, getNamedPeople, t, dispatch, navigation]); - // Clear active custom split and go back to equal splitting + // Clear custom split const isCustomSplitCurrentlyActive = state.activeSplitConfig?.type === 'custom'; - const handleClearCustomSplit = useCallback(() => { dispatch({ type: 'CLEAR_ACTIVE_SPLIT_CONFIG' }); navigation.goBack(); }, [dispatch, navigation]); - // Load a preset into the editor - const handleLoadPreset = useCallback( - (preset: SavedSplitPreset) => { - if (isPresetDeleteMode) return; - const loadedPeople = preset.customSplits.map(split => ({ - ...split, - calculatedAmount: undefined, - })); - setPeople(loadedPeople); - setActivePresetId(preset.id); - }, - [isPresetDeleteMode], - ); - - // Toggle preset selection: deselect if already active, load if not - const handlePresetPress = useCallback( - (preset: SavedSplitPreset) => { - if (isPresetDeleteMode) return; - if (activePresetId === preset.id) { - // Deselect the preset - setActivePresetId(null); - } else { - // Load the preset - handleLoadPreset(preset); - } - }, - [activePresetId, isPresetDeleteMode, handleLoadPreset], - ); - - // Clear active preset and reset to blank state - const handleClearPreset = useCallback(() => { - setActivePresetId(null); - setPeople([createDefaultPerson(0), createDefaultPerson(1)]); - }, []); - - // Check for duplicate presets (name match, config match, or both) - const findDuplicatePreset = useCallback( - ( - name: string, - splits: IndividualSplit[], - ): { type: 'name' | 'config' | 'both'; preset: SavedSplitPreset } | null => { - const lowerName = name.toLowerCase(); - - const isSameConfig = (a: IndividualSplit[], b: IndividualSplit[]) => { - if (a.length !== b.length) return false; - return a.every( - (split, i) => - split.allocationType === b[i].allocationType && - split.value === b[i].value && - split.name === b[i].name, - ); - }; - - for (const existing of state.savedSplitPresets) { - const nameMatch = existing.name.toLowerCase() === lowerName; - const configMatch = isSameConfig(splits, existing.customSplits); - - if (nameMatch && configMatch) return { type: 'both', preset: existing }; - if (nameMatch) return { type: 'name', preset: existing }; - if (configMatch) return { type: 'config', preset: existing }; - } - - return null; - }, - [state.savedSplitPresets], - ); - - // Get named people (fills in default names for unnamed) - const getNamedPeople = useCallback(() => { - return people.map((person, index) => ({ - ...person, - name: person.name.trim() || t('screens.customSplit.personDefault', { number: index + 1 }), - calculatedAmount: undefined, - })); - }, [people, t]); - - // Save as new preset - const handleSavePreset = useCallback(() => { - if (!canSave) return; - const trimmedName = presetNameInput.trim(); - if (!trimmedName) return; - - const namedPeople = getNamedPeople(); - - // Check for duplicates before saving - const duplicate = findDuplicatePreset(trimmedName, namedPeople); - if (duplicate) { - setIsPresetNameModalVisible(false); - setDuplicateAlert(duplicate); - return; - } - - const now = Date.now(); - - const newPreset: SavedSplitPreset = { - id: generateId(), - name: trimmedName, - createdAt: now, - updatedAt: now, - customSplits: namedPeople, - }; - - dispatch({ type: 'SAVE_SPLIT_PRESET', payload: newPreset }); - setActivePresetId(newPreset.id); - setIsPresetNameModalVisible(false); - setPresetNameInput(''); - Toast.show({ type: 'success', text1: t('screens.customSplit.presetSaved') }); - }, [canSave, presetNameInput, getNamedPeople, findDuplicatePreset, dispatch, t]); - - // Update existing preset - const handleUpdatePreset = useCallback(() => { - if (!canSave || !activePresetId) return; - - const existingPreset = state.savedSplitPresets.find(p => p.id === activePresetId); - if (!existingPreset) return; - - const namedPeople = getNamedPeople(); - - const updatedPreset: SavedSplitPreset = { - ...existingPreset, - updatedAt: Date.now(), - customSplits: namedPeople, - }; - - dispatch({ type: 'UPDATE_SPLIT_PRESET', payload: updatedPreset }); - Toast.show({ type: 'success', text1: t('screens.customSplit.presetUpdated') }); - }, [canSave, activePresetId, state.savedSplitPresets, getNamedPeople, dispatch, t]); - - // Delete a preset - const handleDeletePreset = useCallback(() => { - if (!presetToDelete) return; - // Check if this is the last preset before dispatching - const remainingPresetsCount = state.savedSplitPresets.filter( - p => p.id !== presetToDelete, - ).length; - dispatch({ type: 'DELETE_SPLIT_PRESET', payload: presetToDelete }); - if (activePresetId === presetToDelete) { - setActivePresetId(null); - } - setPresetToDelete(null); - setIsDeletePresetVisible(false); - // Exit delete mode if no presets remain after deletion - if (remainingPresetsCount <= 0) { - setIsPresetDeleteMode(false); - } - Toast.show({ type: 'success', text1: t('screens.customSplit.presetDeleted') }); - }, [presetToDelete, activePresetId, dispatch, t, state.savedSplitPresets]); - - // Exit delete mode when all presets are deleted - useEffect(() => { - if (isPresetDeleteMode && state.savedSplitPresets.length === 0) { - setIsPresetDeleteMode(false); - } - }, [state.savedSplitPresets.length, isPresetDeleteMode]); - - // Handle long press on preset card — enter delete mode - const handlePresetLongPress = useCallback(() => { - setIsPresetDeleteMode(true); - }, []); - - // Handle delete button press on a preset card - const handlePresetDeletePress = useCallback((id: string) => { - setPresetToDelete(id); - setIsDeletePresetVisible(true); - }, []); - - // Get brief summary text for a preset + // Get preset summary const getPresetSummary = useCallback( (preset: SavedSplitPreset) => { const counts = { fixed: 0, percentage: 0, remainder: 0 }; @@ -703,34 +153,32 @@ const CustomSplitScreen = () => { [t], ); - // Validation status display - const getValidationIcon = () => { - switch (validation.status) { - case 'complete': - return { name: 'check-circle' as const, color: theme.colors.success }; - case 'under': - return { name: 'alert-circle' as const, color: theme.colors.error_toast }; - case 'over': - return { name: 'alert' as const, color: theme.colors.warning }; - } - }; + // Preset delete handler + const handlePresetDeletePress = useCallback((id: string) => { + setPresetToDelete(id); + setIsDeletePresetVisible(true); + }, []); - const getValidationText = () => { - switch (validation.status) { - case 'complete': - return t('screens.customSplit.validationComplete'); - case 'under': { - const remaining = toFixedWithoutRounding(100 - validation.totalAllocatedPercentage, 1); - return t('screens.customSplit.validationUnder', { remaining: `${remaining}%` }); - } - case 'over': { - const excess = toFixedWithoutRounding(validation.totalAllocatedPercentage - 100, 1); - return t('screens.customSplit.validationOver', { excess: `${excess}%` }); - } - } - }; + // Handle save preset with wrapper + const handleSavePresetWrapper = useCallback(() => { + hSavePreset(people, t, () => { + setActivePresetId(null); + }); + }, [hSavePreset, people, t]); + + // Handle update preset with wrapper + const handleUpdatePresetWrapper = useCallback(() => { + hUpdatePreset(people, activePresetId, t); + }, [hUpdatePreset, people, activePresetId, t]); - const validationIcon = getValidationIcon(); + // Handle delete preset with wrapper + const handleDeletePresetWrapper = useCallback(() => { + hDeletePreset(t, () => { + if (activePresetId === presetToDelete) { + setActivePresetId(null); + } + }); + }, [hDeletePreset, t, activePresetId, presetToDelete]); return ( <> @@ -810,12 +258,12 @@ const CustomSplitScreen = () => { contentContainerStyle={styles.presetsScrollContent} > {state.savedSplitPresets.map(preset => ( - handlePresetPress(preset)} + onPress={() => handlePresetPressWrapper(preset)} onLongPress={handlePresetLongPress} onDelete={handlePresetDeletePress} getPresetSummary={getPresetSummary} @@ -829,16 +277,15 @@ const CustomSplitScreen = () => { )} - {/* Dismiss delete mode on tap outside presets */} + {/* Person Cards */} { if (isPresetDeleteMode) setIsPresetDeleteMode(false); }} accessible={false} > - {/* Person Cards */} - {people.map((person, index) => ( - ( + { {/* Sticky Validation Footer */} - {/* Validation Status */} + {/* Display clear button if custom split is currently active */} + {isCustomSplitCurrentlyActive && ( + + + + {t('screens.customSplit.clearActiveSplit')} + + + )} + + {/* Validation Row */} {getValidationText()} @@ -911,21 +373,6 @@ const CustomSplitScreen = () => { )} - {/* Clear Custom Split Button */} - {isCustomSplitCurrentlyActive && ( - - - - {t('screens.customSplit.clearActiveSplit')} - - - )} - {/* Save Button */} { style={[styles.presetButton, !canSave && styles.presetButtonDisabled]} onPress={() => { if (activePresetId) { - handleUpdatePreset(); + handleUpdatePresetWrapper(); } else { setPresetNameInput(''); setIsPresetNameModalVisible(true); @@ -956,6 +403,7 @@ const CustomSplitScreen = () => { + {/* Save as New option when editing a preset */} {activePresetId && canSave && ( { text: t('common.save'), onPress: () => { if (presetNameInput.trim()) { - handleSavePreset(); + handleSavePresetWrapper(); } else { Toast.show({ type: 'error', text1: t('screens.customSplit.presetNameRequired') }); } @@ -1102,7 +550,7 @@ const CustomSplitScreen = () => { { text: t('common.delete'), style: 'destructive', - onPress: handleDeletePreset, + onPress: handleDeletePresetWrapper, }, ]} onDismiss={() => { @@ -1128,7 +576,6 @@ const stylesheet = createStyleSheet(({ colors, fonts, utils }) => ({ paddingBottom: (UnistylesRuntime.screen.height * 2) / 100, paddingTop: (UnistylesRuntime.screen.height * 1) / 100, }, - // Active Preset Info Bar // Total Bill Banner totalBillBanner: { backgroundColor: colors.accent, @@ -1443,44 +890,6 @@ const stylesheet = createStyleSheet(({ colors, fonts, utils }) => ({ color: colors.white, opacity: 0.7, }, - // Active Preset Info Bar - activePresetBar: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - backgroundColor: utils.hexToRGBA(colors.accent, 0.15), - paddingHorizontal: (UnistylesRuntime.screen.width * 3) / 100, - paddingVertical: (UnistylesRuntime.screen.height * 0.8) / 100, - borderRadius: (UnistylesRuntime.screen.height * 0.8) / 100, - marginBottom: (UnistylesRuntime.screen.height * 1.2) / 100, - borderLeftWidth: 3, - borderLeftColor: colors.accent, - }, - activePresetText: { - fontSize: 12, - fontFamily: fonts.Nunito_SemiBold, - color: colors.accent, - flex: 1, - }, - activePresetName: { - fontSize: 12, - fontFamily: fonts.Nunito_Bold, - color: colors.accent, - }, - clearButton: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 6, - backgroundColor: utils.hexToRGBA(colors.error_toast, 0.15), - borderWidth: 1, - borderColor: colors.error_toast, - marginLeft: 8, - }, - clearButtonText: { - fontSize: 12, - fontFamily: fonts.Nunito_Bold, - color: colors.error_toast, - }, // Preset name input in modal presetNameInput: { width: '100%', diff --git a/tsconfig.json b/tsconfig.json index 9308aa8d..8c28f627 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,9 +6,11 @@ "paths": { "@/*": ["app/*"], "@components": ["app/components/index"], // if you are refering common index file in folder you should add "/index" end of the path + "@components/*": ["app/components/*"], "@styles/*": ["app/styles/*"], // if we want to import all the files in particular folder we should use "*", end of the path "@navigation/*": ["app/navigation/*"], "@hooks": ["app/hooks/index"], + "@hooks/*": ["app/hooks/*"], "@configs": ["app/configs/index"], "@plugins": ["./app/plugins/index"] } From 8e3af12bad1b1673166dca47f107d9106e5569b9 Mon Sep 17 00:00:00 2001 From: Devin Godage Date: Thu, 14 May 2026 01:19:21 +0530 Subject: [PATCH 3/6] fix(custom-split-improve): Resolve AsyncStorage race conditions causing intermittent data loss and improve state persistence by removing redundant save logic in reducers --- .prd/async-storage-race-condition-fix.md | 344 +++++++++++++++++++++++ app/context/reducers.ts | 62 +--- app/context/rootReducer.ts | 2 + app/hooks/usePersistedReducer.ts | 6 +- 4 files changed, 352 insertions(+), 62 deletions(-) create mode 100644 .prd/async-storage-race-condition-fix.md diff --git a/.prd/async-storage-race-condition-fix.md b/.prd/async-storage-race-condition-fix.md new file mode 100644 index 00000000..9bd7bbea --- /dev/null +++ b/.prd/async-storage-race-condition-fix.md @@ -0,0 +1,344 @@ +# PRD: Fix AsyncStorage Race Conditions & State Persistence Data Loss + +**Status:** Implemented ✅ +**Branch:** `feature/custom-split-improve` +**Date:** 2026-05-14 +**Severity:** High — intermittent user-facing data loss on app restart and updates + +--- + +## 1. Problem Statement + +Users intermittently lose persisted data (custom tip options, currency selection, saved tips, split presets, slider configurations) after: + +- App fully killed and reopened +- App update installed +- Sudden restart / crash recovery +- Rapid sequence of changes followed by force-kill + +The bug does not happen 100% of the time, which makes it difficult to reproduce consistently. The intermittent nature is a direct symptom of async race conditions, described in full below. + +--- + +## 2. System Architecture — How Persistence Currently Works + +### 2.1 State Model + +All application state lives in a single `AppState` object defined in `app/context/types.ts`. The fields are: + +| Field | Type | Description | +|---|---|---| +| `tips` | `TipOptionState[]` | Custom tip percentage options | +| `splits` | `SplitOptionState[]` | Custom split count options | +| `tipSliderConfig` | `TipSliderConfigValues` | Min/max/step for tip slider | +| `splitSliderConfig` | `SplitSliderConfigValues` | Min/max/step for split slider | +| `currencyConfig` | `CurrencyType \| undefined` | User-selected currency (undefined = device default) | +| `savedTips` | `SavedTip[]` | History of saved tip calculations | +| `duplicatePreventionWindow` | `number` | Minutes window for duplicate detection | +| `language` | `string \| undefined` | User language preference | +| `isRTL` | `boolean` | RTL layout flag derived from language | +| `activeSplitConfig` | `ActiveSplitConfig \| undefined` | Current active split session | +| `savedSplitPresets` | `SavedSplitPreset[]` | Saved custom split presets | + +The entire `AppState` is serialised as JSON under the single AsyncStorage key `"APPSTATE"`. + +### 2.2 `usePersistedReducer` — Single Persistence Layer + +Located in `app/hooks/usePersistedReducer.ts`. This hook wraps React's `useReducer` and manages all AsyncStorage I/O through two `useEffect`s and a hydration ref: + +**Effect 1 — Load (on mount):** + +```ts +useEffect(() => { + const loadState = async () => { + try { + const savedState = await AsyncStorage.getItem(key); + if (savedState) { + dispatch({ type: 'LOAD_PERSISTED_STATE', payload: JSON.parse(savedState) }); + } + } catch (error) { + console.error('Failed to load state from AsyncStorage', error); + } finally { + isHydratedRef.current = true; + } + }; + loadState(); +}, [key]); +``` + +**Effect 2 — Save (on every state change, guarded by hydration):** + +```ts +useEffect(() => { + if (!isHydratedRef.current) return; + const saveState = async () => { + await AsyncStorage.setItem(key, JSON.stringify(state)); + }; + saveState(); +}, [state, key]); +``` + +The `isHydratedRef` flag (a `useRef`, not `useState`) ensures the save effect cannot fire before the initial load completes, regardless of I/O timing. This is the **only** write path to AsyncStorage. + +### 2.3 `rootReducer` — `LOAD_PERSISTED_STATE` + +Located in `app/context/rootReducer.ts`. When `usePersistedReducer` dispatches `LOAD_PERSISTED_STATE`, the root reducer maps every field from the payload using nullish coalescing (`??`), including the slider config fields: + +```ts +case 'LOAD_PERSISTED_STATE': + return { + ...state, + tips: action.payload.tips ?? state.tips, + splits: action.payload.splits ?? state.splits, + tipSliderConfig: action.payload.tipSliderConfig ?? state.tipSliderConfig, + splitSliderConfig: action.payload.splitSliderConfig ?? state.splitSliderConfig, + currencyConfig: action.payload.currencyConfig ?? state.currencyConfig, + savedTips: action.payload.savedTips ?? state.savedTips, + duplicatePreventionWindow: action.payload.duplicatePreventionWindow ?? state.duplicatePreventionWindow, + language: action.payload.language ?? state.language, + isRTL: action.payload.isRTL ?? state.isRTL, + activeSplitConfig: action.payload.activeSplitConfig ?? state.activeSplitConfig, + savedSplitPresets: action.payload.savedSplitPresets ?? state.savedSplitPresets, + }; +``` + +### 2.4 Reducers — Pure Functions + +All reducers in `app/context/reducers.ts` are now pure functions. They compute and return new state only; no AsyncStorage access, no side effects. The `AsyncStorage` and `Constants` imports have been removed from this file. + +--- + +## 3. Bugs + +### Bug 1 — Startup Race Condition (Root Cause of Intermittent Data Loss) `CRITICAL` + +**Description:** +`usePersistedReducer` Effect 2 (save) runs on the **very first render** because `state` is initialised as `initialState` and is therefore a new reference. Both effects are scheduled after the first render in declaration order, but because both are async, their I/O operations run concurrently. There is no guarantee about which `AsyncStorage` operation completes first. + +**Timeline when Effect 2 wins:** + +``` +App mounts → state = initialState + ├── Effect 1 starts: AsyncStorage.getItem("APPSTATE") ← reading... + └── Effect 2 starts: AsyncStorage.setItem("APPSTATE", initialState) ← writing... + └── Write completes ← overwrites all persisted user data with defaults + └── Effect 1 read completes → reads initialState (just overwritten) → dispatches LOAD_PERSISTED_STATE with defaults +``` + +Result: All saved user preferences are silently erased. + +**Why it's intermittent:** +On slow I/O (first boot, after update) or when the storage is cold (newly written entry), the read in Effect 1 may complete faster. On fast I/O or warm cache, Effect 2 writes before Effect 1 reads. The race is non-deterministic. + +--- + +### Bug 2 — Dual-Write System Creates Concurrent Collisions `HIGH` + +**Description:** +When any action is dispatched, two write paths fire simultaneously and asynchronously: + +1. The per-reducer `saveState(partial)` — reads current storage, merges partial, writes back +2. `usePersistedReducer` Effect 2 — writes entire current state + +Both are uncoordinated. A typical collision scenario: + +``` +Action dispatched → reducer returns new state + ├── Per-reducer saveState: + │ ① getItem("APPSTATE") → reads stale snapshot S0 + │ ② ... waiting ... + └── usePersistedReducer saveEffect: + ① setItem("APPSTATE", S1) → writes updated state S1 + └── Per-reducer saveState: + ② setItem("APPSTATE", merge(S0, partial)) → overwrites S1 with stale data +``` + +The last write wins, and if it contains stale data, the most recent state is lost. + +--- + +### Bug 3 — Broken Array Spreading in `tipReducer` and `splitReducer` `HIGH` + +**Description:** +`tipReducer` receives `state: TipOptionState[]` (an array) and calls: + +```ts +saveState({ ...state, tips: updatedTips }); +``` + +Spreading a JavaScript array into an object produces numeric string keys, not field names: + +```ts +// state = [{ place: 1, value: 5 }, { place: 2, value: 10 }] +{ ...state } === { "0": { place: 1, value: 5 }, "1": { place: 2, value: 10 } } +``` + +So `saveState` writes `{ "0": ..., "1": ..., tips: [...] }` into AsyncStorage. On the next merge, these garbage numeric keys persist and pollute the stored JSON object. `splitReducer` has the identical bug. + +--- + +### Bug 4 — `tipSliderConfig` and `splitSliderConfig` Never Restored `MEDIUM` + +**Description:** +The `LOAD_PERSISTED_STATE` handler in `rootReducer.ts` explicitly maps every field — but `tipSliderConfig` and `splitSliderConfig` are absent: + +```ts +case 'LOAD_PERSISTED_STATE': + return { + ...state, + tips: action.payload.tips ?? state.tips, + splits: action.payload.splits ?? state.splits, + currencyConfig: action.payload.currencyConfig ?? state.currencyConfig, + savedTips: action.payload.savedTips ?? state.savedTips, + duplicatePreventionWindow: action.payload.duplicatePreventionWindow ?? state.duplicatePreventionWindow, + language: action.payload.language ?? state.language, + isRTL: action.payload.isRTL ?? state.isRTL, + activeSplitConfig: action.payload.activeSplitConfig ?? state.activeSplitConfig, + savedSplitPresets: action.payload.savedSplitPresets ?? state.savedSplitPresets, + // tipSliderConfig and splitSliderConfig are missing + }; +``` + +`usePersistedReducer` saves these fields correctly (whole-state serialisation), but the restore handler never applies them. Both fields always revert to their `initialState` defaults after restart. + +--- + +### Bug 5 — Per-Reducer `saveState` Reads Stale Data on Rapid Dispatch `MEDIUM` + +**Description:** +`saveState` uses a read-modify-write pattern. When two actions are dispatched in quick succession (e.g. save tip and update currency simultaneously), the second `getItem` may read a snapshot that predates the first `setItem`. The resulting merge loses the first write. + +This is a subset of Bug 2 and is fully resolved by removing the per-reducer save layer. + +--- + +## 4. Implementation + +### Design Decision + +**Removed the per-reducer `saveState` system entirely.** The `usePersistedReducer` save effect already serialises the complete state on every state change — the per-reducer saves were redundant. The redundancy was the source of all the collision and stale-data bugs. + +The only remaining responsibility was to **prevent the save effect from firing before the initial load completes**, which was solved with a single `useRef` hydration guard. + +The persistence system is now a single, deterministic write path: + +``` +Action dispatched → reducer computes new state → usePersistedReducer saves entire state +``` + +--- + +### Change 1 — Hydration guard in `usePersistedReducer` ✅ + +**File:** `app/hooks/usePersistedReducer.ts` + +- Added `useRef` to React imports +- Added `const isHydratedRef = useRef(false)` after `useReducer` +- Added `finally { isHydratedRef.current = true }` to the load effect — hydration is marked complete whether the read succeeded, found nothing, or threw an error +- Added `if (!isHydratedRef.current) return` as the first line of the save effect + +**Why `useRef` and not `useState`:** +A ref mutation does not trigger a re-render. Using `useState` for the hydration flag would cause an extra render cycle and potentially re-introduce timing issues in the save effect. + +**How it eliminates Bug 1:** + +``` +App mounts → state = initialState, isHydratedRef.current = false + ├── Effect 1 starts: AsyncStorage.getItem("APPSTATE") + └── Effect 2 runs: isHydratedRef.current === false → returns early (no write) + └── Effect 1 completes → isHydratedRef.current = true (set in finally) + → dispatches LOAD_PERSISTED_STATE → state updates + └── Effect 2 triggers from state change → isHydratedRef.current === true → saves ✓ +``` + +--- + +### Change 2 — Removed per-reducer `saveState` system ✅ + +**File:** `app/context/reducers.ts` + +- Removed `import AsyncStorage from '@react-native-async-storage/async-storage'` +- Removed `Constants` from the `@configs` import (was only used inside `saveState`) +- Removed all 15 `saveState(...)` call-sites across all 8 reducers +- Deleted the entire `saveState` async function + +All reducers are now pure functions — they compute and return new state only. Persistence is handled entirely by the hook layer above. Bugs 2, 3, and 5 are eliminated. + +--- + +### Change 3 — Restored missing fields in `LOAD_PERSISTED_STATE` ✅ + +**File:** `app/context/rootReducer.ts` + +Added the two previously absent fields to the `LOAD_PERSISTED_STATE` mapping: + +```ts +tipSliderConfig: action.payload.tipSliderConfig ?? state.tipSliderConfig, +splitSliderConfig: action.payload.splitSliderConfig ?? state.splitSliderConfig, +``` + +Both fields are now saved by `usePersistedReducer` and correctly restored on startup. Bug 4 is eliminated. + +--- + +## 5. Files Changed + +| File | Change | +|---|---| +| `app/hooks/usePersistedReducer.ts` | Add `useRef` import; add `isHydratedRef`; add `finally` block in load effect; add hydration guard in save effect | +| `app/context/reducers.ts` | Remove `AsyncStorage` import; remove `saveState` function; remove all `saveState(...)` call-sites from all reducers | +| `app/context/rootReducer.ts` | Add `tipSliderConfig` and `splitSliderConfig` to `LOAD_PERSISTED_STATE` mapping | + +--- + +## 6. Bugs Fixed per Change + +| Bug | Change 1 | Change 2 | Change 3 | +|---|---|---|---| +| Bug 1 — Startup race condition | ✅ Fixed | | | +| Bug 2 — Dual-write collisions | | ✅ Fixed | | +| Bug 3 — Array spread corruption | | ✅ Fixed | | +| Bug 4 — Missing slider config restore | | | ✅ Fixed | +| Bug 5 — Stale read-modify-write | | ✅ Fixed | | + +--- + +## 7. Verification Plan + +### 7.1 Manual Test Scenarios + +| Scenario | Steps | Expected Result | +|---|---|---| +| Persist across full kill | Change a tip value → fully kill app → reopen | Custom tip value present | +| Persist across update | Change currency → apply app update → reopen | Currency preference retained | +| Rapid changes + force kill | Change tip, change split count, change currency in rapid succession → force kill → reopen | All three changes retained | +| Slider config persists | Adjust tip slider min/max → kill → reopen | Slider config restored | +| Fresh install | Install from scratch → open app | Defaults load without errors | +| Save preset + restart | Save a custom split preset → kill → reopen | Preset visible in list | +| Saved tips persist | Save a tip calculation → kill → reopen | Tip present in history | + +### 7.2 Edge Cases + +| Scenario | Expected Result | +|---|---| +| AsyncStorage unavailable on load | Error logged, initialState used, app functions normally | +| AsyncStorage unavailable on save | Error logged silently, no crash | +| Corrupted AsyncStorage JSON | `JSON.parse` throws, caught in try/catch, `isHydratedRef.current` still set to `true` in `finally` | +| App killed during a write | Partial write may leave corrupt JSON; on next load parse fails → caught → initialState used | + +### 7.3 Regression Checks + +- Language / RTL preference survives restart +- `duplicatePreventionWindow` survives restart +- `activeSplitConfig` survives restart (or clears if that is intended behaviour) + +--- + +## 8. Out of Scope + +The following are known but deliberately excluded from this fix to keep scope minimal: + +- **Write queue / debounce:** A write queue would coalesce rapid state changes into fewer writes, reducing I/O. Not needed for correctness after these fixes. +- **`AsyncStorage.mergeItem`:** Could be used instead of full serialisation, but introduces complexity and doesn't eliminate the hydration race without the ref guard anyway. +- **`activeSplitConfig` session semantics:** Whether the active split configuration should be cleared on app restart is a product decision, not a persistence bug. +- **Schema migration:** If stored JSON from before this fix contains garbage numeric keys (from Bug 3), they will persist in existing installations. A migration step could clean these up but requires additional work and is not critical for correctness going forward. diff --git a/app/context/reducers.ts b/app/context/reducers.ts index 43c8f9b2..be9cac16 100644 --- a/app/context/reducers.ts +++ b/app/context/reducers.ts @@ -1,4 +1,4 @@ -import { Constants, CurrencyType } from '@configs'; +import { CurrencyType } from '@configs'; import { SplitOptionState, SplitAction, @@ -14,7 +14,6 @@ import { SavedSplitPreset, SavedSplitPresetAction, } from './types'; -import AsyncStorage from '@react-native-async-storage/async-storage'; export const tipReducer = (state: TipOptionState[], action: TipAction): TipOptionState[] => { switch (action.type) { @@ -22,11 +21,9 @@ export const tipReducer = (state: TipOptionState[], action: TipAction): TipOptio const updatedTips = state.map(tip => tip.place === action.payload.place ? action.payload : tip, ); - saveState({ ...state, tips: updatedTips }); // Save updated state to AsyncStorage return updatedTips; case 'RESET_TIP_OPTIONS_TO_DEFAULT': const defaultTipOptionsPayload = action.payload; - saveState({ ...state, tips: defaultTipOptionsPayload }); return defaultTipOptionsPayload; default: return state; @@ -42,11 +39,9 @@ export const splitReducer = ( const updatedSplits = state.map(split => split.place === action.payload.place ? action.payload : split, ); - saveState({ ...state, splits: updatedSplits }); // Save updated state to AsyncStorage return updatedSplits; case 'RESET_SPLIT_OPTIONS_TO_DEFAULT': const defaultSplitOptionsPayload = action.payload; - saveState({ ...state, splits: defaultSplitOptionsPayload }); return defaultSplitOptionsPayload; default: return state; @@ -60,10 +55,8 @@ export const currencyConfigReducer = ( switch (action.type) { case 'UPDATE_CURRENCY_SIGN': const updatedCurrencyConfig = action.payload; - saveState({ currencyConfig: updatedCurrencyConfig }); // Save updated currency config to AsyncStorage return updatedCurrencyConfig; case 'RESET_CURRENCY_TO_SYSTEM': - saveState({ currencyConfig: undefined }); // Reset to system default return undefined; default: return state; @@ -74,14 +67,11 @@ export const savedTipsReducer = (state: SavedTip[], action: SavedTipAction): Sav switch (action.type) { case 'SAVE_TIP': const newTips = [action.payload, ...state]; - saveState({ savedTips: newTips }); return newTips; case 'DELETE_TIP': const filteredTips = state.filter(tip => tip.id !== action.payload); - saveState({ savedTips: filteredTips }); return filteredTips; case 'CLEAR_ALL_TIPS': - saveState({ savedTips: [] }); return []; default: return state; @@ -94,7 +84,6 @@ export const duplicatePreventionReducer = ( ): number => { switch (action.type) { case 'UPDATE_DUPLICATE_PREVENTION_WINDOW': - saveState({ duplicatePreventionWindow: action.payload }); return action.payload; default: return state; @@ -116,10 +105,8 @@ export const languageReducer = ( language: action.payload.language, isRTL: action.payload.isRTL, }; - saveState(newLanguageState); return newLanguageState; case 'RESET_LANGUAGE_TO_SYSTEM': - saveState({ language: undefined, isRTL: false }); return { language: undefined, isRTL: false }; default: return state; @@ -132,10 +119,8 @@ export const splitConfigReducer = ( ): ActiveSplitConfig | undefined => { switch (action.type) { case 'SET_ACTIVE_SPLIT_CONFIG': - saveState({ activeSplitConfig: action.payload }); return action.payload; case 'CLEAR_ACTIVE_SPLIT_CONFIG': - saveState({ activeSplitConfig: undefined }); return undefined; default: return state; @@ -149,61 +134,16 @@ export const savedSplitPresetsReducer = ( switch (action.type) { case 'SAVE_SPLIT_PRESET': const afterSave = [action.payload, ...state]; - saveState({ savedSplitPresets: afterSave }); return afterSave; case 'UPDATE_SPLIT_PRESET': const afterUpdate = state.map(preset => preset.id === action.payload.id ? action.payload : preset, ); - saveState({ savedSplitPresets: afterUpdate }); return afterUpdate; case 'DELETE_SPLIT_PRESET': const afterDelete = state.filter(preset => preset.id !== action.payload); - saveState({ savedSplitPresets: afterDelete }); return afterDelete; default: return state; } }; - -// Function to save state to AsyncStorage -const saveState = async ( - partialState: Partial<{ - tips: TipOptionState[]; - splits: SplitOptionState[]; - currencyConfig: CurrencyType | undefined; - savedTips: SavedTip[]; - duplicatePreventionWindow: number; - language: string; - isRTL: boolean; - activeSplitConfig: ActiveSplitConfig | undefined; - savedSplitPresets: SavedSplitPreset[]; - }>, -) => { - try { - const currentState = await AsyncStorage.getItem(Constants.APP_STATE_ASYNCSTORAGE_KEY); - if (currentState) { - const currentStateObject = JSON.parse(currentState) as { - tips: TipOptionState[]; - splits: SplitOptionState[]; - currencyConfig: CurrencyType | undefined; - savedTips: SavedTip[]; - duplicatePreventionWindow: number; - language: string; - isRTL: boolean; - activeSplitConfig: ActiveSplitConfig | undefined; - savedSplitPresets: SavedSplitPreset[]; - }; - const newState = { ...currentStateObject, ...partialState }; - await AsyncStorage.setItem(Constants.APP_STATE_ASYNCSTORAGE_KEY, JSON.stringify(newState)); - } else { - // If no current state exists, save the partial state directly - await AsyncStorage.setItem( - Constants.APP_STATE_ASYNCSTORAGE_KEY, - JSON.stringify(partialState), - ); - } - } catch (error) { - console.error('Failed to save state to AsyncStorage', error); - } -}; diff --git a/app/context/rootReducer.ts b/app/context/rootReducer.ts index 8c24fc05..a247f21f 100644 --- a/app/context/rootReducer.ts +++ b/app/context/rootReducer.ts @@ -9,6 +9,8 @@ export const rootReducer = (state: AppState, action: AppAction): AppState => { ...state, tips: action.payload.tips ?? state.tips, splits: action.payload.splits ?? state.splits, + tipSliderConfig: action.payload.tipSliderConfig ?? state.tipSliderConfig, + splitSliderConfig: action.payload.splitSliderConfig ?? state.splitSliderConfig, currencyConfig: action.payload.currencyConfig ?? state.currencyConfig, savedTips: action.payload.savedTips ?? state.savedTips, duplicatePreventionWindow: action.payload.duplicatePreventionWindow ?? state.duplicatePreventionWindow, diff --git a/app/hooks/usePersistedReducer.ts b/app/hooks/usePersistedReducer.ts index ad279bcb..d8e8976b 100644 --- a/app/hooks/usePersistedReducer.ts +++ b/app/hooks/usePersistedReducer.ts @@ -1,4 +1,4 @@ -import { useReducer, useEffect, Reducer, Dispatch } from 'react'; +import { useReducer, useEffect, useRef, Reducer, Dispatch } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; export const usePersistedReducer = ( @@ -7,6 +7,7 @@ export const usePersistedReducer = ( key: string, ): [S, Dispatch] => { const [state, dispatch] = useReducer(reducer, initialState); + const isHydratedRef = useRef(false); useEffect(() => { const loadState = async () => { @@ -18,6 +19,8 @@ export const usePersistedReducer = ( } } catch (error) { console.error('Failed to load state from AsyncStorage', error); + } finally { + isHydratedRef.current = true; } }; @@ -25,6 +28,7 @@ export const usePersistedReducer = ( }, [key]); useEffect(() => { + if (!isHydratedRef.current) return; const saveState = async () => { try { await AsyncStorage.setItem(key, JSON.stringify(state)); From 01bf80b8b58ccfbab57dc047fda40d877ed13823 Mon Sep 17 00:00:00 2001 From: Devin Godage Date: Thu, 14 May 2026 01:21:18 +0530 Subject: [PATCH 4/6] chore(custom-split-improve): Update @babel/plugin-transform-modules-systemjs to version 7.29.4 and related dependencies in package-lock.json and yarn.lock --- package-lock.json | 14 +++++++------- yarn.lock | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 711b77e6..c3623210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1281,16 +1281,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" diff --git a/yarn.lock b/yarn.lock index c61dd1b2..98e632af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -189,7 +189,7 @@ resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.25.9", "@babel/helper-validator-identifier@^7.28.5": +"@babel/helper-validator-identifier@^7.28.5": version "7.28.5" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== @@ -625,14 +625,14 @@ "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-modules-systemjs@^7.25.9": - version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz" - integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== + version "7.29.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz" + integrity sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w== dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.29.0" "@babel/plugin-transform-modules-umd@^7.25.9": version "7.25.9" From 5652ce2cfd9513d814158b7a5c180d7d20811b48 Mon Sep 17 00:00:00 2001 From: Devin Godage Date: Sun, 17 May 2026 02:02:02 +0530 Subject: [PATCH 5/6] feat(architecture): Add comprehensive architecture documentation for TipMate v1.7.0 including app structure, navigation, state management, hooks, components, and styling details --- docs/architecture/architecture.html | 1447 +++++++++++++++++++++++++++ docs/architecture/architecture.json | 864 ++++++++++++++++ 2 files changed, 2311 insertions(+) create mode 100644 docs/architecture/architecture.html create mode 100644 docs/architecture/architecture.json diff --git a/docs/architecture/architecture.html b/docs/architecture/architecture.html new file mode 100644 index 00000000..4ba3ff4b --- /dev/null +++ b/docs/architecture/architecture.html @@ -0,0 +1,1447 @@ + + + + + + TipMate — Architecture Explorer + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+
+
+ + + + + +
+ + +
+ +
+ + +
+
+ Architecture Overview App Structure +
+
+
+
+
+ + +
+
+ Provider Tree 5 layers +
+
+
+
+
+
+
+ + +
+
+ Navigation Stack + Drawer +
+
+ +
+
+ + +
+
+ Screens +
+
+
+
+
+ + +
+
+ State Management +
+
+
+ Pattern: React Context + useReducer + AsyncStorage +  ·  Hook: usePersistedReducer +  ·  Key: APPSTATE +  ·  Hydrated via LOAD_PERSISTED_STATE +
+
+ + + +
KeyTypeDescriptionPersisted
+
+
+
+ + +
+
+ Reducers +
+
+
+ + +
+
+ Hooks +
+
+
+ + +
+
+ Components +
+
+
+
+
+ + +
+
+ Data Flows 4 flows +
+
+
+
+
+ + +
+
+ Styling & Theming +
+
+
+
react-native-unistyles 2.43

Themes in themes.ts. Breakpoints in breakpoints.ts. Registered via uniStyles.ts. Applied at start: UnistylesRuntime.setTheme().

+
8 Custom Themes

4 accent colors (teal, orange, blue, pink) × light/dark modes. Stored in customThemesConfig.ts. Persisted separately from APPSTATE in AsyncStorage.

+
Fonts

Montserrat Black · Bold · Medium · Regular · SemiBold
Nunito Black · Bold · SemiBold · Medium · Regular

+
+
+
+ + +
+
+ Localization +
+
+
+
i18next + react-i18next

Synchronous device-language init on app start. User preference synced after AsyncStorage hydration. RTL applied via I18nManager.forceRTL().

🇬🇧 English🇪🇸 Spanish🇫🇷 French🇸🇦 Arabic RTL🇱🇰 Sinhala
+
RTL Support

Arabic triggers RTL layout. isRTL boolean persisted in APPSTATE. applyRTLSync() called at Entrypoint init. react-native-localize for device locale detection. Navigation and components respond to the flag.

+
+
+
+ + +
+
+ Storage +
+
+
+ + + + + + + +
AsyncStorage KeyContentsManaged By
APPSTATEtips, splits, sliderConfigs, currencyConfig, savedTips, duplicatePreventionWindow, language, isRTL, savedSplitPresets
Note: activeSplitConfig intentionally NOT persisted
usePersistedReducer
ThemePreferenceActive Unistyles theme name (e.g. "light", "tealDark")asyncStorageHooks.ts
ThemeColorOptionSerialized custom accent color option arrayasyncStorageHooks.ts
+
+
+
+ + +
+
+ Key Dependencies +
+
+
+
+
+ +
+ + + + + + + diff --git a/docs/architecture/architecture.json b/docs/architecture/architecture.json new file mode 100644 index 00000000..5a2b40b0 --- /dev/null +++ b/docs/architecture/architecture.json @@ -0,0 +1,864 @@ +{ + "app": { + "name": "TipMate", + "bundleId": "com.devinapps.tips.tipcalculator", + "version": "1.7.0", + "framework": "React Native", + "rnVersion": "0.84.1", + "reactVersion": "19.2.3", + "platforms": ["iOS", "Android"] + }, + "entryPoints": { + "root": "index.js", + "appEntry": "app/Entrypoint.tsx", + "description": "index.js registers 'App' AppRegistry component which is Entrypoint.tsx" + }, + "providerTree": [ + { + "name": "GestureHandlerRootView", + "source": "react-native-gesture-handler", + "purpose": "Enables gesture recognition across the entire app", + "children": [ + { + "name": "AppProvider", + "source": "app/context/AppContext.tsx", + "purpose": "Global state via usePersistedReducer + AsyncStorage", + "children": [ + { + "name": "ApplicationNavigator", + "source": "app/navigation/ApplicationNavigation.tsx", + "purpose": "Theme init, NavigationContainer, BootSplash, Toast", + "children": [ + { + "name": "SafeAreaProvider", + "source": "react-native-safe-area-context" + }, + { + "name": "NavigationContainer", + "source": "@react-navigation/native" + }, + { + "name": "StackNavigation", + "source": "app/navigation/StackNavigation.tsx" + }, + { + "name": "Toast", + "source": "react-native-toast-message" + } + ] + } + ] + } + ] + } + ], + "navigation": { + "type": "Nested Stack + Drawer", + "stack": { + "name": "StackNavigation", + "file": "app/navigation/StackNavigation.tsx", + "navigator": "createNativeStackNavigator", + "defaultOptions": { + "headerShown": false, + "orientation": "portrait", + "animation": "ios_from_right", + "animationDuration": 250 + }, + "screens": [ + { + "name": "MainStack", + "component": "DrawerNavigation", + "type": "nested-drawer" + }, + { + "name": "SavedTipDetailScreen", + "file": "app/screens/TipScreens/SavedTipDetailScreen.tsx", + "presentation": "push" + }, + { + "name": "CustomSplitScreen", + "file": "app/screens/TipScreens/CustomSplitScreen.tsx", + "presentation": "push" + }, + { + "name": "LicensesScreen", + "file": "app/screens/AppInfoScreens/LicenseScreen.tsx", + "presentation": "push" + }, + { + "name": "LicenseContentModal", + "file": "app/screens/AppInfoScreens/LicenseContentModalScreen.tsx", + "presentation": "modal", + "animation": "slide_from_bottom" + } + ] + }, + "drawer": { + "name": "DrawerNavigation", + "file": "app/navigation/DrawerNavigation.tsx", + "navigator": "createDrawerNavigator", + "defaultOptions": { + "drawerType": "slide", + "overlayColor": "rgba(0,0,0,0.3)", + "swipeEdgeWidth": 80 + }, + "customDrawerContent": "StyledDrawer", + "screens": [ + { + "name": "CalcTipScreen", + "label": "navigation.tipSplit", + "component": "HomeTipScreen", + "file": "app/screens/TipScreens/HomeTipScreen.tsx", + "visible": true + }, + { + "name": "SavedTipScreen", + "label": "navigation.tipSummary", + "component": "SavedTipsScreen", + "file": "app/screens/TipScreens/SavedTipsScreen.tsx", + "visible": true + }, + { + "name": "SettingsScreen", + "label": "navigation.settingsPreferences", + "component": "SettingsScreen", + "file": "app/screens/TipScreens/SettingsScreen.tsx", + "visible": true + }, + { + "name": "AboutUsScreen", + "component": "AboutUsScreen", + "file": "app/screens/AppInfoScreens/AboutUsScreen.tsx", + "visible": false, + "note": "Hidden from drawer, navigated to programmatically" + } + ] + } + }, + "screens": { + "TipScreens": [ + { + "name": "HomeTipScreen", + "file": "app/screens/TipScreens/HomeTipScreen.tsx", + "description": "Main tip calculator. Handles bill input, tip %, split mode, rounding, save & share.", + "stateUsed": ["activeSplitConfig", "currencyConfig", "tips", "splits", "savedTips"], + "hooksUsed": [ + "calculateBillValues", + "calculateBillValuesCustomSplit", + "useSaveTip", + "useShareTipPreview", + "getDeviceCurrency", + "useAppContext" + ], + "componentsUsed": [ + "StyledBillBox", + "StyledHeader", + "StyledTotalAmountInput", + "StyledTipOptions", + "StyledSpiltOptions", + "StyledRoundBox", + "StyledSharePreviewModal", + "StyledAlert" + ], + "localState": [ + "userInputBillAmount", + "userInputTipPercentage", + "userInputSplitCount", + "userInputRound", + "billValues", + "customBillValues" + ], + "navigationTo": ["CustomSplitScreen", "SavedTipDetailScreen"] + }, + { + "name": "SavedTipsScreen", + "file": "app/screens/TipScreens/SavedTipsScreen.tsx", + "description": "Lists all saved tip calculations with swipe-to-delete.", + "stateUsed": ["savedTips"], + "hooksUsed": ["useAppContext"], + "componentsUsed": ["StyledHeader", "StyledSavedTipsList"], + "navigationTo": ["SavedTipDetailScreen"] + }, + { + "name": "SavedTipDetailScreen", + "file": "app/screens/TipScreens/SavedTipDetailScreen.tsx", + "description": "Full detail view of a single saved tip. Supports sharing.", + "stateUsed": ["savedTips"], + "hooksUsed": ["useShareTipDetailsText", "useShareTipDetailsPDF"], + "componentsUsed": ["StyledHeader"] + }, + { + "name": "SettingsScreen", + "file": "app/screens/TipScreens/SettingsScreen.tsx", + "description": "App preferences: theme, currency, language, tip/split defaults, duplicate prevention.", + "stateUsed": [ + "currencyConfig", + "language", + "isRTL", + "duplicatePreventionWindow", + "tips", + "splits" + ], + "hooksUsed": ["useAppContext", "useThemeColorCustomiser"], + "componentsUsed": [ + "StyledHeader", + "StyledThemeBox", + "StyledCurrencySelector", + "StyledLanguageSelector", + "StyledToggle", + "StyledTipOptionsEditMode", + "StyledSplitOptionsEditMode", + "StyledDuplicatePreventionSelector" + ] + }, + { + "name": "CustomSplitScreen", + "file": "app/screens/TipScreens/CustomSplitScreen.tsx", + "description": "Create/edit custom split with per-person allocation (fixed/percentage/remainder). Manages split presets.", + "stateUsed": ["activeSplitConfig", "savedSplitPresets"], + "hooksUsed": [ + "useCustomSplitPeople", + "useCustomSplitValidation", + "useSplitPresets", + "usePresetDuplication", + "useAppContext" + ], + "componentsUsed": [ + "StyledHeader", + "StyledCustomSplitPersonCard", + "StyledCustomSplitFooter", + "StyledCustomSplitPresetCard" + ], + "dispatchedActions": [ + "SET_ACTIVE_SPLIT_CONFIG", + "SAVE_SPLIT_PRESET", + "UPDATE_SPLIT_PRESET", + "DELETE_SPLIT_PRESET" + ] + } + ], + "AppInfoScreens": [ + { + "name": "AboutUsScreen", + "file": "app/screens/AppInfoScreens/AboutUsScreen.tsx", + "description": "App info, links, developer credits.", + "hooksUsed": ["useExternalLinkAlert"], + "componentsUsed": ["StyledHeader"] + }, + { + "name": "LicenseScreen", + "file": "app/screens/AppInfoScreens/LicenseScreen.tsx", + "description": "Lists all open-source licenses. Scroll-to-top spring animation.", + "hooksUsed": ["useAnimations"], + "componentsUsed": ["StyledHeader", "StyledLicenseDetailsCard"], + "navigationTo": ["LicenseContentModal"] + }, + { + "name": "LicenseContentModalScreen", + "file": "app/screens/AppInfoScreens/LicenseContentModalScreen.tsx", + "description": "Full text of a single open-source license, presented as bottom sheet modal.", + "hooksUsed": [], + "componentsUsed": ["StyledHeader"] + } + ] + }, + "stateManagement": { + "pattern": "React Context + useReducer + AsyncStorage persistence", + "contextFile": "app/context/AppContext.tsx", + "typesFile": "app/context/types.ts", + "rootReducerFile": "app/context/rootReducer.ts", + "reducersFile": "app/context/reducers.ts", + "persistenceKey": "APPSTATE", + "persistenceHook": "usePersistedReducer", + "stateShape": { + "tips": { + "type": "TipOptionState[]", + "persisted": true, + "description": "4 quick-select tip % options" + }, + "splits": { + "type": "SplitOptionState[]", + "persisted": true, + "description": "4 quick-select split count options" + }, + "tipSliderConfig": { + "type": "TipSliderConfigValues", + "persisted": true, + "description": "Slider bounds/step for tip %, default {min:0, max:80, step:1}" + }, + "splitSliderConfig": { + "type": "SplitSliderConfigValues", + "persisted": true, + "description": "Slider bounds/step for split count, default {min:1, max:15, step:1}" + }, + "currencyConfig": { + "type": "CurrencyType | undefined", + "persisted": true, + "description": "User-selected currency; undefined = device locale currency" + }, + "savedTips": { + "type": "SavedTip[]", + "persisted": true, + "description": "Persisted tip calculation history" + }, + "duplicatePreventionWindow": { + "type": "number", + "persisted": true, + "description": "Minutes window to detect duplicate saves, default 15" + }, + "language": { + "type": "string | undefined", + "persisted": true, + "description": "User language code override; undefined = device language" + }, + "isRTL": { + "type": "boolean", + "persisted": true, + "description": "RTL layout flag, derived from language selection" + }, + "activeSplitConfig": { + "type": "ActiveSplitConfig | undefined", + "persisted": false, + "description": "Active custom split config; intentionally NOT persisted — resets on app restart" + }, + "savedSplitPresets": { + "type": "SavedSplitPreset[]", + "persisted": true, + "description": "Named custom split configurations" + } + }, + "reducers": [ + { + "name": "tipReducer", + "actions": ["UPDATE_TIP_OPTIONS", "RESET_TIP_OPTIONS_TO_DEFAULT"], + "manages": "tips" + }, + { + "name": "splitReducer", + "actions": ["UPDATE_SPLIT_OPTIONS", "RESET_SPLIT_OPTIONS_TO_DEFAULT"], + "manages": "splits" + }, + { + "name": "currencyConfigReducer", + "actions": ["UPDATE_CURRENCY_SIGN", "RESET_CURRENCY_TO_SYSTEM"], + "manages": "currencyConfig" + }, + { + "name": "savedTipsReducer", + "actions": ["SAVE_TIP", "DELETE_TIP", "CLEAR_ALL_TIPS"], + "manages": "savedTips" + }, + { + "name": "duplicatePreventionReducer", + "actions": ["UPDATE_DUPLICATE_PREVENTION_WINDOW"], + "manages": "duplicatePreventionWindow" + }, + { + "name": "languageReducer", + "actions": ["SET_LANGUAGE", "RESET_LANGUAGE_TO_SYSTEM"], + "manages": "language + isRTL" + }, + { + "name": "splitConfigReducer", + "actions": ["SET_ACTIVE_SPLIT_CONFIG", "CLEAR_ACTIVE_SPLIT_CONFIG"], + "manages": "activeSplitConfig" + }, + { + "name": "savedSplitPresetsReducer", + "actions": ["SAVE_SPLIT_PRESET", "UPDATE_SPLIT_PRESET", "DELETE_SPLIT_PRESET"], + "manages": "savedSplitPresets" + } + ], + "specialAction": { + "name": "LOAD_PERSISTED_STATE", + "description": "Dispatched on app start by usePersistedReducer to hydrate all state from AsyncStorage" + } + }, + "hooks": { + "calculation": [ + { + "name": "calculateBillValues", + "file": "app/hooks/calculateBill.ts", + "purpose": "Calculates bill for equal splits: per-person and overall totals with rounding" + }, + { + "name": "calculateBillValuesCustomSplit", + "file": "app/hooks/calculateBill.ts", + "purpose": "Calculates bill for custom splits: fixed → percentage → remainder allocation order, penny distribution via largest remainder method" + } + ], + "sharing": [ + { + "name": "useShareTipPreview", + "file": "app/hooks/useShareTipPreview.ts", + "purpose": "Manages share preview modal state and delegates to text/PDF share hooks" + }, + { + "name": "useShareTipDetailsText", + "file": "app/hooks/useShareTipDetailsText.ts", + "purpose": "Formats tip data as text and triggers native share sheet" + }, + { + "name": "useShareTipDetailsPDF", + "file": "app/hooks/useShareTipDetailsPDF.ts", + "purpose": "Generates PDF from tip data and triggers native share sheet" + } + ], + "persistence": [ + { + "name": "usePersistedReducer", + "file": "app/hooks/usePersistedReducer.ts", + "purpose": "Wraps useReducer with AsyncStorage: loads state on mount, saves on every dispatch" + }, + { + "name": "asyncStorageUtil", + "file": "app/hooks/asyncStorageUtil.ts", + "purpose": "Low-level AsyncStorage read/write utilities" + }, + { + "name": "asyncStorageHooks", + "file": "app/hooks/asyncStorageHooks.ts", + "purpose": "Theme preference AsyncStorage helpers (getUserPreferredTheme, setUserPreferredTheme, etc.)" + } + ], + "customSplit": [ + { + "name": "useCustomSplitPeople", + "file": "app/hooks/useCustomSplitPeople.ts", + "purpose": "Manages add/remove/edit of people in CustomSplitScreen local state" + }, + { + "name": "useCustomSplitValidation", + "file": "app/hooks/useCustomSplitValidation.ts", + "purpose": "Validates allocation totals (fixed ≤ total, percentage ≤ 100%, ≥1 remainder or exact allocation)" + }, + { + "name": "useSplitPresets", + "file": "app/hooks/useSplitPresets.ts", + "purpose": "Manages saved split preset CRUD, active preset detection by matching current config" + }, + { + "name": "usePresetDuplication", + "file": "app/hooks/usePresetDuplication.ts", + "purpose": "Detects and prevents saving duplicate presets" + } + ], + "tipSaving": [ + { + "name": "useSaveTip", + "file": "app/hooks/useSaveTip.ts", + "purpose": "Encapsulates save tip logic, success/error alert state, and navigation to tip detail" + } + ], + "ui": [ + { + "name": "useAnimations", + "file": "app/hooks/useAnimations.ts", + "exports": [ + "usePressAnimation", + "useFocusScale", + "useScaleSpring", + "useValuePulse", + "useBounce", + "useVisibilityAnimation", + "useModalEntrance", + "useBottomSheetEntrance" + ] + }, + { + "name": "useThemeColorCustomiser", + "file": "app/hooks/useThemeColorCustomiser.ts", + "purpose": "Applies custom accent color overrides to the active Unistyles theme" + }, + { + "name": "useExternalLinkAlert", + "file": "app/hooks/useExternalLinkAlert.ts", + "purpose": "Shows confirmation alert before opening external URLs" + } + ], + "utilities": [ + { + "name": "convertToTwoDecimalPoints", + "file": "app/hooks/convertToTwoDecimals.ts" + }, + { + "name": "toFixedWithoutRounding", + "file": "app/hooks/tofixedWithoutRounding.ts" + }, + { + "name": "getDeviceCurrency", + "file": "app/hooks/currencyUtils.ts", + "purpose": "Reads device locale to determine default currency symbol and code" + }, + { + "name": "validateOptionValues", + "file": "app/hooks/validationHooks.ts" + } + ] + }, + "components": [ + { + "name": "StyledHeader", + "file": "app/components/StyledHeader/", + "purpose": "Shared screen header with title, back/menu button" + }, + { + "name": "StyledTotalAmountInput", + "file": "app/components/StyledTotalAmountInput/", + "purpose": "Bill amount text input with currency symbol" + }, + { + "name": "StyledTipOptions", + "file": "app/components/StyledTipOptions/", + "purpose": "Quick-select tip % capsules + slider" + }, + { + "name": "StyledTipOptionsEditMode", + "file": "app/components/StyledTipOptionsEditMode/", + "purpose": "Editable tip % capsules for SettingsScreen" + }, + { + "name": "StyledSpiltOptions", + "file": "app/components/StyledSplitOptions/", + "purpose": "Quick-select split count capsules + custom split button" + }, + { + "name": "StyledSplitOptionsEditMode", + "file": "app/components/StyledSplitOptionsEditMode/", + "purpose": "Editable split count capsules for SettingsScreen" + }, + { + "name": "StyledBillBox", + "file": "app/components/StyledBillBox/", + "purpose": "Displays calculated totals (overall + per-person)" + }, + { + "name": "StyledRoundBox", + "file": "app/components/StyledRoundBox/", + "purpose": "Rounding method selector (UP / DOWN / NONE)" + }, + { + "name": "StyledSharePreviewModal", + "file": "app/components/StyledSharePreviewModal/", + "purpose": "Bottom sheet modal previewing tip share content with Text/PDF actions", + "animation": "slide" + }, + { + "name": "StyledSavedTipsList", + "file": "app/components/StyledSavedTipsList/", + "purpose": "FlatList of saved tips with swipe-to-delete (ReanimatedSwipeable) and scroll-to-top spring animation" + }, + { + "name": "StyledCustomSplitPersonCard", + "file": "app/components/StyledCustomSplitPersonCard/", + "purpose": "Per-person card in CustomSplitScreen with name, allocation type, and value inputs" + }, + { + "name": "StyledCustomSplitFooter", + "file": "app/components/StyledCustomSplitFooter/", + "purpose": "Footer for CustomSplitScreen with save/cancel actions and validation summary" + }, + { + "name": "StyledCustomSplitPresetCard", + "file": "app/components/StyledCustomSplitPresetCard/", + "purpose": "Displays a saved split preset with load/delete actions" + }, + { + "name": "StyledDrawer", + "file": "app/components/StyledDrawer/", + "purpose": "Custom drawer content with navigation items and app branding" + }, + { + "name": "StyledCurrencySelector", + "file": "app/components/StyledCurrencySelector/", + "purpose": "Searchable currency picker modal", + "animation": "slide" + }, + { + "name": "StyledLanguageSelector", + "file": "app/components/StyledLanguageSelector/", + "purpose": "Language picker modal", + "animation": "slide" + }, + { + "name": "StyledThemeBox", + "file": "app/components/StyledThemeBox/", + "purpose": "Theme selector (light/dark + accent color swatches)" + }, + { + "name": "StyledToggle", + "file": "app/components/StyledToggle/", + "purpose": "Platform-scaled Switch component (iOS 0.8x, Android 0.9x)" + }, + { + "name": "StyledAlert", + "file": "app/components/StyledAlert/", + "purpose": "Custom alert modal", + "animation": "fade" + }, + { + "name": "StyledDuplicatePreventionSelector", + "file": "app/components/StyledDuplicatePreventionSelector/", + "purpose": "Time-window picker for duplicate save detection", + "animation": "slide" + }, + { + "name": "StyledConfigInput", + "file": "app/components/StyledConfigInput/", + "purpose": "Numeric input for configurable values (tip %, split count) in edit mode" + }, + { + "name": "StyledHorizontalSlider", + "file": "app/components/StyledHorizontalSlider/", + "purpose": "Styled slider built on @miblanchard/react-native-slider" + }, + { + "name": "StyledFilterCapsule", + "file": "app/components/StyledFilterCapsule/", + "purpose": "Generic pill/capsule toggle button" + }, + { + "name": "StyledTextInputCapsule", + "file": "app/components/StyledTextInputCapsule/", + "purpose": "Text input styled as a capsule" + }, + { + "name": "StyledLicenseDetailsCard", + "file": "app/components/StyledLicenseDetailsCard/", + "purpose": "Card displaying a single license entry with expand action" + }, + { + "name": "StyledIcons", + "file": "app/components/StyledIcons/", + "purpose": "Themed wrappers around vector icon sets" + }, + { + "name": "StyledSVGIcons", + "file": "app/components/StyledSVGIcons/", + "purpose": "Custom SVG icons via react-native-svg" + } + ], + "styling": { + "library": "react-native-unistyles", + "version": "2.43.0", + "setupFile": "app/styles/uniStyles.ts", + "themesFile": "app/styles/themes.ts", + "breakpointsFile": "app/styles/breakpoints.ts", + "toastConfigFile": "app/styles/toastConfig.tsx", + "themes": { + "light": "lightTheme", + "dark": "darkTheme" + }, + "customThemes": { + "file": "app/configs/customThemesConfig.ts", + "description": "4 accent color variants (teal, orange, blue, pink) × 2 modes = 8 custom themes" + }, + "fonts": [ + "Montserrat-Black", + "Montserrat-Bold", + "Montserrat-Medium", + "Montserrat-Regular", + "Montserrat-Semibold", + "Nunito-Black", + "Nunito-Bold", + "Nunito-SemiBold", + "Nunito-Medium", + "Nunito-Regular" + ], + "themePersistence": "AsyncStorage (separate from app state, via asyncStorageHooks)" + }, + "localization": { + "library": "i18next + react-i18next", + "configFile": "app/localization/localizationConfig.ts", + "i18nFile": "app/localization/i18n.ts", + "rtlFile": "app/localization/useRTL.ts", + "languages": [ + { "code": "en", "name": "English" }, + { "code": "es", "name": "Spanish" }, + { "code": "fr", "name": "French" }, + { "code": "ar", "name": "Arabic", "rtl": true }, + { "code": "si", "name": "Sinhala" } + ], + "initStrategy": "Synchronous init on app start with device language; user preference synced after state hydration", + "rtlSupport": true + }, + "storage": { + "library": "@react-native-async-storage/async-storage", + "keys": { + "APPSTATE": "Main app state (tips, splits, savedTips, currency, language, presets, etc.)", + "ThemePreference": "User-selected theme name", + "ThemeColorOption": "User-selected accent color option" + } + }, + "dataTypes": { + "TipOptionState": { "place": "number", "value": "number" }, + "SplitOptionState": { "place": "number", "value": "number" }, + "IndividualSplit": { + "id": "string", + "name": "string", + "allocationType": "'fixed' | 'percentage' | 'remainder'", + "value": "number | undefined", + "calculatedAmount": "number | undefined" + }, + "ActiveSplitConfig": { + "type": "'equal' | 'custom'", + "customSplits": "IndividualSplit[] | undefined" + }, + "SavedTip": { + "id": "string", + "timestamp": "number", + "amount": "number", + "tip": "number", + "total": "number", + "tipPercentage": "number", + "numberOfPeople": "number", + "splitType": "'equal' | 'custom' | undefined", + "perPerson": "{ amount, tip, total } | undefined", + "individualSplits": "IndividualSplit[] | undefined", + "currencySymbol": "string", + "currencyCode": "string" + }, + "SavedSplitPreset": { + "id": "string", + "name": "string", + "createdAt": "number", + "updatedAt": "number", + "customSplits": "IndividualSplit[]" + }, + "BillCalculationType": { + "perPerson": "{ total, tip, subtotal }", + "overall": "{ total, tip, subtotal }", + "disabledRoundingMethods": "{ UP, DOWN, NO }" + }, + "CustomSplitCalculationType": { + "overall": "{ total, tip, subtotal }", + "individuals": "IndividualSplit[]", + "disabledRoundingMethods": "{ UP, DOWN, NO }" + } + }, + "keyDependencies": { + "navigation": [ + "@react-navigation/native", + "@react-navigation/native-stack", + "@react-navigation/drawer", + "@react-navigation/devtools" + ], + "animation": [ + "react-native-reanimated ^4.2.2", + "react-native-gesture-handler ^2.30.0", + "react-native-worklets ^0.7.4" + ], + "storage": ["@react-native-async-storage/async-storage ^3.0.1"], + "styling": ["react-native-unistyles 2.43.0"], + "i18n": ["i18next ^25.8.13", "react-i18next ^16.5.4", "react-native-localize ^3.7.0"], + "sharing": ["react-native-share 12.0.11", "react-native-html-to-pdf ^1.3.0"], + "ui": [ + "react-native-safe-area-context ^5.7.0", + "react-native-screens ^4.24.0", + "react-native-svg ^15.15.3", + "react-native-toast-message ^2.3.3", + "@miblanchard/react-native-slider ^2.6.0" + ], + "icons": [ + "@react-native-vector-icons/ant-design", + "@react-native-vector-icons/feather", + "@react-native-vector-icons/fontawesome6", + "@react-native-vector-icons/foundation", + "@react-native-vector-icons/ionicons", + "@react-native-vector-icons/lucide", + "@react-native-vector-icons/material-design-icons", + "@react-native-vector-icons/octicons" + ], + "splash": ["react-native-bootsplash ^7.1.0"], + "legal": ["react-native-legal ^1.6.0"] + }, + "componentStateConnections": { + "description": "Components that directly receive and display global state (passed as props from their parent screen)", + "StyledTipOptions": ["tips", "tipSliderConfig"], + "StyledSpiltOptions": ["splits", "splitSliderConfig"], + "StyledTipOptionsEditMode": ["tips"], + "StyledSplitOptionsEditMode": ["splits"], + "StyledSavedTipsList": ["savedTips"], + "StyledCurrencySelector": ["currencyConfig"], + "StyledLanguageSelector": ["language", "isRTL"], + "StyledDuplicatePreventionSelector": ["duplicatePreventionWindow"], + "StyledCustomSplitPresetCard": ["savedSplitPresets"] + }, + "coreBillCalculationFlow": { + "description": "How a bill amount becomes a displayed result", + "steps": [ + "User enters bill amount → setUserInputBillAmount", + "User selects tip % (capsule or slider) → setUserInputTipPercentage", + "User selects split count or custom split → setUserInputSplitCount / activeSplitConfig", + "User selects rounding method → setUserInputRound", + "useEffect fires on any dependency change", + "if isCustomSplitActive: calculateBillValuesCustomSplit() → setCustomBillValues()", + "else: calculateBillValues() → setBillValues()", + "StyledBillBox renders the result" + ] + }, + "customSplitFlow": { + "description": "Flow for creating and applying a custom split", + "steps": [ + "User taps 'Custom' in StyledSplitOptions → navigation.navigate('CustomSplitScreen')", + "useCustomSplitPeople manages local people array (add/remove/edit)", + "useCustomSplitValidation validates allocations in real time", + "User taps Save → dispatch SET_ACTIVE_SPLIT_CONFIG → navigation.goBack()", + "HomeTipScreen useEffect detects activeSplitConfig change", + "calculateBillValuesCustomSplit() runs with new config", + "StyledBillBox shows per-person individual amounts", + "User presses equal-split number → dispatch CLEAR_ACTIVE_SPLIT_CONFIG" + ] + }, + "shareSaveFlow": { + "description": "Flow for saving and sharing a calculated tip", + "steps": [ + "User taps Save/Share button in HomeTipScreen", + "useSaveTip checks for existing duplicate (timestamp window + amount + people)", + "If not duplicate: dispatch SAVE_TIP → savedTipsReducer → AsyncStorage", + "useShareTipPreview opens StyledSharePreviewModal", + "User picks Text Share → useShareTipDetailsText → native share sheet", + "User picks PDF Share → useShareTipDetailsPDF → react-native-html-to-pdf → native share sheet" + ] + }, + "themeBootFlow": { + "description": "App startup: theme application and boot splash dismissal", + "steps": [ + "Entrypoint.tsx: initializeI18n() + applyRTLSync() run synchronously before first render", + "ApplicationNavigator mounts → getUserPreferredTheme() reads ThemePreference from AsyncStorage", + "getUserUpdatedThemeOption() restores ThemeColorOption accent overrides from AsyncStorage", + "useThemeColorCustomiser() applies accent color overrides to active Unistyles theme", + "UnistylesRuntime.setTheme(preferredTheme) — applies the persisted theme", + "UnistylesRuntime.setAdaptiveThemes(false) — prevents OS light/dark from overriding user choice", + "NavigationContainer.onReady() fires → BootSplash.hide({ fade: true })" + ] + }, + "plugins": { + "highContrastPlugin": { + "file": "app/plugins/highContrastPlugin.ts", + "purpose": "Unistyles plugin to enforce high-contrast color overrides when accessibility setting is enabled" + } + }, + "buildConfig": { + "metro": "metro.config.js", + "babel": "babel.config.js — uses @react-native/babel-preset + module-resolver for path aliases", + "typescript": "tsconfig.json", + "pathAliases": { + "@navigation": "app/navigation", + "@hooks": "app/hooks", + "@components": "app/components", + "@configs": "app/configs", + "@styles": "app/styles", + "@/": "app/" + }, + "ios": { + "workspace": "ios/TipCalculator.xcworkspace", + "scheme": "TipCalculator", + "pods": "ios/Podfile" + }, + "android": { + "appGradle": "android/app/build.gradle", + "releaseVariants": ["assembleRelease", "bundleRelease (AAB)"] + } + } +} From 8438c54e3dcd81244a39cb9483a533f349435565 Mon Sep 17 00:00:00 2001 From: Devin Godage Date: Sun, 17 May 2026 02:21:03 +0530 Subject: [PATCH 6/6] [feature/custom-split-improve] created architectural html and json file using arch-skill --- .../from-arch-skill/architecture-data.json | 511 ++++++ docs/architecture/from-arch-skill/index.html | 1462 +++++++++++++++++ 2 files changed, 1973 insertions(+) create mode 100644 docs/architecture/from-arch-skill/architecture-data.json create mode 100644 docs/architecture/from-arch-skill/index.html diff --git a/docs/architecture/from-arch-skill/architecture-data.json b/docs/architecture/from-arch-skill/architecture-data.json new file mode 100644 index 00000000..4a3cd147 --- /dev/null +++ b/docs/architecture/from-arch-skill/architecture-data.json @@ -0,0 +1,511 @@ +{ + "title": "TipMate — Architecture & Flows", + "subtitle": "React Native 0.84.1 · iOS & Android · v1.7.0 — Pick a flow on the right to highlight the path through the system and see what happens at each step.", + "legend": [ + { "label": "Entry / Provider", "color": "bg-pink-500", "type": "entry" }, + { "label": "Navigation", "color": "bg-blue-500", "type": "nav" }, + { "label": "Screen", "color": "bg-yellow-400", "type": "screen" }, + { "label": "Hook", "color": "bg-green-500", "type": "hook" }, + { "label": "State / Reducer", "color": "bg-purple-500", "type": "reducer" }, + { "label": "Storage & Libs", "color": "bg-orange-500", "type": "external" } + ], + "columns": [ + { + "name": "BOOTSTRAP", + "items": [ + { "id": "index-js", "label": "index.js", "sublabel": "AppRegistry root", "type": "entry" }, + { + "id": "entrypoint", + "label": "Entrypoint.tsx", + "sublabel": "i18n init + RTL sync", + "type": "entry" + }, + { + "id": "gesture-root", + "label": "GestureHandlerRootView", + "sublabel": "gesture-handler", + "type": "entry" + }, + { + "id": "app-provider", + "label": "AppProvider", + "sublabel": "AppContext.tsx", + "type": "entry" + }, + { + "id": "app-navigator", + "label": "ApplicationNavigator", + "sublabel": "Theme init + BootSplash", + "type": "entry" + }, + { + "id": "nav-container", + "label": "NavigationContainer", + "sublabel": "@react-navigation/native", + "type": "entry" + } + ] + }, + { + "name": "NAVIGATION", + "items": [ + { + "id": "stack-nav", + "label": "StackNavigation", + "sublabel": "NativeStack push/modal", + "type": "nav" + }, + { + "id": "drawer-nav", + "label": "DrawerNavigation", + "sublabel": "Drawer type: slide", + "type": "nav" + } + ] + }, + { + "name": "SCREENS", + "items": [ + { + "id": "home-tip", + "label": "HomeTipScreen", + "sublabel": "Main calculator", + "type": "screen" + }, + { + "id": "saved-tips", + "label": "SavedTipsScreen", + "sublabel": "Tip history list", + "type": "screen" + }, + { + "id": "saved-detail", + "label": "SavedTipDetailScreen", + "sublabel": "Single tip view", + "type": "screen" + }, + { + "id": "settings", + "label": "SettingsScreen", + "sublabel": "App preferences", + "type": "screen" + }, + { + "id": "custom-split", + "label": "CustomSplitScreen", + "sublabel": "Per-person allocation", + "type": "screen" + }, + { + "id": "about-us", + "label": "AboutUsScreen", + "sublabel": "App info + links", + "type": "screen" + }, + { + "id": "license", + "label": "LicenseScreen", + "sublabel": "OSS licenses list", + "type": "screen" + }, + { + "id": "license-modal", + "label": "LicenseContentModal", + "sublabel": "Full license text", + "type": "screen" + } + ] + }, + { + "name": "HOOKS", + "items": [ + { + "id": "calc-equal", + "label": "calculateBillValues", + "sublabel": "Equal split calc", + "type": "hook" + }, + { + "id": "calc-custom", + "label": "calculateBillValuesCustomSplit", + "sublabel": "Custom split calc", + "type": "hook" + }, + { + "id": "use-save-tip", + "label": "useSaveTip", + "sublabel": "Save + dedup logic", + "type": "hook" + }, + { + "id": "use-share-preview", + "label": "useShareTipPreview", + "sublabel": "Share modal delegate", + "type": "hook" + }, + { + "id": "use-share-text", + "label": "useShareTipDetailsText", + "sublabel": "Text → share sheet", + "type": "hook" + }, + { + "id": "use-share-pdf", + "label": "useShareTipDetailsPDF", + "sublabel": "PDF → share sheet", + "type": "hook" + }, + { + "id": "persisted-reducer", + "label": "usePersistedReducer", + "sublabel": "State + AsyncStorage sync", + "type": "hook" + }, + { + "id": "async-storage-hooks", + "label": "asyncStorageHooks", + "sublabel": "Theme preference R/W", + "type": "hook" + }, + { + "id": "custom-split-people", + "label": "useCustomSplitPeople", + "sublabel": "People array CRUD", + "type": "hook" + }, + { + "id": "custom-split-validation", + "label": "useCustomSplitValidation", + "sublabel": "Allocation validation", + "type": "hook" + }, + { + "id": "use-split-presets", + "label": "useSplitPresets", + "sublabel": "Preset CRUD", + "type": "hook" + }, + { + "id": "use-theme-color", + "label": "useThemeColorCustomiser", + "sublabel": "Accent color overrides", + "type": "hook" + }, + { + "id": "use-animations", + "label": "useAnimations", + "sublabel": "Spring / fade / bounce", + "type": "hook" + } + ] + }, + { + "name": "STATE & REDUCERS", + "items": [ + { + "id": "app-context", + "label": "AppContext", + "sublabel": "Global state context", + "type": "reducer" + }, + { + "id": "root-reducer", + "label": "rootReducer", + "sublabel": "Combines all reducers", + "type": "reducer" + }, + { + "id": "tip-reducer", + "label": "tipReducer", + "sublabel": "tips: TipOptionState[]", + "type": "reducer" + }, + { + "id": "split-reducer", + "label": "splitReducer", + "sublabel": "splits: SplitOptionState[]", + "type": "reducer" + }, + { + "id": "currency-reducer", + "label": "currencyConfigReducer", + "sublabel": "currencyConfig", + "type": "reducer" + }, + { + "id": "saved-tips-reducer", + "label": "savedTipsReducer", + "sublabel": "savedTips: SavedTip[]", + "type": "reducer" + }, + { + "id": "language-reducer", + "label": "languageReducer", + "sublabel": "language + isRTL", + "type": "reducer" + }, + { + "id": "split-config-reducer", + "label": "splitConfigReducer", + "sublabel": "activeSplitConfig (session)", + "type": "reducer" + }, + { + "id": "saved-presets-reducer", + "label": "savedSplitPresetsReducer", + "sublabel": "savedSplitPresets[]", + "type": "reducer" + } + ] + }, + { + "name": "STORAGE & LIBS", + "items": [ + { + "id": "async-storage", + "label": "AsyncStorage", + "sublabel": "APPSTATE · ThemePreference", + "type": "external" + }, + { + "id": "unistyles", + "label": "react-native-unistyles", + "sublabel": "8 themes · Montserrat/Nunito", + "type": "external" + }, + { + "id": "i18next", + "label": "i18next", + "sublabel": "en · es · fr · ar(RTL) · si", + "type": "external" + }, + { + "id": "rn-share", + "label": "react-native-share", + "sublabel": "OS native share sheet", + "type": "external" + }, + { + "id": "html-to-pdf", + "label": "react-native-html-to-pdf", + "sublabel": "HTML → PDF conversion", + "type": "external" + }, + { + "id": "bootsplash", + "label": "react-native-bootsplash", + "sublabel": "Splash → fade dismiss", + "type": "external" + }, + { + "id": "reanimated", + "label": "react-native-reanimated", + "sublabel": "Spring / worklets", + "type": "external" + } + ] + } + ], + "connections": [ + { "number": 1, "from": "index-js", "to": "entrypoint", "label": "AppRegistry" }, + { "number": 2, "from": "entrypoint", "to": "gesture-root", "label": "wraps" }, + { "number": 3, "from": "gesture-root", "to": "app-provider", "label": "wraps" }, + { "number": 4, "from": "app-provider", "to": "app-navigator", "label": "wraps" }, + { "number": 5, "from": "app-navigator", "to": "nav-container", "label": "renders" }, + { "number": 6, "from": "nav-container", "to": "stack-nav", "label": "hosts" }, + { "number": 7, "from": "stack-nav", "to": "drawer-nav", "label": "MainStack" }, + { "number": 8, "from": "drawer-nav", "to": "home-tip", "label": "CalcTipScreen" }, + { "number": 9, "from": "drawer-nav", "to": "saved-tips", "label": "SavedTipScreen" }, + { "number": 10, "from": "drawer-nav", "to": "settings", "label": "SettingsScreen" }, + { "number": 11, "from": "drawer-nav", "to": "about-us", "label": "AboutUsScreen" }, + { "number": 12, "from": "stack-nav", "to": "saved-detail", "label": "push" }, + { "number": 13, "from": "stack-nav", "to": "custom-split", "label": "push" }, + { "number": 14, "from": "stack-nav", "to": "license", "label": "push" }, + { "number": 15, "from": "stack-nav", "to": "license-modal", "label": "modal" }, + { "number": 16, "from": "home-tip", "to": "calc-equal", "label": "useEffect" }, + { "number": 17, "from": "home-tip", "to": "calc-custom", "label": "if customActive" }, + { "number": 18, "from": "home-tip", "to": "use-save-tip", "label": "save tap" }, + { "number": 19, "from": "home-tip", "to": "use-share-preview", "label": "share tap" }, + { "number": 20, "from": "home-tip", "to": "custom-split", "label": "navigate" }, + { "number": 21, "from": "home-tip", "to": "saved-detail", "label": "after save" }, + { "number": 22, "from": "saved-tips", "to": "saved-detail", "label": "tap item" }, + { "number": 23, "from": "saved-detail", "to": "use-share-text", "label": "text share" }, + { "number": 24, "from": "saved-detail", "to": "use-share-pdf", "label": "PDF share" }, + { "number": 25, "from": "custom-split", "to": "custom-split-people", "label": "manages" }, + { "number": 26, "from": "custom-split", "to": "custom-split-validation", "label": "validates" }, + { "number": 27, "from": "custom-split", "to": "use-split-presets", "label": "presets" }, + { "number": 28, "from": "settings", "to": "use-theme-color", "label": "accent change" }, + { "number": 29, "from": "license", "to": "use-animations", "label": "scroll-to-top spring" }, + { "number": 30, "from": "use-share-preview", "to": "use-share-text", "label": "text branch" }, + { "number": 31, "from": "use-share-preview", "to": "use-share-pdf", "label": "PDF branch" }, + { "number": 32, "from": "use-save-tip", "to": "saved-tips-reducer", "label": "SAVE_TIP" }, + { + "number": 33, + "from": "custom-split", + "to": "split-config-reducer", + "label": "SET_ACTIVE_SPLIT_CONFIG" + }, + { + "number": 34, + "from": "custom-split", + "to": "saved-presets-reducer", + "label": "SAVE/UPDATE/DELETE_PRESET" + }, + { "number": 35, "from": "settings", "to": "tip-reducer", "label": "UPDATE_TIP_OPTIONS" }, + { "number": 36, "from": "settings", "to": "split-reducer", "label": "UPDATE_SPLIT_OPTIONS" }, + { "number": 37, "from": "settings", "to": "currency-reducer", "label": "UPDATE_CURRENCY_SIGN" }, + { "number": 38, "from": "settings", "to": "language-reducer", "label": "SET_LANGUAGE" }, + { "number": 39, "from": "saved-tips", "to": "saved-tips-reducer", "label": "DELETE_TIP" }, + { "number": 40, "from": "app-provider", "to": "app-context", "label": "provides" }, + { "number": 41, "from": "app-context", "to": "persisted-reducer", "label": "uses" }, + { "number": 42, "from": "persisted-reducer", "to": "root-reducer", "label": "wraps" }, + { "number": 43, "from": "root-reducer", "to": "tip-reducer", "label": "delegates" }, + { "number": 44, "from": "root-reducer", "to": "split-reducer", "label": "delegates" }, + { "number": 45, "from": "root-reducer", "to": "currency-reducer", "label": "delegates" }, + { "number": 46, "from": "root-reducer", "to": "saved-tips-reducer", "label": "delegates" }, + { "number": 47, "from": "root-reducer", "to": "language-reducer", "label": "delegates" }, + { "number": 48, "from": "root-reducer", "to": "split-config-reducer", "label": "delegates" }, + { "number": 49, "from": "root-reducer", "to": "saved-presets-reducer", "label": "delegates" }, + { "number": 50, "from": "persisted-reducer", "to": "async-storage", "label": "APPSTATE key" }, + { + "number": 51, + "from": "async-storage-hooks", + "to": "async-storage", + "label": "ThemePreference" + }, + { "number": 52, "from": "app-navigator", "to": "async-storage-hooks", "label": "reads theme" }, + { "number": 53, "from": "use-theme-color", "to": "unistyles", "label": "setTheme" }, + { "number": 54, "from": "app-navigator", "to": "unistyles", "label": "setTheme on boot" }, + { "number": 55, "from": "entrypoint", "to": "i18next", "label": "initializeI18n" }, + { "number": 56, "from": "app-navigator", "to": "bootsplash", "label": "hide(fade)" }, + { "number": 57, "from": "use-share-text", "to": "rn-share", "label": "open()" }, + { "number": 58, "from": "use-share-pdf", "to": "html-to-pdf", "label": "generate PDF" }, + { "number": 59, "from": "use-share-pdf", "to": "rn-share", "label": "open()" }, + { "number": 60, "from": "use-animations", "to": "reanimated", "label": "useSharedValue" } + ], + "flows": [ + { + "id": "app-boot", + "name": "App Bootstrap", + "description": "From index.js AppRegistry to BootSplash dismissal — provider tree, theme init, state hydration, i18n setup, and navigator ready.", + "path": [1, 2, 3, 4, 40, 41, 42, 5, 6, 7, 52, 51, 54, 55, 56] + }, + { + "id": "tip-calculation", + "name": "Tip Calculation", + "description": "User enters a bill amount on HomeTipScreen — the calculation hooks fire and StyledBillBox re-renders with per-person totals in real time.", + "path": [7, 8, 16, 17, 41, 42, 43, 44, 50] + }, + { + "id": "custom-split", + "name": "Custom Split Flow", + "description": "Opening CustomSplitScreen, building per-person fixed/percentage/remainder allocations, validating, saving as a preset, and applying back to the calculator.", + "path": [8, 20, 13, 25, 26, 27, 33, 34, 42, 48, 49, 50] + }, + { + "id": "save-share", + "name": "Save & Share Tip", + "description": "Saving a calculated tip to history with duplicate prevention, then sharing via native text message or generated PDF through the OS share sheet.", + "path": [8, 18, 32, 46, 50, 19, 30, 57, 31, 58, 59] + }, + { + "id": "settings-change", + "name": "Settings & Theme", + "description": "Changing app preferences — editing tip/split presets, switching currency or language, and applying a new accent color theme via Unistyles.", + "path": [7, 10, 35, 36, 37, 38, 43, 44, 45, 47, 50, 28, 53] + }, + { + "id": "saved-tips-browse", + "name": "Browse & Share Saved Tips", + "description": "Navigating to the tip history list, tapping a saved calculation for full detail, then sharing as formatted text or PDF.", + "path": [7, 9, 22, 12, 23, 24, 57, 58, 59] + } + ], + "steps": [ + { + "number": 1, + "title": "index.js → AppRegistry", + "description": "index.js calls AppRegistry.registerComponent('App', () => App). This is the native iOS/Android entry point — it hands control to Entrypoint.tsx as the root React component." + }, + { + "number": 2, + "title": "Entrypoint.tsx — i18n + RTL sync", + "description": "Before any UI renders, Entrypoint.tsx synchronously calls initializeI18n() to configure the i18next instance with all 5 locales (en, es, fr, ar, si) and applyRTLSync() to set right-to-left layout for Arabic via I18nManager.forceRTL. This must happen before the component tree mounts." + }, + { + "number": 3, + "title": "Provider tree mounts", + "description": "GestureHandlerRootView (react-native-gesture-handler) wraps the entire app, enabling swipe and gesture recognition. AppProvider (AppContext.tsx) mounts inside it, invoking usePersistedReducer which reads the 'APPSTATE' key from AsyncStorage and dispatches LOAD_PERSISTED_STATE to hydrate all 11 global state fields on first render." + }, + { + "number": 4, + "title": "ApplicationNavigator — theme boot", + "description": "ApplicationNavigator reads ThemePreference and ThemeColorOption from AsyncStorage via asyncStorageHooks. It calls UnistylesRuntime.setTheme(preferredTheme) and UnistylesRuntime.setAdaptiveThemes(false) to lock the user's chosen theme before the first frame paints — preventing a theme flash." + }, + { + "number": 5, + "title": "NavigationContainer ready → BootSplash dismiss", + "description": "NavigationContainer renders the full navigator tree — StackNavigation wraps DrawerNavigation (type: slide, 80px swipe edge). When NavigationContainer.onReady() fires, BootSplash.hide({ fade: true }) dismisses the splash screen with a cross-fade animation. The app is now interactive." + }, + { + "number": 6, + "title": "Bill amount triggers useEffect", + "description": "User types a bill amount into StyledTotalAmountInput on HomeTipScreen. The value updates local state (setUserInputBillAmount). A useEffect in HomeTipScreen watches bill amount, tip %, split count, and rounding method — it fires on every change to recompute results." + }, + { + "number": 7, + "title": "calculateBillValues runs", + "description": "For equal splits: calculateBillValues(amount, tipPct, splitCount, roundMethod) returns perPerson and overall totals with UP/DOWN/NONE rounding. For active custom splits: calculateBillValuesCustomSplit applies allocations in order — fixed amounts first, then percentages, then remainder (largest remainder method ensures exact penny distribution). StyledBillBox re-renders immediately." + }, + { + "number": 8, + "title": "Navigate to CustomSplitScreen", + "description": "User taps 'Custom' in StyledSplitOptions. HomeTipScreen calls navigation.navigate('CustomSplitScreen'). StackNavigation pushes the screen with the default ios_from_right animation (250ms duration)." + }, + { + "number": 9, + "title": "Build per-person split allocations", + "description": "useCustomSplitPeople manages the local IndividualSplit[] array — add, remove, rename, and change each person's allocationType ('fixed' | 'percentage' | 'remainder'). useCustomSplitValidation checks in real time: fixed totals ≤ bill amount, percentage total ≤ 100%, and at least one 'remainder' person OR exact full allocation before Save is enabled." + }, + { + "number": 10, + "title": "Save split config and return", + "description": "User taps Save. CustomSplitScreen dispatches SET_ACTIVE_SPLIT_CONFIG to splitConfigReducer with the IndividualSplit[] array. navigation.goBack() returns to HomeTipScreen. The useEffect detects activeSplitConfig change and runs calculateBillValuesCustomSplit() — StyledBillBox shows per-person individual amounts. Note: activeSplitConfig is intentionally NOT persisted — it resets on app restart." + }, + { + "number": 11, + "title": "Save tip to history", + "description": "User taps the Save button on HomeTipScreen. useSaveTip checks savedTips for a duplicate within the duplicatePreventionWindow (default 15 min) with the same bill amount and person count. If not a duplicate, it dispatches SAVE_TIP to savedTipsReducer. usePersistedReducer automatically serialises the updated savedTips array to AsyncStorage under 'APPSTATE' on every dispatch." + }, + { + "number": 12, + "title": "Share modal and native share sheet", + "description": "useShareTipPreview opens StyledSharePreviewModal (slide animation). User selects 'Text' or 'PDF'. Text path: useShareTipDetailsText formats the tip data as a plain-text message and calls react-native-share.open(). PDF path: useShareTipDetailsPDF renders an HTML template → react-native-html-to-pdf generates a PDF file → react-native-share.open() presents the native OS share sheet with the PDF attachment." + }, + { + "number": 13, + "title": "Edit tip & split option presets", + "description": "User edits default tip % or split count capsules via StyledTipOptionsEditMode / StyledSplitOptionsEditMode on SettingsScreen. Each confirmed value dispatches UPDATE_TIP_OPTIONS or UPDATE_SPLIT_OPTIONS to tipReducer / splitReducer. usePersistedReducer saves the entire state to AsyncStorage on every dispatch — next app launch will restore the custom presets." + }, + { + "number": 14, + "title": "Currency and language preference changes", + "description": "Currency change: StyledCurrencySelector dispatches UPDATE_CURRENCY_SIGN to currencyConfigReducer — all currency symbols on HomeTipScreen update immediately. Language change: StyledLanguageSelector dispatches SET_LANGUAGE to languageReducer, updating both language and isRTL fields. AppContext's useEffect syncs the new language code to i18next so all translation strings re-render. RTL layout changes require an app restart." + }, + { + "number": 15, + "title": "Theme accent color change", + "description": "User picks an accent swatch in StyledThemeBox. SettingsScreen calls useThemeColorCustomiser with the new accent option. The hook applies color overrides to the active Unistyles theme registry via UnistylesRuntime and persists the selection to AsyncStorage via asyncStorageHooks.setUserUpdatedThemeOption(). All styled components re-render with the new accent color immediately." + }, + { + "number": 16, + "title": "Browse and delete saved tips", + "description": "SavedTipsScreen renders the savedTips[] array via StyledSavedTipsList (FlatList). Each row shows bill amount, tip %, person count, and timestamp. Swipe-to-delete is implemented with ReanimatedSwipeable from react-native-gesture-handler. Swiping and confirming dispatches DELETE_TIP; clearing all dispatches CLEAR_ALL_TIPS — both persist automatically." + }, + { + "number": 17, + "title": "View saved tip detail and share", + "description": "Tapping a row navigates to SavedTipDetailScreen (push animation) with the full SavedTip object including overall totals, per-person breakdown, and custom split individual amounts if applicable. Share buttons invoke the same useShareTipDetailsText and useShareTipDetailsPDF hooks used from HomeTipScreen — producing identical text or PDF share outputs." + } + ] +} diff --git a/docs/architecture/from-arch-skill/index.html b/docs/architecture/from-arch-skill/index.html new file mode 100644 index 00000000..865a07bb --- /dev/null +++ b/docs/architecture/from-arch-skill/index.html @@ -0,0 +1,1462 @@ + + + + + + TipMate — Architecture & Flows + + + + + + +
+
+
+
+
+ +
+
+

TipMate — Architecture & Flows

+

System Architecture Visualization

+
+
+
+ +
+
+ + Interactive — click flows to highlight paths +
+ + + + +
+
+
+ +
+ + +
+

React Native 0.84.1 · iOS & Android · v1.7.0 — Pick a flow on the right to highlight the path through the system and see what happens at each step.

+
+ + +
+ LEGEND +
Entry / Provider
Navigation
Screen
Hook
State / Reducer
Storage & Libs
+
+ +
+ + +
+
+
+ + ARCHITECTURE COMPONENTS +
+
7 COLUMNS • 60 CONNECTIONS
+
+ + +
+
+ +
+
+ + +
+
+
+ + KEY CONNECTIONS +
+ Numbered paths used by flows +
+ +
+ +
+
+
+ + +
+ + +
+
+
+ + FLOWS +
+ +
+ +
+ +
+
+ + +
+
+
+ + STEPS +
+
+ +
+ +
+
+ +
+ +
+ + +
+

Generated on May 17, 2026 at 02:09 • Edit the accompanying JSON file and re-run the generator to update

+
+ +
+ + + + \ No newline at end of file