first of all, here I want to create an icon button for bookmarking a story. The mechanism is something like when the user clicks on a bookmark icon on a story card, then the field typed list on my Firebase Firestore will record the user id because the user already clicked the bookmark icon. And if the user clicks for the second time, the user id will be removed from the field typed list on the Firestore. But I got an error.
Here is how I create the bookmark icon
IconButton(
icon: favIcon,
onPressed: () {
setState(
() async {
if (widget.story.favorite.contains(user.uid)) {
widget.story.favorite.remove(user.uid);
} else {
widget.story.favorite.add(user.uid);
await StoryService().updatestory(widget.story);
// await _storyReference
// .doc(widget.story.id)
// .update(widget.story.toMap());
}
},
);
},
),
I also would like to show the method that I use,
Future updatestory(StoryModel story) async {
try {
await _storyReference.doc(story.id).update(story.toMap());
return true;
} catch (e) {
throw e;
}
}
Ok, now the story model part
class StoryModel extends Equatable {
List<String> favorite;
final String id;
final String name;
final String author;
final String imageUrl;
final double rating;
final String storytext;
StoryModel({
required this.id,
required this.favorite,
this.name = '',
this.author = '',
this.imageUrl = '',
this.rating = 0.0,
this.storytext = '',
});
factory StoryModel.fromJson(String id, Map<String, dynamic> json) =>
StoryModel(
id: id,
name: json['name'],
author: json['author'],
imageUrl: json['imageUrl'],
rating: json['rating'].toDouble(),
storytext: json['storytext'],
favorite: json["favorite"] == null
? []
: json["favorite"].json<String>((i) => i as String).toList(),
);
Map<String, dynamic> toMap() {
return {
id: id,
"name": name,
"author": author,
"imageUrl": imageUrl,
"rating": rating,
"storytext": storytext,
"favorite": favorite,
};
}
here the screenshot of the field list
enter image description here
But I got an error like this, and I cannot access my screen application anymore.
NoSuchMethodError: Class 'List<dynamic>' has no Instance method 'json'
Receiver: Instance (length: 1) of '_GrowableList' Tried calling: json<String>(Closure: (dynamic) => String)
Any help would be appreciated, Thanks!
Related
I am trying to fix a bug in a web application using java 8, spring boot, Spring MVC and front end with angular cli. When the user logins the application and is created a menu considering the user profile permission with java, but the application uses angular router with static paths, so if the user rewrite the URL he can access anything even without permissions.
const routes: Routes = [
{
path: '',
component: WebservicesComponent,
children: [
{ path: 'perfis', loadChildren: './wsperfis/wsperfis.module#WsperfisModule', },
{ path: 'acessos', loadChildren: './wsacessos/wsacessos.module#WsacessosModule', },
{ path: 'novoAcesso', loadChildren: './novo-acesso/novo-acesso.module#NovoAcessoModule', },
{ path: 'servicos', loadChildren: './wsservicos/wsservicos.module#WsservicosModule' },
{ path: 'novoperfil', loadChildren: './wsnovoperfil/wsnovoperfil.module#WsnovoperfilModule' }
]
}
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class WebservicesRoutingModule {
}
#CrossOrigin
#RequestMapping("/menu")
public List<Object> menu(#RequestParam(value = "idPerfil") int idPerfil) {
List<Menu> menus = menuService.getMenus(idPerfil);
List<Object> menu = new ArrayList<Object>();
Map<String, Object> mapMenu = new HashMap<String, Object>();
Map<String, String> mapSubMenu = new HashMap<String, String>();
List<Object> listMapSubMenu = new ArrayList<Object>();
for (Menu menuItem : menus) {
if (!mapMenu.containsValue(menuItem.getPaiPrompt())) {
mapMenu = new HashMap<String, Object>();
listMapSubMenu = new ArrayList<Object>();
mapMenu.put(LABEL, menuItem.getPaiPrompt());
mapMenu.put(URL, menuItem.getPaiUrl());
mapMenu.put(ICON, menuItem.getPaiIcon());
for (Menu submenu : menus) {
if (menuItem.getPaiPrompt().equals(submenu.getPaiPrompt())) {
mapSubMenu = new HashMap<String, String>();
mapSubMenu.put(LABEL, submenu.getFilhoPrompt());
mapSubMenu.put(URL, submenu.getFilhoUrl());
mapSubMenu.put(ICON, submenu.getFilhoIcon());
listMapSubMenu.add(mapSubMenu);
}
}
mapMenu.put(ITEMS, listMapSubMenu);
menu.add(mapMenu);
}
}
return menu;
}
You should add a validation on your front and backend, for example, when path changes in frontend and component is mounted it checks for session sending its path id, backend compare that versus asigned menu, all this before making any other api call.
Another solution more complex (and secure) is adding the validation on api itself, by checking menus or user profiles, this way even if user access a page he should not (its mapped in js), he won't access unauthorized apis.
I could do it using a canActiveChild validation in a Guard file, but now I am if a issue in the first time that I call it.
The service that a call there stay with the status pending in the the first time that I call it, but in the next calls it works fine.
Fallow the code:
constructor(private router: Router, private _cookieService: CookieService, private comumService: ComumService) {}
canActivate() {
if (this._cookieService.get('AuthorizationToken')) {
return true;
}
this.router.navigate(['login']);
return false;
}
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log('state.url: ' + state.url);
// tslint:disable-next-line:triple-equals
if (state.url == '/dashboard' || this.validaAcesso(state.url)) {
return true;
} else {
console.log('Entrou aqui!!!');
window.alert('You don\'t have permission to view this page');
this.router.navigate(['dashboard']);
return false;
}
}
validaAcesso(url: string) {
this._cookieService.getAll();
this.comumService.validaAcesso(url).subscribe((data: Boolean) => {
console.log(data.valueOf());
if (data.valueOf()) {
console.log('validaAcesso return true');
this.result = true;
} else {
console.log('validaAcesso return false');
this.result = false;
}
});
return this.result;
}
}
I'm working on a restaurant app and need help creating object to function as a cart that will hold live data but, I'm a little lost on this. This is what I've made so far:
class Cart {
int resId;
String productImage;
CartDetails orderDetails;
Cart(this.resId, this.orderDetails, this.productImage);
}
class CartDetails {
int productId;
int quantity;
CartDetails(this.productId, this.quantity);
}
Then in order to hold the data:
// //Cart Data
List<Cart> _cart = [];
List<Cart> get userCart => _cart;
Map<String, dynamic> orderDetail;
I add items to my object with this:
void addToCart(resId, proudctName, prodId, qty) {
orderDetail = {'prodId': prodId, 'quantity': qty};
try {
List<Map> list;
list.map((i) {
Cart i = Cart as Cart;
i.resId = resId;
i.productName = proudctName;
i.orderDetails.productId = prodId;
i.orderDetails.quantity = qty;
_cart.add(i);
}).toList();
print(_cart.toList());
} catch (e) {
print('Sumptin went wrong bruh');
print(e);
}
print(userCart);
}
then I bring them all together with this:
addToCart(widget.resId, widget.prodName, prodId, cartVal);
When I do that, I get an error :
NoSuchMethodError: The method 'map' was called on null.
Receiver: null
Tried calling: map<Null>(Closure: (Map<dynamic, dynamic>) => Null)
I'm not sure where to go from here to add all the items to the map and then access the data in different places within the app.
Suppose your json string look like this
{
"resId":123,
"productImage":"http",
"CartDetails" :
[
{"productId":1,
"quantity":2},
{"productId":3,
"quantity":4}
]
}
code snippet to create object , convert object to json and convert json string to object
List<CartDetail> listCart = [];
listCart.add(CartDetail(productId: 1, quantity: 2));
listCart.add(CartDetail(productId: 3, quantity: 4));
Payload payload = Payload(resId: 1, productImage: "", cartDetails: listCart);
print('${payload.cartDetails[0].productId.toString()}');
String payloadStr = payloadToJson(payload);
print('${payloadStr}');
final payload1 = payloadFromJson(jsonString);
print('${payload1.cartDetails[0].productId.toString()}');
related class
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
int resId;
String productImage;
List<CartDetail> cartDetails;
Payload({
this.resId,
this.productImage,
this.cartDetails,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
resId: json["resId"] == null ? null : json["resId"],
productImage: json["productImage"] == null ? null : json["productImage"],
cartDetails: json["CartDetails"] == null ? null : List<CartDetail>.from(json["CartDetails"].map((x) => CartDetail.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"resId": resId == null ? null : resId,
"productImage": productImage == null ? null : productImage,
"CartDetails": cartDetails == null ? null : List<dynamic>.from(cartDetails.map((x) => x.toJson())),
};
}
class CartDetail {
int productId;
int quantity;
CartDetail({
this.productId,
this.quantity,
});
factory CartDetail.fromJson(Map<String, dynamic> json) => CartDetail(
productId: json["productId"] == null ? null : json["productId"],
quantity: json["quantity"] == null ? null : json["quantity"],
);
Map<String, dynamic> toJson() => {
"productId": productId == null ? null : productId,
"quantity": quantity == null ? null : quantity,
};
}
full code
import 'package:flutter/material.dart';
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
int resId;
String productImage;
List<CartDetail> cartDetails;
Payload({
this.resId,
this.productImage,
this.cartDetails,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
resId: json["resId"] == null ? null : json["resId"],
productImage: json["productImage"] == null ? null : json["productImage"],
cartDetails: json["CartDetails"] == null ? null : List<CartDetail>.from(json["CartDetails"].map((x) => CartDetail.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"resId": resId == null ? null : resId,
"productImage": productImage == null ? null : productImage,
"CartDetails": cartDetails == null ? null : List<dynamic>.from(cartDetails.map((x) => x.toJson())),
};
}
class CartDetail {
int productId;
int quantity;
CartDetail({
this.productId,
this.quantity,
});
factory CartDetail.fromJson(Map<String, dynamic> json) => CartDetail(
productId: json["productId"] == null ? null : json["productId"],
quantity: json["quantity"] == null ? null : json["quantity"],
);
Map<String, dynamic> toJson() => {
"productId": productId == null ? null : productId,
"quantity": quantity == null ? null : quantity,
};
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String jsonString = '''
{
"resId":123,
"productImage":"http",
"CartDetails" :
[
{"productId":1,
"quantity":2},
{"productId":3,
"quantity":4}
]
}
''';
void _incrementCounter() {
List<CartDetail> listCart = [];
listCart.add(CartDetail(productId: 1, quantity: 2));
listCart.add(CartDetail(productId: 3, quantity: 4));
Payload payload = Payload(resId: 1, productImage: "", cartDetails: listCart);
print('${payload.cartDetails[0].productId.toString()}');
String payloadStr = payloadToJson(payload);
print('${payloadStr}');
final payload1 = payloadFromJson(jsonString);
print('${payload1.cartDetails[0].productId.toString()}');
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Output
I/flutter ( 9822): 1
I/flutter ( 9822): {"resId":1,"productImage":"","CartDetails":
[{"productId":1,"quantity":2},{"productId":3,"quantity":4}]}
I/flutter ( 9822): 1
I'm creating a bot for FAQ . When bot start conversations send a PromptDialog with 2 options: english, french.
I want to forward the dialog to EnglishLuis when user chooses English button, and FrenchLuis when choosing French.
Here is my code :
Rootdialog.cs
public class RootDialog : IDialog<object>
{
private const string EnglishMenu = "English";
private const string FrenchMenu = "French";
private const string QAMenu = "Q&A";
private List<string> mainMenuList = new List<string>() { EnglishMenu, FrenchMenu, QAMenu };
private string location;
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Welcome to Root Dialog");
context.Wait(MessageReceiveAsync);
}
private async Task MessageReceiveAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var reply = await result;
if (reply.Text.ToLower().Contains("help"))
{
await context.PostAsync("You can implement help menu here");
}
else
{
await ShowMainmenu(context);
}
}
private async Task ShowMainmenu(IDialogContext context)
{
//Show menues
PromptDialog.Choice(context, this.CallDialog, this.mainMenuList, "What do you want to do?");
}
private async Task CallDialog(IDialogContext context, IAwaitable<string> result)
{
//This method is resume after user choise menu
// this.luisResult = result;
// var message = await result;
var selectedMenu = await result;
var message = await result;
switch (selectedMenu)
{
case EnglishMenu:
//Call child dialog without data
// context.Call(new EnglishLuis(),ResumeAfterDialog);
// context.Call(new EnglishLuis(), ResumeAfterDialog);
await Conversation.SendAsync(context.MakeMessage(), () => new EnglishLuis());
break;
case FrenchMenu:
//Call child dialog with data
context.Call(new HotelDialog(location), ResumeAfterDialog);
break;
case QAMenu:
context.Call(new LuisCallDialog(),ResumeAfterDialog);
break;
}
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<object> result)
{
//Resume this method after child Dialog is done.
var test = await result;
if (test != null)
{
location = test.ToString();
}
else
{
location = null;
}
await this.ShowMainmenu(context);
}
}
}
EnglishLuis.cs :
public class EnglishLuis : LuisDialog<object>
{
private string location;
// string message = $"welcome to english dialog";
public async Task None(IDialogContext context, LuisResult result)
{
string message = $"Sorry, I did not understand '{result.Query}'. Please try again";
await context.PostAsync(message);
context.Wait(this.MessageReceived);
context.Done(true);
}
[LuisIntent("gretting")]
[LuisIntent("intentfr")]
public async Task Greeting(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
await context.PostAsync("Welcome :) ");
context.Wait(MessageReceived);
context.Done(true);
}
[LuisIntent("test")]
public async Task test(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
await context.PostAsync("Do you want to test our bot ? We suggest to type : hi or who are you, help etc..");
// context.Done(true);
context.Wait(MessageReceived);
context.Done(true);
}
My problem is that when i choose English ( even French ) i got this error : here was an error sending this message to your bot: HTTP status code InternalServerError
Can you please help me how to start luis dialog ?
P.S if i start from MessagesController.cs directly works good...but my intentions is to let people choose between two languages.
I try to call the luis using : await context.Forward(new EnglishLuis(), ResumeAfterDialog, message, CancellationToken.None); but without result .
new file RootDialog.cs ( updated ) :
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System.Threading;
namespace TeamsBot.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
private const string EnglishMenu = "English";
private const string FrenchMenu = "French";
private const string QAMenu = "Q&A";
private List<string> mainMenuList = new List<string>() { EnglishMenu,
FrenchMenu, QAMenu };
private string location;
private string originalMessage;
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Welcome to Root Dialog");
context.Wait(MessageReceiveAsync);
}
private async Task MessageReceiveAsync(IDialogContext context,
IAwaitable<IMessageActivity> result)
{
var reply = await result;
this.originalMessage = reply.Text;
if (reply.Text.ToLower().Contains("help"))
{
await context.PostAsync("You can implement help menu here");
}
else
{
await ShowMainmenu(context);
}
}
private async Task ShowMainmenu(IDialogContext context)
{
//Show menues
PromptDialog.Choice(context, this.CallDialog, this.mainMenuList,
"What do you want to do?");
}
private async Task CallDialog(IDialogContext context, IAwaitable<string>
result)
{
var selectedMenu = await result;
switch (selectedMenu)
{
case EnglishMenu:
//Call child dialog without data
var newMessage = context.MakeMessage();
newMessage.Text = reply.Text;
await context.Forward(new EnglishLuis(), ResumeAfterDialog, newMessage, CancellationToken.None);
break;
case FrenchMenu:
//Call child dialog with data
// context.Call(new HotelDialog(location), ResumeAfterDialog);
var frenchLuis = new FrenchLuis();
var messageToForward = await result;
// await context.Forward(new FrenchLuis(), ResumeAfterDialog, messageToForward, CancellationToken.None);
break;
case QAMenu:
context.Call(new LuisCallDialog(),ResumeAfterDialog);
break;
}
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<object> result)
{
//Resume this method after child Dialog is done.
var test = await result;
if (test != null)
{
location = test.ToString();
}
else
{
location = null;
}
await this.ShowMainmenu(context);
}
}
}
First, doing
context.Wait(this.MessageReceived);
context.Done(true);
it's wrong. You need to choose: or you wait for a new message in the EnglishDialog or you end the EnglishDialog (with Done)
Then, you are trying to send a string in the context.Forward and you need to forward an IMessageActivity. And I suspect you want to send the original message, so you will need to save that in global variable before continuing with the prompt. Try with:
var newMessage = context.MakeMessage();
newMessage.Text = this.originalMessageText //the variable that contains the text of the original message that you will have to save at MessageReceiveAsync
await context.Forward(new EnglishLuis(), ResumeAfterDialog, newMessage, CancellationToken.None);
MessageReceivedAsync in RootDialog should looks like:
private async Task MessageReceiveAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var reply = await result;
if (reply.Text.ToLower().Contains("help"))
{
await context.PostAsync("You can implement help menu here");
}
else
{
this.originalMessage = reply.Text;
await ShowMainmenu(context);
}
}
This is how I would have implemented your method for calling the different dialogs. I tend to use dependency injection of dialogs so I don't have to constantly new them up.
private async Task CallDialog(IDialogContext context, IAwaitable<string> result)
{
//These two variables will be exactly the same, you only need one
//var selectedMenu = await result;
var message = await result;
switch (selectedMenu)
{
case EnglishMenu:
// Forward the context to the new LuisDialog to bring it to the top of the stack.
// This will also send your message to it so it gets processed there.
await context.Forward<object>(new EnglishLuis(), ResumeAfterDialog, message , CancellationToken.None);
break;
case FrenchMenu:
await context.Forward<object>(new HotelDialog(location), ResumeAfterDialog, message , CancellationToken.None);
break;
case QAMenu:
await context.Forward<object>(new LuisCallDialog(), ResumeAfterDialog, message , CancellationToken.None);
context.Call(new LuisCallDialog(),ResumeAfterDialog);
break;
}
}
There is an issue in your EnglishLuis dialog where you are using:
context.Wait(this.MessageReceived);
context.Done(true);
The issue is that both of these lines will execute when it passes through the dialog. The context.Done will cause this dialog to leave the stack so you'll end up going to the previous dialog instead, which clashes with the fact you're trying to wait for a response.
There shouldn't really be a context.Done in your luis dialog unless you want to go back to the previous Dialog. So if you're choosing to use context.Done either put it in the resumeAfter method with an appropriate condition or under a single intent for exiting this part of your program.
You didn't include a stack trace, but something that can cause issues when using Luis is if you're using one from a region other than the US. In which case you need to set the properties accordingly where the domain points to the right Luis service.
public EnglishLuis(ConstructorParameters parameters)
: base(new LuisService(new LuisModelAttribute(
"<AppId>",
"<SubscriptionKey>",
domain: "westeurope.api.cognitive.microsoft.com")))
{
// Constructor Stuff...
}
I have a msbot chat dialog that I want to have the following behaviour:
user -> get me some info about GARY
bot -> which gary, (prompt: choice options)
user -> gary peskett
bot -> sure, (hero card with gary's contact details)
I have this code
public class CustomerRepository
{
private IList<Customer> _customerList = new List<Customer>
{
new Customer
{
Name = "Gary Peskett"
},
new Customer
{
Name = "Gary Richards"
},
new Customer
{
Name = "Barry White"
}
};
public async Task<IEnumerable<Customer>> GetAll()
{
// usually calls a database (which is why async is on this method)
return _customerList;
}
}
public class XDialog : IDialog
{
private readonly IIntent _intent;
private readonly CustomerRepository _customerRepository;
public XDialog(IIntent intent, CustomerRepository customerRepository)
{
// An intent is decided before this point
_intent = intent;
_customerRepository = customerRepository;
}
public async Task StartAsync(IDialogContext context)
{
// // An intent can provide parameters
string name = _intent.Parameters["Name"] as string;
IEnumerable<Customer> customers = await _customerRepository.GetAll();
IList<Customer> limitedList = customers.Where(x => x.Name.Contains(name)).ToList();
if (limitedList.Any())
{
if (limitedList.Count > 1)
{
PromptDialog.Choice(context, LimitListAgain, limitedList,
"Can you specify which customer you wanted?");
}
else
{
Customer customer = limitedList.FirstOrDefault();
Finish(context, customer);
}
}
else
{
context.Done("No customers have been found");
}
}
private static async Task LimitListAgain(IDialogContext context, IAwaitable<Customer> result)
{
Customer customer = await result;
Finish(context, customer);
}
private static void Finish(IDialogContext context, Customer customer)
{
HeroCard heroCard = new HeroCard
{
Title = customer?.Name
};
context.Done(heroCard);
}
}
What i'm finding is that usually when I do context.Done(STRING) then that is output to the user, and this is really useful to end the dialog. As I want to end with a hero card, its outputing the typename
Microsoft.Bot.Connector.HeroCard
Can anyone help by either explaining a better way to use context.Done(R value) or help me return a hero card to end the dialog?
The dialog is being called with
Chain.PostToChain()
.Select(msg => Task.Run(() => _intentionService.Get(msg.ChannelId, msg.From.Id, msg.Text)).Result)
.Select(intent => _actionDialogFactory.Create(intent)) // returns IDialog based on intent
.Unwrap()
.PostToUser();
I think the problem is a side effect of using Chain.
As you may know, the context.Done doesn't post anything back to the user, it just ends the current dialog with the value provided.
The post to user is effectively happening in the .PostToUser() at the end of your Chain. Now, by looking into the PostToUser's code, I realized that at the end of the game, it's doing a context.PostAsync of item.ToString(), being item the payload provided in the context.Done in this case. See this.
One option (I haven't tested this), could be using .Do instead of .PostToUser() and manually perform what the PostToUserDialog does and finally perform a context.PostAsync() by creating a new IMessageActivity and adding the HeroCard as an attachment.
I am working on an application where I would like a url like so User\1\Class\Create would map to the Class controller and the Create action, but when I apply it, it doesn't pick it up.
Below is how I have the route registered (it is at the top of the list):
routes.MapRoute(
name: "UserClass",
url: "User/{userId}/Class/Create",
defaults: new { controller = "Class", action = "Create", userId= "" },
constraints: new { userId= #"\d+" }
);
(I have also tried it by omitting the userId="" default)
This is paired with this code:
public class ClassController : BaseController
{
public ActionResult Create(int userId)
{
var vm = new ClassEditorViewModel
{
Class = new Class { UserId = userId },
ClassEnrollmentStatuses = new SelectList(Db.ClassEnrollmentStatuses.ToList(), "Id", "Name")
};
return View(vm);
}
}
But this doesn't work. When I use Route Debugger (by Phil Haack) it doesn't use the above route and selects the {*catchall} route.
What am I doing wrong with the route configuration to make it not be used?
Deleted the route and re-typing it resolved the issue.