Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions lib/hive/hive_adapters.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion lib/hive/hive_adapters.g.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ types:
index: 4
AppPreferences:
typeId: 28
nextIndex: 20
nextIndex: 21
fields:
defaultThemeProfileIdForNewStrategies:
index: 0
Expand Down Expand Up @@ -413,6 +413,8 @@ types:
index: 18
backgroundDotOpacity:
index: 19
welcomeTourSeen:
index: 20
PlacedViewConeAgent:
typeId: 29
nextIndex: 11
Expand Down
3 changes: 3 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import 'package:icarus/providers/folder_provider.dart';
import 'package:icarus/providers/map_provider.dart';
import 'package:icarus/providers/strategy_provider.dart';
import 'package:icarus/providers/user_preferences_provider.dart';
import 'package:icarus/providers/welcome_tour_provider.dart';
import 'package:icarus/share/current_share_origin.dart';
import 'package:icarus/share/share_link_format.dart';
import 'package:icarus/services/auth_callback_uri.dart';
Expand Down Expand Up @@ -225,6 +226,8 @@ Future<void> main(List<String> args) async {
"Icarus: Valorant Strategies & Line ups ${Settings.versionName}",
);
}
appProviderContainer.read(launchOpensFileOrLinkProvider.notifier).state =
launch.fileOpenArgs.isNotEmpty || _bufferedDeepLinks.isNotEmpty;
runApp(
UncontrolledProviderScope(
container: appProviderContainer,
Expand Down
14 changes: 14 additions & 0 deletions lib/providers/user_preferences_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ class AppPreferences extends HiveObject {
/// (hidden) to 1 (the original look).
final double backgroundDotOpacity;

/// Whether this install has shown the welcome tour, the card that
/// introduces accounts, the cloud, and replays. Stores written before the
/// tour existed read false, so updating users see it once.
final bool welcomeTourSeen;

AppPreferences({
required this.defaultThemeProfileIdForNewStrategies,
this.autosaveEnabled = true,
Expand All @@ -153,6 +158,7 @@ class AppPreferences extends HiveObject {
this.discordPresenceEnabled = true,
this.videoExportStepDurationSeconds = 3.0,
this.backgroundDotOpacity = 1.0,
this.welcomeTourSeen = false,
}) : customColorValues = List.unmodifiable(customColorValues ?? const []),
customShortcutBindings =
Map.unmodifiable(customShortcutBindings ?? const {});
Expand All @@ -177,6 +183,7 @@ class AppPreferences extends HiveObject {
bool? discordPresenceEnabled,
double? videoExportStepDurationSeconds,
double? backgroundDotOpacity,
bool? welcomeTourSeen,
}) {
return AppPreferences(
defaultThemeProfileIdForNewStrategies:
Expand Down Expand Up @@ -208,6 +215,7 @@ class AppPreferences extends HiveObject {
videoExportStepDurationSeconds:
videoExportStepDurationSeconds ?? this.videoExportStepDurationSeconds,
backgroundDotOpacity: backgroundDotOpacity ?? this.backgroundDotOpacity,
welcomeTourSeen: welcomeTourSeen ?? this.welcomeTourSeen,
);
}
}
Expand Down Expand Up @@ -692,6 +700,12 @@ class AppPreferencesNotifier extends Notifier<AppPreferences> {
);
}

Future<void> markWelcomeTourSeen() {
return _updatePreferences(
(current) => current.copyWith(welcomeTourSeen: true),
);
}

Future<void> setDrawingDefaults({
int? colorValue,
double? thickness,
Expand Down
12 changes: 12 additions & 0 deletions lib/providers/welcome_tour_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';

/// True when this launch was asked to open something: a file (.ica, .vrf)
/// or a link (a sign-in callback, a share link). Set in main before runApp.
/// That launch belongs to the thing it opens, so the welcome tour waits for
/// the next plain launch.
final launchOpensFileOrLinkProvider = StateProvider<bool>((ref) => false);

/// Whether this run of the app has already decided about the welcome tour.
/// The library screen can be built more than once in a run; the decision is
/// made only the first time.
final welcomeTourLaunchCheckedProvider = StateProvider<bool>((ref) => false);
8 changes: 5 additions & 3 deletions lib/services/windows_desktop_update_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ class WindowsDesktopUpdateController extends ChangeNotifier {
_ownsHttpClient = httpClient == null,
_restartService = restartService ??
WindowsDesktopUpdateRestartService(updater: updater) {
if (autoCheck) {
unawaited(checkVersion());
}
firstCheck = autoCheck ? checkVersion() : Future<void>.value();
}

/// Completes when the check started at creation has answered, so callers
/// can tell "no update" from "not known yet". It never fails.
late final Future<void> firstCheck;

/// Creates a controller pre-seeded with fake update data so the dialog can
/// be designed/previewed. Download and restart are simulated.
factory WindowsDesktopUpdateController.debugPreview({
Expand Down
29 changes: 26 additions & 3 deletions lib/widgets/dialogs/release_notes_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,32 +1,55 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:icarus/config/platform_policy.dart';
import 'package:icarus/const/release_notes.dart';
import 'package:icarus/providers/release_notes_provider.dart';
import 'package:icarus/widgets/desktop_update_dialog.dart';
import 'package:icarus/widgets/dialogs/welcome_tour/welcome_tour_dialog.dart';
import 'package:icarus/widgets/dot_matrix_loaders.dart';
import 'package:shadcn_ui/shadcn_ui.dart';

/// Every shipped version's patch notes, newest first, with the installed
/// version marked. Read-only: updating stays with the update dialog.
/// version marked. Read-only: updating stays with the update dialog. On
/// desktop it also leads back to the welcome tour.
class ReleaseNotesDialog extends ConsumerWidget {
const ReleaseNotesDialog({super.key});

static const double _width = 520;
static const double _bodyHeight = 460;

static Future<void> show(BuildContext context) {
return showShadDialog<void>(
static Future<void> show(BuildContext context) async {
final showTour = await showShadDialog<bool>(
context: context,
builder: (context) => const ReleaseNotesDialog(),
);
// The tour replaces this dialog rather than stacking on it.
if (showTour == true && context.mounted) {
await WelcomeTourDialog.show(context);
}
}

@override
Widget build(BuildContext context, WidgetRef ref) {
final notes = ref.watch(releaseNotesProvider);
final theme = ShadTheme.of(context);

return ShadDialog(
title: const Text("What's new"),
// The web beta never shows the tour, so it has nothing to go back to.
description: ref.watch(platformPolicyProvider).isWebBeta
? null
: Align(
alignment: Alignment.centerLeft,
child: ShadButton.link(
key: const ValueKey('whats-new-welcome-tour'),
height: 20,
padding: EdgeInsets.zero,
foregroundColor: theme.colorScheme.mutedForeground,
hoverForegroundColor: theme.colorScheme.foreground,
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Show the welcome tour'),
),
),
constraints: const BoxConstraints(maxWidth: _width),
child: SizedBox(
height: _bodyHeight,
Expand Down
Loading
Loading