diff --git a/lib/widgets/content.dart b/lib/widgets/content.dart index 5f39808fdf..f8df933967 100644 --- a/lib/widgets/content.dart +++ b/lib/widgets/content.dart @@ -749,9 +749,7 @@ class RealmContentNetworkImage extends StatelessWidget { this.cacheHeight, }); - /// An absolute URL string for the image. - // TODO: Take a [Uri] object, not a String - final String src; + final Uri src; final double scale; final ImageFrameBuilder? frameBuilder; @@ -780,10 +778,8 @@ class RealmContentNetworkImage extends StatelessWidget { Widget build(BuildContext context) { final account = PerAccountStoreWidget.of(context).account; - final Uri parsedSrc = Uri.parse(src); - return Image.network( - parsedSrc.toString(), + src.toString(), scale: scale, frameBuilder: frameBuilder, @@ -806,7 +802,7 @@ class RealmContentNetworkImage extends StatelessWidget { isAntiAlias: isAntiAlias, // Only send the auth header to the server `auth` belongs to. - headers: parsedSrc.origin == account.realmUrl.origin + headers: src.origin == account.realmUrl.origin ? authHeader(email: account.email, apiKey: account.apiKey) : null, @@ -823,10 +819,9 @@ class RealmContentNetworkImage extends StatelessWidget { /// Resolve `url` to `account`'s realm, if relative // This may dissolve when we start passing around URLs as [Uri] objects instead // of strings. -String resolveUrl(String url, Account account) { +Uri resolveUrl(String url, Account account) { final realmUrl = account.realmUrl; - final resolved = realmUrl.resolve(url); // TODO handle if fails to parse - return resolved.toString(); + return realmUrl.resolve(url); // TODO handle if fails to parse } InlineSpan _errorUnimplemented(UnimplementedNode node) { diff --git a/lib/widgets/lightbox.dart b/lib/widgets/lightbox.dart index e68e15a64d..829b7ab527 100644 --- a/lib/widgets/lightbox.dart +++ b/lib/widgets/lightbox.dart @@ -17,7 +17,7 @@ class _LightboxHeroTag { _LightboxHeroTag({required this.messageId, required this.src}); final int messageId; - final String src; + final Uri src; @override bool operator ==(Object other) { @@ -40,7 +40,7 @@ class LightboxHero extends StatelessWidget { }); final Message message; - final String src; + final Uri src; final Widget child; @override @@ -66,7 +66,7 @@ class LightboxHero extends StatelessWidget { class _CopyLinkButton extends StatelessWidget { const _CopyLinkButton({required this.url}); - final String url; + final Uri url; @override Widget build(BuildContext context) { @@ -76,7 +76,7 @@ class _CopyLinkButton extends StatelessWidget { onPressed: () async { // TODO(i18n) copyWithPopup(context: context, successContent: const Text('Link copied'), - data: ClipboardData(text: url)); + data: ClipboardData(text: url.toString())); }); } } @@ -90,7 +90,7 @@ class _LightboxPage extends StatefulWidget { final Animation routeEntranceAnimation; final Message message; - final String src; + final Uri src; @override State<_LightboxPage> createState() => _LightboxPageState(); @@ -205,7 +205,7 @@ class _LightboxPageState extends State<_LightboxPage> { Route getLightboxRoute({ required BuildContext context, required Message message, - required String src + required Uri src, }) { return AccountPageRouteBuilder( context: context, diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 28331bbae7..e38bc382e8 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -482,10 +482,7 @@ class MessageWithSender extends StatelessWidget { : resolveUrl(author.avatarUrl!, store.account); final avatar = (avatarUrl == null) ? const SizedBox.shrink() - : RealmContentNetworkImage( - avatarUrl, - filterQuality: FilterQuality.medium, - ); + : RealmContentNetworkImage(avatarUrl, filterQuality: FilterQuality.medium); final time = _kMessageTimestampFormat .format(DateTime.fromMillisecondsSinceEpoch(1000 * message.timestamp)); diff --git a/test/widgets/content_test.dart b/test/widgets/content_test.dart index 3bf6021807..03c407ea4a 100644 --- a/test/widgets/content_test.dart +++ b/test/widgets/content_test.dart @@ -121,7 +121,7 @@ void main() { group('RealmContentNetworkImage', () { final authHeaders = authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey); - Future actualAuthHeader(WidgetTester tester, String src) async { + Future actualAuthHeader(WidgetTester tester, Uri src) async { final globalStore = TestZulipBinding.instance.globalStore; addTearDown(TestZulipBinding.instance.reset); await globalStore.add(eg.selfAccount, eg.initialSnapshot()); @@ -144,20 +144,20 @@ void main() { } testWidgets('includes auth header if `src` on-realm', (tester) async { - check(await actualAuthHeader(tester, 'https://chat.example/image.png')) + check(await actualAuthHeader(tester, Uri.parse('https://chat.example/image.png'))) .isNotNull().equals(authHeaders['Authorization']!); debugNetworkImageHttpClientProvider = null; }); testWidgets('excludes auth header if `src` off-realm', (tester) async { - check(await actualAuthHeader(tester, 'https://other.example/image.png')) + check(await actualAuthHeader(tester, Uri.parse('https://other.example/image.png'))) .isNull(); debugNetworkImageHttpClientProvider = null; }); testWidgets('throws if no `PerAccountStoreWidget` ancestor', (WidgetTester tester) async { await tester.pumpWidget( - const RealmContentNetworkImage('https://zulip.invalid/path/to/image.png', filterQuality: FilterQuality.medium)); + RealmContentNetworkImage(Uri.parse('https://zulip.invalid/path/to/image.png'), filterQuality: FilterQuality.medium)); check(tester.takeException()).isA(); }); });