import "dart:convert"; import "dart:typed_data"; import "package:http/http.dart" as http; class QuoteEngagement { final int? likes; final int? retweets; final int? replies; final int? views; QuoteEngagement({ this.likes, this.retweets, this.replies, this.views, }); Map toJson() { return { if (likes != null) "likes": likes, if (retweets != null) "retweets": retweets, if (replies != null) "replies": replies, if (views != null) "views": views, }; } factory QuoteEngagement.fromJson(Map json) { return QuoteEngagement( likes: json["likes"], retweets: json["retweets"], replies: json["replies"], views: json["views"], ); } } class QuoteSessionRequest { final String? displayName; final String? username; final dynamic avatarUrl; final String? text; final dynamic imageUrl; final int? timestamp; final bool? verified; final QuoteEngagement? engagement; final bool clearAvatarUrl; final bool clearImageUrl; final bool clearEngagement; QuoteSessionRequest({ this.displayName, this.username, this.avatarUrl, this.text, this.imageUrl, this.timestamp, this.verified, this.engagement, this.clearAvatarUrl = false, this.clearImageUrl = false, this.clearEngagement = false, }); // convert img bytes to base64 data uri String? _encodeImage(dynamic image) { if (image == null) return null; if (image is String) return image; if (image is Uint8List) { final base64String = base64Encode(image); return "data:image/png;base64,$base64String"; } return null; } Map toJson() { final map = {}; if (displayName != null) map["displayName"] = displayName; if (username != null) map["username"] = username; if (clearAvatarUrl) { map["avatarUrl"] = null; } else { final encodedAvatar = _encodeImage(avatarUrl); if (encodedAvatar != null) map["avatarUrl"] = encodedAvatar; } if (text != null) map["text"] = text; if (clearImageUrl) { map["imageUrl"] = null; } else { final encodedImage = _encodeImage(imageUrl); if (encodedImage != null) map["imageUrl"] = encodedImage; } if (timestamp != null) map["timestamp"] = timestamp; if (verified != null) map["verified"] = verified; if (clearEngagement) { map["engagement"] = null; } else if (engagement != null) { map["engagement"] = engagement!.toJson(); } return map; } } class QuoteSession { final String id; final String? displayName; final String? username; final String? avatarUrl; final String? text; final String? imageUrl; final int? timestamp; final bool? verified; final QuoteEngagement? engagement; final int createdAt; final int updatedAt; QuoteSession({ required this.id, this.displayName, this.username, this.avatarUrl, this.text, this.imageUrl, this.timestamp, this.verified, this.engagement, required this.createdAt, required this.updatedAt, }); factory QuoteSession.fromJson(Map json) { return QuoteSession( id: json["id"], displayName: json["displayName"], username: json["username"], avatarUrl: json["avatarUrl"], text: json["text"], imageUrl: json["imageUrl"], timestamp: json["timestamp"], verified: json["verified"], engagement: json["engagement"] != null ? QuoteEngagement.fromJson(json["engagement"]) : null, createdAt: json["createdAt"], updatedAt: json["updatedAt"], ); } } class QuoteGeneratorApiV2 { static const String _baseUrl = "https://quotes.imbenji.net"; // static const String _baseUrl = "http://localhost:3000"; // create new session static Future createSession(QuoteSessionRequest request) async { final url = Uri.parse("$_baseUrl/v2/quote"); final response = await http.post( url, headers: {"Content-Type": "application/json"}, body: jsonEncode(request.toJson()), ); if (response.statusCode == 201) { final data = jsonDecode(response.body); return QuoteSession.fromJson(data); } else { throw Exception("Failed to create sesion: ${response.statusCode}"); } } // get session state static Future getSession(String sessionId) async { final url = Uri.parse("$_baseUrl/v2/quote/$sessionId"); final response = await http.get(url); if (response.statusCode == 200) { final data = jsonDecode(response.body); return QuoteSession.fromJson(data); } else { throw Exception("Failed to get session: ${response.statusCode}"); } } // update session fields (only send whats changed) static Future updateSession( String sessionId, QuoteSessionRequest updates, ) async { final url = Uri.parse("$_baseUrl/v2/quote/$sessionId"); final response = await http.patch( url, headers: {"Content-Type": "application/json"}, body: jsonEncode(updates.toJson()), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); return QuoteSession.fromJson(data); } else { throw Exception("Failed to updte session: ${response.statusCode}"); } } // render the session as image static Future generateImage(String sessionId) async { final url = Uri.parse("$_baseUrl/v2/quote/$sessionId/image"); final response = await http.get(url); if (response.statusCode == 200) { return response.bodyBytes; } else { throw Exception("Failed to genrate image: ${response.statusCode}"); } } // delete session static Future deleteSession(String sessionId) async { final url = Uri.parse("$_baseUrl/v2/quote/$sessionId"); final response = await http.delete(url); if (response.statusCode != 204) { throw Exception("Failed to delte session: ${response.statusCode}"); } } // helper to get curren timestamp in seconds static int getCurrentTimestamp() { return DateTime.now().millisecondsSinceEpoch ~/ 1000; } }