Update to F# 5 (#27)

This commit is contained in:
Daniel J. Summers 2020-11-15 21:57:09 -05:00
parent e3583f9152
commit b0d3bd4e35
24 changed files with 103 additions and 118 deletions

View File

@ -1,9 +1,9 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<AssemblyVersion>7.4.2.0</AssemblyVersion> <AssemblyVersion>7.5.0.0</AssemblyVersion>
<FileVersion>7.4.2.0</FileVersion> <FileVersion>7.5.0.0</FileVersion>
<Authors>danieljsummers</Authors> <Authors>danieljsummers</Authors>
<Company>Bit Badger Solutions</Company> <Company>Bit Badger Solutions</Company>
<Version>7.4.2</Version> <Version>7.5.0</Version>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -239,7 +239,7 @@ type AppDbContext with
} }
let! grps = q.ToListAsync () let! grps = q.ToListAsync ()
return grps return grps
|> Seq.map (fun grp -> grp.smallGroupId.ToString "N", sprintf "%s | %s" grp.church.name grp.name) |> Seq.map (fun grp -> grp.smallGroupId.ToString "N", $"{grp.church.name} | {grp.name}")
|> List.ofSeq |> List.ofSeq
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -20,8 +20,4 @@
<PackageReference Include="TaskBuilder.fs" Version="2.1.0" /> <PackageReference Include="TaskBuilder.fs" Version="2.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.0" />
</ItemGroup>
</Project> </Project>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -26,8 +26,4 @@
<ProjectReference Include="..\PrayerTracker\PrayerTracker.fsproj" /> <ProjectReference Include="..\PrayerTracker\PrayerTracker.fsproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.0" />
</ItemGroup>
</Project> </Project>

View File

@ -74,15 +74,15 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi
churches churches
|> List.map (fun ch -> |> List.map (fun ch ->
let chId = flatGuid ch.churchId let chId = flatGuid ch.churchId
let delAction = sprintf "/web/church/%s/delete" chId let delAction = $"/web/church/{chId}/delete"
let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.", let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.",
sprintf "%s (%s)" (s.["Church"].Value.ToLower ()) ch.name] $"""{s.["Church"].Value.ToLower ()} ({ch.name})"""]
tr [] [ tr [] [
td [] [ td [] [
a [ _href (sprintf "/web/church/%s/edit" chId); _title s.["Edit This Church"].Value ] [ icon "edit" ] a [ _href $"/web/church/{chId}/edit"; _title s.["Edit This Church"].Value ] [ icon "edit" ]
a [ _href delAction a [ _href delAction
_title s.["Delete This Church"].Value _title s.["Delete This Church"].Value
_onclick (sprintf "return PT.confirmDelete('%s','%A')" delAction delPrompt) ] _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
[ icon "delete_forever" ] [ icon "delete_forever" ]
] ]
td [] [ str ch.name ] td [] [ str ch.name ]
@ -96,7 +96,7 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi
] ]
[ div [ _class "pt-center-text" ] [ [ div [ _class "pt-center-text" ] [
br [] br []
a [ _href (sprintf "/web/church/%s/edit" emptyGuid); _title s.["Add a New Church"].Value ] a [ _href $"/web/church/{emptyGuid}/edit"; _title s.["Add a New Church"].Value ]
[ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Church"] ] [ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Church"] ]
br [] br []
br [] br []

View File

@ -28,7 +28,7 @@ let space = rawText " "
let icon name = i [ _class "material-icons" ] [ rawText name ] let icon name = i [ _class "material-icons" ] [ rawText name ]
/// Generate a Material Design icon, specifying the point size (must be defined in CSS) /// Generate a Material Design icon, specifying the point size (must be defined in CSS)
let iconSized size name = i [ _class (sprintf "material-icons md-%i" size) ] [ rawText name ] let iconSized size name = i [ _class $"material-icons md-{size}" ] [ rawText name ]
/// Generate a CSRF prevention token /// Generate a CSRF prevention token
let csrfToken (ctx : HttpContext) = let csrfToken (ctx : HttpContext) =
@ -72,7 +72,7 @@ let namedColorList name selected attrs (s : IStringLocalizer) =
|> Seq.map (fun color -> |> Seq.map (fun color ->
let (colorName, dispText, txtColor) = color let (colorName, dispText, txtColor) = color
option [ yield _value colorName option [ yield _value colorName
yield _style (sprintf "background-color:%s;color:%s;" colorName txtColor) yield _style $"background-color:{colorName};color:{txtColor};"
match colorName = selected with true -> yield _selected | false -> () ] [ match colorName = selected with true -> yield _selected | false -> () ] [
encodedText (dispText.Value.ToLower ()) encodedText (dispText.Value.ToLower ())
]) ])
@ -97,7 +97,7 @@ let selectList name selected attrs items =
|> select (List.concat [ [ _name name; _id name ]; attrs ]) |> select (List.concat [ [ _name name; _id name ]; attrs ])
/// Generate the text for a default entry at the top of a select list /// Generate the text for a default entry at the top of a select list
let selectDefault text = sprintf "— %s —" text let selectDefault text = $"— {text} "
/// Generate a standard submit button with icon and text /// Generate a standard submit button with icon and text
let submit attrs ico text = button (_type "submit" :: attrs) [ icon ico; rawText " &nbsp;"; locStr text ] let submit attrs ico text = button (_type "submit" :: attrs) [ icon ico; rawText " &nbsp;"; locStr text ]
@ -115,7 +115,7 @@ let blockquote = tag "blockquote"
/// role attribute /// role attribute
let _role = attr "role" let _role = attr "role"
/// aria-* attribute /// aria-* attribute
let _aria typ = attr (sprintf "aria-%s" typ) let _aria typ = attr $"aria-{typ}"
/// onclick attribute /// onclick attribute
let _onclick = attr "onclick" let _onclick = attr "onclick"
/// onsubmit attribute /// onsubmit attribute

View File

@ -35,9 +35,9 @@ let error code vi =
br [] br []
hr [] hr []
div [ _style "font-size:70%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif" ] [ div [ _style "font-size:70%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif" ] [
img [ _src (sprintf "/img/%A.png" s.["footer_en"]) img [ _src $"""/img/%A{s.["footer_en"]}.png"""
_alt (sprintf "%A %A" s.["PrayerTracker"] s.["from Bit Badger Solutions"]) _alt $"""%A{s.["PrayerTracker"]} %A{s.["from Bit Badger Solutions"]}"""
_title (sprintf "%A %A" s.["PrayerTracker"] s.["from Bit Badger Solutions"]) _title $"""%A{s.["PrayerTracker"]} %A{s.["from Bit Badger Solutions"]}"""
_style "vertical-align:text-bottom;" ] _style "vertical-align:text-bottom;" ]
str vi.version str vi.version
] ]

View File

@ -19,4 +19,4 @@ let localizer = lazy (stringLocFactory.Create ("Common", resAsmName))
/// Get a view localizer /// Get a view localizer
let forView (view : string) = let forView (view : string) =
htmlLocFactory.Create (sprintf "Views.%s" (view.Replace ('/', '.')), resAsmName) htmlLocFactory.Create ($"""Views.{view.Replace ('/', '.')}""", resAsmName)

View File

@ -76,7 +76,7 @@ module Navigation =
[ icon "list"; space; locStr s.["View Request List"] ] [ icon "list"; space; locStr s.["View Request List"] ]
] ]
li [] [ li [] [
a [ _href (sprintf "https://docs.prayer.bitbadger.solutions/%s" <| langCode ()) a [ _href $"https://docs.prayer.bitbadger.solutions/{langCode ()}"
_aria "label" s.["Help"].Value; _aria "label" s.["Help"].Value;
_title s.["View Help"].Value _title s.["View Help"].Value
_target "_blank" _target "_blank"
@ -183,9 +183,9 @@ let private htmlHead m pageTitle =
title [] [ locStr pageTitle; titleSep; locStr s.["PrayerTracker"] ] title [] [ locStr pageTitle; titleSep; locStr s.["PrayerTracker"] ]
yield! commonHead yield! commonHead
for cssFile in m.style do for cssFile in m.style do
link [ _rel "stylesheet"; _href (sprintf "/css/%s.css" cssFile); _type "text/css" ] link [ _rel "stylesheet"; _href $"/css/{cssFile}.css"; _type "text/css" ]
for jsFile in m.script do for jsFile in m.script do
script [ _src (sprintf "/js/%s.js" jsFile) ] [] script [ _src $"/js/{jsFile}.js" ] []
] ]
/// Render a link to the help page for the current page /// Render a link to the help page for the current page
@ -194,7 +194,7 @@ let private helpLink link =
sup [] [ sup [] [
a [ _href link a [ _href link
_title s.["Click for Help on This Page"].Value _title s.["Click for Help on This Page"].Value
_onclick (sprintf "return PT.showHelp('%s')" link) ] [ _onclick $"return PT.showHelp('{link}')" ] [
icon "help_outline" icon "help_outline"
] ]
] ]
@ -211,7 +211,7 @@ let private messages m =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
m.messages m.messages
|> List.map (fun msg -> |> List.map (fun msg ->
table [ _class (sprintf "pt-msg %s" (msg.level.ToLower ())) ] [ table [ _class $"pt-msg {msg.level.ToLower ()}" ] [
tr [] [ tr [] [
td [] [ td [] [
match msg.level with match msg.level with
@ -249,7 +249,7 @@ let private htmlFooter m =
] ]
div [ _id "pt-footer" ] [ div [ _id "pt-footer" ] [
a [ _href "/web/"; _style "line-height:28px;" ] [ a [ _href "/web/"; _style "line-height:28px;" ] [
img [ _src (sprintf "/img/%O.png" s.["footer_en"]); _alt imgText; _title imgText ] img [ _src $"""/img/%O{s.["footer_en"]}.png"""; _alt imgText; _title imgText ]
] ]
str m.version str m.version
space space

View File

@ -55,7 +55,7 @@ let edit (m : EditRequest) today ctx vi =
label [] [ locStr s.["Expiration"] ] label [] [ locStr s.["Expiration"] ]
ReferenceList.expirationList s ((m.isNew >> not) ()) ReferenceList.expirationList s ((m.isNew >> not) ())
|> List.map (fun exp -> |> List.map (fun exp ->
let radioId = sprintf "expiration_%s" (fst exp) let radioId = $"expiration_{fst exp}"
span [ _class "text-nowrap" ] [ span [ _class "text-nowrap" ] [
radio "expiration" radioId (fst exp) m.expiration radio "expiration" radioId (fst exp) m.expiration
label [ _for radioId ] [ locStr (snd exp) ] label [ _for radioId ] [ locStr (snd exp) ]
@ -80,13 +80,13 @@ let edit (m : EditRequest) today ctx vi =
/// View for the request e-mail results page /// View for the request e-mail results page
let email m vi = let email m vi =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = sprintf "%s %s" s.["Prayer Requests"].Value m.listGroup.name let pageTitle = $"""{s.["Prayer Requests"].Value} {m.listGroup.name}"""
let prefs = m.listGroup.preferences let prefs = m.listGroup.preferences
let addresses = let addresses =
m.recipients m.recipients
|> List.fold (fun (acc : StringBuilder) mbr -> acc.AppendFormat(", {0} <{1}>", mbr.memberName, mbr.email)) |> List.fold (fun (acc : StringBuilder) mbr -> acc.AppendFormat(", {0} <{1}>", mbr.memberName, mbr.email))
(StringBuilder ()) (StringBuilder ())
[ p [ _style (sprintf "font-family:%s;font-size:%ipt;" prefs.listFonts prefs.textFontSize) ] [ [ p [ _style $"font-family:{prefs.listFonts};font-size:%i{prefs.textFontSize}pt;" ] [
locStr s.["The request list was sent to the following people, via individual e-mails"] locStr s.["The request list was sent to the following people, via individual e-mails"]
rawText ":" rawText ":"
br [] br []
@ -143,9 +143,9 @@ let lists (grps : SmallGroup list) vi =
tr [] [ tr [] [
match grp.preferences.isPublic with match grp.preferences.isPublic with
| true -> | true ->
a [ _href (sprintf "/web/prayer-requests/%s/list" grpId); _title s.["View"].Value ] [ icon "list" ] a [ _href $"/web/prayer-requests/{grpId}/list"; _title s.["View"].Value ] [ icon "list" ]
| false -> | false ->
a [ _href (sprintf "/web/small-group/log-on/%s" grpId); _title s.["Log On"].Value ] a [ _href $"/web/small-group/log-on/{grpId}"; _title s.["Log On"].Value ]
[ icon "verified_user" ] [ icon "verified_user" ]
|> List.singleton |> List.singleton
|> td [] |> td []
@ -179,8 +179,8 @@ let maintain m (ctx : HttpContext) vi =
m.requests m.requests
|> Seq.map (fun req -> |> Seq.map (fun req ->
let reqId = flatGuid req.prayerRequestId let reqId = flatGuid req.prayerRequestId
let reqText = Utils.htmlToPlainText req.text let reqText = htmlToPlainText req.text
let delAction = sprintf "/web/prayer-request/%s/delete" reqId let delAction = $"/web/prayer-request/{reqId}/delete"
let delPrompt = let delPrompt =
[ s.["Are you sure you want to delete this {0}? This action cannot be undone.", [ s.["Are you sure you want to delete this {0}? This action cannot be undone.",
s.["Prayer Request"].Value.ToLower() ] s.["Prayer Request"].Value.ToLower() ]
@ -192,36 +192,36 @@ let maintain m (ctx : HttpContext) vi =
|> String.concat "" |> String.concat ""
tr [] [ tr [] [
td [] [ td [] [
a [ _href (sprintf "/web/prayer-request/%s/edit" reqId); _title l.["Edit This Prayer Request"].Value ] a [ _href $"/web/prayer-request/{reqId}/edit"; _title l.["Edit This Prayer Request"].Value ]
[ icon "edit" ] [ icon "edit" ]
match req.isExpired now m.smallGroup.preferences.daysToExpire with match req.isExpired now m.smallGroup.preferences.daysToExpire with
| true -> | true ->
a [ _href (sprintf "/web/prayer-request/%s/restore" reqId) a [ _href $"/web/prayer-request/{reqId}/restore"
_title l.["Restore This Inactive Request"].Value ] _title l.["Restore This Inactive Request"].Value ]
[ icon "visibility" ] [ icon "visibility" ]
| false -> | false ->
a [ _href (sprintf "/web/prayer-request/%s/expire" reqId) a [ _href $"/web/prayer-request/{reqId}/expire"
_title l.["Expire This Request Immediately"].Value ] _title l.["Expire This Request Immediately"].Value ]
[ icon "visibility_off" ] [ icon "visibility_off" ]
a [ _href delAction; _title l.["Delete This Request"].Value; a [ _href delAction; _title l.["Delete This Request"].Value;
_onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
[ icon "delete_forever" ] [ icon "delete_forever" ]
] ]
td [ updReq req ] [ td [ updReq req ] [
str (req.updatedDate.ToString(s.["MMMM d, yyyy"].Value, System.Globalization.CultureInfo.CurrentUICulture)) str (req.updatedDate.ToString(s.["MMMM d, yyyy"].Value, Globalization.CultureInfo.CurrentUICulture))
] ]
td [] [ locStr typs.[req.requestType] ] td [] [ locStr typs.[req.requestType] ]
td [ reqExp req ] [ str (match req.requestor with Some r -> r | None -> " ") ] td [ reqExp req ] [ str (match req.requestor with Some r -> r | None -> " ") ]
td [] [ td [] [
match reqText.Length with match reqText.Length with
| len when len < 60 -> rawText reqText | len when len < 60 -> rawText reqText
| _ -> rawText (sprintf "%s&hellip;" reqText.[0..59]) | _ -> rawText $"{reqText.[0..59]}&hellip;"
] ]
]) ])
|> List.ofSeq |> List.ofSeq
[ div [ _class "pt-center-text" ] [ [ div [ _class "pt-center-text" ] [
br [] br []
a [ _href (sprintf "/web/prayer-request/%s/edit" emptyGuid); _title s.["Add a New Request"].Value ] a [ _href $"/web/prayer-request/{emptyGuid}/edit"; _title s.["Add a New Request"].Value ]
[ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Request"] ] [ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Request"] ]
rawText " &nbsp; &nbsp; &nbsp; " rawText " &nbsp; &nbsp; &nbsp; "
a [ _href "/web/prayer-requests/view"; _title s.["View Prayer Request List"].Value ] a [ _href "/web/prayer-requests/view"; _title s.["View Prayer Request List"].Value ]
@ -302,14 +302,14 @@ let maintain m (ctx : HttpContext) vi =
/// View for the printable prayer request list /// View for the printable prayer request list
let print m version = let print m version =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = sprintf "%s %s" s.["Prayer Requests"].Value m.listGroup.name let pageTitle = $"""{s.["Prayer Requests"].Value} {m.listGroup.name}"""
let imgAlt = sprintf "%s %s" s.["PrayerTracker"].Value s.["from Bit Badger Solutions"].Value let imgAlt = $"""{s.["PrayerTracker"].Value} {s.["from Bit Badger Solutions"].Value}"""
article [] [ article [] [
rawText (m.asHtml s) rawText (m.asHtml s)
br [] br []
hr [] hr []
div [ _style "font-size:70%;font-family:@Model.ListGroup.preferences.listFonts;" ] [ div [ _style $"font-size:70%%;font-family:{m.listGroup.preferences.listFonts};" ] [
img [ _src (sprintf "/img/%s.png" s.["footer_en"].Value) img [ _src $"""/img/{s.["footer_en"].Value}.png"""
_style "vertical-align:text-bottom;" _style "vertical-align:text-bottom;"
_alt imgAlt _alt imgAlt
_title imgAlt ] _title imgAlt ]
@ -323,13 +323,13 @@ let print m version =
/// View for the prayer request list /// View for the prayer request list
let view m vi = let view m vi =
let s = I18N.localizer.Force () let s = I18N.localizer.Force ()
let pageTitle = sprintf "%s %s" s.["Prayer Requests"].Value m.listGroup.name let pageTitle = $"""{s.["Prayer Requests"].Value} {m.listGroup.name}"""
let spacer = rawText " &nbsp; &nbsp; &nbsp; " let spacer = rawText " &nbsp; &nbsp; &nbsp; "
let dtString = m.date.ToString "yyyy-MM-dd" let dtString = m.date.ToString "yyyy-MM-dd"
[ div [ _class "pt-center-text" ] [ [ div [ _class "pt-center-text" ] [
br [] br []
a [ _class "pt-icon-link" a [ _class "pt-icon-link"
_href (sprintf "/web/prayer-requests/print/%s" dtString) _href $"/web/prayer-requests/print/{dtString}"
_title s.["View Printable"].Value ] [ _title s.["View Printable"].Value ] [
icon "print"; rawText " &nbsp;"; locStr s.["View Printable"] icon "print"; rawText " &nbsp;"; locStr s.["View Printable"]
] ]
@ -345,16 +345,16 @@ let view m vi =
| false -> findSunday (date.AddDays 1.) | false -> findSunday (date.AddDays 1.)
let sunday = findSunday m.date let sunday = findSunday m.date
a [ _class "pt-icon-link" a [ _class "pt-icon-link"
_href (sprintf "/web/prayer-requests/view/%s" (sunday.ToString "yyyy-MM-dd")) _href $"""/web/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}"""
_title s.["List for Next Sunday"].Value ] [ _title s.["List for Next Sunday"].Value ] [
icon "update"; rawText " &nbsp;"; locStr s.["List for Next Sunday"] icon "update"; rawText " &nbsp;"; locStr s.["List for Next Sunday"]
] ]
spacer spacer
let emailPrompt = s.["This will e-mail the current list to every member of your group, without further prompting. Are you sure this is what you are ready to do?"].Value let emailPrompt = s.["This will e-mail the current list to every member of your group, without further prompting. Are you sure this is what you are ready to do?"].Value
a [ _class "pt-icon-link" a [ _class "pt-icon-link"
_href (sprintf "/web/prayer-requests/email/%s" dtString) _href $"/web/prayer-requests/email/{dtString}"
_title s.["Send via E-mail"].Value _title s.["Send via E-mail"].Value
_onclick (sprintf "return PT.requests.view.promptBeforeEmail('%s')" emailPrompt) ] [ _onclick $"return PT.requests.view.promptBeforeEmail('{emailPrompt}')" ] [
icon "mail_outline"; rawText " &nbsp;"; locStr s.["Send via E-mail"] icon "mail_outline"; rawText " &nbsp;"; locStr s.["Send via E-mail"]
] ]
spacer spacer

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -61,8 +61,4 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.0" />
</ItemGroup>
</Project> </Project>

View File

@ -147,7 +147,7 @@ let logOn (grps : SmallGroup list) grpId ctx vi =
| _ -> | _ ->
"", selectDefault s.["Select Group"].Value "", selectDefault s.["Select Group"].Value
yield! grps yield! grps
|> List.map (fun grp -> flatGuid grp.smallGroupId, sprintf "%s | %s" grp.church.name grp.name) |> List.map (fun grp -> flatGuid grp.smallGroupId, $"{grp.church.name} | {grp.name}")
} }
|> selectList "smallGroupId" grpId [ _required ] |> selectList "smallGroupId" grpId [ _required ]
] ]
@ -190,15 +190,15 @@ let maintain (grps : SmallGroup list) ctx vi =
grps grps
|> List.map (fun g -> |> List.map (fun g ->
let grpId = flatGuid g.smallGroupId let grpId = flatGuid g.smallGroupId
let delAction = sprintf "/web/small-group/%s/delete" grpId let delAction = $"/web/small-group/{grpId}/delete"
let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.", let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.",
sprintf "%s (%s)" (s.["Small Group"].Value.ToLower ()) g.name].Value $"""{s.["Small Group"].Value.ToLower ()} ({g.name})""" ].Value
tr [] [ tr [] [
td [] [ td [] [
a [ _href (sprintf "/web/small-group/%s/edit" grpId); _title s.["Edit This Group"].Value ] [ icon "edit" ] a [ _href $"/web/small-group/{grpId}/edit"; _title s.["Edit This Group"].Value ] [ icon "edit" ]
a [ _href delAction a [ _href delAction
_title s.["Delete This Group"].Value _title s.["Delete This Group"].Value
_onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
[ icon "delete_forever" ] [ icon "delete_forever" ]
] ]
td [] [ str g.name ] td [] [ str g.name ]
@ -209,7 +209,7 @@ let maintain (grps : SmallGroup list) ctx vi =
] ]
[ div [ _class "pt-center-text" ] [ [ div [ _class "pt-center-text" ] [
br [] br []
a [ _href (sprintf "/web/small-group/%s/edit" emptyGuid); _title s.["Add a New Group"].Value ] [ a [ _href $"/web/small-group/{emptyGuid}/edit"; _title s.["Add a New Group"].Value ] [
icon "add_circle" icon "add_circle"
rawText " &nbsp;" rawText " &nbsp;"
locStr s.["Add a New Group"] locStr s.["Add a New Group"]
@ -244,18 +244,18 @@ let members (mbrs : Member list) (emailTyps : Map<string, LocalizedString>) ctx
mbrs mbrs
|> List.map (fun mbr -> |> List.map (fun mbr ->
let mbrId = flatGuid mbr.memberId let mbrId = flatGuid mbr.memberId
let delAction = sprintf "/web/small-group/member/%s/delete" mbrId let delAction = $"/web/small-group/member/{mbrId}/delete"
let delPrompt = let delPrompt =
s.["Are you sure you want to delete this {0}? This action cannot be undone.", s.["group member"]] s.["Are you sure you want to delete this {0}? This action cannot be undone.", s.["group member"]]
.Value .Value
.Replace("?", sprintf " (%s)?" mbr.memberName) .Replace("?", $" ({mbr.memberName})?")
tr [] [ tr [] [
td [] [ td [] [
a [ _href (sprintf "/web/small-group/member/%s/edit" mbrId); _title s.["Edit This Group Member"].Value ] a [ _href $"/web/small-group/member/{mbrId}/edit"; _title s.["Edit This Group Member"].Value ]
[ icon "edit" ] [ icon "edit" ]
a [ _href delAction a [ _href delAction
_title s.["Delete This Group Member"].Value _title s.["Delete This Group Member"].Value
_onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
[ icon "delete_forever" ] [ icon "delete_forever" ]
] ]
td [] [ str mbr.memberName ] td [] [ str mbr.memberName ]
@ -266,7 +266,7 @@ let members (mbrs : Member list) (emailTyps : Map<string, LocalizedString>) ctx
] ]
[ div [ _class"pt-center-text" ] [ [ div [ _class"pt-center-text" ] [
br [] br []
a [ _href (sprintf "/web/small-group/member/%s/edit" emptyGuid); _title s.["Add a New Group Member"].Value ] a [ _href $"/web/small-group/member/{emptyGuid}/edit"; _title s.["Add a New Group Member"].Value ]
[ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Group Member"] ] [ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New Group Member"] ]
br [] br []
br [] br []

View File

@ -21,7 +21,7 @@ let assignGroups m groups curGroups ctx vi =
] ]
groups groups
|> List.map (fun (grpId, grpName) -> |> List.map (fun (grpId, grpName) ->
let inputId = sprintf "id-%s" grpId let inputId = $"id-{grpId}"
tr [] [ tr [] [
td [] [ td [] [
input [ _type "checkbox" input [ _type "checkbox"
@ -49,7 +49,7 @@ let changePassword ctx vi =
] ]
form [ _action "/web/user/password/change" form [ _action "/web/user/password/change"
_method "post" _method "post"
_onsubmit (sprintf "return PT.compareValidation('newPassword','newPasswordConfirm','%A')" s.["The passwords do not match"]) ] [ _onsubmit $"""return PT.compareValidation('newPassword','newPasswordConfirm','%A{s.["The passwords do not match"]}')""" ] [
style [ _scoped ] [ rawText "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; } "] style [ _scoped ] [ rawText "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; } "]
csrfToken ctx csrfToken ctx
div [ _class "pt-field-row" ] [ div [ _class "pt-field-row" ] [
@ -84,7 +84,7 @@ let edit (m : EditUser) ctx vi =
let pageTitle = match m.isNew () with true -> "Add a New User" | false -> "Edit User" let pageTitle = match m.isNew () with true -> "Add a New User" | false -> "Edit User"
let pwPlaceholder = s.[match m.isNew () with true -> "" | false -> "No change"].Value let pwPlaceholder = s.[match m.isNew () with true -> "" | false -> "No change"].Value
[ form [ _action "/web/user/edit/save"; _method "post"; _class "pt-center-columns" [ form [ _action "/web/user/edit/save"; _method "post"; _class "pt-center-columns"
_onsubmit (sprintf "return PT.compareValidation('password','passwordConfirm','%A')" s.["The passwords do not match"]) ] [ _onsubmit $"""return PT.compareValidation('password','passwordConfirm','%A{s.["The passwords do not match"]}')""" ] [
style [ _scoped ] style [ _scoped ]
[ rawText "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; } #emailAddress { width: 20rem; } " ] [ rawText "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; } #emailAddress { width: 20rem; } " ]
csrfToken ctx csrfToken ctx
@ -123,7 +123,7 @@ let edit (m : EditUser) ctx vi =
] ]
div [ _class "pt-field-row" ] [ submit [] "save" s.["Save User"] ] div [ _class "pt-field-row" ] [ submit [] "save" s.["Save User"] ]
] ]
script [] [ rawText (sprintf "PT.onLoad(PT.user.edit.onPageLoad(%s))" ((string (m.isNew ())).ToLower ())) ] script [] [ rawText $"PT.onLoad(PT.user.edit.onPageLoad({(string (m.isNew ())).ToLower ()}))" ]
] ]
|> Layout.Content.standard |> Layout.Content.standard
|> Layout.standard vi pageTitle |> Layout.standard vi pageTitle
@ -189,17 +189,17 @@ let maintain (users : User list) ctx vi =
users users
|> List.map (fun user -> |> List.map (fun user ->
let userId = flatGuid user.userId let userId = flatGuid user.userId
let delAction = sprintf "/web/user/%s/delete" userId let delAction = $"/web/user/{userId}/delete"
let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.", let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.",
(sprintf "%s (%s)" (s.["User"].Value.ToLower()) user.fullName)].Value $"""{s.["User"].Value.ToLower ()} ({user.fullName})"""].Value
tr [] [ tr [] [
td [] [ td [] [
a [ _href (sprintf "/web/user/%s/edit" userId); _title s.["Edit This User"].Value ] [ icon "edit" ] a [ _href $"/web/user/{userId}/edit"; _title s.["Edit This User"].Value ] [ icon "edit" ]
a [ _href (sprintf "/web/user/%s/small-groups" userId); _title s.["Assign Groups to This User"].Value ] a [ _href $"/web/user/{userId}/small-groups"; _title s.["Assign Groups to This User"].Value ]
[ icon "group" ] [ icon "group" ]
a [ _href delAction a [ _href delAction
_title s.["Delete This User"].Value _title s.["Delete This User"].Value
_onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] _onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
[ icon "delete_forever" ] [ icon "delete_forever" ]
] ]
td [] [ str user.fullName ] td [] [ str user.fullName ]
@ -213,7 +213,7 @@ let maintain (users : User list) ctx vi =
] ]
[ div [ _class "pt-center-text" ] [ [ div [ _class "pt-center-text" ] [
br [] br []
a [ _href (sprintf "/web/user/%s/edit" emptyGuid); _title s.["Add a New User"].Value ] a [ _href $"/web/user/{emptyGuid}/edit"; _title s.["Add a New User"].Value ]
[ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New User"] ] [ icon "add_circle"; rawText " &nbsp;"; locStr s.["Add a New User"] ]
br [] br []
br [] br []

View File

@ -54,9 +54,9 @@ let stripTags allowedTags input =
|> List.fold |> List.fold
(fun acc t -> (fun acc t ->
acc acc
|| htmlTag.IndexOf (sprintf "<%s>" t) = 0 || htmlTag.IndexOf $"<{t}>" = 0
|| htmlTag.IndexOf (sprintf "<%s " t) = 0 || htmlTag.IndexOf $"<{t} " = 0
|| htmlTag.IndexOf (sprintf "</%s" t) = 0) false || htmlTag.IndexOf $"</{t}" = 0) false
match isAllowed with match isAllowed with
| true -> () | true -> ()
| false -> output <- String.replaceFirst tag.Value "" output | false -> output <- String.replaceFirst tag.Value "" output
@ -200,7 +200,7 @@ module Help =
/// Help link for user password change page /// Help link for user password change page
let changePassword = "user/password" let changePassword = "user/password"
/// Create a full link for a help page /// Create a full link for a help page
let fullLink lang url = sprintf "https://docs.prayer.bitbadger.solutions/%s/%s.html" lang url let fullLink lang url = $"https://docs.prayer.bitbadger.solutions/%s{lang}/%s{url}.html"
/// This class serves as a common anchor for resources /// This class serves as a common anchor for resources
type Common () = type Common () =

View File

@ -25,7 +25,7 @@ module ReferenceList =
| HtmlFormat -> s.["HTML Format"].Value | HtmlFormat -> s.["HTML Format"].Value
| PlainTextFormat -> s.["Plain-Text Format"].Value | PlainTextFormat -> s.["Plain-Text Format"].Value
seq { seq {
"", LocalizedString ("", sprintf "%s (%s)" s.["Group Default"].Value defaultType) "", LocalizedString ("", $"""{s.["Group Default"].Value} ({defaultType})""")
HtmlFormat.code, s.["HTML Format"] HtmlFormat.code, s.["HTML Format"]
PlainTextFormat.code, s.["Plain-Text Format"] PlainTextFormat.code, s.["Plain-Text Format"]
} }
@ -594,12 +594,12 @@ with
let asOfSize = Math.Round (float prefs.textFontSize * 0.8, 2) let asOfSize = Math.Round (float prefs.textFontSize * 0.8, 2)
[ match this.showHeader with [ match this.showHeader with
| true -> | true ->
div [ _style (sprintf "text-align:center;font-family:%s" prefs.listFonts) ] [ div [ _style $"text-align:center;font-family:{prefs.listFonts}" ] [
span [ _style (sprintf "font-size:%ipt;" prefs.headingFontSize) ] [ span [ _style $"font-size:%i{prefs.headingFontSize}pt;" ] [
strong [] [ str s.["Prayer Requests"].Value ] strong [] [ str s.["Prayer Requests"].Value ]
] ]
br [] br []
span [ _style (sprintf "font-size:%ipt;" prefs.textFontSize) ] [ span [ _style $"font-size:%i{prefs.textFontSize}pt;" ] [
strong [] [ str this.listGroup.name ] strong [] [ str this.listGroup.name ]
br [] br []
str (this.date.ToString s.["MMMM d, yyyy"].Value) str (this.date.ToString s.["MMMM d, yyyy"].Value)
@ -616,10 +616,9 @@ with
let reqs = this.requestsInCategory cat let reqs = this.requestsInCategory cat
let catName = typs |> List.filter (fun t -> fst t = cat) |> List.head |> snd let catName = typs |> List.filter (fun t -> fst t = cat) |> List.head |> snd
div [ _style "padding-left:10px;padding-bottom:.5em;" ] [ div [ _style "padding-left:10px;padding-bottom:.5em;" ] [
table [ _style (sprintf "font-family:%s;page-break-inside:avoid;" prefs.listFonts) ] [ table [ _style $"font-family:{prefs.listFonts};page-break-inside:avoid;" ] [
tr [] [ tr [] [
td [ _style (sprintf "font-size:%ipt;color:%s;padding:3px 0;border-top:solid 3px %s;border-bottom:solid 3px %s;font-weight:bold;" 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;" ] [
prefs.headingFontSize prefs.headingColor prefs.lineColor prefs.lineColor) ] [
rawText "&nbsp; &nbsp; "; str catName.Value; rawText "&nbsp; &nbsp; " rawText "&nbsp; &nbsp; "; str catName.Value; rawText "&nbsp; &nbsp; "
] ]
] ]
@ -628,8 +627,7 @@ with
reqs reqs
|> List.map (fun req -> |> List.map (fun req ->
let bullet = match this.isNew req with true -> "circle" | false -> "disc" let bullet = match this.isNew req with true -> "circle" | false -> "disc"
li [ _style (sprintf "list-style-type:%s;font-family:%s;font-size:%ipt;padding-bottom:.25em;" li [ _style $"list-style-type:{bullet};font-family:{prefs.listFonts};font-size:%i{prefs.textFontSize}pt;padding-bottom:.25em;" ] [
bullet prefs.listFonts prefs.textFontSize) ] [
match req.requestor with match req.requestor with
| Some rqstr when rqstr <> "" -> | Some rqstr when rqstr <> "" ->
strong [] [ str rqstr ] strong [] [ str rqstr ]
@ -646,7 +644,7 @@ with
| ShortDate -> req.updatedDate.ToShortDateString () | ShortDate -> req.updatedDate.ToShortDateString ()
| LongDate -> req.updatedDate.ToLongDateString () | LongDate -> req.updatedDate.ToLongDateString ()
| _ -> "" | _ -> ""
i [ _style (sprintf "font-size:%.2fpt" asOfSize) ] [ i [ _style $"font-size:%.2f{asOfSize}pt" ] [
rawText "&nbsp; ("; str s.["as of"].Value; str " "; str dt; rawText ")" rawText "&nbsp; ("; str s.["as of"].Value; str " "; str dt; rawText ")"
] ]
]) ])
@ -672,7 +670,7 @@ with
let typ = (typs |> List.filter (fun t -> fst t = cat) |> List.head |> snd).Value let typ = (typs |> List.filter (fun t -> fst t = cat) |> List.head |> snd).Value
let dashes = String.replicate (typ.Length + 4) "-" let dashes = String.replicate (typ.Length + 4) "-"
dashes dashes
sprintf @" %s" (typ.ToUpper ()) $" {typ.ToUpper ()}"
dashes dashes
for req in reqs do for req in reqs do
let bullet = match this.isNew req with true -> "+" | false -> "-" let bullet = match this.isNew req with true -> "+" | false -> "-"
@ -685,7 +683,7 @@ with
| ShortDate -> req.updatedDate.ToShortDateString () | ShortDate -> req.updatedDate.ToShortDateString ()
| LongDate -> req.updatedDate.ToLongDateString () | LongDate -> req.updatedDate.ToLongDateString ()
| _ -> "" | _ -> ""
sprintf " (%s %s)" s.["as of"].Value dt $""" ({s.["as of"].Value} {dt})"""
|> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.text) |> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.text)
" " " "
} }

View File

@ -26,7 +26,7 @@ module Configure =
let configuration (ctx : WebHostBuilderContext) (cfg : IConfigurationBuilder) = let configuration (ctx : WebHostBuilderContext) (cfg : IConfigurationBuilder) =
cfg.SetBasePath(ctx.HostingEnvironment.ContentRootPath) cfg.SetBasePath(ctx.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", optional = true, reloadOnChange = true) .AddJsonFile("appsettings.json", optional = true, reloadOnChange = true)
.AddJsonFile(sprintf "appsettings.%s.json" ctx.HostingEnvironment.EnvironmentName, optional = true) .AddJsonFile($"appsettings.{ctx.HostingEnvironment.EnvironmentName}.json", optional = true)
.AddEnvironmentVariables() .AddEnvironmentVariables()
|> ignore |> ignore

View File

@ -24,7 +24,7 @@ let toSelectList<'T> valFunc textFunc withDefault emptyText (items : 'T seq) =
[ match withDefault with [ match withDefault with
| true -> | true ->
let s = PrayerTracker.Views.I18N.localizer.Force () let s = PrayerTracker.Views.I18N.localizer.Force ()
yield SelectListItem (sprintf "&mdash; %A &mdash;" s.[emptyText], "") yield SelectListItem ($"""&mdash; %A{s.[emptyText]} &mdash;""", "")
| _ -> () | _ -> ()
yield! items |> Seq.map (fun x -> SelectListItem (textFunc x, valFunc x)) yield! items |> Seq.map (fun x -> SelectListItem (textFunc x, valFunc x))
] ]
@ -41,15 +41,15 @@ let toSelectListWithDefault<'T> valFunc textFunc (items : 'T seq) =
let appVersion = let appVersion =
let v = Assembly.GetExecutingAssembly().GetName().Version let v = Assembly.GetExecutingAssembly().GetName().Version
#if (DEBUG) #if (DEBUG)
sprintf "v%A" v $"v{v}"
#else #else
seq { seq {
sprintf "v%d" v.Major $"v%d{v.Major}"
match v.Minor with match v.Minor with
| 0 -> match v.Build with 0 -> () | _ -> sprintf ".0.%d" v.Build | 0 -> match v.Build with 0 -> () | _ -> $".0.%d{v.Build}"
| _ -> | _ ->
sprintf ".%d" v.Minor $".%d{v.Minor}"
match v.Build with 0 -> () | _ -> sprintf ".%d" v.Build match v.Build with 0 -> () | _ -> $".%d{v.Build}"
} }
|> String.concat "" |> String.concat ""
#endif #endif

View File

@ -121,7 +121,7 @@ type UserCookie =
/// Create a salted hash to use to validate the idle timeout key /// Create a salted hash to use to validate the idle timeout key
let saltedTimeoutHash (c : TimeoutCookie) = let saltedTimeoutHash (c : TimeoutCookie) =
sha1Hash (sprintf "Prayer%ATracker%AIdle%dTimeout" c.Id c.GroupId c.Until) sha1Hash $"Prayer%A{c.Id}Tracker%A{c.GroupId}Idle%d{c.Until}Timeout"
/// Cookie options to push an expiration out by 100 days /// Cookie options to push an expiration out by 100 days
let autoRefresh = let autoRefresh =

View File

@ -32,9 +32,9 @@ let createMessage (grp : SmallGroup) subj =
/// Create an HTML-format e-mail message /// Create an HTML-format e-mail message
let createHtmlMessage grp subj body (s : IStringLocalizer) = let createHtmlMessage grp subj body (s : IStringLocalizer) =
let bodyText = let bodyText =
[ @"<!DOCTYPE html><html xmlns=""http://www.w3.org/1999/xhtml""><head><title></title></head><body>" [ """<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><title></title></head><body>"""
body body
@"<hr><div style=""text-align:right;font-family:Arial,Helvetica,sans-serif;font-size:8pt;padding-right:10px;"">" """<hr><div style="text-align:right;font-family:Arial,Helvetica,sans-serif;font-size:8pt;padding-right:10px;">"""
s.["Generated by P R A Y E R T R A C K E R"].Value s.["Generated by P R A Y E R T R A C K E R"].Value
"<br><small>" "<br><small>"
s.["from Bit Badger Solutions"].Value s.["from Bit Badger Solutions"].Value

View File

@ -35,7 +35,7 @@ let language culture : HttpHandler =
| "" | ""
| "en" -> "en-US" | "en" -> "en-US"
| "es" -> "es-MX" | "es" -> "es-MX"
| _ -> sprintf "%s-%s" culture (culture.ToUpper ()) | _ -> $"{culture}-{culture.ToUpper ()}"
|> (CultureInfo >> Option.ofObj) |> (CultureInfo >> Option.ofObj)
with with
| :? CultureNotFoundException | :? CultureNotFoundException

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -34,8 +34,4 @@
<ProjectReference Include="..\PrayerTracker.UI\PrayerTracker.UI.fsproj" /> <ProjectReference Include="..\PrayerTracker.UI\PrayerTracker.UI.fsproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.7.0" />
</ItemGroup>
</Project> </Project>

View File

@ -157,7 +157,7 @@ let logOnSubmit : HttpHandler =
return! redirectTo false "/web/prayer-requests/view" next ctx return! redirectTo false "/web/prayer-requests/view" next ctx
| None -> | None ->
addError ctx s.["Password incorrect - login unsuccessful"] addError ctx s.["Password incorrect - login unsuccessful"]
return! redirectTo false (sprintf "/web/small-group/log-on/%s" (flatGuid m.smallGroupId)) next ctx return! redirectTo false $"/web/small-group/log-on/{flatGuid m.smallGroupId}" next ctx
| Error e -> return! bindError e next ctx | Error e -> return! bindError e next ctx
} }
@ -352,7 +352,7 @@ let sendAnnouncement : HttpHandler =
// Reformat the text to use the class's font stylings // Reformat the text to use the class's font stylings
let requestText = ckEditorToText m.text let requestText = ckEditorToText m.text
let htmlText = let htmlText =
p [ _style (sprintf "font-family:%s;font-size:%dpt;" grp.preferences.listFonts grp.preferences.textFontSize) ] p [ _style $"font-family:{grp.preferences.listFonts};font-size:%d{grp.preferences.textFontSize}pt;" ]
[ rawText requestText ] [ rawText requestText ]
|> renderHtmlNode |> renderHtmlNode
let plainText = (htmlToPlainText >> wordWrap 74) htmlText let plainText = (htmlToPlainText >> wordWrap 74) htmlText

View File

@ -262,7 +262,7 @@ let save : HttpHandler =
|> Some |> Some
} }
|> addUserMessage ctx |> addUserMessage ctx
return! redirectTo false (sprintf "/web/user/%s/small-groups" (flatGuid u.userId)) next ctx return! redirectTo false $"/web/user/{flatGuid u.userId}/small-groups" next ctx
| false -> | false ->
addInfo ctx s.["Successfully {0} user", s.["Updated"].Value.ToLower ()] addInfo ctx s.["Successfully {0} user", s.["Updated"].Value.ToLower ()]
return! redirectTo false "/web/users" next ctx return! redirectTo false "/web/users" next ctx
@ -283,7 +283,7 @@ let saveGroups : HttpHandler =
match Seq.length m.smallGroups with match Seq.length m.smallGroups with
| 0 -> | 0 ->
addError ctx s.["You must select at least one group to assign"] addError ctx s.["You must select at least one group to assign"]
return! redirectTo false (sprintf "/web/user/%s/small-groups" (flatGuid m.userId)) next ctx return! redirectTo false $"/web/user/{flatGuid m.userId}/small-groups" next ctx
| _ -> | _ ->
let db = ctx.dbContext () let db = ctx.dbContext ()
match! db.TryUserByIdWithGroups m.userId with match! db.TryUserByIdWithGroups m.userId with

3
src/Publish-App.ps1 Normal file
View File

@ -0,0 +1,3 @@
Set-Location PrayerTracker
dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true --self-contained false
Set-Location bin\Release\net5.0\linux-x64\publish