mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: open latest view after launch
This commit is contained in:
parent
b3764e601f
commit
c8945133dc
@ -14,6 +14,7 @@ import 'package:app_flowy/workspace/application/menu/prelude.dart';
|
|||||||
import 'package:app_flowy/user/application/prelude.dart';
|
import 'package:app_flowy/user/application/prelude.dart';
|
||||||
import 'package:app_flowy/user/presentation/router.dart';
|
import 'package:app_flowy/user/presentation/router.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||||
|
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||||
@ -50,6 +51,8 @@ void _resolveUserDeps(GetIt getIt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _resolveHomeDeps(GetIt getIt) {
|
void _resolveHomeDeps(GetIt getIt) {
|
||||||
|
getIt.registerSingleton(MenuSharedState());
|
||||||
|
|
||||||
getIt.registerFactoryParam<UserListener, UserProfile, void>(
|
getIt.registerFactoryParam<UserListener, UserProfile, void>(
|
||||||
(user, _) => UserListener(user: user),
|
(user, _) => UserListener(user: user),
|
||||||
);
|
);
|
||||||
@ -113,8 +116,8 @@ void _resolveFolderDeps(GetIt getIt) {
|
|||||||
getIt.registerFactoryParam<AppBloc, App, void>(
|
getIt.registerFactoryParam<AppBloc, App, void>(
|
||||||
(app, _) => AppBloc(
|
(app, _) => AppBloc(
|
||||||
app: app,
|
app: app,
|
||||||
service: AppService(),
|
appService: AppService(),
|
||||||
listener: AppListener(appId: app.id),
|
appListener: AppListener(appId: app.id),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import 'package:app_flowy/plugin/plugin.dart';
|
import 'package:app_flowy/plugin/plugin.dart';
|
||||||
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
import 'package:app_flowy/workspace/application/app/app_listener.dart';
|
import 'package:app_flowy/workspace/application/app/app_listener.dart';
|
||||||
import 'package:app_flowy/workspace/application/app/app_service.dart';
|
import 'package:app_flowy/workspace/application/app/app_service.dart';
|
||||||
|
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
||||||
|
import 'package:expandable/expandable.dart';
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
@ -13,71 +17,83 @@ part 'app_bloc.freezed.dart';
|
|||||||
|
|
||||||
class AppBloc extends Bloc<AppEvent, AppState> {
|
class AppBloc extends Bloc<AppEvent, AppState> {
|
||||||
final App app;
|
final App app;
|
||||||
final AppService service;
|
final AppService appService;
|
||||||
final AppListener listener;
|
final AppListener appListener;
|
||||||
|
|
||||||
AppBloc({required this.app, required this.service, required this.listener}) : super(AppState.initial(app)) {
|
AppBloc({required this.app, required this.appService, required this.appListener}) : super(AppState.initial(app)) {
|
||||||
on<AppEvent>((event, emit) async {
|
on<AppEvent>((event, emit) async {
|
||||||
await event.map(initial: (e) async {
|
await event.map(initial: (e) async {
|
||||||
listener.start(
|
_startListening();
|
||||||
viewsChanged: _handleViewsChanged,
|
await _loadViews(emit);
|
||||||
appUpdated: (app) => add(AppEvent.appDidUpdate(app)),
|
|
||||||
);
|
|
||||||
await _fetchViews(emit);
|
|
||||||
}, createView: (CreateView value) async {
|
}, createView: (CreateView value) async {
|
||||||
final viewOrFailed = await service.createView(
|
await _createView(value, emit);
|
||||||
appId: app.id,
|
}, didReceiveViewUpdated: (e) async {
|
||||||
name: value.name,
|
await _didReceiveViewUpdated(e.views, emit);
|
||||||
desc: value.desc,
|
|
||||||
dataType: value.dataType,
|
|
||||||
pluginType: value.pluginType,
|
|
||||||
);
|
|
||||||
viewOrFailed.fold(
|
|
||||||
(view) => emit(state.copyWith(
|
|
||||||
latestCreatedView: view,
|
|
||||||
successOrFailure: left(unit),
|
|
||||||
)),
|
|
||||||
(error) {
|
|
||||||
Log.error(error);
|
|
||||||
emit(state.copyWith(successOrFailure: right(error)));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}, didReceiveViews: (e) async {
|
|
||||||
await handleDidReceiveViews(e.views, emit);
|
|
||||||
}, delete: (e) async {
|
}, delete: (e) async {
|
||||||
final result = await service.delete(appId: app.id);
|
await _deleteView(emit);
|
||||||
result.fold(
|
|
||||||
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
|
||||||
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
|
||||||
);
|
|
||||||
}, rename: (e) async {
|
}, rename: (e) async {
|
||||||
final result = await service.updateApp(appId: app.id, name: e.newName);
|
await _renameView(e, emit);
|
||||||
result.fold(
|
|
||||||
(l) => emit(state.copyWith(successOrFailure: left(unit))),
|
|
||||||
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
|
||||||
);
|
|
||||||
}, appDidUpdate: (e) async {
|
}, appDidUpdate: (e) async {
|
||||||
emit(state.copyWith(app: e.app));
|
emit(state.copyWith(app: e.app));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
void _startListening() {
|
||||||
Future<void> close() async {
|
appListener.start(
|
||||||
await listener.close();
|
viewsChanged: (result) {
|
||||||
return super.close();
|
result.fold(
|
||||||
|
(views) => add(AppEvent.didReceiveViewUpdated(views)),
|
||||||
|
(error) => Log.error(error),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
appUpdated: (app) => add(AppEvent.appDidUpdate(app)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleViewsChanged(Either<List<View>, FlowyError> result) {
|
Future<void> _renameView(Rename e, Emitter<AppState> emit) async {
|
||||||
|
final result = await appService.updateApp(appId: app.id, name: e.newName);
|
||||||
result.fold(
|
result.fold(
|
||||||
(views) => add(AppEvent.didReceiveViews(views)),
|
(l) => emit(state.copyWith(successOrFailure: left(unit))),
|
||||||
|
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _deleteView(Emitter<AppState> emit) async {
|
||||||
|
final result = await appService.delete(appId: app.id);
|
||||||
|
result.fold(
|
||||||
|
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
||||||
|
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
|
||||||
|
final viewOrFailed = await appService.createView(
|
||||||
|
appId: app.id,
|
||||||
|
name: value.name,
|
||||||
|
desc: value.desc,
|
||||||
|
dataType: value.dataType,
|
||||||
|
pluginType: value.pluginType,
|
||||||
|
);
|
||||||
|
viewOrFailed.fold(
|
||||||
|
(view) => emit(state.copyWith(
|
||||||
|
latestCreatedView: view,
|
||||||
|
successOrFailure: left(unit),
|
||||||
|
)),
|
||||||
(error) {
|
(error) {
|
||||||
Log.error(error);
|
Log.error(error);
|
||||||
|
emit(state.copyWith(successOrFailure: right(error)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleDidReceiveViews(List<View> views, Emitter<AppState> emit) async {
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await appListener.close();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _didReceiveViewUpdated(List<View> views, Emitter<AppState> emit) async {
|
||||||
final latestCreatedView = state.latestCreatedView;
|
final latestCreatedView = state.latestCreatedView;
|
||||||
AppState newState = state.copyWith(views: views);
|
AppState newState = state.copyWith(views: views);
|
||||||
if (latestCreatedView != null) {
|
if (latestCreatedView != null) {
|
||||||
@ -90,10 +106,10 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
emit(newState);
|
emit(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchViews(Emitter<AppState> emit) async {
|
Future<void> _loadViews(Emitter<AppState> emit) async {
|
||||||
final viewsOrFailed = await service.getViews(appId: app.id);
|
final viewsOrFailed = await appService.getViews(appId: app.id);
|
||||||
viewsOrFailed.fold(
|
viewsOrFailed.fold(
|
||||||
(apps) => emit(state.copyWith(views: apps)),
|
(views) => emit(state.copyWith(views: views)),
|
||||||
(error) {
|
(error) {
|
||||||
Log.error(error);
|
Log.error(error);
|
||||||
emit(state.copyWith(successOrFailure: right(error)));
|
emit(state.copyWith(successOrFailure: right(error)));
|
||||||
@ -113,7 +129,7 @@ class AppEvent with _$AppEvent {
|
|||||||
) = CreateView;
|
) = CreateView;
|
||||||
const factory AppEvent.delete() = Delete;
|
const factory AppEvent.delete() = Delete;
|
||||||
const factory AppEvent.rename(String newName) = Rename;
|
const factory AppEvent.rename(String newName) = Rename;
|
||||||
const factory AppEvent.didReceiveViews(List<View> views) = ReceiveViews;
|
const factory AppEvent.didReceiveViewUpdated(List<View> views) = ReceiveViews;
|
||||||
const factory AppEvent.appDidUpdate(App app) = AppDidUpdate;
|
const factory AppEvent.appDidUpdate(App app) = AppDidUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,17 +137,62 @@ class AppEvent with _$AppEvent {
|
|||||||
class AppState with _$AppState {
|
class AppState with _$AppState {
|
||||||
const factory AppState({
|
const factory AppState({
|
||||||
required App app,
|
required App app,
|
||||||
required bool isLoading,
|
required List<View> views,
|
||||||
required List<View>? views,
|
|
||||||
View? latestCreatedView,
|
View? latestCreatedView,
|
||||||
required Either<Unit, FlowyError> successOrFailure,
|
required Either<Unit, FlowyError> successOrFailure,
|
||||||
}) = _AppState;
|
}) = _AppState;
|
||||||
|
|
||||||
factory AppState.initial(App app) => AppState(
|
factory AppState.initial(App app) => AppState(
|
||||||
app: app,
|
app: app,
|
||||||
isLoading: false,
|
views: [],
|
||||||
views: null,
|
|
||||||
latestCreatedView: null,
|
|
||||||
successOrFailure: left(unit),
|
successOrFailure: left(unit),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AppViewDataNotifier extends ChangeNotifier {
|
||||||
|
List<View> _views = [];
|
||||||
|
View? _selectedView;
|
||||||
|
ExpandableController expandController = ExpandableController(initialExpanded: false);
|
||||||
|
|
||||||
|
AppViewDataNotifier() {
|
||||||
|
_setLatestView(getIt<MenuSharedState>().latestOpenView);
|
||||||
|
getIt<MenuSharedState>().addLatestViewListener((view) {
|
||||||
|
_setLatestView(view);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setLatestView(View? view) {
|
||||||
|
view?.freeze();
|
||||||
|
_selectedView = view;
|
||||||
|
_expandIfNeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
View? get selectedView => _selectedView;
|
||||||
|
|
||||||
|
set views(List<View> views) {
|
||||||
|
if (_views != views) {
|
||||||
|
_views = views;
|
||||||
|
_expandIfNeed();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _expandIfNeed() {
|
||||||
|
if (_selectedView == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_views.contains(_selectedView!)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expandController.expanded == false) {
|
||||||
|
// Workaround: Delay 150 milliseconds to make the smooth animation while expanding
|
||||||
|
Future.delayed(const Duration(milliseconds: 150), () {
|
||||||
|
expandController.expanded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnmodifiableListView<View> get views => UnmodifiableListView(_views);
|
||||||
|
}
|
||||||
|
@ -121,6 +121,9 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier,
|
collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final latestView = widget.workspaceSetting.hasLatestView() ? widget.workspaceSetting.latestView : null;
|
||||||
|
getIt<MenuSharedState>().latestOpenView = latestView;
|
||||||
|
|
||||||
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
|
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import 'package:app_flowy/workspace/application/appearance.dart';
|
|||||||
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
||||||
import 'package:expandable/expandable.dart';
|
import 'package:expandable/expandable.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:app_flowy/startup/startup.dart';
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
@ -19,11 +18,11 @@ class MenuApp extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MenuAppState extends State<MenuApp> {
|
class _MenuAppState extends State<MenuApp> {
|
||||||
late AppDataNotifier notifier;
|
late AppViewDataNotifier notifier;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
notifier = AppDataNotifier();
|
notifier = AppViewDataNotifier();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,30 +38,34 @@ class _MenuAppState extends State<MenuApp> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: BlocSelector<AppBloc, AppState, AppDataNotifier>(
|
child: MultiBlocListener(
|
||||||
selector: (state) {
|
listeners: [
|
||||||
final menuSharedState = Provider.of<MenuSharedState>(context, listen: false);
|
BlocListener<AppBloc, AppState>(
|
||||||
if (state.latestCreatedView != null) {
|
listenWhen: (p, c) => p.latestCreatedView != c.latestCreatedView,
|
||||||
menuSharedState.forcedOpenView.value = state.latestCreatedView!;
|
listener: (context, state) => getIt<MenuSharedState>().latestOpenView = state.latestCreatedView,
|
||||||
}
|
|
||||||
|
|
||||||
notifier.views = state.views;
|
|
||||||
notifier.selectedView = menuSharedState.selectedView.value;
|
|
||||||
return notifier;
|
|
||||||
},
|
|
||||||
builder: (context, notifier) => ChangeNotifierProvider.value(
|
|
||||||
value: notifier,
|
|
||||||
child: Consumer(
|
|
||||||
builder: (BuildContext context, AppDataNotifier notifier, Widget? child) {
|
|
||||||
return expandableWrapper(context, notifier);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
BlocListener<AppBloc, AppState>(
|
||||||
|
listenWhen: (p, c) => p.views != c.views,
|
||||||
|
listener: (context, state) => notifier.views = state.views,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: BlocBuilder<AppBloc, AppState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return ChangeNotifierProvider.value(
|
||||||
|
value: notifier,
|
||||||
|
child: Consumer<AppViewDataNotifier>(
|
||||||
|
builder: (context, notifier, _) {
|
||||||
|
return expandableWrapper(context, notifier);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpandableNotifier expandableWrapper(BuildContext context, AppDataNotifier notifier) {
|
ExpandableNotifier expandableWrapper(BuildContext context, AppViewDataNotifier notifier) {
|
||||||
return ExpandableNotifier(
|
return ExpandableNotifier(
|
||||||
controller: notifier.expandController,
|
controller: notifier.expandController,
|
||||||
child: ScrollOnExpand(
|
child: ScrollOnExpand(
|
||||||
@ -92,11 +95,11 @@ class _MenuAppState extends State<MenuApp> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _renderViewSection(AppDataNotifier notifier) {
|
Widget _renderViewSection(AppViewDataNotifier notifier) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [ChangeNotifierProvider.value(value: notifier)],
|
providers: [ChangeNotifierProvider.value(value: notifier)],
|
||||||
child: Consumer(
|
child: Consumer(
|
||||||
builder: (context, AppDataNotifier notifier, child) {
|
builder: (context, AppViewDataNotifier notifier, child) {
|
||||||
return ViewSection(appData: notifier);
|
return ViewSection(appData: notifier);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -119,44 +122,3 @@ class MenuAppSizes {
|
|||||||
static double scale = 1;
|
static double scale = 1;
|
||||||
static double get expandedPadding => iconSize * scale + headerPadding;
|
static double get expandedPadding => iconSize * scale + headerPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppDataNotifier extends ChangeNotifier {
|
|
||||||
List<View> _views = [];
|
|
||||||
View? _selectedView;
|
|
||||||
ExpandableController expandController = ExpandableController(initialExpanded: false);
|
|
||||||
|
|
||||||
AppDataNotifier();
|
|
||||||
|
|
||||||
set selectedView(View? view) {
|
|
||||||
_selectedView = view;
|
|
||||||
|
|
||||||
if (view != null && _views.isNotEmpty) {
|
|
||||||
final isExpanded = _views.contains(view);
|
|
||||||
if (expandController.expanded == false && expandController.expanded != isExpanded) {
|
|
||||||
// Workaround: Delay 150 milliseconds to make the smooth animation while expanding
|
|
||||||
Future.delayed(const Duration(milliseconds: 150), () {
|
|
||||||
expandController.expanded = isExpanded;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
View? get selectedView => _selectedView;
|
|
||||||
|
|
||||||
set views(List<View>? views) {
|
|
||||||
if (views == null) {
|
|
||||||
if (_views.isNotEmpty) {
|
|
||||||
_views = List.empty(growable: false);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_views != views) {
|
|
||||||
_views = views;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<View> get views => _views;
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:app_flowy/startup/startup.dart';
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
|
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
|
||||||
import 'package:app_flowy/workspace/application/view/view_ext.dart';
|
import 'package:app_flowy/workspace/application/view/view_ext.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
||||||
@ -13,13 +14,13 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
import 'item.dart';
|
import 'item.dart';
|
||||||
|
|
||||||
class ViewSection extends StatelessWidget {
|
class ViewSection extends StatelessWidget {
|
||||||
final AppDataNotifier appData;
|
final AppViewDataNotifier appData;
|
||||||
const ViewSection({Key? key, required this.appData}) : super(key: key);
|
const ViewSection({Key? key, required this.appData}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// The ViewSectionNotifier will be updated after AppDataNotifier changed passed by parent widget
|
// The ViewSectionNotifier will be updated after AppDataNotifier changed passed by parent widget
|
||||||
return ChangeNotifierProxyProvider<AppDataNotifier, ViewSectionNotifier>(
|
return ChangeNotifierProxyProvider<AppViewDataNotifier, ViewSectionNotifier>(
|
||||||
create: (_) {
|
create: (_) {
|
||||||
return ViewSectionNotifier(
|
return ViewSectionNotifier(
|
||||||
context: context,
|
context: context,
|
||||||
@ -29,7 +30,7 @@ class ViewSection extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
update: (_, notifier, controller) => controller!..update(notifier),
|
update: (_, notifier, controller) => controller!..update(notifier),
|
||||||
child: Consumer(builder: (context, ViewSectionNotifier notifier, child) {
|
child: Consumer(builder: (context, ViewSectionNotifier notifier, child) {
|
||||||
return RenderSectionItems(views: notifier.views);
|
return _SectionItems(views: notifier.views);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -63,16 +64,16 @@ class ViewSection extends StatelessWidget {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderSectionItems extends StatefulWidget {
|
class _SectionItems extends StatefulWidget {
|
||||||
const RenderSectionItems({Key? key, required this.views}) : super(key: key);
|
const _SectionItems({Key? key, required this.views}) : super(key: key);
|
||||||
|
|
||||||
final List<View> views;
|
final List<View> views;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RenderSectionItems> createState() => _RenderSectionItemsState();
|
State<_SectionItems> createState() => _SectionItemsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RenderSectionItemsState extends State<RenderSectionItems> {
|
class _SectionItemsState extends State<_SectionItems> {
|
||||||
List<View> views = <View>[];
|
List<View> views = <View>[];
|
||||||
|
|
||||||
/// Maps the hasmap value of the section items to their index in the reorderable list.
|
/// Maps the hasmap value of the section items to their index in the reorderable list.
|
||||||
@ -123,10 +124,7 @@ class _RenderSectionItemsState extends State<RenderSectionItems> {
|
|||||||
(view) => ViewSectionItem(
|
(view) => ViewSectionItem(
|
||||||
view: view,
|
view: view,
|
||||||
isSelected: _isViewSelected(context, view.id),
|
isSelected: _isViewSelected(context, view.id),
|
||||||
onSelected: (view) {
|
onSelected: (view) => getIt<MenuSharedState>().latestOpenView = view,
|
||||||
context.read<ViewSectionNotifier>().selectedView = view;
|
|
||||||
Provider.of<MenuSharedState>(context, listen: false).selectedView.value = view;
|
|
||||||
},
|
|
||||||
).padding(vertical: 4),
|
).padding(vertical: 4),
|
||||||
)
|
)
|
||||||
.toList()[index],
|
.toList()[index],
|
||||||
@ -150,6 +148,7 @@ class ViewSectionNotifier with ChangeNotifier {
|
|||||||
List<View> _views;
|
List<View> _views;
|
||||||
View? _selectedView;
|
View? _selectedView;
|
||||||
Timer? _notifyListenerOperation;
|
Timer? _notifyListenerOperation;
|
||||||
|
VoidCallback? _latestViewDidChangeFn;
|
||||||
|
|
||||||
ViewSectionNotifier({
|
ViewSectionNotifier({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
@ -157,16 +156,10 @@ class ViewSectionNotifier with ChangeNotifier {
|
|||||||
View? initialSelectedView,
|
View? initialSelectedView,
|
||||||
}) : _views = views,
|
}) : _views = views,
|
||||||
_selectedView = initialSelectedView {
|
_selectedView = initialSelectedView {
|
||||||
final menuSharedState = Provider.of<MenuSharedState>(context, listen: false);
|
_latestViewDidChangeFn = getIt<MenuSharedState>().addLatestViewListener((latestOpenView) {
|
||||||
// The forcedOpenView will be the view after creating the new view
|
if (_views.contains(latestOpenView)) {
|
||||||
menuSharedState.forcedOpenView.addPublishListener((forcedOpenView) {
|
selectedView = latestOpenView;
|
||||||
selectedView = forcedOpenView;
|
} else {
|
||||||
});
|
|
||||||
|
|
||||||
menuSharedState.selectedView.addListener(() {
|
|
||||||
// Cancel the selected view of this section by setting the selectedView to null
|
|
||||||
// that will notify the listener to refresh the ViewSection UI
|
|
||||||
if (menuSharedState.selectedView.value != _selectedView) {
|
|
||||||
selectedView = null;
|
selectedView = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -199,7 +192,7 @@ class ViewSectionNotifier with ChangeNotifier {
|
|||||||
|
|
||||||
View? get selectedView => _selectedView;
|
View? get selectedView => _selectedView;
|
||||||
|
|
||||||
void update(AppDataNotifier notifier) {
|
void update(AppViewDataNotifier notifier) {
|
||||||
views = notifier.views;
|
views = notifier.views;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +209,10 @@ class ViewSectionNotifier with ChangeNotifier {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
isDisposed = true;
|
isDisposed = true;
|
||||||
_notifyListenerOperation?.cancel();
|
_notifyListenerOperation?.cancel();
|
||||||
|
if (_latestViewDidChangeFn != null) {
|
||||||
|
getIt<MenuSharedState>().removeLatestViewListener(_latestViewDidChangeFn!);
|
||||||
|
_latestViewDidChangeFn = null;
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,30 +88,24 @@ class _HomeMenuState extends State<HomeMenu> {
|
|||||||
final theme = context.watch<AppTheme>();
|
final theme = context.watch<AppTheme>();
|
||||||
return Container(
|
return Container(
|
||||||
color: theme.bg1,
|
color: theme.bg1,
|
||||||
child: ChangeNotifierProvider(
|
child: Column(
|
||||||
create: (_) =>
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
MenuSharedState(view: widget.workspaceSetting.hasLatestView() ? widget.workspaceSetting.latestView : null),
|
children: [
|
||||||
child: Consumer(builder: (context, MenuSharedState sharedState, child) {
|
Expanded(
|
||||||
return Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
const MenuTopBar(),
|
||||||
child: Column(
|
const VSpace(10),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
_renderApps(context),
|
||||||
children: [
|
],
|
||||||
const MenuTopBar(),
|
).padding(horizontal: Insets.l),
|
||||||
const VSpace(10),
|
),
|
||||||
_renderApps(context),
|
const VSpace(20),
|
||||||
],
|
_renderTrash(context).padding(horizontal: Insets.l),
|
||||||
).padding(horizontal: Insets.l),
|
const VSpace(20),
|
||||||
),
|
_renderNewAppButton(context),
|
||||||
const VSpace(20),
|
],
|
||||||
_renderTrash(context).padding(horizontal: Insets.l),
|
|
||||||
const VSpace(20),
|
|
||||||
_renderNewAppButton(context),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -201,18 +195,32 @@ class _HomeMenuState extends State<HomeMenu> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MenuSharedState extends ChangeNotifier {
|
class MenuSharedState {
|
||||||
PublishNotifier<View> forcedOpenView = PublishNotifier();
|
final ValueNotifier<View?> _latestOpenView = ValueNotifier<View?>(null);
|
||||||
ValueNotifier<View?> selectedView = ValueNotifier<View?>(null);
|
|
||||||
|
|
||||||
MenuSharedState({View? view}) {
|
MenuSharedState({View? view}) {
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
selectedView.value = view;
|
_latestOpenView.value = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
View? get latestOpenView => _latestOpenView.value;
|
||||||
|
|
||||||
|
set latestOpenView(View? view) {
|
||||||
|
_latestOpenView.value = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
VoidCallback addLatestViewListener(void Function(View?) latestViewDidChange) {
|
||||||
|
onChanged() {
|
||||||
|
latestViewDidChange(_latestOpenView.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
forcedOpenView.addPublishListener((view) {
|
_latestOpenView.addListener(onChanged);
|
||||||
selectedView.value = view;
|
return onChanged;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
void removeLatestViewListener(VoidCallback fn) {
|
||||||
|
_latestOpenView.removeListener(fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class MenuTrash extends StatelessWidget {
|
|||||||
height: 26,
|
height: 26,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Provider.of<MenuSharedState>(context, listen: false).selectedView.value = null;
|
getIt<MenuSharedState>().latestOpenView = null;
|
||||||
getIt<HomeStackManager>().setPlugin(makePlugin(pluginType: DefaultPlugin.trash.type()));
|
getIt<HomeStackManager>().setPlugin(makePlugin(pluginType: DefaultPlugin.trash.type()));
|
||||||
},
|
},
|
||||||
child: _render(context),
|
child: _render(context),
|
||||||
|
Loading…
Reference in New Issue
Block a user