From e9179127139058e287e3fd8493ea460477ee9d33 Mon Sep 17 00:00:00 2001 From: Manuito Date: Sat, 7 Dec 2024 17:16:22 +0100 Subject: [PATCH 1/3] Fixed Profile Check widget not showing for NID URLs --- lib/utils/stats_calculator.dart | 30 ++++++++------ lib/utils/webview/webview_utils.dart | 44 +++++++++++++++++++++ lib/widgets/webviews/webview_full.dart | 54 +++++++++++++++----------- 3 files changed, 93 insertions(+), 35 deletions(-) create mode 100644 lib/utils/webview/webview_utils.dart diff --git a/lib/utils/stats_calculator.dart b/lib/utils/stats_calculator.dart index a2b9ea22b..3844f8c5d 100644 --- a/lib/utils/stats_calculator.dart +++ b/lib/utils/stats_calculator.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + class StatsCalculator { static final statsLevelTriggers = [2, 6, 11, 26, 31, 50, 71, 100]; static final statsCrimesTriggers = [100, 5000, 10000, 20000, 30000, 50000]; @@ -48,19 +50,23 @@ class StatsCalculator { required int? networth, required String? rank, }) { - final levelIndex = statsLevelTriggers.lastIndexWhere((x) => x <= level!) + 1; - final crimeIndex = statsCrimesTriggers.lastIndexWhere((x) => x <= criminalRecordTotal!) + 1; - final networthIndex = statsNetworthTriggers.lastIndexWhere((x) => x <= networth!) + 1; - var rankIndex = 0; - statsRanksTriggers.forEach((tornRank, index) { - if (rank!.contains(tornRank)) { - rankIndex = index; - } - }); + try { + final levelIndex = statsLevelTriggers.lastIndexWhere((x) => x <= level!) + 1; + final crimeIndex = statsCrimesTriggers.lastIndexWhere((x) => x <= criminalRecordTotal!) + 1; + final networthIndex = statsNetworthTriggers.lastIndexWhere((x) => x <= networth!) + 1; + var rankIndex = 0; + statsRanksTriggers.forEach((tornRank, index) { + if (rank!.contains(tornRank)) { + rankIndex = index; + } + }); - final finalIndex = rankIndex - levelIndex - crimeIndex - networthIndex - 1; - if (finalIndex >= 0 && finalIndex <= 6) { - return statsResults[finalIndex]; + final finalIndex = rankIndex - levelIndex - crimeIndex - networthIndex - 1; + if (finalIndex >= 0 && finalIndex <= 6) { + return statsResults[finalIndex]; + } + } catch (e) { + log(e.toString()); } return "unk"; } diff --git a/lib/utils/webview/webview_utils.dart b/lib/utils/webview/webview_utils.dart new file mode 100644 index 000000000..959feffcd --- /dev/null +++ b/lib/utils/webview/webview_utils.dart @@ -0,0 +1,44 @@ +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:html/parser.dart'; + +class WebViewUtils { + /// Checks for a specific element by [selector] in the HTML from [webViewController] + /// It will attempt to find the element(s) up to [maxSeconds] total, checking every [intervalSeconds] + /// + /// If [returnElements] is true, it returns a tuple-like result: + /// { + /// 'document': dom.Document, + /// 'elements': List + /// } + /// + /// Otherwise, it only returns the [dom.Document] + /// + /// If the element is never found, returns null + static Future?> waitForElement({ + required InAppWebViewController webViewController, + required String selector, + int maxSeconds = 6, + int intervalSeconds = 1, + bool returnElements = false, + }) async { + final int attempts = (maxSeconds / intervalSeconds).ceil(); + + for (int attempt = 0; attempt < attempts; attempt++) { + await Future.delayed(Duration(seconds: intervalSeconds)); + + final html = await webViewController.getHtml(); + final document = parse(html); + final elements = document.querySelectorAll(selector); + + if (elements.isNotEmpty) { + if (returnElements) { + return {'document': document, 'elements': elements}; + } else { + return {'document': document}; + } + } + } + + return null; + } +} diff --git a/lib/widgets/webviews/webview_full.dart b/lib/widgets/webviews/webview_full.dart index 3ae0dd532..a4782100f 100644 --- a/lib/widgets/webviews/webview_full.dart +++ b/lib/widgets/webviews/webview_full.dart @@ -64,6 +64,7 @@ import 'package:torn_pda/torn-pda-native/auth/native_user_provider.dart'; import 'package:torn_pda/utils/html_parser.dart' as pda_parser; import 'package:torn_pda/utils/js_snippets.dart'; import 'package:torn_pda/utils/shared_prefs.dart'; +import 'package:torn_pda/utils/webview/webview_utils.dart'; import 'package:torn_pda/widgets/bounties/bounties_widget.dart'; import 'package:torn_pda/widgets/chaining/chain_widget.dart'; import 'package:torn_pda/widgets/city/city_widget.dart'; @@ -2838,7 +2839,9 @@ class WebViewFullState extends State with WidgetsBindingObserver { _cityTriggered = false; _profileTriggered = false; _attackTriggered = false; - } else if (_currentUrl.contains("torn.com/profiles.php?XID=") && _profileTriggered) { + } else if ((_currentUrl.contains("torn.com/profiles.php?XID=") || + _currentUrl.contains("torn.com/profiles.php?NID=")) && + _profileTriggered) { _crimesTriggered = false; _gymTriggered = false; _vaultTriggered = false; @@ -4007,7 +4010,7 @@ class WebViewFullState extends State with WidgetsBindingObserver { } // ASSESS PROFILES - Future _assessProfileAttack({dom.Document? document, String pageTitle = ""}) async { + Future _assessProfileAttack({required dom.Document document, String pageTitle = ""}) async { if (mounted) { if (!_currentUrl.contains('loader.php?sid=attack&user2ID=') && !_currentUrl.contains('loader2.php?sid=getInAttack&user2ID=') && @@ -4046,29 +4049,34 @@ class WebViewFullState extends State with WidgetsBindingObserver { // When the URL is constructed with name instead of ID (e.g.: when the heart icon is pressed), // we capture the ID from the profile element, ensuring that it's besides the correct name - final RegExp regId = RegExp(r"php\?NID=([^&]+)"); - final matches = regId.allMatches(_currentUrl); - final String username = matches.elementAt(0).group(1)!; + final result = await WebViewUtils.waitForElement( + webViewController: webViewController!, + selector: 'a.profile-image-wrapper[href*="XID="]', + maxSeconds: 6, + intervalSeconds: 1, + returnElements: true, + ); - final dom.Element userInfoValue = document!.querySelector('div.user-info-value')!; - final String textContent = userInfoValue.querySelector('span.bold')!.text.trim(); - final RegExp regUsername = RegExp('($username' r')\s*\[([0-9]+)\]'); - final match = regUsername.firstMatch(textContent); - if (match != null) { - setState(() { - _profileAttackWidget = ProfileAttackCheckWidget( - key: UniqueKey(), - profileId: int.parse(match.group(2)!), - apiKey: _userProvider?.basic?.userApiKey ?? "", - profileCheckType: ProfileCheckType.profile, - themeProvider: _themeProvider, - ); - }); - } - } else { - userId = 0; + if (result == null) throw ("No html tag found"); + + document = result['document'] as dom.Document; + final elements = result['elements'] as List; + final anchor = elements.first; + final match = RegExp(r"XID=([^&]+)").firstMatch(anchor.attributes['href']!)!; + userId = int.parse(match.group(1)!); + + setState(() { + _profileAttackWidget = ProfileAttackCheckWidget( + key: UniqueKey(), + profileId: userId, + apiKey: _userProvider?.basic?.userApiKey ?? "", + profileCheckType: ProfileCheckType.profile, + themeProvider: _themeProvider, + ); + }); } - } catch (e) { + } catch (e, trace) { + log("Issue locating NID user ID: $e, $trace", name: "Profile Check"); userId = 0; } } else if (_currentUrl.contains('loader.php?sid=attack&user2ID=') || From 22085a9b42f2135ec7ed70cb378cd514228b7071 Mon Sep 17 00:00:00 2001 From: Manuito Date: Sat, 7 Dec 2024 17:16:33 +0100 Subject: [PATCH 2/3] Fixed tab bar reorder --- lib/widgets/webviews/circular_menu/circular_menu_tabs.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/webviews/circular_menu/circular_menu_tabs.dart b/lib/widgets/webviews/circular_menu/circular_menu_tabs.dart index cebecc33d..ca5a21853 100644 --- a/lib/widgets/webviews/circular_menu/circular_menu_tabs.dart +++ b/lib/widgets/webviews/circular_menu/circular_menu_tabs.dart @@ -138,7 +138,7 @@ class CircularMenuTabsState extends State with SingleTickerPro if (!mounted) return; _onTabTapped(context); }, - child: ReorderableDragStartListener( + child: ReorderableDelayedDragStartListener( index: widget.tabIndex, child: Container( alignment: Alignment.center, From dcb739a9201f3f05f6731ad873e4b4ad5516acf2 Mon Sep 17 00:00:00 2001 From: Manuito Date: Sat, 7 Dec 2024 17:17:45 +0100 Subject: [PATCH 3/3] Beta versioning --- android/app/build.gradle | 4 ++-- ios/Runner.xcodeproj/project.pbxproj | 12 ++++++------ lib/main.dart | 6 +++--- lib/utils/changelog.dart | 5 +++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index ddb0eb34d..15ef83505 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -16,12 +16,12 @@ if (localPropertiesFile.exists()) { def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { - flutterVersionCode = '465' + flutterVersionCode = '466' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { - flutterVersionName = '3.6.1' + flutterVersionName = '3.6.2' } def keystoreProperties = new Properties() diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8b41bbb59..6c7845f63 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -549,7 +549,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 465; + CURRENT_PROJECT_VERSION = 466; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 53KVJRJS99; ENABLE_BITCODE = NO; @@ -567,7 +567,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 3.6.1; + MARKETING_VERSION = 3.6.2; PRODUCT_BUNDLE_IDENTIFIER = com.manuito.tornpda; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -698,7 +698,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 465; + CURRENT_PROJECT_VERSION = 466; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 53KVJRJS99; ENABLE_BITCODE = NO; @@ -716,7 +716,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 3.6.1; + MARKETING_VERSION = 3.6.2; PRODUCT_BUNDLE_IDENTIFIER = com.manuito.tornpda; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -739,7 +739,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 465; + CURRENT_PROJECT_VERSION = 466; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 53KVJRJS99; ENABLE_BITCODE = NO; @@ -757,7 +757,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 3.6.1; + MARKETING_VERSION = 3.6.2; PRODUCT_BUNDLE_IDENTIFIER = com.manuito.tornpda; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/lib/main.dart b/lib/main.dart index da56368f9..30b2b3a3c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -68,9 +68,9 @@ import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:workmanager/workmanager.dart'; // TODO (App release) -const String appVersion = '3.6.1'; -const String androidCompilation = '465'; -const String iosCompilation = '465'; +const String appVersion = '3.6.2'; +const String androidCompilation = '466'; +const String iosCompilation = '466'; // TODO (App release) // Note: if using Windows and calling HTTP functions, we need to change the URL in [firebase_functions.dart] diff --git a/lib/utils/changelog.dart b/lib/utils/changelog.dart index 3d537fe15..3e7ff6591 100644 --- a/lib/utils/changelog.dart +++ b/lib/utils/changelog.dart @@ -51,11 +51,12 @@ class ChangeLogState extends State { void _createItems() { final itemList = []; - // v3.6.1 - Build 465 - 06/11/2024 + // v3.6.2 - Build 466 - 07/11/2024 itemList.add( ChangeLogItem() - ..version = 'Torn PDA v3.6.1' + ..version = 'Torn PDA v3.6.2' ..date = '8 DEC 2024' + ..infoString = 'Hotfix: resolved an issue affecting the reordering of tabs' ..features = [ "Added channel info to chat notifications", "Fixed notifications for own chat messages",