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, }; } } class QuoteRequest { final String? displayName; final String? username; final dynamic avatarUrl; // can be String or Uint8List final String? text; final dynamic imageUrl; // can be String or Uint8List final int? timestamp; final bool? verified; final QuoteEngagement? engagement; QuoteRequest({ this.displayName, this.username, this.avatarUrl, this.text, this.imageUrl, this.timestamp, this.verified, this.engagement, }); // convert image 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() { return { "displayName": displayName, "username": username, "avatarUrl": _encodeImage(avatarUrl), "text": text, "imageUrl": _encodeImage(imageUrl), "timestamp": timestamp, if (verified != null) "verified": verified, if (engagement != null) "engagement": engagement!.toJson(), }; } String toQueryString() { final params = {}; if (displayName != null) params["displayName"] = displayName!; if (username != null) params["username"] = username!; final encodedAvatar = _encodeImage(avatarUrl); if (encodedAvatar != null) params["avatarUrl"] = encodedAvatar; if (text != null) params["text"] = text!; final encodedImage = _encodeImage(imageUrl); if (encodedImage != null) params["imageUrl"] = encodedImage; if (timestamp != null) params["timestamp"] = timestamp.toString(); if (verified != null) params["verified"] = verified.toString(); // engagment is complex obj, better suited for POST reqests if (engagement != null) { params["engagement"] = jsonEncode(engagement!.toJson()); } return Uri(queryParameters: params).query; } } class QuoteGeneratorApi { // static const String _baseUrl = "https://quotes.imbenji.net"; static const String _baseUrl = "http://localhost:3000"; // genrate a quote image using POST static Future generateQuotePost(QuoteRequest request) async { final url = Uri.parse("$_baseUrl/generate"); final requestBody = request.toJson(); final response = await http.post( url, headers: { "Content-Type": "application/json", "User-Agent": "QuoteGen-Flutter/1.0", }, body: jsonEncode(requestBody), ); if (response.statusCode == 200) { return response.bodyBytes; } else { throw Exception("Failed to genrate quote: ${response.statusCode}"); } } // generate a quote image usng GET static Future generateQuoteGet(QuoteRequest request) async { final queryString = request.toQueryString(); final url = Uri.parse("$_baseUrl/generate?$queryString"); final response = await http.get( url, headers: {"User-Agent": "QuoteGen-Flutter/1.0"}, ); if (response.statusCode == 200) { return response.bodyBytes; } else { throw Exception("Failed to generate quote: ${response.statusCode}"); } } // convienence method that uses POST by defualt static Future generateQuote(QuoteRequest request) async { return generateQuotePost(request); } // check api helth static Future checkHealth() async { try { final url = Uri.parse("$_baseUrl/health"); final response = await http.get( url, headers: {"User-Agent": "QuoteGen-Flutter/1.0"}, ); if (response.statusCode == 200) { final data = jsonDecode(response.body); return data["status"] == "ok"; } return false; } catch (e) { return false; } } // helper to get current timestmp in seconds static int getCurrentTimestamp() { return DateTime.now().millisecondsSinceEpoch ~/ 1000; } }