♻️ Refactor snackbar

This commit is contained in:
2025-06-25 22:05:37 +08:00
parent 0062d3baf0
commit 86258acc6e
27 changed files with 590 additions and 96 deletions

View File

@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart';
@ -22,6 +23,23 @@ import 'package:island/widgets/post/draft_manager.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
part 'compose.freezed.dart';
part 'compose.g.dart';
@freezed
sealed class PostComposeInitialState with _$PostComposeInitialState {
const factory PostComposeInitialState({
String? title,
String? description,
String? content,
@Default([]) List<UniversalFile> attachments,
int? visibility,
}) = _PostComposeInitialState;
factory PostComposeInitialState.fromJson(Map<String, dynamic> json) =>
_$PostComposeInitialStateFromJson(json);
}
@RoutePage()
class PostEditScreen extends HookConsumerWidget {
final String id;
@ -54,12 +72,14 @@ class PostComposeScreen extends HookConsumerWidget {
final SnPost? repliedPost;
final SnPost? forwardedPost;
final int? type;
final PostComposeInitialState? initialState;
const PostComposeScreen({
super.key,
this.originalPost,
this.repliedPost,
this.forwardedPost,
@QueryParam('type') this.type,
this.initialState,
});
@override
@ -107,11 +127,28 @@ class PostComposeScreen extends HookConsumerWidget {
return null;
}, [publishers]);
// Load draft if available (only for new posts)
// Load initial state if provided (for sharing functionality)
useEffect(() {
if (initialState != null) {
state.titleController.text = initialState!.title ?? '';
state.descriptionController.text = initialState!.description ?? '';
state.contentController.text = initialState!.content ?? '';
if (initialState!.visibility != null) {
state.visibility.value = initialState!.visibility!;
}
if (initialState!.attachments.isNotEmpty) {
state.attachments.value = List.from(initialState!.attachments);
}
}
return null;
}, [initialState]);
// Load draft if available (only for new posts without initial state)
useEffect(() {
if (originalPost == null &&
effectiveForwardedPost == null &&
effectiveRepliedPost == null) {
effectiveRepliedPost == null &&
initialState == null) {
// Try to load the most recent draft
final drafts = ref.read(composeStorageNotifierProvider);
if (drafts.isNotEmpty) {

View File

@ -0,0 +1,166 @@
// dart format width=80
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'compose.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$PostComposeInitialState {
String? get title; String? get description; String? get content; List<UniversalFile> get attachments; int? get visibility;
/// Create a copy of PostComposeInitialState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PostComposeInitialStateCopyWith<PostComposeInitialState> get copyWith => _$PostComposeInitialStateCopyWithImpl<PostComposeInitialState>(this as PostComposeInitialState, _$identity);
/// Serializes this PostComposeInitialState to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PostComposeInitialState&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.content, content) || other.content == content)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.visibility, visibility) || other.visibility == visibility));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,title,description,content,const DeepCollectionEquality().hash(attachments),visibility);
@override
String toString() {
return 'PostComposeInitialState(title: $title, description: $description, content: $content, attachments: $attachments, visibility: $visibility)';
}
}
/// @nodoc
abstract mixin class $PostComposeInitialStateCopyWith<$Res> {
factory $PostComposeInitialStateCopyWith(PostComposeInitialState value, $Res Function(PostComposeInitialState) _then) = _$PostComposeInitialStateCopyWithImpl;
@useResult
$Res call({
String? title, String? description, String? content, List<UniversalFile> attachments, int? visibility
});
}
/// @nodoc
class _$PostComposeInitialStateCopyWithImpl<$Res>
implements $PostComposeInitialStateCopyWith<$Res> {
_$PostComposeInitialStateCopyWithImpl(this._self, this._then);
final PostComposeInitialState _self;
final $Res Function(PostComposeInitialState) _then;
/// Create a copy of PostComposeInitialState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? title = freezed,Object? description = freezed,Object? content = freezed,Object? attachments = null,Object? visibility = freezed,}) {
return _then(_self.copyWith(
title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
as String?,attachments: null == attachments ? _self.attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<UniversalFile>,visibility: freezed == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
@JsonSerializable()
class _PostComposeInitialState implements PostComposeInitialState {
const _PostComposeInitialState({this.title, this.description, this.content, final List<UniversalFile> attachments = const [], this.visibility}): _attachments = attachments;
factory _PostComposeInitialState.fromJson(Map<String, dynamic> json) => _$PostComposeInitialStateFromJson(json);
@override final String? title;
@override final String? description;
@override final String? content;
final List<UniversalFile> _attachments;
@override@JsonKey() List<UniversalFile> get attachments {
if (_attachments is EqualUnmodifiableListView) return _attachments;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_attachments);
}
@override final int? visibility;
/// Create a copy of PostComposeInitialState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$PostComposeInitialStateCopyWith<_PostComposeInitialState> get copyWith => __$PostComposeInitialStateCopyWithImpl<_PostComposeInitialState>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PostComposeInitialStateToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PostComposeInitialState&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.content, content) || other.content == content)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.visibility, visibility) || other.visibility == visibility));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,title,description,content,const DeepCollectionEquality().hash(_attachments),visibility);
@override
String toString() {
return 'PostComposeInitialState(title: $title, description: $description, content: $content, attachments: $attachments, visibility: $visibility)';
}
}
/// @nodoc
abstract mixin class _$PostComposeInitialStateCopyWith<$Res> implements $PostComposeInitialStateCopyWith<$Res> {
factory _$PostComposeInitialStateCopyWith(_PostComposeInitialState value, $Res Function(_PostComposeInitialState) _then) = __$PostComposeInitialStateCopyWithImpl;
@override @useResult
$Res call({
String? title, String? description, String? content, List<UniversalFile> attachments, int? visibility
});
}
/// @nodoc
class __$PostComposeInitialStateCopyWithImpl<$Res>
implements _$PostComposeInitialStateCopyWith<$Res> {
__$PostComposeInitialStateCopyWithImpl(this._self, this._then);
final _PostComposeInitialState _self;
final $Res Function(_PostComposeInitialState) _then;
/// Create a copy of PostComposeInitialState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? title = freezed,Object? description = freezed,Object? content = freezed,Object? attachments = null,Object? visibility = freezed,}) {
return _then(_PostComposeInitialState(
title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
as String?,attachments: null == attachments ? _self._attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<UniversalFile>,visibility: freezed == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
// dart format on

View File

@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'compose.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_PostComposeInitialState _$PostComposeInitialStateFromJson(
Map<String, dynamic> json,
) => _PostComposeInitialState(
title: json['title'] as String?,
description: json['description'] as String?,
content: json['content'] as String?,
attachments:
(json['attachments'] as List<dynamic>?)
?.map((e) => UniversalFile.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
visibility: (json['visibility'] as num?)?.toInt(),
);
Map<String, dynamic> _$PostComposeInitialStateToJson(
_PostComposeInitialState instance,
) => <String, dynamic>{
'title': instance.title,
'description': instance.description,
'content': instance.content,
'attachments': instance.attachments.map((e) => e.toJson()).toList(),
'visibility': instance.visibility,
};