appsok/lib/src/app.dart

174 lines
5.4 KiB
Dart

import 'package:flutter/material.dart';
import 'features/app_shell.dart';
import 'features/devices/devices_page.dart';
import 'services/adb_service.dart';
import 'services/artifact_staging_service.dart';
import 'services/jenkins_artifact_session.dart';
import 'services/jenkins_client.dart';
import 'services/token_store.dart';
import 'theme/app_theme.dart';
class AppSokApp extends StatefulWidget {
const AppSokApp({
super.key,
this.tokenStore,
this.jenkinsClient,
this.artifactStagingService,
this.session,
this.adbDeviceLoader,
this.adbInstaller,
this.adbDiagnosticsLoader,
this.webLoginLauncher,
this.onWebLoginCompleted,
this.adbLogcatStarter,
});
final TokenStore? tokenStore;
final JenkinsClient? jenkinsClient;
final ArtifactStagingService? artifactStagingService;
final JenkinsArtifactSession? session;
final AdbDeviceLoader? adbDeviceLoader;
final AdbInstaller? adbInstaller;
final AdbDiagnosticsLoader? adbDiagnosticsLoader;
final JenkinsWebLoginLauncher? webLoginLauncher;
final void Function(JenkinsWebLoginResult result)? onWebLoginCompleted;
final AdbLogcatSessionStarter? adbLogcatStarter;
@override
State<AppSokApp> createState() => _AppSokAppState();
}
class _AppSokAppState extends State<AppSokApp> {
late final JenkinsArtifactSession _session =
widget.session ??
JenkinsArtifactSession(
store: widget.tokenStore ?? TokenStore(),
client: widget.jenkinsClient ?? JenkinsClient(),
stager: widget.artifactStagingService ?? ArtifactStagingService(),
);
late final AdbService _adbService = AdbService();
// Mutable so that _onSessionSaved and _retryRestore can trigger re-restore.
late Future<JenkinsSessionRestoreResult> _startup =
_session.restoreDetailed();
void _onSessionSaved() {
if (!mounted) return;
setState(() {
_startup = _session.restoreDetailed();
});
}
Future<void> _clearSession() async {
await _session.clear();
if (!mounted) return;
setState(() {
_startup = Future.value(
const JenkinsSessionRestoreResult(
kind: JenkinsSessionRestoreKind.missing,
),
);
});
}
void _retryRestore() {
if (!mounted) return;
setState(() {
_startup = _session.restoreDetailed();
});
}
Future<bool> _saveToken(
String baseUrl,
String username,
String apiToken,
) async {
final store = _session.store;
final baseUri = Uri.tryParse(baseUrl);
if (baseUri == null ||
(baseUri.scheme != 'http' && baseUri.scheme != 'https')) {
return false;
}
try {
await store.saveSession(
JenkinsSessionConfig(
baseUrl: baseUri,
credentials: JenkinsCredentials(
username: username,
apiToken: apiToken,
),
),
);
final result = await _session.restoreDetailed();
if (!result.isRestored) {
await store.clearSession();
return false;
}
return true;
} catch (_) {
await store.clearSession();
return false;
}
}
JenkinsConnectionStatus? _connectionStatus(JenkinsSessionRestoreResult r) {
if (r.isRestored) return null;
return switch (r.kind) {
JenkinsSessionRestoreKind.reauthRequired =>
JenkinsConnectionStatus.reauthRequired,
JenkinsSessionRestoreKind.networkFailure =>
JenkinsConnectionStatus.networkFailure,
JenkinsSessionRestoreKind.serverFailure =>
JenkinsConnectionStatus.serverFailure,
JenkinsSessionRestoreKind.missing || JenkinsSessionRestoreKind.restored =>
JenkinsConnectionStatus.missing,
};
}
@override
Widget build(BuildContext context) {
return FutureBuilder<JenkinsSessionRestoreResult>(
future: _startup,
builder: (context, snapshot) {
final result = snapshot.data;
final isReady = result?.isRestored ?? false;
final connStatus = result != null ? _connectionStatus(result) : null;
final canRetry =
connStatus == JenkinsConnectionStatus.networkFailure ||
connStatus == JenkinsConnectionStatus.serverFailure;
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'AppSok',
theme: AppTheme.light(),
home: AppSokShell(
jobLoader: isReady ? _session.loadJobs : null,
buildLoader: isReady ? _session.loadBuilds : null,
artifactDownloader: isReady ? _session.downloadArtifact : null,
artifactStager: isReady ? _session.stageApk : null,
artifactCleaner: isReady ? _session.cleanup : null,
deviceLoader: widget.adbDeviceLoader ?? _adbService.listDevices,
installer:
widget.adbInstaller ??
(device, pending) => _adbService.installApk(
serial: device.serial,
apkPath: pending.apkPath,
),
adbDiagnosticsLoader:
widget.adbDiagnosticsLoader ?? _adbService.collectDiagnostics,
logcatStarter: widget.adbLogcatStarter ?? _adbService.startLogcat,
webLoginLauncher: widget.webLoginLauncher,
onWebLoginCompleted: widget.onWebLoginCompleted,
onSessionSaved: _onSessionSaved,
tokenSaver: _saveToken,
sessionClearer: _clearSession,
connectionStatus: connStatus,
onRetryConnection: canRetry ? _retryRestore : null,
),
);
},
);
}
}