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