This commit is contained in:
@@ -3,6 +3,29 @@ 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<String, dynamic> 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;
|
||||
@@ -10,6 +33,8 @@ class QuoteRequest {
|
||||
final String? text;
|
||||
final dynamic imageUrl; // can be String or Uint8List
|
||||
final int? timestamp;
|
||||
final bool? verified;
|
||||
final QuoteEngagement? engagement;
|
||||
|
||||
QuoteRequest({
|
||||
this.displayName,
|
||||
@@ -18,6 +43,8 @@ class QuoteRequest {
|
||||
this.text,
|
||||
this.imageUrl,
|
||||
this.timestamp,
|
||||
this.verified,
|
||||
this.engagement,
|
||||
});
|
||||
|
||||
|
||||
@@ -40,6 +67,8 @@ class QuoteRequest {
|
||||
"text": text,
|
||||
"imageUrl": _encodeImage(imageUrl),
|
||||
"timestamp": timestamp,
|
||||
if (verified != null) "verified": verified,
|
||||
if (engagement != null) "engagement": engagement!.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,13 +87,20 @@ class QuoteRequest {
|
||||
|
||||
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";
|
||||
// static const String _baseUrl = "https://quotes.imbenji.net";
|
||||
static const String _baseUrl = "http://localhost:3000";
|
||||
|
||||
|
||||
// genrate a quote image using POST
|
||||
|
||||
251
lib/services/quote_api_v2.dart
Normal file
251
lib/services/quote_api_v2.dart
Normal file
@@ -0,0 +1,251 @@
|
||||
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<String, dynamic> 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<String, dynamic> 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<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
|
||||
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<String, dynamic> 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<QuoteSession> 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<QuoteSession> 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<QuoteSession> 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<Uint8List> 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<void> 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user