PrayerTracker/src/PrayerTracker.UI/ViewModels.fs

852 lines
27 KiB
Forth
Raw Normal View History

2019-02-18 01:25:07 +00:00
namespace PrayerTracker.ViewModels
open Microsoft.AspNetCore.Html
open Microsoft.Extensions.Localization
open PrayerTracker
open PrayerTracker.Entities
/// Helper module to return localized reference lists
module ReferenceList =
2022-07-13 02:43:01 +00:00
/// A localized list of the AsOfDateDisplay DU cases
let asOfDateList (s : IStringLocalizer) =
[ NoDisplay.code, s["Do not display the “as of” date"]
ShortDate.code, s["Display a short “as of” date"]
LongDate.code, s["Display a full “as of” date"]
]
/// A list of e-mail type options
let emailTypeList def (s : IStringLocalizer) =
// Localize the default type
let defaultType =
s[match def with HtmlFormat -> "HTML Format" | PlainTextFormat -> "Plain-Text Format"].Value
seq {
"", LocalizedString ("", $"""{s["Group Default"].Value} ({defaultType})""")
HtmlFormat.code, s["HTML Format"]
PlainTextFormat.code, s["Plain-Text Format"]
}
/// A list of expiration options
let expirationList (s : IStringLocalizer) includeExpireNow =
[ Automatic.code, s["Expire Normally"]
Manual.code, s["Request Never Expires"]
if includeExpireNow then Forced.code, s["Expire Immediately"]
]
/// A list of request types
let requestTypeList (s : IStringLocalizer) =
[ CurrentRequest, s["Current Requests"]
LongTermRequest, s["Long-Term Requests"]
PraiseReport, s["Praise Reports"]
Expecting, s["Expecting"]
Announcement, s["Announcements"]
]
/// A user message level
type MessageLevel =
/// An informational message to the user
| Info
/// A message with information the user should consider
| Warning
/// A message indicating that something went wrong
| Error
/// Support for the MessageLevel type
module MessageLevel =
/// Convert a message level to its string representation
let toString =
function
| Info -> "Info"
| Warning -> "WARNING"
| Error -> "ERROR"
let toCssClass level = (toString level).ToLowerInvariant ()
2019-02-18 01:25:07 +00:00
/// This is used to create a message that is displayed to the user
[<NoComparison; NoEquality>]
type UserMessage =
2022-07-13 02:43:01 +00:00
{ /// The type
Level : MessageLevel
2022-07-13 02:43:01 +00:00
/// The actual message
Text : HtmlString
2022-07-13 02:43:01 +00:00
/// The description (further information)
Description : HtmlString option
2019-02-18 01:25:07 +00:00
}
2022-07-13 02:43:01 +00:00
/// Support for the UserMessage type
module UserMessage =
2022-07-13 02:43:01 +00:00
/// Error message template
let error =
{ Level = Error
Text = HtmlString.Empty
Description = None
2022-07-13 02:43:01 +00:00
}
/// Warning message template
let warning =
{ Level = Warning
Text = HtmlString.Empty
Description = None
2022-07-13 02:43:01 +00:00
}
/// Info message template
let info =
{ Level = Info
Text = HtmlString.Empty
Description = None
2022-07-13 02:43:01 +00:00
}
/// The template with which the content will be rendered
type LayoutType =
/// A full page load
| FullPage
/// A response that will provide a new body tag
| PartialPage
/// A response that will replace the page content
| ContentOnly
2019-02-18 01:25:07 +00:00
2022-07-13 02:43:01 +00:00
open System
2019-02-18 01:25:07 +00:00
/// View model required by the layout template, given as first parameter for all pages in PrayerTracker
[<NoComparison; NoEquality>]
type AppViewInfo =
2022-07-13 02:43:01 +00:00
{ /// CSS files for the page
Style : string list
2022-07-13 02:43:01 +00:00
/// JavaScript files for the page
Script : string list
2022-07-13 02:43:01 +00:00
/// The link for help on this page
HelpLink : string option
2022-07-13 02:43:01 +00:00
/// Messages to be displayed to the user
Messages : UserMessage list
2022-07-13 02:43:01 +00:00
/// The current version of PrayerTracker
Version : string
2022-07-13 02:43:01 +00:00
/// The ticks when the request started
RequestStart : int64
2022-07-13 02:43:01 +00:00
/// The currently logged on user, if there is one
User : User option
2022-07-13 02:43:01 +00:00
/// The currently logged on small group, if there is one
Group : SmallGroup option
/// The layout with which the content will be rendered
Layout : LayoutType
2019-02-18 01:25:07 +00:00
}
2022-07-13 02:43:01 +00:00
/// Support for the AppViewInfo type
module AppViewInfo =
2022-07-13 02:43:01 +00:00
/// A fresh version that can be populated to process the current request
let fresh =
{ Style = []
Script = []
HelpLink = None
Messages = []
Version = ""
RequestStart = DateTime.Now.Ticks
User = None
Group = None
Layout = FullPage
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Form for sending a small group or system-wide announcement
[<CLIMutable; NoComparison; NoEquality>]
type Announcement =
2022-07-13 02:43:01 +00:00
{ /// Whether the announcement should be sent to the class or to PrayerTracker users
SendToClass : string
2022-07-13 02:43:01 +00:00
/// The text of the announcement
Text : string
2022-07-13 02:43:01 +00:00
/// Whether this announcement should be added to the "Announcements" of the prayer list
AddToRequestList : bool option
2022-07-13 02:43:01 +00:00
/// The ID of the request type to which this announcement should be added
RequestType : string option
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// The text of the announcement, in plain text
member this.PlainText
with get () = (htmlToPlainText >> wordWrap 74) this.Text
2019-02-18 01:25:07 +00:00
/// Form for assigning small groups to a user
[<CLIMutable; NoComparison; NoEquality>]
type AssignGroups =
2022-07-13 02:43:01 +00:00
{ /// The Id of the user being assigned
UserId : UserId
2022-07-13 02:43:01 +00:00
/// The full name of the user being assigned
UserName : string
2022-07-13 02:43:01 +00:00
/// The Ids of the small groups to which the user is authorized
SmallGroups : string
2019-02-18 01:25:07 +00:00
}
2022-07-13 02:43:01 +00:00
/// Support for the AssignGroups type
module AssignGroups =
2022-07-13 02:43:01 +00:00
/// Create an instance of this form from an existing user
let fromUser (u : User) =
{ UserId = u.userId
UserName = u.fullName
SmallGroups = ""
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Form to allow users to change their password
[<CLIMutable; NoComparison; NoEquality>]
type ChangePassword =
2022-07-13 02:43:01 +00:00
{ /// The user's current password
OldPassword : string
2022-07-13 02:43:01 +00:00
/// The user's new password
NewPassword : string
2022-07-13 02:43:01 +00:00
/// The user's new password, confirmed
NewPasswordConfirm : string
2019-02-18 01:25:07 +00:00
}
/// Form for adding or editing a church
[<CLIMutable; NoComparison; NoEquality>]
type EditChurch =
2022-07-13 02:43:01 +00:00
{ /// The Id of the church
ChurchId : ChurchId
2022-07-13 02:43:01 +00:00
/// The name of the church
Name : string
2022-07-13 02:43:01 +00:00
/// The city for the church
City : string
2022-07-13 02:43:01 +00:00
/// The state or province for the church
State : string
2022-07-13 02:43:01 +00:00
/// Whether the church has an active Virtual Prayer Room interface
HasInterface : bool option
2022-07-13 02:43:01 +00:00
/// The address for the interface
InterfaceAddress : string option
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// Is this a new church?
member this.IsNew
with get () = Guid.Empty = this.ChurchId
2022-07-13 02:43:01 +00:00
/// Populate a church from this form
member this.PopulateChurch (church : Church) =
2022-07-13 02:43:01 +00:00
{ church with
name = this.Name
city = this.City
st = this.State
hasInterface = match this.HasInterface with Some x -> x | None -> false
interfaceAddress = match this.HasInterface with Some x when x -> this.InterfaceAddress | _ -> None
2022-07-13 02:43:01 +00:00
}
/// Support for the EditChurch type
module EditChurch =
2022-07-13 02:43:01 +00:00
/// Create an instance from an existing church
let fromChurch (ch : Church) =
{ ChurchId = ch.churchId
Name = ch.name
City = ch.city
State = ch.st
HasInterface = match ch.hasInterface with true -> Some true | false -> None
InterfaceAddress = ch.interfaceAddress
2022-07-13 02:43:01 +00:00
}
/// An instance to use for adding churches
let empty =
{ ChurchId = Guid.Empty
Name = ""
City = ""
State = ""
HasInterface = None
InterfaceAddress = None
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Form for adding/editing small group members
[<CLIMutable; NoComparison; NoEquality>]
type EditMember =
2022-07-13 02:43:01 +00:00
{ /// The Id for this small group member (not user-entered)
MemberId : MemberId
2022-07-13 02:43:01 +00:00
/// The name of the member
Name : string
2022-07-13 02:43:01 +00:00
/// The e-mail address
Email : string
2022-07-13 02:43:01 +00:00
/// The e-mail format
Format : string
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// Is this a new member?
member this.IsNew
with get () = Guid.Empty = this.MemberId
2022-07-13 02:43:01 +00:00
/// Support for the EditMember type
module EditMember =
2022-07-13 02:43:01 +00:00
/// Create an instance from an existing member
let fromMember (m : Member) =
{ MemberId = m.memberId
Name = m.memberName
Email = m.email
Format = match m.format with Some f -> f | None -> ""
2022-07-13 02:43:01 +00:00
}
/// An empty instance
let empty =
{ MemberId = Guid.Empty
Name = ""
Email = ""
Format = ""
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// This form allows the user to set class preferences
[<CLIMutable; NoComparison; NoEquality>]
type EditPreferences =
2022-07-13 02:43:01 +00:00
{ /// The number of days after which requests are automatically expired
ExpireDays : int
2022-07-13 02:43:01 +00:00
/// The number of days requests are considered "new"
DaysToKeepNew : int
2022-07-13 02:43:01 +00:00
/// The number of weeks after which a long-term requests is flagged as requiring an update
LongTermUpdateWeeks : int
2022-07-13 02:43:01 +00:00
/// Whether to sort by updated date or requestor/subject
RequestSort : string
2022-07-13 02:43:01 +00:00
/// The name from which e-mail will be sent
EmailFromName : string
2022-07-13 02:43:01 +00:00
/// The e-mail address from which e-mail will be sent
EmailFromAddress : string
2022-07-13 02:43:01 +00:00
/// The default e-mail type for this group
DefaultEmailType : string
2022-07-13 02:43:01 +00:00
/// Whether the heading line color uses named colors or R/G/B
LineColorType : string
2022-07-13 02:43:01 +00:00
/// The named color for the heading lines
LineColor : string
2022-07-13 02:43:01 +00:00
/// Whether the heading text color uses named colors or R/G/B
HeadingColorType : string
2022-07-13 02:43:01 +00:00
/// The named color for the heading text
HeadingColor : string
2022-07-13 02:43:01 +00:00
/// The fonts to use for the list
Fonts : string
2022-07-13 02:43:01 +00:00
/// The font size for the heading text
HeadingFontSize : int
2022-07-13 02:43:01 +00:00
/// The font size for the list text
ListFontSize : int
2022-07-13 02:43:01 +00:00
/// The time zone for the class
TimeZone : string
2022-07-13 02:43:01 +00:00
/// The list visibility
Visibility : int
2022-07-13 02:43:01 +00:00
/// The small group password
GroupPassword : string option
2022-07-13 02:43:01 +00:00
/// The page size for search / inactive requests
PageSize : int
2022-07-13 02:43:01 +00:00
/// How the as-of date should be displayed
AsOfDate : string
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// Set the properties of a small group based on the form's properties
member this.PopulatePreferences (prefs : ListPreferences) =
2022-07-13 02:43:01 +00:00
let isPublic, grpPw =
match this.Visibility with
2022-07-13 02:43:01 +00:00
| RequestVisibility.``public`` -> true, ""
| RequestVisibility.passwordProtected -> false, (defaultArg this.GroupPassword "")
2022-07-13 02:43:01 +00:00
| RequestVisibility.``private``
| _ -> false, ""
{ prefs with
daysToExpire = this.ExpireDays
daysToKeepNew = this.DaysToKeepNew
longTermUpdateWeeks = this.LongTermUpdateWeeks
requestSort = RequestSort.fromCode this.RequestSort
emailFromName = this.EmailFromName
emailFromAddress = this.EmailFromAddress
defaultEmailType = EmailFormat.fromCode this.DefaultEmailType
lineColor = this.LineColor
headingColor = this.HeadingColor
listFonts = this.Fonts
headingFontSize = this.HeadingFontSize
textFontSize = this.ListFontSize
timeZoneId = this.TimeZone
2022-07-13 02:43:01 +00:00
isPublic = isPublic
groupPassword = grpPw
pageSize = this.PageSize
asOfDateDisplay = AsOfDateDisplay.fromCode this.AsOfDate
2022-07-13 02:43:01 +00:00
}
/// Support for the EditPreferences type
module EditPreferences =
2022-07-13 02:43:01 +00:00
/// Populate an edit form from existing preferences
let fromPreferences (prefs : ListPreferences) =
let setType (x : string) = match x.StartsWith "#" with true -> "RGB" | false -> "Name"
{ ExpireDays = prefs.daysToExpire
DaysToKeepNew = prefs.daysToKeepNew
LongTermUpdateWeeks = prefs.longTermUpdateWeeks
RequestSort = prefs.requestSort.code
EmailFromName = prefs.emailFromName
EmailFromAddress = prefs.emailFromAddress
DefaultEmailType = prefs.defaultEmailType.code
LineColorType = setType prefs.lineColor
LineColor = prefs.lineColor
HeadingColorType = setType prefs.headingColor
HeadingColor = prefs.headingColor
Fonts = prefs.listFonts
HeadingFontSize = prefs.headingFontSize
ListFontSize = prefs.textFontSize
TimeZone = prefs.timeZoneId
GroupPassword = Some prefs.groupPassword
PageSize = prefs.pageSize
AsOfDate = prefs.asOfDateDisplay.code
Visibility =
2022-07-13 02:43:01 +00:00
match true with
| _ when prefs.isPublic -> RequestVisibility.``public``
| _ when prefs.groupPassword = "" -> RequestVisibility.``private``
| _ -> RequestVisibility.passwordProtected
}
2019-02-18 01:25:07 +00:00
/// Form for adding or editing prayer requests
[<CLIMutable; NoComparison; NoEquality>]
type EditRequest =
2022-07-13 02:43:01 +00:00
{ /// The Id of the request
RequestId : PrayerRequestId
2022-07-13 02:43:01 +00:00
/// The type of the request
RequestType : string
2022-07-13 02:43:01 +00:00
/// The date of the request
EnteredDate : DateTime option
2022-07-13 02:43:01 +00:00
/// Whether to update the date or not
SkipDateUpdate : bool option
2022-07-13 02:43:01 +00:00
/// The requestor or subject
Requestor : string option
2022-07-13 02:43:01 +00:00
/// How this request is expired
Expiration : string
2022-07-13 02:43:01 +00:00
/// The text of the request
Text : string
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// Is this a new request?
member this.IsNew
with get () = Guid.Empty = this.RequestId
2022-07-13 02:43:01 +00:00
/// Support for the EditRequest type
module EditRequest =
2022-07-13 02:43:01 +00:00
/// An empty instance to use for new requests
let empty =
{ RequestId = Guid.Empty
RequestType = CurrentRequest.code
EnteredDate = None
SkipDateUpdate = None
Requestor = None
Expiration = Automatic.code
Text = ""
2022-07-13 02:43:01 +00:00
}
/// Create an instance from an existing request
let fromRequest req =
{ empty with
RequestId = req.prayerRequestId
RequestType = req.requestType.code
Requestor = req.requestor
Expiration = req.expiration.code
Text = req.text
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Form for the admin-level editing of small groups
[<CLIMutable; NoComparison; NoEquality>]
type EditSmallGroup =
2022-07-13 02:43:01 +00:00
{ /// The Id of the small group
SmallGroupId : SmallGroupId
2022-07-13 02:43:01 +00:00
/// The name of the small group
Name : string
2022-07-13 02:43:01 +00:00
/// The Id of the church to which this small group belongs
ChurchId : ChurchId
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// Is this a new small group?
member this.IsNew
with get () = Guid.Empty = this.SmallGroupId
2022-07-13 02:43:01 +00:00
/// Populate a small group from this form
member this.populateGroup (grp : SmallGroup) =
{ grp with
name = this.Name
churchId = this.ChurchId
}
2022-07-13 02:43:01 +00:00
/// Support for the EditSmallGroup type
module EditSmallGroup =
2022-07-13 02:43:01 +00:00
/// Create an instance from an existing small group
let fromGroup (g : SmallGroup) =
{ SmallGroupId = g.smallGroupId
Name = g.name
ChurchId = g.churchId
2022-07-13 02:43:01 +00:00
}
/// An empty instance (used when adding a new group)
let empty =
{ SmallGroupId = Guid.Empty
Name = ""
ChurchId = Guid.Empty
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Form for the user edit page
[<CLIMutable; NoComparison; NoEquality>]
type EditUser =
2022-07-13 02:43:01 +00:00
{ /// The Id of the user
UserId : UserId
2022-07-13 02:43:01 +00:00
/// The first name of the user
FirstName : string
2022-07-13 02:43:01 +00:00
/// The last name of the user
LastName : string
2022-07-13 02:43:01 +00:00
/// The e-mail address for the user
Email : string
2022-07-13 02:43:01 +00:00
/// The password for the user
Password : string
2022-07-13 02:43:01 +00:00
/// The password hash for the user a second time
PasswordConfirm : string
2022-07-13 02:43:01 +00:00
/// Is this user a PrayerTracker administrator?
IsAdmin : bool option
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// Is this a new user?
member this.IsNew
with get () = Guid.Empty = this.UserId
2022-07-13 02:43:01 +00:00
/// Populate a user from the form
member this.PopulateUser (user : User) hasher =
2022-07-13 02:43:01 +00:00
{ user with
firstName = this.FirstName
lastName = this.LastName
emailAddress = this.Email
isAdmin = defaultArg this.IsAdmin false
2022-07-13 02:43:01 +00:00
}
|> function
| u when isNull this.Password || this.Password = "" -> u
| u -> { u with passwordHash = hasher this.Password }
2022-07-13 02:43:01 +00:00
/// Support for the EditUser type
module EditUser =
2022-07-13 02:43:01 +00:00
/// An empty instance
let empty =
{ UserId = Guid.Empty
FirstName = ""
LastName = ""
Email = ""
Password = ""
PasswordConfirm = ""
IsAdmin = None
2022-07-13 02:43:01 +00:00
}
/// Create an instance from an existing user
let fromUser (user : User) =
{ empty with
UserId = user.userId
FirstName = user.firstName
LastName = user.lastName
Email = user.emailAddress
IsAdmin = if user.isAdmin then Some true else None
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Form for the small group log on page
[<CLIMutable; NoComparison; NoEquality>]
type GroupLogOn =
2022-07-13 02:43:01 +00:00
{ /// The ID of the small group to which the user is logging on
SmallGroupId : SmallGroupId
2022-07-13 02:43:01 +00:00
/// The password entered
Password : string
2022-07-13 02:43:01 +00:00
/// Whether to remember the login
RememberMe : bool option
2019-02-18 01:25:07 +00:00
}
2022-07-13 02:43:01 +00:00
/// Support for the GroupLogOn type
module GroupLogOn =
2022-07-13 02:43:01 +00:00
/// An empty instance
let empty =
{ SmallGroupId = Guid.Empty
Password = ""
RememberMe = None
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Items needed to display the request maintenance page
[<NoComparison; NoEquality>]
type MaintainRequests =
2022-07-13 02:43:01 +00:00
{ /// The requests to be displayed
Requests : PrayerRequest list
2022-07-13 02:43:01 +00:00
/// The small group to which the requests belong
SmallGroup : SmallGroup
2022-07-13 02:43:01 +00:00
/// Whether only active requests are included
OnlyActive : bool option
2022-07-13 02:43:01 +00:00
/// The search term for the requests
SearchTerm : string option
2022-07-13 02:43:01 +00:00
/// The page number of the results
PageNbr : int option
}
2022-07-13 02:43:01 +00:00
/// Support for the MaintainRequests type
module MaintainRequests =
2022-07-13 02:43:01 +00:00
/// An empty instance
let empty =
{ Requests = []
SmallGroup = SmallGroup.empty
OnlyActive = None
SearchTerm = None
PageNbr = None
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
/// Items needed to display the small group overview page
[<NoComparison; NoEquality>]
2019-02-18 01:25:07 +00:00
type Overview =
2022-07-13 02:43:01 +00:00
{ /// The total number of active requests
TotalActiveReqs : int
2022-07-13 02:43:01 +00:00
/// The numbers of active requests by request type
ActiveReqsByType : Map<PrayerRequestType, int>
2022-07-13 02:43:01 +00:00
/// A count of all requests
AllReqs : int
2022-07-13 02:43:01 +00:00
/// A count of all members
TotalMembers : int
2019-02-18 01:25:07 +00:00
}
/// Form for the user log on page
[<CLIMutable; NoComparison; NoEquality>]
type UserLogOn =
2022-07-13 02:43:01 +00:00
{ /// The e-mail address of the user
Email : string
2022-07-13 02:43:01 +00:00
/// The password entered
Password : string
2022-07-13 02:43:01 +00:00
/// The ID of the small group to which the user is logging on
SmallGroupId : SmallGroupId
2022-07-13 02:43:01 +00:00
/// Whether to remember the login
RememberMe : bool option
2022-07-13 02:43:01 +00:00
/// The URL to which the user should be redirected once login is successful
RedirectUrl : string option
2019-02-18 01:25:07 +00:00
}
2022-07-13 02:43:01 +00:00
/// Support for the UserLogOn type
module UserLogOn =
2022-07-13 02:43:01 +00:00
/// An empty instance
let empty =
{ Email = ""
Password = ""
SmallGroupId = Guid.Empty
RememberMe = None
RedirectUrl = None
2022-07-13 02:43:01 +00:00
}
2019-02-18 01:25:07 +00:00
open Giraffe.ViewEngine
2019-02-18 01:25:07 +00:00
/// This represents a list of requests
type RequestList =
2022-07-13 02:43:01 +00:00
{ /// The prayer request list
Requests : PrayerRequest list
2022-07-13 02:43:01 +00:00
/// The date for which this list is being generated
Date : DateTime
2022-07-13 02:43:01 +00:00
/// The small group to which this list belongs
SmallGroup : SmallGroup
2022-07-13 02:43:01 +00:00
/// Whether to show the class header
ShowHeader : bool
2022-07-13 02:43:01 +00:00
/// The list of recipients (populated if requests are e-mailed)
Recipients : Member list
2022-07-13 02:43:01 +00:00
/// Whether the user can e-mail this list
CanEmail : bool
2019-02-18 01:25:07 +00:00
}
with
2022-07-13 02:43:01 +00:00
/// Group requests by their type, along with the type and its localized string
member this.RequestsByType (s : IStringLocalizer) =
2022-07-13 02:43:01 +00:00
ReferenceList.requestTypeList s
|> List.map (fun (typ, name) ->
let sort =
match this.SmallGroup.preferences.requestSort with
| SortByDate -> Seq.sortByDescending (fun req -> req.updatedDate)
| SortByRequestor -> Seq.sortBy (fun req -> req.requestor)
let reqs =
this.Requests
|> Seq.ofList
|> Seq.filter (fun req -> req.requestType = typ)
|> sort
|> List.ofSeq
typ, name, reqs)
2022-07-13 02:43:01 +00:00
|> List.filter (fun (_, _, reqs) -> not (List.isEmpty reqs))
/// Is this request new?
member this.IsNew (req : PrayerRequest) =
(this.Date - req.updatedDate).Days <= this.SmallGroup.preferences.daysToKeepNew
2022-07-13 02:43:01 +00:00
/// Generate this list as HTML
member this.AsHtml (s : IStringLocalizer) =
let prefs = this.SmallGroup.preferences
2022-07-13 02:43:01 +00:00
let asOfSize = Math.Round (float prefs.textFontSize * 0.8, 2)
[ if this.ShowHeader then
2022-07-13 02:43:01 +00:00
div [ _style $"text-align:center;font-family:{prefs.listFonts}" ] [
span [ _style $"font-size:%i{prefs.headingFontSize}pt;" ] [
strong [] [ str s["Prayer Requests"].Value ]
]
br []
span [ _style $"font-size:%i{prefs.textFontSize}pt;" ] [
strong [] [ str this.SmallGroup.name ]
2022-07-13 02:43:01 +00:00
br []
str (this.Date.ToString s["MMMM d, yyyy"].Value)
2022-07-13 02:43:01 +00:00
]
2019-02-18 01:25:07 +00:00
]
2022-07-13 02:43:01 +00:00
br []
for _, name, reqs in this.RequestsByType s do
2022-07-13 02:43:01 +00:00
div [ _style "padding-left:10px;padding-bottom:.5em;" ] [
table [ _style $"font-family:{prefs.listFonts};page-break-inside:avoid;" ] [
tr [] [
td [ _style $"font-size:%i{prefs.headingFontSize}pt;color:{prefs.headingColor};padding:3px 0;border-top:solid 3px {prefs.lineColor};border-bottom:solid 3px {prefs.lineColor};font-weight:bold;" ] [
rawText "&nbsp; &nbsp; "; str name.Value; rawText "&nbsp; &nbsp; "
]
]
2019-09-24 01:40:47 +00:00
]
2022-07-13 02:43:01 +00:00
]
reqs
|> List.map (fun req ->
let bullet = if this.IsNew req then "circle" else "disc"
2022-07-13 02:43:01 +00:00
li [ _style $"list-style-type:{bullet};font-family:{prefs.listFonts};font-size:%i{prefs.textFontSize}pt;padding-bottom:.25em;" ] [
match req.requestor with
| Some r when r <> "" ->
strong [] [ str r ]
rawText " &mdash; "
| Some _ -> ()
| None -> ()
rawText req.text
match prefs.asOfDateDisplay with
| NoDisplay -> ()
| ShortDate
| LongDate ->
let dt =
match prefs.asOfDateDisplay with
| ShortDate -> req.updatedDate.ToShortDateString ()
| LongDate -> req.updatedDate.ToLongDateString ()
| _ -> ""
i [ _style $"font-size:%.2f{asOfSize}pt" ] [
rawText "&nbsp; ("; str s["as of"].Value; str " "; str dt; rawText ")"
]
])
|> ul []
br []
]
|> RenderView.AsString.htmlNodes
/// Generate this list as plain text
member this.AsText (s : IStringLocalizer) =
2022-07-13 02:43:01 +00:00
seq {
this.SmallGroup.name
2022-07-13 02:43:01 +00:00
s["Prayer Requests"].Value
this.Date.ToString s["MMMM d, yyyy"].Value
2022-07-13 02:43:01 +00:00
" "
for _, name, reqs in this.RequestsByType s do
2022-07-13 02:43:01 +00:00
let dashes = String.replicate (name.Value.Length + 4) "-"
dashes
$" {name.Value.ToUpper ()}"
dashes
for req in reqs do
let bullet = if this.IsNew req then "+" else "-"
2022-07-13 02:43:01 +00:00
let requestor = match req.requestor with Some r -> $"{r} - " | None -> ""
match this.SmallGroup.preferences.asOfDateDisplay with
2022-07-13 02:43:01 +00:00
| NoDisplay -> ""
| _ ->
let dt =
match this.SmallGroup.preferences.asOfDateDisplay with
2022-07-13 02:43:01 +00:00
| ShortDate -> req.updatedDate.ToShortDateString ()
| LongDate -> req.updatedDate.ToLongDateString ()
| _ -> ""
$""" ({s["as of"].Value} {dt})"""
|> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.text)
" "
}
|> String.concat "\n"
|> wordWrap 74