- Update of FormBase for better async event management

- Update of ControlBase for partial rendering options
- Updating serveral base classes to better asyc event management
- separating Enums into different folder
- some small bug fixes
- removed "CustomEventManagement" cause it doesnt make sense now
This commit is contained in:
FlorianDahn 2019-07-14 22:22:04 +02:00
parent cb4a797b12
commit e4da70d37d
17 changed files with 404 additions and 103 deletions

View File

@ -11,7 +11,12 @@ namespace TelegramBaseTest.Tests
public class ButtonTestForm : AutoCleanForm
{
public override async Task Opened()
public ButtonTestForm()
{
this.Opened += ButtonTestForm_Opened;
}
private async Task ButtonTestForm_Opened(object sender, EventArgs e)
{
await this.Device.Send("Hello world! (Click 'back' to get back to Start)");
}
@ -83,6 +88,8 @@ namespace TelegramBaseTest.Tests
btn.AddButtonRow(new ButtonBase("Button 3", new CallbackData("a", "button3").Serialize()), new ButtonBase("Button 4", new CallbackData("a", "button4").Serialize()));
btn.AddButtonRow(new ButtonBase("Google.com", "google", "https://www.google.com"), new ButtonBase("Telegram", "telegram", "https://telegram.org/"));
btn.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize()));
await this.Device.Send("Click a button", btn);

View File

@ -14,10 +14,13 @@ namespace TelegramBaseTest.Tests
public ProgressTest()
{
this.DeleteMode = eDeleteMode.OnLeavingForm;
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Opened += ProgressTest_Opened;
this.Closed += ProgressTest_Closed;
}
public override async Task Opened()
private async Task ProgressTest_Opened(object sender, EventArgs e)
{
await this.Device.Send("Welcome to ProgressTest");
}
@ -87,14 +90,14 @@ namespace TelegramBaseTest.Tests
//Render Progress bar and show some "example" progress
await Bar.Render();
await Bar.Render(message);
this.Controls.Add(Bar);
for (int i = 0; i <= 100; i++)
{
Bar.Value++;
await Bar.Render();
await Bar.Render(message);
Thread.Sleep(250);
}
@ -117,17 +120,10 @@ namespace TelegramBaseTest.Tests
await this.Device.Send("Choose your progress bar:", btn);
}
public override async Task Closed()
private async Task ProgressTest_Closed(object sender, EventArgs e)
{
foreach (var b in this.Controls)
{
await b.Cleanup();
}
await this.Device.Send("Ciao from ProgressTest");
}
}
}

View File

@ -12,11 +12,17 @@ namespace TelegramBaseTest.Tests.Register.Steps
{
public Data UserData { get; set; }
public async override Task Init(params object[] args)
public Step1()
{
this.Init += Step1_Init;
}
private async Task Step1_Init(object sender, InitEventArgs e)
{
this.UserData = new Data();
}
public async override Task Load(MessageResult message)
{
if (message.Handled)

View File

@ -13,16 +13,17 @@ namespace TelegramBaseTest.Tests
public SimpleForm()
{
this.DeleteSide = eSide.Both;
this.DeleteMode = eDeleteMode.OnLeavingForm;
this.DeleteSide = TelegramBotBase.Enums.eSide.Both;
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Opened += SimpleForm_Opened;
}
public override async Task Opened()
private async Task SimpleForm_Opened(object sender, EventArgs e)
{
await this.Device.Send("Hello world! (send 'back' to get back to Start)\r\nOr\r\nhi, hello, maybe, bye and ciao");
}
public override async Task Load(MessageResult message)
{
//message.MessageText will work also, cause it is a string you could manage a lot different scenerios here

View File

@ -8,8 +8,12 @@ using TelegramBotBase.Form;
namespace TelegramBaseTest.Tests
{
public class Start : FormBase
public class Start : AutoCleanForm
{
public Start()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
}
public override async Task Action(MessageResult message)
{
@ -79,6 +83,36 @@ namespace TelegramBaseTest.Tests
await this.NavigateTo(data);
break;
case "calendar":
message.Handled = true;
var calendar = new Controls.CalendarPickerForm();
await this.NavigateTo(calendar);
break;
case "month":
message.Handled = true;
var month = new Controls.MonthPickerForm();
await this.NavigateTo(month);
break;
case "treeview":
message.Handled = true;
var tree = new Controls.TreeViewForms();
await this.NavigateTo(tree);
break;
}
@ -99,6 +133,10 @@ namespace TelegramBaseTest.Tests
btn.AddButtonRow(new ButtonBase("#6 Form2 Command", new CallbackData("a", "form2").Serialize()));
btn.AddButtonRow(new ButtonBase("#7 Data Handling", new CallbackData("a", "data").Serialize()));
btn.AddButtonRow(new ButtonBase("#8 Calendar Picker", new CallbackData("a", "calendar").Serialize()));
btn.AddButtonRow(new ButtonBase("#9 Month Picker", new CallbackData("a", "month").Serialize()));
btn.AddButtonRow(new ButtonBase("#10 TreeView", new CallbackData("a", "treeview").Serialize()));
await this.Device.Send("Choose your test:", btn);
}

View File

@ -15,13 +15,18 @@ namespace TelegramBaseTest.Tests
String LastMessage { get; set; }
public TestForm()
{
this.Opened += TestForm_Opened;
this.Closed += TestForm_Closed;
}
public override async Task Opened()
private async Task TestForm_Opened(object sender, EventArgs e)
{
await this.Device.Send("Welcome to Form 1");
}
public override async Task Closed()
private async Task TestForm_Closed(object sender, EventArgs e)
{
await this.Device.Send("Ciao from Form 1");
}

View File

@ -16,17 +16,25 @@ namespace TelegramBaseTest.Tests
{
public override async Task Opened()
public TestForm2()
{
this.Opened += TestForm2_Opened;
this.Closed += TestForm2_Closed;
}
private async Task TestForm2_Opened(object sender, EventArgs e)
{
await this.Device.Send("Welcome to Form 2");
}
public override async Task Closed()
private async Task TestForm2_Closed(object sender, EventArgs e)
{
await this.Device.Send("Ciao from Form 2");
}
public override async Task Action(MessageResult message)
{
var call = message.GetData<CallbackData>();

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Base
{
public static class Async
{
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs : EventArgs;
public static IEnumerable<AsyncEventHandler<TEventArgs>> GetHandlers<TEventArgs>(
this AsyncEventHandler<TEventArgs> handler)
where TEventArgs : EventArgs
=> handler.GetInvocationList().Cast<AsyncEventHandler<TEventArgs>>();
public static Task InvokeAllAsync<TEventArgs>(this AsyncEventHandler<TEventArgs> handler, object sender, TEventArgs e)
where TEventArgs : EventArgs
=> Task.WhenAll(
handler.GetHandlers()
.Select(handleAsync => handleAsync(sender, e)));
}
}

View File

@ -13,9 +13,26 @@ namespace TelegramBotBase.Base
{
public Sessions.DeviceSession Device { get; set; }
public virtual async Task Render()
public int ID { get; set; }
/// <summary>
/// Defines if the control should be rendered and invoked with actions
/// </summary>
public bool Enabled { get; set; } = true;
public virtual async Task Action(MessageResult result)
{
}
public virtual async Task Render(MessageResult result)
{
}
public virtual async Task Cleanup()

View File

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Sessions;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Form
{
@ -13,12 +15,11 @@ namespace TelegramBotBase.Form
/// </summary>
public class FormBase : IDisposable
{
public DeviceSession Device { get; set; }
public MessageClient Client { get; set; }
public bool CustomEventManagement { get; set; } = false;
/// <summary>
/// has this formular already been disposed ?
/// </summary>
@ -26,34 +27,119 @@ namespace TelegramBotBase.Form
public List<ControlBase> Controls { get; set; }
private EventHandlerList Events = new EventHandlerList();
private static object __evInit = new object();
private static object __evOpened = new object();
private static object __evClosed = new object();
public FormBase()
{
this.Controls = new List<Base.ControlBase>();
}
public FormBase(MessageClient Client): this()
public FormBase(MessageClient Client) : this()
{
this.Client = Client;
}
/// <summary>
/// Will get called at the initialization (once per context)
/// </summary>
public virtual async Task Init(params object[] args)
public async Task OnInit(InitEventArgs e)
{
if (this.Events[__evInit] == null)
return;
var handler = this.Events[__evInit].GetInvocationList().Cast<AsyncEventHandler<InitEventArgs>>();
foreach(var h in handler)
{
await Async.InvokeAllAsync<InitEventArgs>(h, this, e);
}
}
///// <summary>
///// Will get called at the initialization (once per context)
///// </summary>
public event AsyncEventHandler<InitEventArgs> Init
{
add
{
this.Events.AddHandler(__evInit, value);
}
remove
{
this.Events.RemoveHandler(__evInit, value);
}
}
public async Task OnOpened(EventArgs e)
{
if (this.Events[__evOpened] == null)
return;
var handler = this.Events[__evOpened].GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
foreach (var h in handler)
{
await Async.InvokeAllAsync<EventArgs>(h, this, e);
}
}
/// <summary>
/// Gets invoked if gets navigated to this form
/// </summary>
/// <returns></returns>
public virtual async Task Opened()
public event AsyncEventHandler<EventArgs> Opened
{
add
{
this.Events.AddHandler(__evOpened, value);
}
remove
{
this.Events.RemoveHandler(__evOpened, value);
}
}
public virtual async Task Closed()
public async Task OnClosed(EventArgs e)
{
if (this.Events[__evClosed] == null)
return;
var handler = this.Events[__evClosed].GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
foreach (var h in handler)
{
await Async.InvokeAllAsync<EventArgs>(h, this, e);
}
}
/// <summary>
/// Form has been closed (left)
/// </summary>
/// <returns></returns>
public event AsyncEventHandler<EventArgs> Closed
{
add
{
this.Events.AddHandler(__evClosed, value);
}
remove
{
this.Events.RemoveHandler(__evClosed, value);
}
}
/// <summary>
/// Pre to form close, cleanup all controls
/// </summary>
/// <returns></returns>
public async Task CloseControls()
{
foreach (var b in this.Controls)
{
@ -76,6 +162,22 @@ namespace TelegramBotBase.Form
}
/// <summary>
/// Gets invoked if the user clicked a button.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task ActionControls(MessageResult message)
{
foreach (var b in this.Controls)
{
if (!b.Enabled)
continue;
await b.Action(message);
}
}
/// <summary>
/// Gets invoked if the user has clicked a button.
/// </summary>
@ -96,6 +198,22 @@ namespace TelegramBotBase.Form
}
/// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task RenderControls(MessageResult message)
{
foreach (var b in this.Controls)
{
if (!b.Enabled)
continue;
await b.Render(message);
}
}
/// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary>
@ -106,6 +224,8 @@ namespace TelegramBotBase.Form
}
/// <summary>
/// Navigates to a new form
/// </summary>
@ -125,11 +245,20 @@ namespace TelegramBotBase.Form
newForm.Client = this.Client;
newForm.Device = ds;
await newForm.Init(args);
await newForm.OnInit(new InitEventArgs(args));
await this.Closed();
await this.CloseControls();
await newForm.Opened();
await this.OnClosed(new EventArgs());
await newForm.OnOpened(new EventArgs());
}
public void AddControl(ControlBase control)
{
control.ID = this.Controls.Count + 1;
control.Device = this.Device;
this.Controls.Add(control);
}
/// <summary>

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Base
{
public class InitEventArgs : EventArgs
{
public object[] Args { get; set; }
public InitEventArgs(params object[] args)
{
this.Args = args;
}
}
}

View File

@ -258,16 +258,12 @@ namespace TelegramBotBase
do
{
i++;
//Reset navigation
ds.FormSwitched = false;
activeForm = ds.ActiveForm;
//If the form manages the events by itselfs, skip here
if (activeForm.CustomEventManagement)
return;
//Pre Loading Event
await activeForm.PreLoad(e);
@ -337,10 +333,6 @@ namespace TelegramBotBase
activeForm = ds.ActiveForm;
//If the form manages the events by itselfs, skip here
if (activeForm.CustomEventManagement)
return;
//Pre Loading Event
await activeForm.PreLoad(e);
@ -350,6 +342,10 @@ namespace TelegramBotBase
//Action Event
if (!ds.FormSwitched)
{
//Send Action event to controls
await activeForm.ActionControls(e);
//Send Action event to form itself
await activeForm.Action(e);
if (!e.Handled)
@ -374,7 +370,11 @@ namespace TelegramBotBase
//Render Event
if (!ds.FormSwitched)
{
await activeForm.RenderControls(e);
await activeForm.Render(e);
}
} while (ds.FormSwitched && i < NavigationMaximum);

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Enums
{
public enum eDeleteMode
{
/// <summary>
/// Don't delete any message.
/// </summary>
None = 0,
/// <summary>
/// Delete messages on every callback/action.
/// </summary>
OnEveryCall = 1,
/// <summary>
/// Delete on leaving this form.
/// </summary>
OnLeavingForm = 2
}
public enum eSide
{
/// <summary>
/// Delete only messages from this bot.
/// </summary>
BotOnly = 0,
/// <summary>
/// Delete only user messages.
/// </summary>
UserOnly = 1,
/// <summary>
/// Delete all messages in this context.
/// </summary>
Both = 2
}
public enum eMonthPickerMode
{
/// <summary>
/// Shows the calendar with day picker mode
/// </summary>
day = 0,
/// <summary>
/// Shows the calendar with month overview
/// </summary>
month = 1,
/// <summary>
/// Shows the calendar with year overview
/// </summary>
year = 2
}
}

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
namespace TelegramBotBase.Form
{
@ -19,37 +20,7 @@ namespace TelegramBotBase.Form
public eSide DeleteSide { get; set; }
public enum eDeleteMode
{
/// <summary>
/// Don't delete any message.
/// </summary>
None = 0,
/// <summary>
/// Delete messages on every callback/action.
/// </summary>
OnEveryCall = 1,
/// <summary>
/// Delete on leaving this form.
/// </summary>
OnLeavingForm = 2
}
public enum eSide
{
/// <summary>
/// Delete only messages from this bot.
/// </summary>
BotOnly = 0,
/// <summary>
/// Delete only user messages.
/// </summary>
UserOnly = 1,
/// <summary>
/// Delete all messages in this context.
/// </summary>
Both = 2
}
public AutoCleanForm()
{
@ -57,9 +28,14 @@ namespace TelegramBotBase.Form
this.DeleteMode = eDeleteMode.OnEveryCall;
this.DeleteSide = eSide.BotOnly;
this.Init += AutoCleanForm_Init;
this.Closed += AutoCleanForm_Closed;
}
public override async Task Init(params object[] args)
private async Task AutoCleanForm_Init(object sender, InitEventArgs e)
{
if (this.Device == null)
return;
@ -127,7 +103,7 @@ namespace TelegramBotBase.Form
this.OldMessages.RemoveAt(this.OldMessages.Count - 1);
}
public async override Task Closed()
private async Task AutoCleanForm_Closed(object sender, EventArgs e)
{
if (this.DeleteMode != eDeleteMode.OnLeavingForm)
return;
@ -135,7 +111,6 @@ namespace TelegramBotBase.Form
await MessageCleanup();
}
/// <summary>
/// Cleans up all remembered messages.
/// </summary>

View File

@ -77,6 +77,9 @@ namespace TelegramBotBase.Form
public static implicit operator InlineKeyboardMarkup(ButtonForm form)
{
if (form == null)
return null;
InlineKeyboardMarkup ikm = new InlineKeyboardMarkup(form.ToArray());
return ikm;

View File

@ -67,9 +67,9 @@ namespace TelegramBotBase
DeviceSession ds = new Sessions.DeviceSession(deviceId, start);
start.Device = ds;
await start.Init();
await start.OnInit(new InitEventArgs());
await start.Opened();
await start.OnOpened(new EventArgs());
this[deviceId] = ds;
return ds;

View File

@ -127,11 +127,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = null;
if (buttons != null)
{
markup = buttons;
}
InlineKeyboardMarkup markup = buttons;
try
{
@ -160,11 +156,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = null;
if (buttons != null)
{
markup = buttons;
}
InlineKeyboardMarkup markup = buttons;
try
{
@ -194,11 +186,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = null;
if (buttons != null)
{
markup = buttons;
}
InlineKeyboardMarkup markup = buttons;
Message m = null;
@ -253,6 +241,39 @@ namespace TelegramBotBase.Sessions
return m;
}
/// <summary>
/// Sends a simple text message
/// </summary>
/// <param name="text"></param>
/// <param name="markup"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> Send(String text, ReplyKeyboardMarkup markup, int replyTo = 0, bool disableNotification = false)
{
if (this.ActiveForm == null)
return null;
Message m = null;
try
{
m = await (this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, text, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
OnMessageSent(new MessageSentEventArgs(m));
}
catch (ApiRequestException ex)
{
return null;
}
catch
{
return null;
}
return m;
}
/// <summary>
/// Sends an image
/// </summary>
@ -266,11 +287,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = null;
if (buttons != null)
{
markup = buttons;
}
InlineKeyboardMarkup markup = buttons;
Message m = null;