Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
weblate committed Sep 7, 2023
2 parents 789d0ad + 6ca00aa commit 05fe71c
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 123 deletions.
31 changes: 22 additions & 9 deletions lib/app/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ Future<Widget> initOuiSyncApp(List<String> args, String appSuffix) async {
logPath: logPath,
);

// NOTE: When the app exits, the `State.dispose()` methods are not guaranteed to be called for
// some reason. To ensure resources are properly disposed of, we need to do it via this lifecycle
// listener.
// NOTE: The lifecycle listener itself is never `dispose`d but that's OK because it's supposed to
// live as long as the app itself.
AppLifecycleListener(
onExitRequested: () async {
await session.dispose();
windowManager.dispose();

return AppExitResponse.exit;
},
);

// Make sure to only output logs after Session is created (which sets up the log subscriber),
// otherwise the logs will go nowhere.
Loggy.initLoggy(logPrinter: AppLogPrinter());
Expand Down Expand Up @@ -153,14 +167,12 @@ class _OuiSyncAppState extends State<OuiSyncApp> with AppLogger {
@override
void dispose() {
_mediaReceiver.dispose();

widget.windowManager.dispose();

super.dispose();
}

@override
Widget build(BuildContext context) {
// TODO: We are recreating this on every rebuild. Is that what we want?
final upgradeExists = UpgradeExistsCubit(
widget.session.currentProtocolVersion, widget.settings);

Expand Down Expand Up @@ -189,12 +201,13 @@ class _OuiSyncAppState extends State<OuiSyncApp> with AppLogger {
loggy.app('Drop exited: ${detail.localPosition}');
},
child: MainPage(
session: widget.session,
upgradeExists: upgradeExists,
backgroundServiceManager: _backgroundServiceManager,
mediaReceiver: _mediaReceiver,
settings: widget.settings,
windowManager: widget.windowManager))),
session: widget.session,
upgradeExists: upgradeExists,
backgroundServiceManager: _backgroundServiceManager,
mediaReceiver: _mediaReceiver,
settings: widget.settings,
windowManager: widget.windowManager,
))),
);
}
}
Expand Down
20 changes: 11 additions & 9 deletions lib/app/cubits/cubits.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:io' as io;

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

Expand Down Expand Up @@ -36,20 +34,24 @@ class Cubits {
final UpgradeExistsCubit upgradeExists;
final BackgroundServiceManager backgroundServiceManager;
final PlatformWindowManager windowManager;
// Is not null only on operating system where mounting is supported.
final MountCubit? mount;
final MountCubit mount;

Cubits(this.repositories, this.powerControl, this.panicCounter,
this.upgradeExists, this.backgroundServiceManager, this.windowManager)
: mount =
(io.Platform.isWindows) ? MountCubit(repositories.session) : null;
Cubits({
required this.repositories,
required this.powerControl,
required this.panicCounter,
required this.upgradeExists,
required this.backgroundServiceManager,
required this.windowManager,
required this.mount,
});

Color? mainNotificationBadgeColor() {
final upgradeExists = this.upgradeExists.state;
final panicCount = panicCounter.state ?? 0;
final isNetworkEnabled = powerControl.state.isNetworkEnabled ?? true;
final showWarning = backgroundServiceManager.showWarning();
final mountState = mount?.state;
final mountState = mount.state;

if (upgradeExists || panicCount > 0 || mountState is MountStateError) {
return Constants.errorColor;
Expand Down
22 changes: 15 additions & 7 deletions lib/app/cubits/mount.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import '../utils/log.dart';

class MountState {}

class MountStateDisabled extends MountState {}

class MountStateMounting extends MountState {}

class MountStateSuccess extends MountState {}
Expand All @@ -17,16 +19,22 @@ class MountStateError extends MountState {
}

class MountCubit extends Cubit<MountState> with AppLogger {
MountCubit(oui.Session session) : super(MountStateMounting()) {
unawaited(_mountFileSystem(session));
}
final oui.Session session;

MountCubit(this.session) : super(MountStateDisabled());

Future<void> mount(String mountPoint) async {
emit(MountStateMounting());

Future<void> _mountFileSystem(oui.Session session) async {
try {
await session.mountAllRepositories("O:");
await session.mountAllRepositories(mountPoint);
emit(MountStateSuccess());
} on oui.Error catch (error) {
loggy.app("Failed to mount repositories ${error.code}: ${error.message}");
} on oui.Error catch (error, st) {
loggy.error(
'Failed to mount repositories at $mountPoint:',
error.message,
st,
);
emit(MountStateError(error.code, error.message));
}
}
Expand Down
80 changes: 52 additions & 28 deletions lib/app/pages/main_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:move_to_background/move_to_background.dart';
import 'package:ouisync_plugin/ouisync_plugin.dart';
import 'package:ouisync_plugin/state_monitor.dart' as oui;
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:url_launcher/url_launcher.dart';

import '../../generated/l10n.dart';
import '../cubits/cubits.dart';
Expand All @@ -26,13 +27,14 @@ typedef MoveEntryCallback = void Function(
String origin, String path, EntryType type);

class MainPage extends StatefulWidget {
const MainPage(
{required this.session,
required this.upgradeExists,
required this.backgroundServiceManager,
required this.mediaReceiver,
required this.settings,
required this.windowManager});
const MainPage({
required this.session,
required this.upgradeExists,
required this.backgroundServiceManager,
required this.mediaReceiver,
required this.settings,
required this.windowManager,
});

final Session session;
final UpgradeExistsCubit upgradeExists;
Expand All @@ -42,8 +44,13 @@ class MainPage extends StatefulWidget {
final PlatformWindowManager windowManager;

@override
State<StatefulWidget> createState() => _MainPageState(session, upgradeExists,
backgroundServiceManager, settings, windowManager);
State<StatefulWidget> createState() => _MainPageState(
session,
upgradeExists,
backgroundServiceManager,
settings,
windowManager,
);
}

class _MainPageState extends State<MainPage>
Expand All @@ -63,23 +70,38 @@ class _MainPageState extends State<MainPage>
_MainPageState._(this._cubits);

factory _MainPageState(
Session session,
UpgradeExistsCubit upgradeExists,
BackgroundServiceManager backgroundServiceManager,
Settings settings,
PlatformWindowManager windowManager) {
Session session,
UpgradeExistsCubit upgradeExists,
BackgroundServiceManager backgroundServiceManager,
Settings settings,
PlatformWindowManager windowManager,
) {
final repositories = ReposCubit(
session: session,
settings: settings,
);
final powerControl = PowerControl(session, settings);
final panicCounter = StateMonitorIntCubit(
repositories.rootStateMonitor
.child(oui.MonitorId.expectUnique("Session")),
"panic_counter");
repositories.rootStateMonitor
.child(oui.MonitorId.expectUnique("Session")),
"panic_counter",
);

return _MainPageState._(Cubits(repositories, powerControl, panicCounter,
upgradeExists, backgroundServiceManager, windowManager));
final mount = MountCubit(session);
final mountPoint = settings.getMountPoint();
if (mountPoint != null) {
unawaited(mount.mount(mountPoint));
}

return _MainPageState._(Cubits(
repositories: repositories,
powerControl: powerControl,
panicCounter: panicCounter,
upgradeExists: upgradeExists,
backgroundServiceManager: backgroundServiceManager,
windowManager: windowManager,
mount: mount,
));
}

RepoEntry? get _currentRepo => _cubits.repositories.currentRepo;
Expand Down Expand Up @@ -445,24 +467,27 @@ class _MainPageState extends State<MainPage>
);
}

Future<void> _previewFile(
RepoCubit repo, FileItem item, String authority) async {
Future<void> _previewFile(RepoCubit repo, FileItem item) async {
if (io.Platform.isAndroid) {
// TODO: Consider using `launchUrl` also here, using the 'content://' scheme.

await NativeChannels.previewOuiSyncFile(
authority,
Constants.androidAppAuthority,
item.path,
item.size ?? 0,
useDefaultApp: true,
);
} else if (io.Platform.isWindows) {
} else if (io.Platform.isWindows ||
io.Platform.isLinux ||
io.Platform.isMacOS) {
final mountedDirectory = repo.mountedDirectory();
if (mountedDirectory == null) {
showSnackBar(context, message: S.current.messageRepositoryNotMounted);
return;
}
var result = await io.Process.run(
'cmd', ['/c', 'start', '', '$mountedDirectory${item.path}']);
loggy.app(result.stdout);

final url = Uri.parse('file:$mountedDirectory${item.path}');
await launchUrl(url);
} else {
// Only the above platforms are supported right now.
showSnackBar(context, message: S.current.messageFilePreviewNotAvailable);
Expand Down Expand Up @@ -492,8 +517,7 @@ class _MainPageState extends State<MainPage>
return;
}

await _previewFile(
currentRepo, item, Constants.androidAppAuthority);
await _previewFile(currentRepo, item);
};
} else if (item is FolderItem) {
actionByType = () {
Expand Down
40 changes: 32 additions & 8 deletions lib/app/utils/settings.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'dart:io' as io show Directory, Platform;
import 'dart:io' show Directory, Platform;

import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart' as path_provider;
Expand All @@ -13,7 +13,7 @@ class SettingsRepoEntry {
RepoMetaInfo info;

String get name => info.name;
io.Directory get dir => info.dir;
Directory get dir => info.dir;

SettingsRepoEntry(this.databaseId, this.info);
}
Expand Down Expand Up @@ -70,6 +70,9 @@ class Settings with AppLogger {
// directory and moved everything there into this settings.
static const String _legacyReposIncluded = "LEGACY_REPOS_INCLUDED";

// Path to mount repositories at
static const String _mountPoint = "MOUNT_POINT";

final SharedPreferences _prefs;
final _CachedString _defaultRepo;

Expand Down Expand Up @@ -140,7 +143,7 @@ class Settings with AppLogger {
await _prefs.setBool(_launchAtStartup, value);
}

Future<io.Directory> defaultRepoLocation() async {
Future<Directory> defaultRepoLocation() async {
try {
// Docs says this throws on non Android systems.
// https://pub.dev/documentation/path_provider/latest/path_provider/getExternalStorageDirectory.html
Expand All @@ -160,15 +163,15 @@ class Settings with AppLogger {
// also gets deleted when the app is un/re-installed.
final alternativeDir =
await path_provider.getApplicationDocumentsDirectory();
if (io.Platform.isAndroid) {
if (Platform.isAndroid) {
return alternativeDir;
}

final context = p.Context(style: p.Style.posix);
final nonAndroidAlternativePath =
context.join(alternativeDir.path, 'ouisync');

final documents = await io.Directory(nonAndroidAlternativePath).create();
final documents = await Directory(nonAndroidAlternativePath).create();
return documents;
}

Expand Down Expand Up @@ -196,7 +199,7 @@ class Settings with AppLogger {
RepoMetaInfo? repoMetaInfo(String repoName) {
final dir = _repos[repoName];
if (dir == null) return null;
return RepoMetaInfo.fromDirAndName(io.Directory(dir), repoName);
return RepoMetaInfo.fromDirAndName(Directory(dir), repoName);
}

Future<void> setDefaultRepo(String? name) async {
Expand Down Expand Up @@ -232,7 +235,9 @@ class Settings with AppLogger {
await forgetRepository(oldName);

return SettingsRepoEntry(
databaseId, RepoMetaInfo.fromDirAndName(io.Directory(path), newName));
databaseId,
RepoMetaInfo.fromDirAndName(Directory(path), newName),
);
}

Future<SettingsRepoEntry?> addRepo(
Expand Down Expand Up @@ -302,6 +307,9 @@ class Settings with AppLogger {
await _setRepositoryString(repoName, _authenticationMode, str);
}

String? getMountPoint() =>
_prefs.getString(_mountPoint) ?? _defaultMountPoint();

// Read and remove a bool property. This functions exists to facilitate migration to the new
// repository config system where config values are stored in the repository metadata.
bool? takeRepositoryBool(String repoName, String key) {
Expand Down Expand Up @@ -349,7 +357,7 @@ class Settings with AppLogger {
// We used to have all the repositories in a single place in the internal
// memory. The disadvantage was that the user had no access to them and
// thus couldn't back them up or put them on an SD card.
final dir = io.Directory(p.join(
final dir = Directory(p.join(
(await path_provider.getApplicationSupportDirectory()).path,
Constants.folderRepositoriesName));

Expand Down Expand Up @@ -397,3 +405,19 @@ class _CachedString {
}
}
}

String? _defaultMountPoint() {
if (Platform.isLinux || Platform.isMacOS) {
final home = Platform.environment['HOME'];

if (home == null) {
return null;
}

return '$home/Ouisync';
} else if (Platform.isWindows) {
return 'O:';
} else {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,8 @@ class RepositoryCreation extends HookWidget with AppLogger {

try {
shareToken = await ShareToken.fromString(cubit.session, initialToken);

if (shareToken == null) {
throw "Failed to construct the token from \"$initialToken\"";
}
} catch (e, st) {
loggy.app('Extract repository token exception', e, st);
loggy.error('Extract repository token exception:', e, st);
showSnackBar(context, message: S.current.messageErrorTokenInvalid);
}

Expand Down
Loading

0 comments on commit 05fe71c

Please sign in to comment.