mobile bluesky app made with flutter
lazurite.stormlightlabs.org/
mobile
bluesky
flutter
1part of 'compose_bloc.dart';
2
3/// Sentinel used by copyWith to distinguish "leave unchanged" from "set to null".
4class _Undefined {
5 const _Undefined();
6}
7
8enum ComposeStatus { initial, ready, submitting, success, error }
9
10enum VideoUploadStatus { idle, checkingLimits, uploading, processing, ready, error }
11
12class VideoAttachment extends Equatable {
13 const VideoAttachment({
14 required this.localPath,
15 this.status = VideoUploadStatus.idle,
16 this.uploadProgress = 0,
17 this.blob,
18 this.altText = '',
19 this.jobId,
20 this.errorMessage,
21 });
22
23 final String localPath;
24 final VideoUploadStatus status;
25 final int uploadProgress;
26 final Blob? blob;
27 final String altText;
28 final String? jobId;
29 final String? errorMessage;
30
31 bool get isActive =>
32 status == VideoUploadStatus.checkingLimits ||
33 status == VideoUploadStatus.uploading ||
34 status == VideoUploadStatus.processing;
35 bool get isReady => status == VideoUploadStatus.ready;
36 bool get hasError => status == VideoUploadStatus.error;
37
38 VideoAttachment copyWith({
39 String? localPath,
40 VideoUploadStatus? status,
41 int? uploadProgress,
42 Object? blob = const _Undefined(),
43 String? altText,
44 Object? jobId = const _Undefined(),
45 Object? errorMessage = const _Undefined(),
46 }) {
47 return VideoAttachment(
48 localPath: localPath ?? this.localPath,
49 status: status ?? this.status,
50 uploadProgress: uploadProgress ?? this.uploadProgress,
51 blob: blob is _Undefined ? this.blob : blob as Blob?,
52 altText: altText ?? this.altText,
53 jobId: jobId is _Undefined ? this.jobId : jobId as String?,
54 errorMessage: errorMessage is _Undefined ? this.errorMessage : errorMessage as String?,
55 );
56 }
57
58 @override
59 List<Object?> get props => [localPath, status, uploadProgress, blob, altText, jobId, errorMessage];
60}
61
62class ComposeState extends Equatable {
63 const ComposeState._({
64 required this.status,
65 this.text = '',
66 this.graphemeCount = 0,
67 this.isOverLimit = false,
68 this.isEmpty = true,
69 this.mediaAttachments = const [],
70 this.draftId,
71 this.scheduledAt,
72 this.replyParentUri,
73 this.replyParentCid,
74 this.replyRootUri,
75 this.replyRootCid,
76 this.quoteUri,
77 this.quoteCid,
78 this.editPostUri,
79 this.editPostCid,
80 this.editRecord,
81 this.errorMessage,
82 this.drafts = const [],
83 this.isSavingDraft = false,
84 this.isLoadingDrafts = false,
85 this.canSubmit = false,
86 this.videoAttachment,
87 this.isDraftDirty = true,
88 });
89
90 const ComposeState.initial() : this._(status: ComposeStatus.initial);
91
92 const ComposeState.ready({
93 String text = '',
94 int graphemeCount = 0,
95 bool isOverLimit = false,
96 bool isEmpty = true,
97 List<MediaAttachment> mediaAttachments = const [],
98 int? draftId,
99 DateTime? scheduledAt,
100 String? replyParentUri,
101 String? replyParentCid,
102 String? replyRootUri,
103 String? replyRootCid,
104 String? quoteUri,
105 String? quoteCid,
106 String? editPostUri,
107 String? editPostCid,
108 Map<String, dynamic>? editRecord,
109 VideoAttachment? videoAttachment,
110 bool isDraftDirty = true,
111 }) : this._(
112 status: ComposeStatus.ready,
113 text: text,
114 graphemeCount: graphemeCount,
115 isOverLimit: isOverLimit,
116 isEmpty: isEmpty,
117 mediaAttachments: mediaAttachments,
118 draftId: draftId,
119 scheduledAt: scheduledAt,
120 replyParentUri: replyParentUri,
121 replyParentCid: replyParentCid,
122 replyRootUri: replyRootUri,
123 replyRootCid: replyRootCid,
124 quoteUri: quoteUri,
125 quoteCid: quoteCid,
126 editPostUri: editPostUri,
127 editPostCid: editPostCid,
128 editRecord: editRecord,
129 videoAttachment: videoAttachment,
130 canSubmit: !isOverLimit && !isEmpty,
131 isDraftDirty: isDraftDirty,
132 );
133
134 final ComposeStatus status;
135 final String text;
136 final int graphemeCount;
137 final bool isOverLimit;
138 final bool isEmpty;
139 final List<MediaAttachment> mediaAttachments;
140 final int? draftId;
141 final DateTime? scheduledAt;
142 final String? replyParentUri;
143 final String? replyParentCid;
144 final String? replyRootUri;
145 final String? replyRootCid;
146 final String? quoteUri;
147 final String? quoteCid;
148 final String? editPostUri;
149 final String? editPostCid;
150 final Map<String, dynamic>? editRecord;
151 final String? errorMessage;
152 final List<DraftEntry> drafts;
153 final bool isSavingDraft;
154 final bool isLoadingDrafts;
155 final bool canSubmit;
156 final VideoAttachment? videoAttachment;
157 final bool isDraftDirty;
158
159 bool get isSubmitting => status == ComposeStatus.submitting;
160 bool get hasError => status == ComposeStatus.error;
161 bool get isSuccess => status == ComposeStatus.success;
162 bool get isReady => status == ComposeStatus.ready;
163 bool get hasMedia => mediaAttachments.isNotEmpty;
164 bool get hasVideo => videoAttachment != null;
165 bool get canAddMoreMedia => !isEditing && mediaAttachments.length < 4 && videoAttachment == null;
166 bool get canAddVideo => !isEditing && mediaAttachments.isEmpty && videoAttachment == null;
167 bool get hasScheduledTime => scheduledAt != null;
168 bool get isReply => replyParentUri != null;
169 bool get isQuote => quoteUri != null;
170 bool get isEditing => editPostUri != null && editPostCid != null && editRecord != null;
171
172 ComposeState copyWith({
173 ComposeStatus? status,
174 String? text,
175 int? graphemeCount,
176 bool? isOverLimit,
177 bool? isEmpty,
178 List<MediaAttachment>? mediaAttachments,
179 Object? draftId = const _Undefined(),
180 Object? scheduledAt = const _Undefined(),
181 Object? replyParentUri = const _Undefined(),
182 Object? replyParentCid = const _Undefined(),
183 Object? replyRootUri = const _Undefined(),
184 Object? replyRootCid = const _Undefined(),
185 Object? quoteUri = const _Undefined(),
186 Object? quoteCid = const _Undefined(),
187 Object? editPostUri = const _Undefined(),
188 Object? editPostCid = const _Undefined(),
189 Object? editRecord = const _Undefined(),
190 Object? errorMessage = const _Undefined(),
191 List<DraftEntry>? drafts,
192 bool? isSavingDraft,
193 bool? isLoadingDrafts,
194 bool? canSubmit,
195 Object? videoAttachment = const _Undefined(),
196 bool? isDraftDirty,
197 }) {
198 return ComposeState._(
199 status: status ?? this.status,
200 text: text ?? this.text,
201 graphemeCount: graphemeCount ?? this.graphemeCount,
202 isOverLimit: isOverLimit ?? this.isOverLimit,
203 isEmpty: isEmpty ?? this.isEmpty,
204 mediaAttachments: mediaAttachments ?? this.mediaAttachments,
205 draftId: draftId is _Undefined ? this.draftId : draftId as int?,
206 scheduledAt: scheduledAt is _Undefined ? this.scheduledAt : scheduledAt as DateTime?,
207 replyParentUri: replyParentUri is _Undefined ? this.replyParentUri : replyParentUri as String?,
208 replyParentCid: replyParentCid is _Undefined ? this.replyParentCid : replyParentCid as String?,
209 replyRootUri: replyRootUri is _Undefined ? this.replyRootUri : replyRootUri as String?,
210 replyRootCid: replyRootCid is _Undefined ? this.replyRootCid : replyRootCid as String?,
211 quoteUri: quoteUri is _Undefined ? this.quoteUri : quoteUri as String?,
212 quoteCid: quoteCid is _Undefined ? this.quoteCid : quoteCid as String?,
213 editPostUri: editPostUri is _Undefined ? this.editPostUri : editPostUri as String?,
214 editPostCid: editPostCid is _Undefined ? this.editPostCid : editPostCid as String?,
215 editRecord: editRecord is _Undefined ? this.editRecord : editRecord as Map<String, dynamic>?,
216 errorMessage: errorMessage is _Undefined ? this.errorMessage : errorMessage as String?,
217 drafts: drafts ?? this.drafts,
218 isSavingDraft: isSavingDraft ?? this.isSavingDraft,
219 isLoadingDrafts: isLoadingDrafts ?? this.isLoadingDrafts,
220 canSubmit: canSubmit ?? this.canSubmit,
221 videoAttachment: videoAttachment is _Undefined ? this.videoAttachment : videoAttachment as VideoAttachment?,
222 isDraftDirty: isDraftDirty ?? this.isDraftDirty,
223 );
224 }
225
226 @override
227 List<Object?> get props => [
228 status,
229 text,
230 graphemeCount,
231 isOverLimit,
232 isEmpty,
233 mediaAttachments,
234 draftId,
235 scheduledAt,
236 replyParentUri,
237 replyParentCid,
238 replyRootUri,
239 replyRootCid,
240 quoteUri,
241 quoteCid,
242 editPostUri,
243 editPostCid,
244 editRecord,
245 errorMessage,
246 drafts,
247 isSavingDraft,
248 isLoadingDrafts,
249 canSubmit,
250 videoAttachment,
251 isDraftDirty,
252 ];
253}
254
255class MediaAttachment extends Equatable {
256 const MediaAttachment({
257 required this.localPath,
258 this.blobRef,
259 this.altText = '',
260 this.width,
261 this.height,
262 this.isUploaded = false,
263 this.isUploading = false,
264 this.uploadError,
265 });
266
267 final String localPath;
268 final BlobRef? blobRef;
269 final String altText;
270 final int? width;
271 final int? height;
272 final bool isUploaded;
273 final bool isUploading;
274 final String? uploadError;
275
276 MediaAttachment copyWith({
277 String? localPath,
278 Object? blobRef = const _Undefined(),
279 String? altText,
280 Object? width = const _Undefined(),
281 Object? height = const _Undefined(),
282 bool? isUploaded,
283 bool? isUploading,
284 Object? uploadError = const _Undefined(),
285 }) {
286 return MediaAttachment(
287 localPath: localPath ?? this.localPath,
288 blobRef: blobRef is _Undefined ? this.blobRef : blobRef as BlobRef?,
289 altText: altText ?? this.altText,
290 width: width is _Undefined ? this.width : width as int?,
291 height: height is _Undefined ? this.height : height as int?,
292 isUploaded: isUploaded ?? this.isUploaded,
293 isUploading: isUploading ?? this.isUploading,
294 uploadError: uploadError is _Undefined ? this.uploadError : uploadError as String?,
295 );
296 }
297
298 @override
299 List<Object?> get props => [localPath, blobRef, altText, width, height, isUploaded, isUploading, uploadError];
300}