From 4183ebd8e6a3271f0c2508734db3e3ac72b2fd4b Mon Sep 17 00:00:00 2001 From: otsmr Date: Sat, 22 Aug 2026 20:31:38 +0200 Subject: [PATCH] moving more dependencies into the subrepo --- .gitignore | 1 + dependencies | 2 +- dependencies.py | 204 +++++++++++++++++ dependencies.yaml | 109 +++++++++ lib/src/model/json/userdata.model.dart | 1 - lib/src/model/json/userdata.model.g.dart | 2 - lib/src/utils/avatars.dart | 43 ++-- .../components/cached_network_image.dart | 148 ++++++++++++ .../layers/filters/image_filter.dart | 2 +- .../link_preview/cards/custom.card.dart | 2 +- .../link_preview/cards/mastodon.card.dart | 2 +- .../link_preview/cards/twitter.card.dart | 2 +- .../link_preview/cards/youtube.card.dart | 2 +- .../message_context_menu.dart | 2 +- .../views/settings/help/credits.view.dart | 2 +- .../visual/views/settings/help/news.view.dart | 2 +- .../settings/profile/modify_avatar.view.dart | 10 +- pubspec.lock | 214 +++++------------- pubspec.yaml | 165 ++++++++------ test/features/link_parser_test.dart | 3 +- .../elements/better_text_element_test.dart | 2 +- 21 files changed, 656 insertions(+), 264 deletions(-) create mode 100644 dependencies.py create mode 100644 dependencies.yaml create mode 100644 lib/src/visual/components/cached_network_image.dart diff --git a/.gitignore b/.gitignore index f6e6d121..10e2410d 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ devtools_options.yaml rust/target rust_dependencies/target fastlane/repo/status/running.json +.cache/ \ No newline at end of file diff --git a/dependencies b/dependencies index 32882a31..d1d70e15 160000 --- a/dependencies +++ b/dependencies @@ -1 +1 @@ -Subproject commit 32882a31904a0c1ffd38974bf5158ceee0f3ecb7 +Subproject commit d1d70e1559a67bd5c4336547d215a03260153b00 diff --git a/dependencies.py b/dependencies.py new file mode 100644 index 00000000..f78827ea --- /dev/null +++ b/dependencies.py @@ -0,0 +1,204 @@ +import yaml +import os +import shutil +import subprocess +import argparse +import sys + +def print_blue(text): + BLUE = '\x1b[34m' + RESET = '\x1b[0m' + print(f"{BLUE}{text}{RESET}") + +def print_yellow(text): + YELLOW = '\x1b[33m' + RESET = '\x1b[0m' + print(f"{YELLOW}{text}{RESET}") + +def get_git_head(repo_path='.'): + result = subprocess.run( + ['git', 'rev-parse', 'HEAD'], + cwd=repo_path, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=False + ) + if result.returncode != 0: + raise RuntimeError(f"git error: {result.stderr.strip()}") + return result.stdout.strip() + +def get_default_branch(repo_path='.'): + result = subprocess.run( + ['git', 'symbolic-ref', 'refs/remotes/origin/HEAD'], + cwd=repo_path, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=False + ) + if result.returncode == 0: + return result.stdout.strip().split('/')[-1] + return 'main' + +def integrate_package(folder_name, data, cache_dir, out_dir): + repo_url = data['git'] + keep_list = ["lib", "LICENSE", "pubspec.yaml", "android", "ios", "darwin"] + if "keep" in data: + keep_list += [item.rstrip('/') for item in data['keep']] + + print(f"Processing {folder_name}...") + + cache_path = os.path.join(cache_dir, folder_name) + if not os.path.exists(cache_path): + subprocess.run(["git", "clone", repo_url, cache_path], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + else: + result = subprocess.run(["git", "fetch", "--all"], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cache_path) + if result.returncode != 0: + print_yellow(f"Warning: Could not fetch updates for {folder_name}. You might be offline.") + + if "commit" in data: + commit_hash = data["commit"] + subprocess.run(["git", "checkout", commit_hash], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cache_path) + elif "tag" in data: + tag_name = data["tag"] + subprocess.run(["git", "checkout", tag_name], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cache_path) + else: + print_yellow(f"Warning: No commit or tag specified for {folder_name}. Using default branch.") + default_branch = get_default_branch(cache_path) + subprocess.run(["git", "checkout", default_branch], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cache_path) + subprocess.run(["git", "pull"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cache_path) + last_commit_hash = get_git_head(cache_path) + data["commit"] = last_commit_hash + print_blue(f"Recorded commit {last_commit_hash} for {folder_name}") + + results = [] # List of (pkg_name, version) + + if "subpackages" in data: + packages_to_extract = data["subpackages"] + else: + packages_to_extract = [{"name": folder_name, "path": data.get("path", "")}] + + for pkg in packages_to_extract: + pkg_name = pkg["name"] + subpath = pkg.get("path", "") + + out_path = os.path.join(out_dir, pkg_name) + if os.path.exists(out_path): + shutil.rmtree(out_path) + os.makedirs(out_path) + + package_src_path = os.path.join(cache_path, subpath) if subpath else cache_path + + for item in keep_list: + src_item = os.path.join(package_src_path, item) + dst_item = os.path.join(out_path, item) + + if os.path.exists(src_item): + os.makedirs(os.path.dirname(dst_item), exist_ok=True) + if os.path.isdir(src_item): + shutil.copytree(src_item, dst_item, dirs_exist_ok=True) + else: + shutil.copy2(src_item, dst_item) + + version = "any" + try: + pubspec_path = os.path.join(package_src_path, "pubspec.yaml") + if os.path.exists(pubspec_path): + with open(pubspec_path, "r") as f: + ps = yaml.safe_load(f) + if ps and isinstance(ps, dict): + version = ps.get("version", "any") + except Exception as e: + print_yellow(f"Warning: Could not read version from {pkg_name}/pubspec.yaml") + + results.append((pkg_name, version)) + + return results + +def main(): + parser = argparse.ArgumentParser(description="Update specific or all repositories.") + parser.add_argument('repo_name', nargs='?', default=None, help="Name of the repository to update (optional)") + args = parser.parse_args() + + with open("dependencies.yaml", "r") as f: + config = yaml.safe_load(f) + + cache_dir = config.get('cache', './.cache') + out_dir = config.get('outdir', './dependencies') + deps = config.get('dependencies', {}) + + if not os.path.exists(cache_dir): + os.makedirs(cache_dir) + if not os.path.exists(out_dir): + os.makedirs(out_dir) + + repos_to_update = [args.repo_name] if args.repo_name else list(deps.keys()) + + pubspec_overrides = [] + pubspec_deps = [] + + def process_deps_recursive(deps_dict, to_update=None): + for name, data in deps_dict.items(): + if to_update is None or name in to_update: + extracted_packages = integrate_package(name, data, cache_dir, out_dir) + for pkg_name, version in extracted_packages: + pubspec_overrides.append(f" {pkg_name}:\n path: {out_dir}/{pkg_name}\n") + if version and version != "any": + pubspec_deps.append(f" {pkg_name}: ^{version}\n") + else: + pubspec_deps.append(f" {pkg_name}: any\n") + if "dependencies" in data: + # If we updated the parent, we should update children? Or if no args provided, update all. + # Actually, the original logic updated children automatically if parent is updated. + process_deps_recursive(data["dependencies"], None if (to_update is None or name in to_update) else []) + + process_deps_recursive(deps, repos_to_update if args.repo_name else None) + + def sort_dependencies(d): + sorted_d = {k: d[k] for k in sorted(d.keys())} + for k, v in sorted_d.items(): + if "dependencies" in v and isinstance(v["dependencies"], dict): + v["dependencies"] = sort_dependencies(v["dependencies"]) + return sorted_d + + if "dependencies" in config: + config["dependencies"] = sort_dependencies(config["dependencies"]) + + with open("dependencies.yaml", "w") as f: + yaml.safe_dump(config, f, sort_keys=False) + + # Update pubspec.yaml + if not args.repo_name: + with open("pubspec.yaml", "r") as f: + pubspec_lines = f.readlines() + + start_marker_overrides = "## --- Start Managed Dependency Overrides ---" + end_marker_overrides = "## --- End Managed Dependency Overrides ---" + + start_marker_deps = "## --- Start Managed Dependencies ---" + end_marker_deps = "## --- End Managed Dependencies ---" + + try: + start_idx_overrides = next(i for i, line in enumerate(pubspec_lines) if line.strip() == start_marker_overrides) + end_idx_overrides = next(i for i, line in enumerate(pubspec_lines) if line.strip() == end_marker_overrides) + + # Update overrides section + new_lines = pubspec_lines[:start_idx_overrides + 1] + pubspec_overrides + pubspec_lines[end_idx_overrides:] + + # Now find the deps markers in the updated lines + start_idx_deps = next(i for i, line in enumerate(new_lines) if line.strip() == start_marker_deps) + end_idx_deps = next(i for i, line in enumerate(new_lines) if line.strip() == end_marker_deps) + + # Update dependencies section + final_lines = new_lines[:start_idx_deps + 1] + pubspec_deps + new_lines[end_idx_deps:] + + with open("pubspec.yaml", "w") as f: + f.writelines(final_lines) + print_blue("Updated pubspec.yaml successfully.") + except ValueError as e: + print_yellow("Error: Could not find professional markers in pubspec.yaml.") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/dependencies.yaml b/dependencies.yaml new file mode 100644 index 00000000..5e695d6f --- /dev/null +++ b/dependencies.yaml @@ -0,0 +1,109 @@ +cache: ./.cache +outdir: ./dependencies +dependencies: + audio_waveforms: + git: https://github.com/SimformSolutionsPvtLtd/audio_waveforms.git + tag: 2.0.2 + avatar_maker: + git: https://github.com/RoadTripMoustache/avatar_maker.git + tag: 1.5.0 + keep: + - assets/icons + blurhash_dart: + git: https://github.com/justacid/blurhash-dart.git + tag: v1.2.1 + exif: + git: https://github.com/bigflood/dartexif.git + dependencies: + sprintf: + git: https://github.com/Naddiseo/dart-sprintf.git + commit: f1e74f2f4c339d983f9d011b4ba1df4ec8b8857c + commit: bf170d5639f0b6fcb0947060cf8bd7b623df9069 + flutter_blurhash: + git: https://github.com/fluttercommunity/flutter_blurhash.git + commit: c5cdec4986432e835bb91f5ce00564534450cdc7 + flutter_markdown_plus: + git: https://github.com/foresightmobile/flutter_markdown_plus.git + commit: dc1185c933fbf9dba559ef6c91586ff1503be3ee + flutter_packages: + git: https://github.com/otsmr/flutter-packages.git + subpackages: + - name: video_player + path: packages/video_player/video_player + - name: video_player_android + path: packages/video_player/video_player_android + - name: video_player_avfoundation + path: packages/video_player/video_player_avfoundation + - name: camera_android_camerax + path: packages/camera/camera_android_camerax + commit: bb0e9f500828f3117a6ea96b898eaee3710e43d9 + flutter_sharing_intent: + git: https://github.com/bhagat-techind/flutter_sharing_intent.git + commit: aa1672f547d6579585fa27df0b28ffa2a2544aaa + hand_signature: + git: https://github.com/RomanBase/hand_signature.git + commit: 1beedb164d093643365b0832277c377353c7464f + hashlib: + git: https://github.com/bitanon/hashlib.git + replace: + - - abstract class MACHashBase + - abstract mixin class MACHashBase + dependencies: + hashlib_codecs: + git: https://github.com/bitanon/hashlib_codecs.git + commit: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b + commit: bc9c2f8dd7bbc72f47ccab0ce1111d40259c49bc + image: + git: https://github.com/brendan-duncan/image.git + tag: v4.9.2 + introduction_screen: + git: https://github.com/Pyozer/introduction_screen.git + dependencies: + dots_indicator: + git: https://github.com/Pyozer/dots_indicator.git + commit: 508f5883ac79bdbc10254092de3f28f571d261cd + commit: 4a90e557630b28834479ed9c64a9d2d0185d8e48 + libsignal_protocol_dart: + git: https://github.com/MixinNetwork/libsignal_protocol_dart.git + dependencies: + adaptive_number: + git: https://github.com/lemoony/adaptive_number_dart + commit: ea9178fdd4d82ac45cf0ec966ac870dae661124f + ed25519_edwards: + git: https://github.com/Tougee/ed25519.git + commit: 7353ba759ea9f4646cbf481c2ef949625c8ce4cf + optional: + git: https://github.com/tonio-ramirez/optional.dart.git + commit: 71c638891ce4f2aff35c7387727989f31f9d877d + pointycastle: + git: https://github.com/bcgit/pc-dart.git + commit: bbd8569f68a7fccbdf0b92d0b44a9219c126c8dd + x25519: + git: https://github.com/Tougee/curve25519.git + commit: ecb1d357714537bba6e276ef45f093846d4beaee + commit: c95a1586057022acdbb9c76b1692d94cc549bcc7 + lottie: + git: https://github.com/xvrh/lottie-flutter.git + commit: 127bc29f2c6bd8b32ec4064a09e54e6b31cd0a88 + mutex: + git: https://github.com/hoylen/dart-mutex.git + commit: 84ca903a3ac863735e3228c75a212133621f680f + photo_view: + git: https://github.com/bluefireteam/photo_view.git + commit: a13ca2fc387a3fb1276126959e092c44d0029987 + pro_video_editor: + git: https://github.com/hm21/pro_video_editor.git + tag: v2.11.3 + qr_flutter: + git: https://github.com/theyakka/qr.flutter.git + dependencies: + qr: + git: https://github.com/kevmoo/qr.dart.git + commit: 7b1e9665ca976f484e7975356cf26fc7a0ccf02e + commit: d5e7206396105d643113618290bbcc755d05f492 + restart_app: + git: https://github.com/gabrimatic/restart_app + commit: 66897cb67e235bab85421647bfae036acb4438cb + screen_protector: + git: https://github.com/prongbang/screen_protector.git + commit: 019c04d622d7b610d2903d3a347edc3ba76a6ed0 diff --git a/lib/src/model/json/userdata.model.dart b/lib/src/model/json/userdata.model.dart index 875ab4f8..82f15b09 100644 --- a/lib/src/model/json/userdata.model.dart +++ b/lib/src/model/json/userdata.model.dart @@ -23,7 +23,6 @@ class UserData { String username; String displayName; String? avatarSvg; - String? avatarJson; @JsonKey(defaultValue: 0) int appVersion = 0; diff --git a/lib/src/model/json/userdata.model.g.dart b/lib/src/model/json/userdata.model.g.dart index b1449373..6cd7e9e8 100644 --- a/lib/src/model/json/userdata.model.g.dart +++ b/lib/src/model/json/userdata.model.g.dart @@ -16,7 +16,6 @@ UserData _$UserDataFromJson(Map json) => appVersion: (json['appVersion'] as num?)?.toInt() ?? 0, ) ..avatarSvg = json['avatarSvg'] as String? - ..avatarJson = json['avatarJson'] as String? ..avatarCounter = (json['avatarCounter'] as num?)?.toInt() ?? 0 ..isDeveloper = json['isDeveloper'] as bool? ?? false ..deviceId = (json['deviceId'] as num?)?.toInt() ?? 0 @@ -129,7 +128,6 @@ Map _$UserDataToJson(UserData instance) => { 'username': instance.username, 'displayName': instance.displayName, 'avatarSvg': instance.avatarSvg, - 'avatarJson': instance.avatarJson, 'appVersion': instance.appVersion, 'avatarCounter': instance.avatarCounter, 'isDeveloper': instance.isDeveloper, diff --git a/lib/src/utils/avatars.dart b/lib/src/utils/avatars.dart index 8bfe349d..ec65ff6f 100644 --- a/lib/src/utils/avatars.dart +++ b/lib/src/utils/avatars.dart @@ -5,6 +5,7 @@ import 'dart:ui' as ui; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:mutex/mutex.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/utils/log.dart'; @@ -114,29 +115,33 @@ File currentUserAvatarFile(int avatarCounter) { return File('${avatarsDirectory.path}/user_$avatarCounter.png'); } +final _avatarMutex = Mutex(); + Future getUserAvatar() async { if (userService.currentUser.avatarSvg == null) { return null; } - final avatarCounter = userService.currentUser.avatarCounter; - final file = currentUserAvatarFile(avatarCounter); - if (file.existsSync()) { + return _avatarMutex.protect(() async { + final avatarCounter = userService.currentUser.avatarCounter; + final file = currentUserAvatarFile(avatarCounter); + if (file.existsSync()) { + return file.path; + } + + final pictureInfo = await vg.loadPicture( + SvgStringLoader(userService.currentUser.avatarSvg!), + null, + ); + + final image = await pictureInfo.picture.toImage(270, 300); + + final byteData = await image.toByteData(format: ui.ImageByteFormat.png); + final pngBytes = byteData!.buffer.asUint8List(); + + await file.writeAsBytes(pngBytes, flush: true); + pictureInfo.picture.dispose(); + return file.path; - } - - final pictureInfo = await vg.loadPicture( - SvgStringLoader(userService.currentUser.avatarSvg!), - null, - ); - - final image = await pictureInfo.picture.toImage(270, 300); - - final byteData = await image.toByteData(format: ui.ImageByteFormat.png); - final pngBytes = byteData!.buffer.asUint8List(); - - await file.writeAsBytes(pngBytes); - pictureInfo.picture.dispose(); - - return file.path; + }); } diff --git a/lib/src/visual/components/cached_network_image.dart b/lib/src/visual/components/cached_network_image.dart new file mode 100644 index 00000000..2bfef9fb --- /dev/null +++ b/lib/src/visual/components/cached_network_image.dart @@ -0,0 +1,148 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:crypto/crypto.dart'; +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; + +class CachedNetworkImage extends StatefulWidget { + const CachedNetworkImage({ + required this.imageUrl, + super.key, + this.width, + this.height, + this.fit, + this.placeholder, + this.errorWidget, + }); + final String imageUrl; + final double? width; + final double? height; + final BoxFit? fit; + final Widget Function(BuildContext, String)? placeholder; + final Widget Function(BuildContext, String, dynamic)? errorWidget; + + @override + State createState() => _CachedNetworkImageState(); +} + +class _CachedNetworkImageState extends State { + File? _imageFile; + bool _isLoading = true; + dynamic _error; + static bool _hasCleanedUp = false; + + @override + void initState() { + super.initState(); + _loadImage(); + } + + @override + void didUpdateWidget(CachedNetworkImage oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.imageUrl != widget.imageUrl) { + _loadImage(); + } + } + + Future _loadImage() async { + if (!mounted) return; + setState(() { + _isLoading = true; + _error = null; + }); + + try { + final cacheDir = await getTemporaryDirectory(); + final urlHash = md5.convert(utf8.encode(widget.imageUrl)).toString(); + final file = File('${cacheDir.path}/custom_cached_image_$urlHash'); + + if (!_hasCleanedUp) { + _hasCleanedUp = true; + unawaited(_cleanupOldFiles(cacheDir)); // Run asynchronously + } + + if (file.existsSync()) { + if (mounted) { + setState(() { + _imageFile = file; + _isLoading = false; + }); + } + return; + } + + final client = HttpClient(); + final request = await client.getUrl(Uri.parse(widget.imageUrl)); + final response = await request.close(); + + if (response.statusCode == 200) { + await response.pipe(file.openWrite()); + if (mounted) { + setState(() { + _imageFile = file; + _isLoading = false; + }); + } + } else { + throw Exception('Failed to load image: ${response.statusCode}'); + } + } catch (e) { + if (mounted) { + setState(() { + _error = e; + _isLoading = false; + }); + } + } + } + + Future _cleanupOldFiles(Directory cacheDir) async { + try { + final files = cacheDir.listSync(); + final now = DateTime.now(); + for (final f in files) { + if (f is File && f.path.contains('custom_cached_image_')) { + final stat = f.statSync(); + if (now.difference(stat.modified).inDays >= 7) { + await f.delete(); + } + } + } + } catch (_) { + // Ignore cleanup errors + } + } + + @override + Widget build(BuildContext context) { + if (_isLoading) { + if (widget.placeholder != null) { + return widget.placeholder!(context, widget.imageUrl); + } + return SizedBox(width: widget.width, height: widget.height); + } + + if (_error != null || _imageFile == null) { + if (widget.errorWidget != null) { + return widget.errorWidget!(context, widget.imageUrl, _error); + } + return SizedBox(width: widget.width, height: widget.height); + } + + return Image.file( + _imageFile!, + width: widget.width, + height: widget.height, + fit: widget.fit, + errorBuilder: (context, error, stackTrace) { + if (widget.errorWidget != null) { + return widget.errorWidget!(context, widget.imageUrl, error); + } + return SizedBox(width: widget.width, height: widget.height); + }, + ); + } +} diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/image_filter.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/image_filter.dart index 8c6bfdcd..99c1ecb8 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/image_filter.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/image_filter.dart @@ -1,5 +1,5 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:twonly/src/visual/components/cached_network_image.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart'; class ImageFilter extends StatelessWidget { diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/custom.card.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/custom.card.dart index bf30b80e..973afb02 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/custom.card.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/custom.card.dart @@ -1,7 +1,7 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/cached_network_image.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/link_preview/parser/base.dart'; class CustomLinkCard extends StatelessWidget { diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/mastodon.card.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/mastodon.card.dart index 40041bdb..a02ebce2 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/mastodon.card.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/mastodon.card.dart @@ -1,7 +1,7 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/visual/components/cached_network_image.dart'; import 'package:twonly/src/visual/loader/three_rotating_dots.loader.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/link_preview/parser/base.dart'; diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/twitter.card.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/twitter.card.dart index 8bdc763c..4d051dfa 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/twitter.card.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/twitter.card.dart @@ -1,7 +1,7 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/visual/components/cached_network_image.dart'; // Assuming the same Metadata import structure import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/link_preview/parser/base.dart'; diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/youtube.card.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/youtube.card.dart index 4c253125..b58f9a69 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/youtube.card.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/link_preview/cards/youtube.card.dart @@ -1,7 +1,7 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/visual/components/cached_network_image.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/link_preview/parser/base.dart'; class YouTubePostCard extends StatelessWidget { diff --git a/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart b/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart index fbd259c2..085a779e 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart @@ -19,8 +19,8 @@ import 'package:twonly/src/visual/components/emoji_picker.bottom.dart'; import 'package:twonly/src/visual/context_menu/context_menu.helper.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart'; -import 'package:twonly/src/visual/views/chats/message_info.view.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_list_entry.dart'; +import 'package:twonly/src/visual/views/chats/message_info.view.dart'; import 'package:twonly/src/visual/views/memories/synchronized_viewer.view.dart'; class MessageContextMenu extends StatelessWidget { diff --git a/lib/src/visual/views/settings/help/credits.view.dart b/lib/src/visual/views/settings/help/credits.view.dart index e015ed94..94ad3d52 100644 --- a/lib/src/visual/views/settings/help/credits.view.dart +++ b/lib/src/visual/views/settings/help/credits.view.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/cached_network_image.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/src/visual/views/settings/help/news.view.dart b/lib/src/visual/views/settings/help/news.view.dart index c0ad7a66..f15e33ff 100644 --- a/lib/src/visual/views/settings/help/news.view.dart +++ b/lib/src/visual/views/settings/help/news.view.dart @@ -1,8 +1,8 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/components/cached_network_image.dart'; import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/src/visual/views/settings/profile/modify_avatar.view.dart b/lib/src/visual/views/settings/profile/modify_avatar.view.dart index 7b533a78..c75a476e 100644 --- a/lib/src/visual/views/settings/profile/modify_avatar.view.dart +++ b/lib/src/visual/views/settings/profile/modify_avatar.view.dart @@ -31,10 +31,9 @@ class _ModifyAvatarViewState extends State { } } - Future updateUserAvatar(String json, String svg) async { + Future updateUserAvatar(String svg) async { await UserService.update( (u) => u - ..avatarJson = json ..avatarSvg = svg ..avatarCounter = u.avatarCounter + 1, ); @@ -104,9 +103,8 @@ class _ModifyAvatarViewState extends State { Future storeAvatarAndExit() async { await _avatarMakerController.saveAvatarSVG(); - final json = _avatarMakerController.getJsonOptionsSync(); final svg = _avatarMakerController.getAvatarSVGSync(); - await updateUserAvatar(json, svg); + await updateUserAvatar(svg); if (mounted) { Navigator.pop(context, true); } @@ -118,8 +116,8 @@ class _ModifyAvatarViewState extends State { canPop: false, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; - if (_avatarMakerController.getJsonOptionsSync() != - userService.currentUser.avatarJson) { + if (_avatarMakerController.getAvatarSVGSync() != + userService.currentUser.avatarSvg) { // there where changes final shouldPop = await _showBackDialog() ?? false; if (context.mounted && shouldPop) { diff --git a/pubspec.lock b/pubspec.lock index 90bbb4de..08733f02 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -18,7 +18,7 @@ packages: source: hosted version: "1.3.68" adaptive_number: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/adaptive_number" relative: true @@ -91,19 +91,17 @@ packages: audio_waveforms: dependency: "direct main" description: - name: audio_waveforms - sha256: "03b3430ecf430a2e90185518a228c02be3d26653c62dd931e50d671213a6dbc8" - url: "https://pub.dev" - source: hosted + path: "dependencies/audio_waveforms" + relative: true + source: path version: "2.0.2" avatar_maker: dependency: "direct main" description: - name: avatar_maker - sha256: ca182e33343846427da68fc226325f630063da2de2f0bdb49e683f0c843b80c2 - url: "https://pub.dev" - source: hosted - version: "0.4.0" + path: "dependencies/avatar_maker" + relative: true + source: path + version: "1.5.0" background_downloader: dependency: "direct main" description: @@ -115,10 +113,9 @@ packages: blurhash_dart: dependency: "direct main" description: - name: blurhash_dart - sha256: "43955b6c2e30a7d440028d1af0fa185852f3534b795cc6eb81fbf397b464409f" - url: "https://pub.dev" - source: hosted + path: "dependencies/blurhash_dart" + relative: true + source: path version: "1.2.1" boolean_selector: dependency: transitive @@ -184,30 +181,6 @@ packages: url: "https://pub.dev" source: hosted version: "8.12.5" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" - url: "https://pub.dev" - source: hosted - version: "3.4.1" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" - url: "https://pub.dev" - source: hosted - version: "4.1.1" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" - url: "https://pub.dev" - source: hosted - version: "1.3.1" camera: dependency: "direct main" description: @@ -217,14 +190,12 @@ packages: source: hosted version: "0.12.0+1" camera_android_camerax: - dependency: "direct overridden" + dependency: "direct main" description: - path: "packages/camera/camera_android_camerax" - ref: e83fb3a27d4da2c37a3c8acbf2486283965b4f69 - resolved-ref: e83fb3a27d4da2c37a3c8acbf2486283965b4f69 - url: "https://github.com/otsmr/flutter-packages.git" - source: git - version: "0.7.1+2" + path: "dependencies/camera_android_camerax" + relative: true + source: path + version: "0.7.4+6" camera_avfoundation: dependency: transitive description: @@ -237,10 +208,10 @@ packages: dependency: transitive description: name: camera_platform_interface - sha256: "98cfc9357e04bad617671b4c1f78a597f25f08003089dd94050709ae54effc63" + sha256: "4524ca6eb4176b066864036ad4fe02c3e4863e63b77eadc21a5bf56824f43498" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.1" camera_web: dependency: transitive description: @@ -410,7 +381,7 @@ packages: source: hosted version: "8.1.0" dots_indicator: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/dots_indicator" relative: true @@ -441,7 +412,7 @@ packages: source: hosted version: "0.3.0" ed25519_edwards: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/ed25519_edwards" relative: true @@ -628,14 +599,6 @@ packages: relative: true source: path version: "0.9.1" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" - url: "https://pub.dev" - source: hosted - version: "3.4.1" flutter_driver: dependency: transitive description: flutter @@ -986,7 +949,7 @@ packages: source: path version: "2.3.0" hashlib_codecs: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/hashlib_codecs" relative: true @@ -1035,11 +998,10 @@ packages: image: dependency: "direct main" description: - name: image - sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce - url: "https://pub.dev" - source: hosted - version: "4.8.0" + path: "dependencies/image" + relative: true + source: path + version: "4.9.2" image_picker: dependency: "direct main" description: @@ -1306,6 +1268,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.0" + material_symbols_icons: + dependency: transitive + description: + name: material_symbols_icons + sha256: bd513edc2bc9b034108d5518c48cf5cebbb67d0653728cc452368cb27ca80bb0 + url: "https://pub.dev" + source: hosted + version: "4.2960.0" meta: dependency: "direct main" description: @@ -1361,16 +1331,8 @@ packages: url: "https://pub.dev" source: hosted version: "9.3.0" - octo_image: - dependency: transitive - description: - name: octo_image - sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" optional: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/optional" relative: true @@ -1552,7 +1514,7 @@ packages: source: hosted version: "2.1.8" pointycastle: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/pointycastle" relative: true @@ -1577,11 +1539,10 @@ packages: pro_video_editor: dependency: "direct main" description: - name: pro_video_editor - sha256: cfed1424b3ca3d5981cc81efdd20b844c995c0ad2818e185eb5bc06a8674f728 - url: "https://pub.dev" - source: hosted - version: "1.14.2" + path: "dependencies/pro_video_editor" + relative: true + source: path + version: "2.11.3" process: dependency: transitive description: @@ -1623,7 +1584,7 @@ packages: source: hosted version: "1.5.0" qr: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/qr" relative: true @@ -1658,14 +1619,6 @@ packages: relative: true source: path version: "0.0.1" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" screen_protector: dependency: "direct main" description: @@ -1815,52 +1768,12 @@ packages: source: hosted version: "1.10.2" sprintf: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/sprintf" relative: true source: path version: "7.0.0" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_android: - dependency: transitive - description: - name: sqflite_android - sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" - url: "https://pub.dev" - source: hosted - version: "2.4.2+3" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - sqflite_darwin: - dependency: transitive - description: - name: sqflite_darwin - sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_platform_interface: - dependency: transitive - description: - name: sqflite_platform_interface - sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.dev" - source: hosted - version: "2.4.0" sqlcipher_flutter_libs: dependency: transitive description: @@ -1933,14 +1846,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.dev" - source: hosted - version: "3.4.0" term_glyph: dependency: transitive description: @@ -2088,35 +1993,32 @@ packages: video_player: dependency: "direct main" description: - name: video_player - sha256: "48a7bdaa38a3d50ec10c78627abdbfad863fdf6f0d6e08c7c3c040cfd80ae36f" - url: "https://pub.dev" - source: hosted - version: "2.11.1" + path: "dependencies/video_player" + relative: true + source: path + version: "2.14.0" video_player_android: - dependency: transitive + dependency: "direct main" description: - name: video_player_android - sha256: "877a6c7ba772456077d7bfd71314629b3fe2b73733ce503fc77c3314d43a0ca0" - url: "https://pub.dev" - source: hosted - version: "2.9.5" + path: "dependencies/video_player_android" + relative: true + source: path + version: "2.12.0" video_player_avfoundation: - dependency: transitive + dependency: "direct main" description: - name: video_player_avfoundation - sha256: af0e5b8a7a4876fb37e7cc8cb2a011e82bb3ecfa45844ef672e32cb14a1f259e - url: "https://pub.dev" - source: hosted - version: "2.9.4" + path: "dependencies/video_player_avfoundation" + relative: true + source: path + version: "2.11.0" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface - sha256: "57c5d73173f76d801129d0531c2774052c5a7c11ccb962f1830630decd9f24ec" + sha256: "92c0fbabe20c788e71fd10d26cea998d0d253282e65d145aed0818731cf593ce" url: "https://pub.dev" source: hosted - version: "6.6.0" + version: "6.9.0" video_player_web: dependency: transitive description: @@ -2222,7 +2124,7 @@ packages: source: hosted version: "0.9.1+1" x25519: - dependency: "direct overridden" + dependency: "direct main" description: path: "dependencies/x25519" relative: true diff --git a/pubspec.yaml b/pubspec.yaml index b58ed5ef..f9c2a79d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,7 +37,6 @@ dependencies: path_provider: ^2.1.5 url_launcher: ^6.3.2 vector_graphics: ^1.1.19 - video_player: ^2.10.1 in_app_purchase: ^3.3.0 go_router: ^17.1.0 @@ -72,34 +71,13 @@ dependencies: # Overwritten by self-controlled repository emoji_picker_flutter: ^4.3.0 - # Packages which got overwritten using the twonly-app-dependencies repository - # Idea: Every change goes though a git commit, where every change can be reviewed. - restart_app: ^1.3.2 - photo_view: ^0.15.0 - hashlib: ^2.0.0 - libsignal_protocol_dart: ^0.7.4 - lottie: ^3.3.1 - mutex: ^3.1.0 - introduction_screen: ^4.0.0 - qr_flutter: ^4.1.0 - hand_signature: ^3.0.3 - flutter_sharing_intent: ^2.0.4 - screen_protector: ^1.5.1 - flutter_markdown_plus: ^1.0.7 - exif: ^3.3.0 - flutter_blurhash: ^0.9.1 - # With high download. (But should be checked nonetheless.) app_links: ^7.0.0 # 1.6 mio - image: ^4.3.0 # 3.3 mio flutter_secure_storage: ^10.3.1 # 1.85 mio permission_handler: ^12.0.0+1 # 2 mio # Not yet checked - audio_waveforms: ^2.0.2 - avatar_maker: ^0.4.0 background_downloader: ^9.4.0 - cached_network_image: ^3.4.1 cryptography_flutter_plus: ^3.0.0 cryptography_plus: ^3.0.0 flutter_android_volume_keydown: ^1.0.1 @@ -109,60 +87,47 @@ dependencies: photo_manager: ^3.9.0 google_mlkit_barcode_scanning: ^0.14.1 google_mlkit_face_detection: ^0.13.1 - pro_video_editor: ^1.6.1 rust_lib_twonly: path: rust_builder flutter_rust_bridge: 2.12.0 + +## --- Start Managed Dependencies --- + audio_waveforms: ^2.0.2 + avatar_maker: ^1.5.0 blurhash_dart: ^1.2.1 + exif: ^3.3.0 + sprintf: ^7.0.0 + flutter_blurhash: ^0.9.1 + flutter_markdown_plus: ^1.0.7 + video_player: ^2.14.0 + video_player_android: ^2.12.0 + video_player_avfoundation: ^2.11.0 + camera_android_camerax: ^0.7.4+6 + flutter_sharing_intent: ^2.0.4 + hand_signature: ^3.1.0+2 + hashlib: ^2.3.0 + hashlib_codecs: ^3.0.1 + image: ^4.9.2 + introduction_screen: ^4.0.0 + dots_indicator: ^4.0.1 + libsignal_protocol_dart: ^0.8.0 + adaptive_number: ^1.0.0 + ed25519_edwards: ^0.3.1 + optional: ^6.1.0+1 + pointycastle: ^4.0.0 + x25519: ^0.1.1 + lottie: ^3.5.1 + mutex: ^3.1.0 + photo_view: ^0.15.0 + pro_video_editor: ^2.11.3 + qr_flutter: ^4.1.0 + qr: ^3.1.0-wip + restart_app: ^1.7.3 + screen_protector: ^1.5.1 +## --- End Managed Dependencies --- dependency_overrides: - dots_indicator: - path: ./dependencies/dots_indicator - restart_app: - path: ./dependencies/restart_app - hashlib: - path: ./dependencies/hashlib - introduction_screen: - path: ./dependencies/introduction_screen - libsignal_protocol_dart: - path: ./dependencies/libsignal_protocol_dart - flutter_sharing_intent: - path: ./dependencies/flutter_sharing_intent - lottie: - path: ./dependencies/lottie - mutex: - path: ./dependencies/mutex - photo_view: - path: ./dependencies/photo_view - qr: - path: ./dependencies/qr - adaptive_number: - path: ./dependencies/adaptive_number - ed25519_edwards: - path: ./dependencies/ed25519_edwards - hand_signature: - path: ./dependencies/hand_signature - hashlib_codecs: - path: ./dependencies/hashlib_codecs - optional: - path: ./dependencies/optional - pointycastle: - path: ./dependencies/pointycastle - x25519: - path: ./dependencies/x25519 - qr_flutter: - path: ./dependencies/qr_flutter - screen_protector: - path: ./dependencies/screen_protector - flutter_markdown_plus: - path: ./dependencies/flutter_markdown_plus - camera_android_camerax: - # path: ../flutter-packages/packages/camera/camera_android_camerax - git: - url: https://github.com/otsmr/flutter-packages.git - path: packages/camera/camera_android_camerax - ref: e83fb3a27d4da2c37a3c8acbf2486283965b4f69 emoji_picker_flutter: # Fixes the issue with recent emojis (solved by https://github.com/Fintasys/emoji_picker_flutter/pull/238) # Using override until this gets merged. @@ -173,12 +138,74 @@ dependency_overrides: git: url: https://github.com/yenchieh/flutter_android_volume_keydown.git ref: fix/lStar-not-found-error + +## --- Start Managed Dependency Overrides --- + audio_waveforms: + path: ./dependencies/audio_waveforms + avatar_maker: + path: ./dependencies/avatar_maker + blurhash_dart: + path: ./dependencies/blurhash_dart exif: path: ./dependencies/exif sprintf: path: ./dependencies/sprintf flutter_blurhash: path: ./dependencies/flutter_blurhash + flutter_markdown_plus: + path: ./dependencies/flutter_markdown_plus + video_player: + path: ./dependencies/video_player + video_player_android: + path: ./dependencies/video_player_android + video_player_avfoundation: + path: ./dependencies/video_player_avfoundation + camera_android_camerax: + path: ./dependencies/camera_android_camerax + flutter_sharing_intent: + path: ./dependencies/flutter_sharing_intent + hand_signature: + path: ./dependencies/hand_signature + hashlib: + path: ./dependencies/hashlib + hashlib_codecs: + path: ./dependencies/hashlib_codecs + image: + path: ./dependencies/image + introduction_screen: + path: ./dependencies/introduction_screen + dots_indicator: + path: ./dependencies/dots_indicator + libsignal_protocol_dart: + path: ./dependencies/libsignal_protocol_dart + adaptive_number: + path: ./dependencies/adaptive_number + ed25519_edwards: + path: ./dependencies/ed25519_edwards + optional: + path: ./dependencies/optional + pointycastle: + path: ./dependencies/pointycastle + x25519: + path: ./dependencies/x25519 + lottie: + path: ./dependencies/lottie + mutex: + path: ./dependencies/mutex + photo_view: + path: ./dependencies/photo_view + pro_video_editor: + path: ./dependencies/pro_video_editor + qr_flutter: + path: ./dependencies/qr_flutter + qr: + path: ./dependencies/qr + restart_app: + path: ./dependencies/restart_app + screen_protector: + path: ./dependencies/screen_protector +## --- End Managed Dependency Overrides --- + dev_dependencies: build_runner: ^2.4.15 diff --git a/test/features/link_parser_test.dart b/test/features/link_parser_test.dart index c48c2b31..b0d851cb 100644 --- a/test/features/link_parser_test.dart +++ b/test/features/link_parser_test.dart @@ -1,5 +1,6 @@ +import 'dart:io'; + import 'package:flutter_test/flutter_test.dart'; -import 'package:pro_video_editor/core/platform/io/io_helper.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/link_preview/parse_link.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/link_preview/parser/base.dart'; diff --git a/test/visual/elements/better_text_element_test.dart b/test/visual/elements/better_text_element_test.dart index eca25644..3e4b6efa 100644 --- a/test/visual/elements/better_text_element_test.dart +++ b/test/visual/elements/better_text_element_test.dart @@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:twonly/src/visual/elements/better_text.element.dart'; void main() { - testWidgets('BetterText parses URLs correctly', (WidgetTester tester) async { + testWidgets('BetterText parses URLs correctly', (tester) async { const text = 'Test: (https://google.com) and another link https://example.com/#fragment, plus www.test.com. Also check https://wikipedia.org/wiki/Test_(disambiguation) !';