mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: create option with different color
This commit is contained in:
parent
bd0907b0a4
commit
0b8083cfc6
@ -17,6 +17,7 @@ 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/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show EditFieldContext;
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
|
||||
@ -201,8 +202,8 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<FieldSwitcherBloc, SwitchFieldContext, void>(
|
||||
(context, _) => FieldSwitcherBloc(context),
|
||||
getIt.registerFactoryParam<FieldEditorPannelBloc, EditFieldContext, void>(
|
||||
(context, _) => FieldEditorPannelBloc(context),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<DateTypeOptionBloc, DateTypeOption, void>(
|
||||
|
@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'field_service.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
|
||||
part 'field_editor_bloc.freezed.dart';
|
||||
|
||||
@ -25,10 +26,13 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
await _getEditFieldContext(emit);
|
||||
},
|
||||
updateName: (_UpdateName value) {
|
||||
emit(state.copyWith(fieldName: value.name));
|
||||
final newContext = _updateEditContext(name: value.name);
|
||||
emit(state.copyWith(editFieldContext: newContext));
|
||||
},
|
||||
switchField: (_SwitchField value) {
|
||||
emit(state.copyWith(field: Some(value.field), typeOptionData: value.typeOptionData));
|
||||
updateField: (_UpdateField value) {
|
||||
final newContext = _updateEditContext(field: value.field, typeOptionData: value.typeOptionData);
|
||||
|
||||
emit(state.copyWith(editFieldContext: newContext));
|
||||
},
|
||||
done: (_Done value) async {
|
||||
await _saveField(emit);
|
||||
@ -43,14 +47,49 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
return super.close();
|
||||
}
|
||||
|
||||
Option<EditFieldContext> _updateEditContext({
|
||||
String? name,
|
||||
Field? field,
|
||||
List<int>? typeOptionData,
|
||||
}) {
|
||||
return state.editFieldContext.fold(
|
||||
() => none(),
|
||||
(context) {
|
||||
context.freeze();
|
||||
final newContext = context.rebuild((newContext) {
|
||||
newContext.gridField.rebuild((newField) {
|
||||
if (name != null) {
|
||||
newField.name = name;
|
||||
}
|
||||
|
||||
newContext.gridField = newField;
|
||||
});
|
||||
|
||||
if (field != null) {
|
||||
newContext.gridField = field;
|
||||
}
|
||||
|
||||
if (typeOptionData != null) {
|
||||
newContext.typeOptionData = typeOptionData;
|
||||
}
|
||||
});
|
||||
service.insertField(
|
||||
field: newContext.gridField,
|
||||
typeOptionData: newContext.typeOptionData,
|
||||
);
|
||||
|
||||
return Some(newContext);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _saveField(Emitter<FieldEditorState> emit) async {
|
||||
await state.field.fold(
|
||||
await state.editFieldContext.fold(
|
||||
() async => null,
|
||||
(field) async {
|
||||
field.name = state.fieldName;
|
||||
(context) async {
|
||||
final result = await service.insertField(
|
||||
field: field,
|
||||
typeOptionData: state.typeOptionData,
|
||||
field: context.gridField,
|
||||
typeOptionData: context.typeOptionData,
|
||||
);
|
||||
result.fold((l) => null, (r) => null);
|
||||
},
|
||||
@ -60,11 +99,9 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
Future<void> _getEditFieldContext(Emitter<FieldEditorState> emit) async {
|
||||
final result = await _loader.load();
|
||||
result.fold(
|
||||
(editContext) {
|
||||
(context) {
|
||||
emit(state.copyWith(
|
||||
field: Some(editContext.gridField),
|
||||
typeOptionData: editContext.typeOptionData,
|
||||
fieldName: editContext.gridField.name,
|
||||
editFieldContext: Some(context),
|
||||
));
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
@ -76,25 +113,21 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
class FieldEditorEvent with _$FieldEditorEvent {
|
||||
const factory FieldEditorEvent.initial() = _InitialField;
|
||||
const factory FieldEditorEvent.updateName(String name) = _UpdateName;
|
||||
const factory FieldEditorEvent.switchField(Field field, Uint8List typeOptionData) = _SwitchField;
|
||||
const factory FieldEditorEvent.updateField(Field field, Uint8List typeOptionData) = _UpdateField;
|
||||
const factory FieldEditorEvent.done() = _Done;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FieldEditorState with _$FieldEditorState {
|
||||
const factory FieldEditorState({
|
||||
required String fieldName,
|
||||
required String gridId,
|
||||
required String errorText,
|
||||
required Option<Field> field,
|
||||
required List<int> typeOptionData,
|
||||
required Option<EditFieldContext> editFieldContext,
|
||||
}) = _FieldEditorState;
|
||||
|
||||
factory FieldEditorState.initial(String gridId) => FieldEditorState(
|
||||
gridId: gridId,
|
||||
fieldName: '',
|
||||
field: none(),
|
||||
editFieldContext: none(),
|
||||
errorText: '',
|
||||
typeOptionData: List<int>.empty(),
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
part 'field_editor_pannel_bloc.freezed.dart';
|
||||
|
||||
class FieldEditorPannelBloc extends Bloc<FieldEditorPannelEvent, FieldEditorPannelState> {
|
||||
FieldEditorPannelBloc(EditFieldContext editContext) : super(FieldEditorPannelState.initial(editContext)) {
|
||||
on<FieldEditorPannelEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
toFieldType: (_ToFieldType value) async {
|
||||
emit(state.copyWith(
|
||||
field: value.field,
|
||||
typeOptionData: Uint8List.fromList(value.typeOptionData),
|
||||
));
|
||||
},
|
||||
didUpdateTypeOptionData: (_DidUpdateTypeOptionData value) {
|
||||
emit(state.copyWith(typeOptionData: value.typeOptionData));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FieldEditorPannelEvent with _$FieldEditorPannelEvent {
|
||||
const factory FieldEditorPannelEvent.toFieldType(Field field, List<int> typeOptionData) = _ToFieldType;
|
||||
const factory FieldEditorPannelEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FieldEditorPannelState with _$FieldEditorPannelState {
|
||||
const factory FieldEditorPannelState({
|
||||
required String gridId,
|
||||
required Field field,
|
||||
required Uint8List typeOptionData,
|
||||
}) = _FieldEditorPannelState;
|
||||
|
||||
factory FieldEditorPannelState.initial(EditFieldContext context) => FieldEditorPannelState(
|
||||
gridId: context.gridId,
|
||||
field: context.gridField,
|
||||
typeOptionData: Uint8List.fromList(context.typeOptionData),
|
||||
);
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
part 'field_switch_bloc.freezed.dart';
|
||||
|
||||
class FieldSwitcherBloc extends Bloc<FieldSwitchEvent, FieldSwitchState> {
|
||||
FieldSwitcherBloc(SwitchFieldContext editContext) : super(FieldSwitchState.initial(editContext)) {
|
||||
on<FieldSwitchEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
toFieldType: (_ToFieldType value) async {
|
||||
emit(state.copyWith(
|
||||
field: value.field,
|
||||
typeOptionData: Uint8List.fromList(value.typeOptionData),
|
||||
));
|
||||
},
|
||||
didUpdateTypeOptionData: (_DidUpdateTypeOptionData value) {
|
||||
emit(state.copyWith(typeOptionData: value.typeOptionData));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FieldSwitchEvent with _$FieldSwitchEvent {
|
||||
const factory FieldSwitchEvent.toFieldType(Field field, List<int> typeOptionData) = _ToFieldType;
|
||||
const factory FieldSwitchEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FieldSwitchState with _$FieldSwitchState {
|
||||
const factory FieldSwitchState({
|
||||
required String gridId,
|
||||
required Field field,
|
||||
required Uint8List typeOptionData,
|
||||
}) = _FieldSwitchState;
|
||||
|
||||
factory FieldSwitchState.initial(SwitchFieldContext switchContext) => FieldSwitchState(
|
||||
gridId: switchContext.gridId,
|
||||
field: switchContext.field,
|
||||
typeOptionData: Uint8List.fromList(switchContext.typeOptionData),
|
||||
);
|
||||
}
|
||||
|
||||
class SwitchFieldContext {
|
||||
final String gridId;
|
||||
final Field field;
|
||||
final List<int> typeOptionData;
|
||||
|
||||
SwitchFieldContext(this.gridId, this.field, this.typeOptionData);
|
||||
}
|
@ -8,7 +8,7 @@ export 'grid_header_bloc.dart';
|
||||
export 'field/field_service.dart';
|
||||
export 'field/field_action_sheet_bloc.dart';
|
||||
export 'field/field_editor_bloc.dart';
|
||||
export 'field/field_switch_bloc.dart';
|
||||
export 'field/field_editor_pannel_bloc.dart';
|
||||
|
||||
// Field Type Option
|
||||
export 'field/type_option/date_bloc.dart';
|
||||
|
@ -1,18 +1,15 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_editor_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_switch_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'field_name_input.dart';
|
||||
import 'field_switcher.dart';
|
||||
import 'field_editor_pannel.dart';
|
||||
|
||||
class FieldEditor extends FlowyOverlayDelegate {
|
||||
final String gridId;
|
||||
@ -30,7 +27,6 @@ class FieldEditor extends FlowyOverlayDelegate {
|
||||
BuildContext context, {
|
||||
AnchorDirection anchorDirection = AnchorDirection.bottomWithLeftAligned,
|
||||
}) {
|
||||
Log.trace("Show $identifier()");
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
@ -69,16 +65,24 @@ class _FieldEditorWidget extends StatelessWidget {
|
||||
value: editorBloc,
|
||||
child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
|
||||
builder: (context, state) {
|
||||
return state.field.fold(
|
||||
return state.editFieldContext.fold(
|
||||
() => const SizedBox(),
|
||||
(field) => ListView(
|
||||
(editFieldContext) => ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
|
||||
const VSpace(10),
|
||||
const _FieldNameTextField(),
|
||||
const VSpace(10),
|
||||
_renderSwitchButton(context, field, state),
|
||||
FieldEditorPannel(
|
||||
editFieldContext: editFieldContext,
|
||||
onSwitchToField: (fieldId, fieldType) {
|
||||
return fieldContextLoader.switchToField(fieldId, fieldType);
|
||||
},
|
||||
onUpdated: (field, typeOptionData) {
|
||||
context.read<FieldEditorBloc>().add(FieldEditorEvent.updateField(field, typeOptionData));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -86,18 +90,6 @@ class _FieldEditorWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderSwitchButton(BuildContext context, Field field, FieldEditorState state) {
|
||||
return FieldSwitcher(
|
||||
switchContext: SwitchFieldContext(state.gridId, field, state.typeOptionData),
|
||||
onSwitchToField: (fieldId, fieldType) {
|
||||
return fieldContextLoader.switchToField(fieldId, fieldType);
|
||||
},
|
||||
onUpdated: (field, typeOptionData) {
|
||||
context.read<FieldEditorBloc>().add(FieldEditorEvent.switchField(field, typeOptionData));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FieldNameTextField extends StatelessWidget {
|
||||
@ -105,11 +97,16 @@ class _FieldNameTextField extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
|
||||
buildWhen: (previous, current) => previous.fieldName != current.fieldName,
|
||||
builder: (context, state) {
|
||||
return BlocSelector<FieldEditorBloc, FieldEditorState, String>(
|
||||
selector: (state) {
|
||||
return state.editFieldContext.fold(
|
||||
() => "",
|
||||
(editFieldContext) => editFieldContext.gridField.name,
|
||||
);
|
||||
},
|
||||
builder: (context, name) {
|
||||
return FieldNameTextField(
|
||||
name: state.fieldName,
|
||||
name: name,
|
||||
errorText: context.read<FieldEditorBloc>().state.errorText,
|
||||
onNameChanged: (newName) {
|
||||
context.read<FieldEditorBloc>().add(FieldEditorEvent.updateName(newName));
|
||||
|
@ -30,30 +30,30 @@ typedef SwitchToFieldCallback = Future<Either<EditFieldContext, FlowyError>> Fun
|
||||
FieldType fieldType,
|
||||
);
|
||||
|
||||
class FieldSwitcher extends StatefulWidget {
|
||||
final SwitchFieldContext switchContext;
|
||||
class FieldEditorPannel extends StatefulWidget {
|
||||
final EditFieldContext editFieldContext;
|
||||
final UpdateFieldCallback onUpdated;
|
||||
final SwitchToFieldCallback onSwitchToField;
|
||||
|
||||
const FieldSwitcher({
|
||||
required this.switchContext,
|
||||
const FieldEditorPannel({
|
||||
required this.editFieldContext,
|
||||
required this.onUpdated,
|
||||
required this.onSwitchToField,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<FieldSwitcher> createState() => _FieldSwitcherState();
|
||||
State<FieldEditorPannel> createState() => _FieldEditorPannelState();
|
||||
}
|
||||
|
||||
class _FieldSwitcherState extends State<FieldSwitcher> {
|
||||
class _FieldEditorPannelState extends State<FieldEditorPannel> {
|
||||
String? currentOverlayIdentifier;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<FieldSwitcherBloc>(param1: widget.switchContext),
|
||||
child: BlocConsumer<FieldSwitcherBloc, FieldSwitchState>(
|
||||
create: (context) => getIt<FieldEditorPannelBloc>(param1: widget.editFieldContext),
|
||||
child: BlocConsumer<FieldEditorPannelBloc, FieldEditorPannelState>(
|
||||
listener: (context, state) {
|
||||
widget.onUpdated(state.field, state.typeOptionData);
|
||||
},
|
||||
@ -87,8 +87,8 @@ class _FieldSwitcherState extends State<FieldSwitcher> {
|
||||
widget.onSwitchToField(field.id, newFieldType).then((result) {
|
||||
result.fold(
|
||||
(editFieldContext) {
|
||||
context.read<FieldSwitcherBloc>().add(
|
||||
FieldSwitchEvent.toFieldType(
|
||||
context.read<FieldEditorPannelBloc>().add(
|
||||
FieldEditorPannelEvent.toFieldType(
|
||||
editFieldContext.gridField,
|
||||
editFieldContext.typeOptionData,
|
||||
),
|
||||
@ -108,7 +108,7 @@ class _FieldSwitcherState extends State<FieldSwitcher> {
|
||||
|
||||
Widget? _typeOptionWidget({
|
||||
required BuildContext context,
|
||||
required FieldSwitchState state,
|
||||
required FieldEditorPannelState state,
|
||||
}) {
|
||||
final overlayDelegate = TypeOptionOverlayDelegate(
|
||||
showOverlay: _showOverlay,
|
||||
@ -116,7 +116,7 @@ class _FieldSwitcherState extends State<FieldSwitcher> {
|
||||
);
|
||||
|
||||
final dataDelegate = TypeOptionDataDelegate(didUpdateTypeOptionData: (data) {
|
||||
context.read<FieldSwitcherBloc>().add(FieldSwitchEvent.didUpdateTypeOptionData(data));
|
||||
context.read<FieldEditorPannelBloc>().add(FieldEditorPannelEvent.didUpdateTypeOptionData(data));
|
||||
});
|
||||
|
||||
final typeOptionContext = TypeOptionContext(
|
@ -1,7 +1,7 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/date_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
|
||||
import 'package:easy_localization/easy_localization.dart' hide DateFormat;
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/field_option_pannel_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
|
@ -3,7 +3,7 @@ import 'package:app_flowy/workspace/application/grid/field/type_option/number_bl
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/number_format_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/common/text_field.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'field_option_pannel.dart';
|
||||
|
Loading…
Reference in New Issue
Block a user