refactor(app): isLoopback and formatFileSize move into core

A second feature needs each: the device's dictionaries, like a Jellyfin
server, come over plain HTTP only from this machine, and say their sizes
as the PDF list does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-15 21:54:48 +02:00
commit 8fe2cf3436
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
4 changed files with 20 additions and 17 deletions

View file

@ -0,0 +1,11 @@
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
/// [bytes] as a size: kilobytes below a megabyte, else megabytes to a
/// tenth.
String formatFileSize(AppLocalizations l10n, int bytes) {
const kb = 1024;
const mb = kb * 1024;
return bytes < mb
? l10n.fileSizeKb((bytes / kb).ceil())
: l10n.fileSizeMb((bytes / mb).toStringAsFixed(1));
}

View file

@ -0,0 +1,7 @@
import 'dart:io';
/// Whether [address] names this machine: `localhost` or a loopback address,
/// whose traffic never leaves it, so plain HTTP to it is safe.
bool isLoopback(Uri address) =>
address.host == 'localhost' ||
(InternetAddress.tryParse(address.host)?.isLoopback ?? false);

View file

@ -1,7 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:halkyon_learn_flutter/src/core/async/serial_queue.dart'; import 'package:halkyon_learn_flutter/src/core/async/serial_queue.dart';
import 'package:halkyon_learn_flutter/src/core/network/loopback.dart';
import 'package:halkyon_learn_flutter/src/core/video/subtitles.dart'; import 'package:halkyon_learn_flutter/src/core/video/subtitles.dart';
import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_gateway.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_gateway.dart';
import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_store.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_store.dart';
@ -274,8 +273,3 @@ class JellyfinRepository(
} }
} }
} }
/// Whether [address] names this machine: `localhost` or a loopback address.
bool isLoopback(Uri address) =>
address.host == 'localhost' ||
(InternetAddress.tryParse(address.host)?.isLoopback ?? false);

View file

@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
import 'package:halkyon_learn_flutter/l10n/app_localizations.dart'; import 'package:halkyon_learn_flutter/l10n/app_localizations.dart';
import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies_scope.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/dependencies_scope.dart';
import 'package:halkyon_learn_flutter/src/core/dependencies/person_scope.dart'; import 'package:halkyon_learn_flutter/src/core/dependencies/person_scope.dart';
import 'package:halkyon_learn_flutter/src/core/l10n/file_size.dart';
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
import 'package:halkyon_learn_flutter/src/core/sync/uuid_time.dart'; import 'package:halkyon_learn_flutter/src/core/sync/uuid_time.dart';
import 'package:halkyon_learn_flutter/src/core/theme/theme_extras.dart'; import 'package:halkyon_learn_flutter/src/core/theme/theme_extras.dart';
@ -567,16 +568,6 @@ class _RenameDialogState() extends State<_RenameDialog> {
} }
} }
/// [bytes] as a size: kilobytes below a megabyte, else megabytes to a
/// tenth.
String formatFileSize(AppLocalizations l10n, int bytes) {
const kb = 1024;
const mb = kb * 1024;
return bytes < mb
? l10n.fileSizeKb((bytes / kb).ceil())
: l10n.fileSizeMb((bytes / mb).toStringAsFixed(1));
}
/// The chapters of [source], read from its file; none when the file isn't on /// The chapters of [source], read from its file; none when the file isn't on
/// this device or can't be read. /// this device or can't be read.
Future<List<PdfChapter>> chaptersOfPdf( Future<List<PdfChapter>> chaptersOfPdf(