using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using TelegramBotBase.Args; using TelegramBotBase.Base; using TelegramBotBase.Sessions; using static TelegramBotBase.Base.Async; namespace TelegramBotBase.Form { /// /// Base class for forms /// public class FormBase : IDisposable { public DeviceSession Device { get; set; } public MessageClient Client { get; set; } /// /// has this formular already been disposed ? /// public bool IsDisposed { get; set; } = false; public List Controls { get; set; } public 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(); } public FormBase(MessageClient Client) : this() { this.Client = Client; } public async Task OnInit(InitEventArgs e) { if (this.Events[__evInit] == null) return; var handler = this.Events[__evInit].GetInvocationList().Cast>(); foreach (var h in handler) { await Async.InvokeAllAsync(h, this, e); } } ///// ///// Will get called at the initialization (once per context) ///// public event AsyncEventHandler 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>(); foreach (var h in handler) { await Async.InvokeAllAsync(h, this, e); } } /// /// Gets invoked if gets navigated to this form /// /// public event AsyncEventHandler Opened { add { this.Events.AddHandler(__evOpened, value); } remove { this.Events.RemoveHandler(__evOpened, value); } } public async Task OnClosed(EventArgs e) { if (this.Events[__evClosed] == null) return; var handler = this.Events[__evClosed].GetInvocationList().Cast>(); foreach (var h in handler) { await Async.InvokeAllAsync(h, this, e); } } /// /// Form has been closed (left) /// /// public event AsyncEventHandler Closed { add { this.Events.AddHandler(__evClosed, value); } remove { this.Events.RemoveHandler(__evClosed, value); } } /// /// Pre to form close, cleanup all controls /// /// public async Task CloseControls() { foreach (var b in this.Controls) { b.Cleanup().Wait(); } } public virtual async Task PreLoad(MessageResult message) { } /// /// Gets invoked if a message was sent or an action triggered /// /// /// public async Task LoadControls(MessageResult message) { //Looking for the control by id, if not listened, raise event for all if (message.RawData?.StartsWith("#c") ?? false) { var c = this.Controls.FirstOrDefault(a => a.ControlID == message.RawData.Split('_')[0]); if (c != null) { await c.Load(message); return; } } foreach (var b in this.Controls) { if (!b.Enabled) continue; await b.Load(message); } } /// /// Gets invoked if the form gets loaded and on every message belongs to this context /// /// /// public virtual async Task Load(MessageResult message) { } /// /// Gets invoked if the user clicked a button. /// /// /// public async Task ActionControls(MessageResult message) { //Looking for the control by id, if not listened, raise event for all if (message.RawData.StartsWith("#c")) { var c = this.Controls.FirstOrDefault(a => a.ControlID == message.RawData.Split('_')[0]); if (c != null) { await c.Action(message, message.RawData.Split('_')[1]); return; } } foreach (var b in this.Controls) { if (!b.Enabled) continue; await b.Action(message); if (message.Handled) return; } } /// /// Gets invoked if the user has clicked a button. /// /// /// public virtual async Task Action(MessageResult message) { } /// /// Gets invoked if the user has sent some media (Photo, Audio, Video, Contact, Location, Document) /// /// /// public virtual async Task SentData(DataResult message) { } /// /// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc... /// /// /// public async Task RenderControls(MessageResult message) { foreach (var b in this.Controls) { if (!b.Enabled) continue; await b.Render(message); } } /// /// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc... /// /// /// public virtual async Task Render(MessageResult message) { } /// /// Navigates to a new form /// /// /// public async Task NavigateTo(FormBase newForm, params object[] args) { DeviceSession ds = this.Device; if (ds == null) return; ds.FormSwitched = true; ds.PreviousForm = ds.ActiveForm; ds.ActiveForm = newForm; newForm.Client = this.Client; newForm.Device = ds; this.CloseControls().Wait(); await this.OnClosed(new EventArgs()); await newForm.OnInit(new InitEventArgs(args)); await newForm.OnOpened(new EventArgs()); } /// /// Opens this form modal, but don't closes the original ones /// /// /// public async Task OpenModal(ModalDialog newForm, params object[] args) { DeviceSession ds = this.Device; if (ds == null) return; var parentForm = this; ds.FormSwitched = true; ds.PreviousForm = ds.ActiveForm; ds.ActiveForm = newForm; newForm.Client = parentForm.Client; newForm.Device = ds; newForm.Closed += async (s, en) => { await CloseModal(newForm, parentForm); }; await newForm.OnInit(new InitEventArgs(args)); await newForm.OnOpened(new EventArgs()); } public async Task CloseModal(ModalDialog modalForm, FormBase oldForm) { DeviceSession ds = this.Device; if (ds == null) return; if (modalForm == null) throw new Exception("No modal form"); ds.FormSwitched = true; ds.PreviousForm = ds.ActiveForm; ds.ActiveForm = oldForm; } /// /// Adds a control to the formular and sets its ID and Device. /// /// public void AddControl(ControlBase control) { control.ID = this.Controls.Count + 1; control.Device = this.Device; this.Controls.Add(control); } /// /// Removes control from the formular and runs a cleanup on it. /// /// public void RemoveControl(ControlBase control) { if (!this.Controls.Contains(control)) return; control.Cleanup().Wait(); this.Controls.Remove(control); } /// /// Cleanup /// public void Dispose() { this.Client = null; this.Device = null; this.IsDisposed = true; } } }