JJ_MKVMaker/lib/ui/browser/dns_block.dart

95 lines
4.4 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import '../../app/net_guard.dart';
import '../../l10n/tr.dart';
import '../theme.dart';
/// 250: 페이지를 DNS 서버에 닿지 않아 열지 못함 (주소 이름 · DNS 서버). 엔진의 오류 화면 위에 이유와 할 일을 보인다.
/// 약속: [find] 가 막혔다고 보면 새 값을 주고 (같거나 아니면 null), [set] 으로 정하고 걷는다.
/// 덮개가 보이는 동안은 NetGuard 의 아래 알림을 막는다 (248: 같은 안내 · [시스템 DNS 로 바꾸기] 가 두 개 보였다 - a4 933)
class DnsBlockWatch {
(String, String)? _blocked;
/// 이번 읽기를 시작한 시각 (이보다 앞선 DNS 실패는 이 페이지와 상관없다)
DateTime loadStartAt = DateTime.now();
(String, String)? get blocked => _blocked;
/// 덮개는 여기서만 바꾼다 (새로 읽기 시작 · [다시 시도] 에서 걷는다 - Android 는 오류 뒤에 읽기 끝이 또 온다)
void set((String, String)? v) {
final g = NetGuard.instance.noticeBlockers;
if (_blocked == null && v != null) g.value++;
if (_blocked != null && v == null) g.value--;
_blocked = v;
}
/// [error]: 엔진이 알려 준 본문 오류. 프록시 터널 실패이고 DNS 서버에 지금 닿지 않는 중이면, 그 이름의 실패 기록이 없어도
/// (Chromium 이 실패한 프록시를 기억해 다시 묻지 않는 때 등) DNS 때문으로 본다.
(String, String)? find({required String host, String? error}) {
if (host.isEmpty) return null;
final g = NetGuard.instance;
var server = g.dnsUnreachableFor(host, loadStartAt);
final f = g.failure.value;
if (server == null && f != null && error != null && (error.contains('TUNNEL') || error.contains('PROXY'))) server = f.server;
if (server == null) return null;
final next = (host, server);
return next == _blocked ? null : next;
}
/// 화면을 닫을 때: 막아 둔 알림을 푼다 (닫는 중에는 다른 위젯을 바꾸지 않게 다음 차례에)
void dispose() {
if (_blocked != null) scheduleMicrotask(() => NetGuard.instance.noticeBlockers.value--);
}
}
/// 250: DNS 실패면 웹 뷰 위에 덮는다 ([시스템 DNS 로 바꾸기] [다시 시도]).
/// 트리 모양은 늘 같게 (Stack 첫 칸이 웹 뷰) - 덮개를 띄울 때 모양이 바뀌면 웹 뷰가 새로 만들어져 페이지를 다시 읽고,
/// 다시 읽기가 시작되며 덮개가 걷혔다 (931 노트 10+ 에서 덮개가 보이지 않던 것, Windows 실제 시험에서 재현)
class DnsBlockCover extends StatelessWidget {
final Widget view;
/// (주소 이름, DNS 서버). null 이면 웹 뷰만
final (String, String)? blocked;
final VoidCallback onSystemDns;
final VoidCallback onRetry;
const DnsBlockCover({super.key, required this.view, required this.blocked, required this.onSystemDns, required this.onRetry});
@override
Widget build(BuildContext context) {
final b = blocked;
return Stack(children: [
Positioned.fill(child: view),
if (b != null)
Positioned.fill(
child: ColoredBox(
color: JjColors.bg,
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 520),
child: Column(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.dns_outlined, size: 40, color: Colors.orange),
const SizedBox(height: 12),
Text(trf('DNS 서버 ({0}) 에 닿지 않아 {1} 을(를) 열 수 없습니다.', [b.$2, b.$1]),
textAlign: TextAlign.center, style: const TextStyle(fontSize: 16)),
const SizedBox(height: 8),
Text(tr('인터넷 연결이나 환경 설정 > 네트워크 · DNS 의 서버 주소를 확인하세요.'),
textAlign: TextAlign.center, style: TextStyle(fontSize: 13, color: JjColors.textDim)),
const SizedBox(height: 16),
Wrap(spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, children: [
FilledButton(onPressed: onSystemDns, child: Text(tr('시스템 DNS 로 바꾸기'))),
OutlinedButton(onPressed: onRetry, child: Text(tr('다시 시도'))),
]),
]),
),
),
),
),
),
]);
}
}