修复处理内容包括公告 WebView、文件预览页、分享页兜底获取下载链接的隐藏 WebView。
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as desktop;
|
||||
import 'package:webview_flutter/webview_flutter.dart' as mobile;
|
||||
|
||||
import '../../../data/models/file_model.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
|
||||
bool get _useDesktopWebView =>
|
||||
!kIsWeb && (Platform.isWindows || Platform.isLinux);
|
||||
|
||||
/// 完全交给 Cloudreve 官方 Web 前端处理文件打开。
|
||||
///
|
||||
/// 这里不再自己读取 file_viewers,也不再自己创建 viewerSession。
|
||||
@@ -26,7 +32,8 @@ class CloudreveFileAppPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
late final WebViewController _controller;
|
||||
mobile.WebViewController? _mobileController;
|
||||
desktop.InAppWebViewController? _desktopController;
|
||||
|
||||
bool _isPreparing = true;
|
||||
bool _sessionInjected = false;
|
||||
@@ -40,36 +47,47 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setBackgroundColor(Colors.transparent)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onProgress: (progress) {
|
||||
if (!mounted) return;
|
||||
setState(() => _progress = progress);
|
||||
},
|
||||
onPageFinished: (_) => _injectSessionAndOpenIfNeeded(),
|
||||
onWebResourceError: (error) {
|
||||
if (!mounted) return;
|
||||
if (error.isForMainFrame == true) {
|
||||
setState(() {
|
||||
_error = '${error.errorCode}: ${error.description}';
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
if (!_useDesktopWebView) {
|
||||
_mobileController = mobile.WebViewController()
|
||||
..setJavaScriptMode(mobile.JavaScriptMode.unrestricted)
|
||||
..setBackgroundColor(Colors.transparent)
|
||||
..setNavigationDelegate(
|
||||
mobile.NavigationDelegate(
|
||||
onProgress: (progress) {
|
||||
if (!mounted) return;
|
||||
setState(() => _progress = progress);
|
||||
},
|
||||
onPageFinished: (_) => _injectSessionAndOpenIfNeeded(),
|
||||
onWebResourceError: (error) {
|
||||
if (!mounted) return;
|
||||
if (error.isForMainFrame == true) {
|
||||
setState(() {
|
||||
_error = '${error.errorCode}: ${error.description}';
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_prepareAndOpen();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_desktopController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _prepareAndOpen() async {
|
||||
setState(() {
|
||||
_isPreparing = true;
|
||||
_error = null;
|
||||
_progress = 0;
|
||||
_sessionInjected = false;
|
||||
if (_useDesktopWebView) {
|
||||
_desktopController = null;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -80,7 +98,7 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
|
||||
// 先打开同源页面,再注入 localStorage。
|
||||
// 这样 Cloudreve 官方前端可以直接复用 App 的 access_token。
|
||||
await _controller.loadRequest(_originUri);
|
||||
await _loadOrigin();
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() => _isPreparing = false);
|
||||
@@ -105,7 +123,9 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
|
||||
Uri _buildCloudreveHomeUri() {
|
||||
final parent = _parentUri(widget.file.relativePath);
|
||||
final openTarget = widget.file.id.isNotEmpty ? widget.file.id : widget.file.relativePath;
|
||||
final openTarget = widget.file.id.isNotEmpty
|
||||
? widget.file.id
|
||||
: widget.file.relativePath;
|
||||
|
||||
return Uri(
|
||||
scheme: _originUri.scheme,
|
||||
@@ -121,7 +141,9 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
}
|
||||
|
||||
String _parentUri(String uri) {
|
||||
final normalized = uri.endsWith('/') ? uri.substring(0, uri.length - 1) : uri;
|
||||
final normalized = uri.endsWith('/')
|
||||
? uri.substring(0, uri.length - 1)
|
||||
: uri;
|
||||
final index = normalized.lastIndexOf('/');
|
||||
if (index <= 'cloudreve://my'.length) {
|
||||
return 'cloudreve://my';
|
||||
@@ -138,7 +160,9 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
|
||||
Map<String, dynamic>? user;
|
||||
try {
|
||||
final response = await ApiService.instance.get<Map<String, dynamic>>('/user/me');
|
||||
final response = await ApiService.instance.get<Map<String, dynamic>>(
|
||||
'/user/me',
|
||||
);
|
||||
user = Map<String, dynamic>.from(response);
|
||||
} catch (_) {
|
||||
user = null;
|
||||
@@ -181,15 +205,16 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
|
||||
try {
|
||||
if (sessionJson != null) {
|
||||
final script = '''
|
||||
final script =
|
||||
'''
|
||||
try {
|
||||
localStorage.setItem('cloudreve_session', ${jsonEncode(sessionJson)});
|
||||
} catch (e) {}
|
||||
window.location.replace(${jsonEncode(target)});
|
||||
''';
|
||||
await _controller.runJavaScript(script);
|
||||
await _runJavaScript(script);
|
||||
} else {
|
||||
await _controller.loadRequest(_targetUri);
|
||||
await _loadTarget();
|
||||
}
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
@@ -199,17 +224,52 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
|
||||
Future<void> _openTargetAgain() async {
|
||||
_sessionInjected = true;
|
||||
await _controller.loadRequest(_targetUri);
|
||||
await _loadTarget();
|
||||
}
|
||||
|
||||
Future<void> _reloadWebView() async {
|
||||
if (_useDesktopWebView) {
|
||||
await _desktopController?.reload();
|
||||
} else {
|
||||
await _mobileController?.reload();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadOrigin() async {
|
||||
if (_useDesktopWebView) {
|
||||
final controller = _desktopController;
|
||||
if (controller == null) return;
|
||||
await _desktopController?.loadUrl(
|
||||
urlRequest: desktop.URLRequest(url: desktop.WebUri.uri(_originUri)),
|
||||
);
|
||||
} else {
|
||||
await _mobileController!.loadRequest(_originUri);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadTarget() async {
|
||||
if (_useDesktopWebView) {
|
||||
await _desktopController?.loadUrl(
|
||||
urlRequest: desktop.URLRequest(url: desktop.WebUri.uri(_targetUri)),
|
||||
);
|
||||
} else {
|
||||
await _mobileController!.loadRequest(_targetUri);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _runJavaScript(String source) async {
|
||||
if (_useDesktopWebView) {
|
||||
await _desktopController?.evaluateJavascript(source: source);
|
||||
} else {
|
||||
await _mobileController!.runJavaScript(source);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
widget.file.name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
title: Text(widget.file.name, overflow: TextOverflow.ellipsis),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '重新打开',
|
||||
@@ -219,7 +279,7 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
IconButton(
|
||||
tooltip: '刷新',
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () => _controller.reload(),
|
||||
onPressed: _reloadWebView,
|
||||
),
|
||||
],
|
||||
bottom: PreferredSize(
|
||||
@@ -260,6 +320,32 @@ class _CloudreveFileAppPageState extends State<CloudreveFileAppPage> {
|
||||
);
|
||||
}
|
||||
|
||||
return WebViewWidget(controller: _controller);
|
||||
if (_useDesktopWebView) {
|
||||
return desktop.InAppWebView(
|
||||
initialSettings: desktop.InAppWebViewSettings(
|
||||
javaScriptEnabled: true,
|
||||
domStorageEnabled: true,
|
||||
transparentBackground: true,
|
||||
supportZoom: false,
|
||||
),
|
||||
onWebViewCreated: (controller) async {
|
||||
_desktopController = controller;
|
||||
if (!_isPreparing) {
|
||||
await _loadOrigin();
|
||||
}
|
||||
},
|
||||
onProgressChanged: (_, progress) {
|
||||
if (!mounted) return;
|
||||
setState(() => _progress = progress);
|
||||
},
|
||||
onLoadStop: (_, url) => _injectSessionAndOpenIfNeeded(),
|
||||
onReceivedError: (_, request, error) {
|
||||
if (!mounted || request.isForMainFrame != true) return;
|
||||
setState(() => _error = '${error.type}: ${error.description}');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return mobile.WebViewWidget(controller: _mobileController!);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user