feat: add gallery feature with database integration and rendering capabilities
Build Android App / build (push) Failing after 2m19s
Build Android App / build (push) Failing after 2m19s
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import "dart:async";
|
||||
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:quotegen_client/services/quote_api_v2.dart";
|
||||
|
||||
|
||||
// tracks whether the render backend is reachable, so the ui can hide stuff
|
||||
// that actually needs it (video render, snapshot links etc) when its down
|
||||
class BackendStatusProvider extends ChangeNotifier {
|
||||
|
||||
// start optimistic - dont want everything flashing away before the first check lands
|
||||
bool isOnline = true;
|
||||
bool hasChecked = false;
|
||||
|
||||
Timer? _pollTimer;
|
||||
bool _disposed = false;
|
||||
|
||||
BackendStatusProvider() {
|
||||
checkNow();
|
||||
_pollTimer = Timer.periodic(const Duration(seconds: 15), (_) => checkNow());
|
||||
}
|
||||
|
||||
Future<void> checkNow() async {
|
||||
final online = await QuoteGeneratorApiV2.checkHealth();
|
||||
|
||||
if (_disposed) return;
|
||||
|
||||
hasChecked = true;
|
||||
|
||||
if (online != isOnline) {
|
||||
isOnline = online;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
_pollTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import "dart:convert";
|
||||
import "dart:typed_data";
|
||||
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:quotegen_client/services/quote_api_v2.dart";
|
||||
import "package:shadcn_flutter/shadcn_flutter.dart";
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
|
||||
|
||||
class FormProvider extends ChangeNotifier {
|
||||
|
||||
String displayName = "";
|
||||
String handle = "";
|
||||
String content = "";
|
||||
bool verified = false;
|
||||
DateTime date = DateTime.now();
|
||||
TimeOfDay time = TimeOfDay.now();
|
||||
Uint8List? avatarImage;
|
||||
Uint8List? postImage;
|
||||
String replies = "";
|
||||
String retweets = "";
|
||||
String likes = "";
|
||||
String views = "";
|
||||
|
||||
int? savedTimestamp;
|
||||
|
||||
|
||||
void setDisplayName(String v) {
|
||||
displayName = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setHandle(String v) {
|
||||
handle = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setContent(String v) {
|
||||
content = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setVerified(bool v) {
|
||||
verified = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setDate(DateTime v) {
|
||||
date = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setTime(TimeOfDay v) {
|
||||
time = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setAvatarImage(Uint8List? v) {
|
||||
avatarImage = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setPostImage(Uint8List? v) {
|
||||
postImage = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setReplies(String v) {
|
||||
replies = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setRetweets(String v) {
|
||||
retweets = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setLikes(String v) {
|
||||
likes = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setViews(String v) {
|
||||
views = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setSavedTimestamp(int? v) {
|
||||
savedTimestamp = v;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// auto-fill empty engagement fields with "1" if any have a value
|
||||
// returns true if anything was filled
|
||||
bool fillEmptyEngagement() {
|
||||
bool hasValue = replies != "" || retweets != "" || likes != "" || views != "";
|
||||
bool didFill = false;
|
||||
|
||||
if (hasValue) {
|
||||
if (replies == "") { replies = "1"; didFill = true; }
|
||||
if (retweets == "") { retweets = "1"; didFill = true; }
|
||||
if (likes == "") { likes = "1"; didFill = true; }
|
||||
if (views == "") { views = "1"; didFill = true; }
|
||||
}
|
||||
|
||||
if (didFill) notifyListeners();
|
||||
return didFill;
|
||||
}
|
||||
|
||||
int combineDateTime() {
|
||||
DateTime combined = DateTime(
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
);
|
||||
return combined.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
int? _parseNum(String val) {
|
||||
if (val.isEmpty) return null;
|
||||
return int.tryParse(val.replaceAll(",", ""));
|
||||
}
|
||||
|
||||
QuoteEngagement? buildEngagement(String field) {
|
||||
if (field == "engagement") {
|
||||
if (likes == "" && retweets == "" && replies == "" && views == "") {
|
||||
return null;
|
||||
}
|
||||
return QuoteEngagement(
|
||||
likes: _parseNum(likes),
|
||||
retweets: _parseNum(retweets),
|
||||
replies: _parseNum(replies),
|
||||
views: _parseNum(views),
|
||||
);
|
||||
}
|
||||
|
||||
return QuoteEngagement(
|
||||
likes: field == "engagement_likes" ? _parseNum(likes) : null,
|
||||
retweets: field == "engagement_retweets" ? _parseNum(retweets) : null,
|
||||
replies: field == "engagement_replies" ? _parseNum(replies) : null,
|
||||
views: field == "engagement_views" ? _parseNum(views) : null,
|
||||
);
|
||||
}
|
||||
|
||||
QuoteSessionRequest buildFullRequest() {
|
||||
QuoteEngagement? engagement;
|
||||
if (likes != "" || retweets != "" || replies != "" || views != "") {
|
||||
engagement = QuoteEngagement(
|
||||
likes: _parseNum(likes),
|
||||
retweets: _parseNum(retweets),
|
||||
replies: _parseNum(replies),
|
||||
views: _parseNum(views),
|
||||
);
|
||||
}
|
||||
|
||||
return QuoteSessionRequest(
|
||||
displayName: displayName,
|
||||
username: handle.toLowerCase(),
|
||||
text: content,
|
||||
timestamp: combineDateTime(),
|
||||
verified: verified,
|
||||
avatarUrl: avatarImage,
|
||||
imageUrl: postImage,
|
||||
engagement: engagement,
|
||||
);
|
||||
}
|
||||
|
||||
// load form data from shared prefs
|
||||
Future<void> loadFormData() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
final dn = prefs.getString("display_name") ?? "";
|
||||
final h = prefs.getString("handle") ?? "";
|
||||
final c = prefs.getString("content") ?? "";
|
||||
final ts = prefs.getInt("timestamp") ?? 0;
|
||||
final rep = prefs.getString("replies") ?? "";
|
||||
final ret = prefs.getString("retweets") ?? "";
|
||||
final lk = prefs.getString("likes") ?? "";
|
||||
final vw = prefs.getString("views") ?? "";
|
||||
final ver = prefs.getBool("verified") ?? false;
|
||||
|
||||
if (ts > 0) savedTimestamp = ts;
|
||||
|
||||
Uint8List? avatarImg;
|
||||
Uint8List? postImg;
|
||||
|
||||
String? avatarBase64 = prefs.getString("avatar_image");
|
||||
if (avatarBase64 != null && avatarBase64.isNotEmpty) {
|
||||
avatarImg = base64Decode(avatarBase64);
|
||||
}
|
||||
|
||||
String? postBase64 = prefs.getString("post_image");
|
||||
if (postBase64 != null && postBase64.isNotEmpty) {
|
||||
postImg = base64Decode(postBase64);
|
||||
}
|
||||
|
||||
print("Loaded form data: name=$dn, handle=$h");
|
||||
|
||||
displayName = dn;
|
||||
handle = h;
|
||||
content = c;
|
||||
verified = ver;
|
||||
replies = rep;
|
||||
retweets = ret;
|
||||
likes = lk;
|
||||
views = vw;
|
||||
avatarImage = avatarImg;
|
||||
postImage = postImg;
|
||||
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
print("Error loading form data: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveFormData() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
print("Saving form data: name=$displayName, handle=$handle");
|
||||
|
||||
int tsMillis = combineDateTime() * 1000;
|
||||
|
||||
await prefs.setString("display_name", displayName);
|
||||
await prefs.setString("handle", handle);
|
||||
await prefs.setString("content", content);
|
||||
await prefs.setInt("timestamp", tsMillis);
|
||||
await prefs.setString("replies", replies);
|
||||
await prefs.setString("retweets", retweets);
|
||||
await prefs.setString("likes", likes);
|
||||
await prefs.setString("views", views);
|
||||
await prefs.setBool("verified", verified);
|
||||
|
||||
if (avatarImage != null) {
|
||||
await prefs.setString("avatar_image", base64Encode(avatarImage!));
|
||||
} else {
|
||||
await prefs.remove("avatar_image");
|
||||
}
|
||||
|
||||
if (postImage != null) {
|
||||
await prefs.setString("post_image", base64Encode(postImage!));
|
||||
} else {
|
||||
await prefs.remove("post_image");
|
||||
}
|
||||
|
||||
print("Form data saved");
|
||||
} catch (e) {
|
||||
print("Error savin form data: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import "dart:convert";
|
||||
import "dart:typed_data";
|
||||
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:quotegen_client/models/gallery_item.dart";
|
||||
import "package:quotegen_client/providers/form_provider.dart";
|
||||
import "package:quotegen_client/services/gallery_db.dart";
|
||||
|
||||
|
||||
class GalleryProvider extends ChangeNotifier {
|
||||
|
||||
List<GalleryItem> items = [];
|
||||
bool isLoading = false;
|
||||
|
||||
|
||||
GalleryProvider() {
|
||||
load();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
items = await GalleryDB.getAllItems();
|
||||
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// returns false if a duplicate already exists
|
||||
Future<bool> saveCurrentForm(FormProvider form) async {
|
||||
final avatarB64 = form.avatarImage != null ? base64Encode(form.avatarImage!) : null;
|
||||
final postB64 = form.postImage != null ? base64Encode(form.postImage!) : null;
|
||||
final ts = form.combineDateTime();
|
||||
|
||||
final isDuplicate = items.any((i) =>
|
||||
i.displayName == form.displayName &&
|
||||
i.handle == form.handle &&
|
||||
i.content == form.content &&
|
||||
i.verified == form.verified &&
|
||||
i.timestamp == ts &&
|
||||
i.avatarImageB64 == avatarB64 &&
|
||||
i.postImageB64 == postB64 &&
|
||||
i.replies == form.replies &&
|
||||
i.retweets == form.retweets &&
|
||||
i.likes == form.likes &&
|
||||
i.views == form.views
|
||||
);
|
||||
|
||||
if (isDuplicate) return false;
|
||||
|
||||
final id = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
|
||||
final item = GalleryItem(
|
||||
id: id,
|
||||
displayName: form.displayName,
|
||||
handle: form.handle,
|
||||
content: form.content,
|
||||
verified: form.verified,
|
||||
timestamp: ts,
|
||||
avatarImageB64: avatarB64,
|
||||
postImageB64: postB64,
|
||||
replies: form.replies,
|
||||
retweets: form.retweets,
|
||||
likes: form.likes,
|
||||
views: form.views,
|
||||
createdAt: DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
|
||||
await GalleryDB.saveItem(item);
|
||||
items.insert(0, item);
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> deleteItem(String id) async {
|
||||
await GalleryDB.deleteItem(id);
|
||||
items.removeWhere((i) => i.id == id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// decode image bytes for a gallery item - returns null if no image
|
||||
Uint8List? decodeAvatar(GalleryItem item) {
|
||||
if (item.avatarImageB64 == null) return null;
|
||||
return base64Decode(item.avatarImageB64!);
|
||||
}
|
||||
|
||||
Uint8List? decodePostImage(GalleryItem item) {
|
||||
if (item.postImageB64 == null) return null;
|
||||
return base64Decode(item.postImageB64!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
import "dart:async";
|
||||
import "dart:io";
|
||||
import "dart:typed_data";
|
||||
import "dart:ui" as ui;
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter/rendering.dart";
|
||||
import "package:path/path.dart" as path;
|
||||
import "package:path_provider/path_provider.dart";
|
||||
import "package:quotegen_client/services/quote_api_v2.dart";
|
||||
import "package:video_player/video_player.dart";
|
||||
|
||||
|
||||
class RenderProvider extends ChangeNotifier {
|
||||
|
||||
Uint8List? postPreviewImage;
|
||||
String? postPreviewContentType;
|
||||
|
||||
int renderProgress = 0;
|
||||
String renderStatus = "idle";
|
||||
String renderStage = "idle";
|
||||
|
||||
VideoPlayerController? videoController;
|
||||
bool videoLoadFailed = false;
|
||||
|
||||
StreamSubscription? _sseSubscription;
|
||||
bool _disposed = false;
|
||||
|
||||
|
||||
void clearRender() {
|
||||
if (postPreviewImage == null && renderStatus == "idle") return;
|
||||
|
||||
videoController?.dispose();
|
||||
videoController = null;
|
||||
|
||||
postPreviewImage = null;
|
||||
postPreviewContentType = null;
|
||||
renderStatus = "idle";
|
||||
renderProgress = 0;
|
||||
renderStage = "idle";
|
||||
videoLoadFailed = false;
|
||||
|
||||
if (!_disposed) notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> renderOutput(String? sessionId, bool isVideoInForm, {GlobalKey? previewKey}) async {
|
||||
if (!_disposed) {
|
||||
renderProgress = 0;
|
||||
renderStatus = "pending";
|
||||
renderStage = "idle";
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
if (isVideoInForm) {
|
||||
// video still goes through the server, so no session means theres genuinely nothing we can do
|
||||
if (sessionId == null) {
|
||||
print("Cant render video, session isnt ready (or never got created)");
|
||||
if (!_disposed) {
|
||||
renderStatus = "failed";
|
||||
notifyListeners();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
print("Starting video render for session: $sessionId");
|
||||
|
||||
if (!_disposed) {
|
||||
renderStatus = "rendering";
|
||||
renderStage = "rendering";
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// cancel any existing sub before starting new one
|
||||
await _sseSubscription?.cancel();
|
||||
|
||||
_sseSubscription = QuoteGeneratorApiV2.watchRenderProgress(sessionId).listen(
|
||||
(progress) {
|
||||
print("Render progress: ${progress.status} - ${progress.stage} - ${progress.progress}%");
|
||||
|
||||
if (_disposed) return;
|
||||
|
||||
renderProgress = progress.progress;
|
||||
renderStatus = progress.status;
|
||||
renderStage = progress.stage ?? "idle";
|
||||
notifyListeners();
|
||||
|
||||
if (progress.isComplete) {
|
||||
print("Render complete, fetching media...");
|
||||
fetchRenderedMedia(sessionId);
|
||||
} else if (progress.isFailed) {
|
||||
print("Render failed: ${progress.error}");
|
||||
if (!_disposed) {
|
||||
renderStatus = "failed";
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: (error) {
|
||||
print("Error watching render progress: $error");
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
await QuoteGeneratorApiV2.generateMedia(sessionId);
|
||||
} catch (e) {
|
||||
print("Error starting render: $e");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// image render - grab it straight off the live preview widget, dont need the server for this bit anymore
|
||||
try {
|
||||
if (previewKey == null) {
|
||||
throw Exception("no preview key given, cant capture anything");
|
||||
}
|
||||
|
||||
// renderStatus flipped to pending up top already, which swaps the ui back to the live
|
||||
// tweet card - just gotta wait for that frame to actually paint before we grab it
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
|
||||
final bytes = await _capturePreview(previewKey);
|
||||
|
||||
if (_disposed) return;
|
||||
|
||||
videoController?.dispose();
|
||||
videoController = null;
|
||||
|
||||
postPreviewImage = bytes;
|
||||
postPreviewContentType = "image/png";
|
||||
videoLoadFailed = false;
|
||||
renderStatus = "completed";
|
||||
renderProgress = 100;
|
||||
renderStage = "completed";
|
||||
notifyListeners();
|
||||
|
||||
} catch (e) {
|
||||
print("Error rendering: $e");
|
||||
if (!_disposed) {
|
||||
renderStatus = "failed";
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rasterizes a repaint boundary to png, scaled to a fixed resolution regardless of its onscreen size
|
||||
Future<Uint8List> _capturePreview(GlobalKey key, {double targetSize = 1080}) async {
|
||||
final renderObj = key.currentContext?.findRenderObject();
|
||||
|
||||
if (renderObj is! RenderRepaintBoundary) {
|
||||
throw Exception("preview widget isnt mounted, nothing to capture");
|
||||
}
|
||||
|
||||
final logicalSize = renderObj.size;
|
||||
final pixelRatio = logicalSize.shortestSide == 0 ? 1.0 : targetSize / logicalSize.shortestSide;
|
||||
|
||||
final image = await renderObj.toImage(pixelRatio: pixelRatio);
|
||||
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
image.dispose();
|
||||
|
||||
if (byteData == null) {
|
||||
throw Exception("failed to encode captured png");
|
||||
}
|
||||
|
||||
return byteData.buffer.asUint8List();
|
||||
}
|
||||
|
||||
Future<void> fetchRenderedMedia(String sessionId) async {
|
||||
try {
|
||||
MediaResponse media = await QuoteGeneratorApiV2.generateMedia(sessionId);
|
||||
|
||||
if (_disposed) return;
|
||||
|
||||
videoController?.dispose();
|
||||
videoController = null;
|
||||
|
||||
postPreviewImage = media.bytes;
|
||||
postPreviewContentType = media.contentType;
|
||||
videoLoadFailed = false;
|
||||
renderStatus = "completed";
|
||||
renderProgress = 100;
|
||||
renderStage = "completed";
|
||||
notifyListeners();
|
||||
|
||||
if (media.isVideo) {
|
||||
await initializeVideoPlayer(media.bytes);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
print("Error fetching rendered media: $e");
|
||||
if (!_disposed) {
|
||||
renderStatus = "failed";
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> initializeVideoPlayer(Uint8List videoBytes) async {
|
||||
try {
|
||||
videoLoadFailed = false;
|
||||
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final videoPath = path.join(tempDir.path, "preview_video_${DateTime.now().millisecondsSinceEpoch}.mp4");
|
||||
final file = File(videoPath);
|
||||
await file.writeAsBytes(videoBytes);
|
||||
|
||||
print("Video saved to: $videoPath (${videoBytes.length} bytes)");
|
||||
|
||||
videoController = VideoPlayerController.file(file);
|
||||
|
||||
videoController!.addListener(() {
|
||||
if (!_disposed) notifyListeners();
|
||||
});
|
||||
|
||||
await videoController!.initialize();
|
||||
|
||||
videoController!.setLooping(true);
|
||||
videoController!.play();
|
||||
|
||||
if (!_disposed) {
|
||||
videoLoadFailed = false;
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error initializing video player: $e");
|
||||
if (!_disposed) {
|
||||
videoLoadFailed = true;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
_sseSubscription?.cancel();
|
||||
videoController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import "dart:async";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:quotegen_client/services/quote_api_v2.dart";
|
||||
|
||||
|
||||
class SessionProvider extends ChangeNotifier {
|
||||
|
||||
String? currentSessionId;
|
||||
bool isLoading = true;
|
||||
bool canRender = true;
|
||||
|
||||
Timer? _renderEnableTimer;
|
||||
int _submitCount = 0;
|
||||
|
||||
|
||||
Future<void> initializeSession(QuoteSessionRequest request) async {
|
||||
try {
|
||||
QuoteSession session = await QuoteGeneratorApiV2.createSession(request);
|
||||
currentSessionId = session.id;
|
||||
print("Session created: ${session.id}");
|
||||
} catch (e) {
|
||||
print("Failed to creat session: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void setLoaded() {
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void onSubmit({
|
||||
String? changedField,
|
||||
required QuoteSessionRequest request,
|
||||
VoidCallback? onDebounceSettled,
|
||||
}) async {
|
||||
|
||||
int currentSubmit = ++_submitCount;
|
||||
|
||||
_renderEnableTimer?.cancel();
|
||||
if (canRender) {
|
||||
canRender = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
_renderEnableTimer = Timer(Duration(seconds: 5), () {
|
||||
canRender = true;
|
||||
notifyListeners();
|
||||
});
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 700));
|
||||
|
||||
if (currentSubmit != _submitCount) return;
|
||||
|
||||
// fire the callback so render gets cleared
|
||||
onDebounceSettled?.call();
|
||||
|
||||
print("Submitting field: $changedField");
|
||||
|
||||
// wait for session to be ready
|
||||
while (currentSessionId == null) {
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
}
|
||||
|
||||
QuoteSessionRequest updateRequest;
|
||||
|
||||
if (changedField == null) {
|
||||
updateRequest = request;
|
||||
} else {
|
||||
final engagement = (changedField == "engagement" || changedField.startsWith("engagement_"))
|
||||
? _buildPartialEngagement(changedField, request)
|
||||
: null;
|
||||
|
||||
final shouldClearEngagement = changedField == "engagement" && engagement == null;
|
||||
|
||||
updateRequest = QuoteSessionRequest(
|
||||
displayName: changedField == "display_name" ? request.displayName : null,
|
||||
username: changedField == "handle" ? request.username : null,
|
||||
text: changedField == "content" ? request.text : null,
|
||||
timestamp: changedField == "datetime" ? request.timestamp : null,
|
||||
avatarUrl: changedField == "avatar_image" ? request.avatarUrl : null,
|
||||
imageUrl: changedField == "post_image" ? request.imageUrl : null,
|
||||
verified: changedField == "verified" ? request.verified : null,
|
||||
engagement: engagement,
|
||||
clearAvatarUrl: changedField == "avatar_image" && request.avatarUrl == null,
|
||||
clearImageUrl: changedField == "post_image" && request.imageUrl == null,
|
||||
clearEngagement: shouldClearEngagement,
|
||||
);
|
||||
}
|
||||
|
||||
await QuoteGeneratorApiV2.updateSession(currentSessionId!, updateRequest);
|
||||
print("Session updated");
|
||||
}
|
||||
|
||||
QuoteEngagement? _buildPartialEngagement(String field, QuoteSessionRequest req) {
|
||||
final eng = req.engagement;
|
||||
if (field == "engagement") {
|
||||
return eng;
|
||||
}
|
||||
return QuoteEngagement(
|
||||
likes: field == "engagement_likes" ? eng?.likes : null,
|
||||
retweets: field == "engagement_retweets" ? eng?.retweets : null,
|
||||
replies: field == "engagement_replies" ? eng?.replies : null,
|
||||
views: field == "engagement_views" ? eng?.views : null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_renderEnableTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user