improved Uri display formatting

This commit is contained in:
Fabian Freund
2026-04-28 17:55:48 +02:00
parent 4a54b8ee83
commit 845c9a1cfc
3 changed files with 46 additions and 18 deletions
+9 -12
View File
@@ -47,18 +47,11 @@ extension UriX on Uri {
return ''; return '';
} }
final segments = pathSegments.join('/'); try {
final buffer = StringBuffer(); return Uri.decodeComponent(path);
} on FormatException {
if (path.startsWith('/')) { return path;
buffer.write('/');
} }
buffer.write(segments);
if (path.length > 1 && path.endsWith('/')) {
buffer.write('/');
}
return buffer.toString();
} }
String get displayString { String get displayString {
@@ -94,6 +87,10 @@ extension UriStringX on String {
if (uri == null || uri.toString() != this) { if (uri == null || uri.toString() != this) {
return this; return this;
} }
return uri.displayString; try {
return uri.displayString;
} on FormatException {
return this;
}
} }
} }
@@ -21,6 +21,7 @@ import 'dart:convert';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:weblibre/extensions/uri.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/domain/converters/hit_result_converter.dart'; import 'package:weblibre/features/geckoview/features/contextmenu/domain/converters/hit_result_converter.dart';
extension HitResultJson on HitResult { extension HitResultJson on HitResult {
@@ -82,14 +83,15 @@ extension HitResultX on HitResult {
const maxTitleLength = 2500; const maxTitleLength = 2500;
return switch (this) { return switch (this) {
UnknownHitResult(src: final src) => src, UnknownHitResult(src: final src) => src.uriDisplayString,
ImageSrcHitResult(uri: final uri) => uri, ImageSrcHitResult(uri: final uri) => uri.uriDisplayString,
ImageHitResult(src: final src, title: final title) => ImageHitResult(src: final src, title: final title) => title.isEmpty
title.isEmpty ? (src.length > maxTitleLength ? 'image' : src) : title!, ? (src.length > maxTitleLength ? 'image' : src.uriDisplayString)
: title!,
VideoHitResult(src: final src, title: final title) => VideoHitResult(src: final src, title: final title) =>
(title.isEmpty) ? src : title!, (title.isEmpty) ? src.uriDisplayString : title!,
AudioHitResult(src: final src, title: final title) => AudioHitResult(src: final src, title: final title) =>
(title.isEmpty) ? src : title!, (title.isEmpty) ? src.uriDisplayString : title!,
_ => 'about:blank', _ => 'about:blank',
}; };
} }
@@ -65,5 +65,34 @@ void main() {
'https://example.com/%ZZ?query=%', 'https://example.com/%ZZ?query=%',
); );
}); });
test('preserves trailing slash exactly once', () {
final uri = Uri.parse('http://x.com/a/b/');
expect(uri.displayString, 'http://x.com/a/b/');
});
test('decodes Cyrillic Reddit URL with trailing slash', () {
final uri = Uri.parse(
'https://www.reddit.com/r/KafkaFPS/comments/1sn0j5w/'
'%D0%BF%D0%B5%D1%80%D0%B5%D1%81%D1%82%D0%B0%D0%BD%D1%8C%D1%82%D0%B5'
'_%D1%81%D0%BE%D0%BF%D1%80%D0%BE%D1%82%D0%B8%D0%B2%D0%BB%D1%8F'
'%D1%82%D1%8C%D1%81%D1%8F_%D0%BC%D0%B8%D1%81%D1%82%D0%B5%D1%80'
'_%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%BE%D0%BD/',
);
expect(
uri.displayString,
'https://www.reddit.com/r/KafkaFPS/comments/1sn0j5w/'
'перестаньте_сопротивляться_мистер_андерсон/',
);
});
test('keeps IDN punycode host as-is', () {
expect(
'https://xn--j1ail.xn--p1ai/'.uriDisplayString,
'https://xn--j1ail.xn--p1ai/',
);
});
}); });
} }