how to refresh message stream for chat inbox in flutter - api

I'm new to the flutter, I start to create a simple messenger app using flutter and flutter stream to handle API call for get message content.
also created the message controller to update and refresh the message list
class MessageService {
Client httpClient = Client();
List<MessageModal> _messageList = [];
Future<AppConfig> _getApiURL() async {
final config = await AppConfig.forEnvironment('dev');
return config;
}
Future<List<MessageModal>> getMessageThread(
String senderId, String receiverId) async {
var config = await _getApiURL();
var url = config.baseUrl + "message/history";
final response = await httpClient.post(url,
headers: {"content-type": "application/json"},
body: json.encode({
"senderId": senderId,
"receiverId": receiverId,
}));
if (response.statusCode == 200) {
_messageList = messageListFromJson(response.body);
} else {
_messageList = [];
}
return _messageList;
}
}
Here is the message service class for fetch API data
class MessageService {
Client httpClient = Client();
List<MessageModal> _messageList = [];
Future<AppConfig> _getApiURL() async {
final config = await AppConfig.forEnvironment('dev');
return config;
}
Future<List<MessageModal>> getMessageThread(
String senderId, String receiverId) async {
var config = await _getApiURL();
var url = config.baseUrl + "message/history";
final response = await httpClient.post(url,
headers: {"content-type": "application/json"},
body: json.encode({
"senderId": senderId,
"receiverId": receiverId,
}));
if (response.statusCode == 200) {
_messageList = messageListFromJson(response.body);
} else {
_messageList = [];
}
return _messageList;
}
}
Here is the ui preview to create the message list preview
StreamBuilder<List<MessageModal>> _buildStreamBuilder() {
return StreamBuilder<List<MessageModal>>(
// stream: _messageService.getMessageThread("UID1", "UID2").asStream(),
stream: streamController.counter,
initialData: _messageList,
builder:
(BuildContext context, AsyncSnapshot<List<MessageModal>> snapshot) {
print(snapshot.data);
if (snapshot.hasError) {
print(snapshot.error);
return Center(
child: Text("Something went wrong!"),
);
} else if (snapshot.hasData) {
List<MessageModal> messages = snapshot.data;
return _buildMessageHistory(messages);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
I need to do the messages update and also keep updating (send the API call and fetch data to stream) the message preview. can anybody help me on this one.

It's been a while since this question was originally asked. Since then the Stream Chat Flutter support has improved a lot. Implementing what this question asks is now really easy, with varying levels of customisability.
Pre-built UI widgets package : https://pub.dev/packages/stream_chat_flutter
This package is plug and play to add chat support, with a lot of customisability options.
If you want more control: https://pub.dev/packages/stream_chat_flutter_core
This package provides convenient builders to enable you to build your own UI components. It does the heavy lifting for you but will require more implementation on your side.
If you want low level control:
https://pub.dev/packages/stream_chat
Check out the tutorial for an easy getting started guide: https://getstream.io/chat/flutter/tutorial/
See here for awesome Stream examples of what you can build: https://github.com/GetStream/flutter-samples
Video tutorials: https://www.youtube.com/playlist?list=PLNBhvhkAJG6t-BxkRAnSqa67lm5C1mpKk

Related

Stripe ASP.NET Core Webhook always returns BadRequest

I am currently working on a project and integrating Stripe payment. So far I have done the Stripe Checkout Session with the prebuilt template from here - https://stripe.com/docs/payments/accept-a-payment?integration=checkout. This is what I have done:
[HttpPost]
public async Task<ActionResult> CreateCheckoutSession()
{
var consultation = await this.offerService.GetOnlineConsultationModelAsync();
var options = new SessionCreateOptions
{
CustomerEmail = this.User.FindFirst(ClaimTypes.Email).Value,
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions
{
PriceData = new SessionLineItemPriceDataOptions
{
UnitAmount = (long)(consultation.Price * 100),
Currency = "bgn",
ProductData = new SessionLineItemPriceDataProductDataOptions
{
Name = consultation.Name,
Images = new List<string>
{
"https://res.cloudinary.com/dpo3vbxnl/image/upload/v1641585449/pediamed/onlineConsultation_vyvebl.jpg"
}
},
},
Quantity = 1,
},
},
PaymentIntentData = new SessionPaymentIntentDataOptions
{
CaptureMethod = "automatic",
},
PaymentMethodTypes = new List<string>
{
"card"
},
Mode = "payment",
SuccessUrl = "http://pediamed-001-site1.btempurl.com/Checkout/SuccessfulPayment",
CancelUrl = "http://pediamed-001-site1.btempurl.com/Checkout/CanceledPayment",
};
var service = new SessionService();
try
{
var session = service.Create(options);
await this.paymentService.CreateChekoutSession(session.Id, session.PaymentIntentId, session.ClientReferenceId);
Response.Headers.Add("Location", session.Url);
return new StatusCodeResult(303);
}
catch (StripeException ex)
{
System.Console.WriteLine(ex.StripeError.Message);
return BadRequest(new { Message = ex.StripeError.Message });
}
}
So far so good - everything works. Then I created the template from the Fulfill your orders article - https://stripe.com/docs/payments/checkout/fulfill-orders. This is what I currently have:
[HttpPost]
public async Task<IActionResult> Index()
{
string secret = "whsec_......";
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
try
{
var stripeEvent = EventUtility.ConstructEvent(
json,
Request.Headers["Stripe-Signature"],
secret
);
// Handle the checkout.session.completed event
if (stripeEvent.Type == Events.CheckoutSessionCompleted)
{
var session = stripeEvent.Data.Object as Session;
// Fulfill the purchase...
await this.FulfillOrder(session);
}
return Ok();
}
catch (StripeException ex)
{
return BadRequest(new {message = ex.StripeError.Message});
}
}
The problem is when I use the Stripe CLI I get this:
Every time I get Bad Request, even in the details of the events in the Stripe Dashboard, but if I go to the payments dashboard they are all succeeded. Everything is done in localhost, but when deployed and using the correct api keys it is all the same. Can anyone help me with the Stripe webhook.
I suspect you might be using the signing secret from your dashboard, but when forwarding via the CLI using listen you need to use the CLI-specific signing secret that's provided in output when you run listen.
If that's not the case, can you provide more detail about the exception that's raised that leads to the bad request response path? ie, where in your code do you encounter an error?

Flutter Chopper 401 renew & retry interceptor

I'm using Chopper in my flutter app and what I need to do is, when I get 401 response status code (unauthorized) from my API, I must call another endpoint that will refresh my token and save it into secured storage, when all of this is done, I need to retry the request instantly (so that user cannot notice that his token expired). Is this dooable with Chopper only, or I have to use some other package?
It is possible. You need to use the authenticator field on the Chopper client, e.g.
final ChopperClient client = ChopperClient(
baseUrl: backendUrl,
interceptors: [HeaderInterceptor()],
services: <ChopperService>[
_$UserApiService(),
],
converter: converter,
authenticator: MyAuthenticator(),
);
And your authenticator class, should look something like this:
class MyAuthenticator extends Authenticator {
#override
FutureOr<Request?> authenticate(
Request request, Response<dynamic> response) async {
if (response.statusCode == 401) {
String? newToken = await refreshToken();
final Map<String, String> updatedHeaders =
Map<String, String>.of(request.headers);
if (newToken != null) {
newToken = 'Bearer $newToken';
updatedHeaders.update('Authorization', (String _) => newToken!,
ifAbsent: () => newToken!);
return request.copyWith(headers: updatedHeaders);
}
}
return null;
}
Admittedly, it wasn't that easy to find/understand (though it is the first property of the chopper client mentioned in their docs), but it is precisely what this property is for. I was going to move to dio myself, but I still had the same issue with type conversion on a retry.
EDIT: You will probably want to keep a retry count somewhere so you don't end up in a loop.
I searched couple of days for answer, and I came to conclusion that this is not possible with Chopper... Meanwhile I switched to Dio as my Networking client, but I used Chopper for generation of functions/endpoints.
Here is my Authenticator. FYI I'm storing auth-token and refresh-token in preferences.
class AppAuthenticator extends Authenticator {
#override
FutureOr<Request?> authenticate(Request request, Response response, [Request? originalRequest]) async {
if (response.statusCode == HttpStatus.unauthorized) {
final client = CustomChopperClient.createChopperClient();
AuthorizationApiService authApi = client.getService<AuthorizationApiService>();
String refreshTokenValue = await Prefs.refreshToken;
Map<String, String> refreshToken = {'refresh_token': refreshTokenValue};
var tokens = await authApi.refresh(refreshToken);
final theTokens = tokens.body;
if (theTokens != null) {
Prefs.setAccessToken(theTokens.auth_token);
Prefs.setRefreshToken(theTokens.refresh_token);
request.headers.remove('Authorization');
request.headers.putIfAbsent('Authorization', () => 'Bearer ${theTokens.auth_token}');
return request;
}
}
return null;
}
}
Based on this example: github
And Chopper Client:
class CustomChopperClient {
static ChopperClient createChopperClient() {
final client = ChopperClient(
baseUrl: 'https://example.com/api/',
services: <ChopperService>[
AuthorizationApiService.create(),
ProfileApiService.create(),
AccountingApiService.create(), // and others
],
interceptors: [
HttpLoggingInterceptor(),
(Request request) async => request.copyWith(headers: {
'Accept': "application/json",
'Content-type': "application/json",
'locale': await Prefs.locale,
'Authorization': "Bearer ${await Prefs.accessToken}",
}),
],
converter: BuiltValueConverter(errorType: ErrorDetails),
errorConverter: BuiltValueConverter(errorType: ErrorDetails),
authenticator: AppAuthenticator(),
);
return client;
}
}

API Request in Dialogflow Fulfillment (Javascript)

So I'm trying to make a google action using Dialogflow that requires an external API. I've always used jQuery .getJSON() to make API calls, so I had no idea how to do this. After searching this up online, I found a way to do this using vanilla javascript (I also tested the way on my website and it worked fine). The code for that is below:
function loadXMLDoc() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
console.log(xmlhttp.responseText);
}
};
xmlhttp.open("GET", "https://translate.yandex.net/api/v1.5/tr.json/translate?lang=en-es&key=trnsl.1.1.20190105T052356Z.7f8f950adbfaa46e.9bb53211cb35a84da9ce6ef4b30649c6119514a4&text=eat", true);
xmlhttp.send();
}
The code worked fine on my website, but as soon as I added it to the Dialogflow, it would give me the error
XMLHttpRequest is not defined
Obviously that happened because I never defined it (using var), except it worked without me doing anything. So then, I tried adding this line
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
to the code, and it stopped giving me the error (because I defined XMLHttpRequest). But then, my code wouldn't work.
TL;DR: How can I make an external API call using Dialogflow fulfillment?
You can use https. But make sure that you upgrade to Blaze Pay(or any other plans) to make external API calls, else you will receive an error such as
Error:
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions.
Code to make external api call,
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
"use strict";
const functions = require("firebase-functions");
const { WebhookClient } = require("dialogflow-fulfillment");
const { Card, Suggestion } = require("dialogflow-fulfillment");
const https = require("https");
process.env.DEBUG = "dialogflow:debug"; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(
(request, response) => {
const agent = new WebhookClient({ request, response });
console.log(
"Dialogflow Request headers: " + JSON.stringify(request.headers)
);
console.log("Dialogflow Request body: " + JSON.stringify(request.body));
function getWeather() {
return weatherAPI()
.then(chat => {
agent.add(chat);
})
.catch(() => {
agent.add(`I'm sorry.`);
});
}
function weatherAPI() {
const url =
"https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
return new Promise((resolve, reject) => {
https.get(url, function(resp) {
var json = "";
resp.on("data", function(chunk) {
console.log("received JSON response: " + chunk);
json += chunk;
});
resp.on("end", function() {
let jsonData = JSON.parse(json);
let chat = "The weather is " + jsonData.weather[0].description;
resolve(chat);
});
});
});
}
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set("Default Welcome Intent", welcome);
intentMap.set("Default Fallback Intent", fallback);
intentMap.set("Weather Intent", getWeather);
agent.handleRequest(intentMap);
}
);
This article is a diamond! It really helped to clarify what's going on and what's required in Dialogflow fullfilments.
A small suggestion is to gracefully catch the error in the connection to the webservice:
function weatherAPI() {
const url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
return new Promise((resolve, reject) => {
https.get(url, function(resp) {
var json = "";
resp.on("data", function(chunk) {
console.log("received JSON response: " + chunk);
json += chunk;
});
resp.on("end", function() {
let jsonData = JSON.parse(json);
let chat = "The weather is " + jsonData.weather[0].description;
resolve(chat);
});
}).on("error", (err) => {
reject("Error: " + err.message);
});
});
}

How to display images, received from an API response in flutter?

I am using the http Dart package to send a GET request to the MapQuest Static Maps API to obtain an image. However, the response to this request directly returns an image, and not a Uri, or maybe I am doing something wrong.
Can you please help me display the received image?
Here's the request Code:
final http.Response response = await http.get(
'https://www.mapquestapi.com/geocoding/v1/address?key=[MYAPIKEY]&inFormat=kvp&outFormat=json&location=${address}&thumbMaps=false&maxResults=1');
final decodedResponse = json.decode(response.body);
setState(() {
_coords = decodedResponse['results'][0]['locations'][0]['latLng'];
});
final http.Response staticMapResponse = await http.get(
'https://www.mapquestapi.com/staticmap/v5/map?key=[MYAPIKEY]&center=${coords['lat']},${coords['lng']}&zoom=13&type=hyb&locations=${coords['lat']},${coords['lng']}&size=500,300#2x');
The coordinates are received from the Geocoding API of MapQuest which is an async request.
As suggested by Günter Zöchbauer I included the Request Url directly in my Image.network() widget and it worked.
Here's the Code:
void getStaticMapCoordinates(String address) async {
if (address.isEmpty) {
return;
}
final http.Response response = await http.get(
'https://www.mapquestapi.com/geocoding/v1/address?key=[APIKEY]&inFormat=kvp&outFormat=json&location=${address}&thumbMaps=false&maxResults=1');
final decodedResponse = json.decode(response.body);
setState(() {
_coords = decodedResponse['results'][0]['locations'][0]['latLng'];
});
}
Widget _buildStaticMapImage() {
if(_coords == null) {
return Image.asset('assets/product.jpg');
}
return FadeInImage(
image: NetworkImage(
'https://www.mapquestapi.com/staticmap/v5/map?key=[APIKEY]&center=${_coords['lat']},${_coords['lng']}&zoom=13&type=hyb&locations=${_coords['lat']},${_coords['lng']}&size=500,300#2x'),
placeholder: AssetImage('assets/product.jpg'),
);
}
The getStaticMapCoordinates function executes everytime the user changes the address field and as a result of setState, the Image Widget re-renders.
I used Image class to create an in-memory image object from the response body in a byte stream form.
var _profileImage = Image.memory(response.bodyBytes).image;
Now you can directly load this image in your component.
If a image is the repose from your URL:
//...
child: new ClipRRect(
borderRadius: new BorderRadius.circular(30.0),
child: Image.network('https://www.mapquestapi.com/staticmap/v5/map?key=[MYAPIKEY]&center=${coords['lat']},${coords['lng']}&zoom=13&type=hyb&locations=${coords['lat']},${coords['lng']}&size=500,300#2x',
fit: BoxFit.cover,
height: 60.0, width: 60.0))
//...
if you need to parse:
final response = await http
.get('https://www.mapquestapi.com/staticmap/v5/map?key=[MYAPIKEY]&center=${coords['lat']},${coords['lng']}&zoom=13&type=hyb&locations=${coords['lat']},${coords['lng']}&size=500,300#2x');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return json.decode(response.body)["imageURL"]; // <------- DO THE PARSING HERE AND THEN PASS THE URL TO THE ABOVE EXAMPLE
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
Sometimes fetching image from URL with complex headers are cumbersome. So at that time, we can use the header map directly in the NetworkImage widget and it works like a charm-
.
.
child: CircleAvatar(
backgroundImage:
NetworkImage("www.mypropic.com",headers:getTokenHeaders());)
.
.
static Map<String, String> getTokenHeaders() {
Map<String, String> headers = new Map();
headers['Authorization'] = _bearerToken!;
headers['content-type'] = 'application/json';
return headers;
}
You can add the headers to the NetworkImage widget like :
NetworkImage( "your endpoint URL ", headers: {header:value})
Below link has more information.
https://api.flutter.dev/flutter/widgets/Image/Image.network.html

angularjs post file to web api

I've a angular function for to upload file to web api
$scope.uploadFile = function () {
var file = $scope.file;
console.log("file: " + file);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", uri);
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Name", file.fileName);
xhr.setRequestHeader("X-File-Size", file.fileSize);
xhr.setRequestHeader("X-File-Type", file.type);
$scope.progressVisible = true;
xhr.send(file);
}
function uploadProgress(evt) {
$scope.$apply(function () {
if (evt.lengthComputable) {
$scope.progress = Math.round(evt.loaded * 100 / evt.total);
}
})
}
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}
function uploadCanceled(evt) {
$scope.$apply(function () {
$scope.progressVisible = false;
})
alert("The upload has been canceled by the user or the browser dropped the connection.");
}
The code is available here http://jsfiddle.net/vishalvasani/4hqVu/
I need a web api controller to manage file post, how can I read it?
Is it possible to use PostValue instead of task async?
Server side I get the file, read content, query a database and return JSON response
Thanks