myPrayerJournal v2 #27
12
.gitignore
vendored
12
.gitignore
vendored
@ -253,11 +253,11 @@ paket-files/
|
|||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
|
||||||
# Compiled files / application
|
# Compiled files / application
|
||||||
src/api/build
|
src/build
|
||||||
src/api/MyPrayerJournal.Api/wwwroot/favicon.ico
|
src/MyPrayerJournal.Api/wwwroot/favicon.ico
|
||||||
src/api/MyPrayerJournal.Api/wwwroot/index.html
|
src/MyPrayerJournal.Api/wwwroot/index.html
|
||||||
src/api/MyPrayerJournal.Api/wwwroot/css
|
src/MyPrayerJournal.Api/wwwroot/css
|
||||||
src/api/MyPrayerJournal.Api/wwwroot/js
|
src/MyPrayerJournal.Api/wwwroot/js
|
||||||
src/api/MyPrayerJournal.Api/appsettings.development.json
|
src/MyPrayerJournal.Api/appsettings.development.json
|
||||||
/build
|
/build
|
||||||
src/*.exe
|
src/*.exe
|
||||||
|
156
src/MyPrayerJournal.Api/Data.fs
Normal file
156
src/MyPrayerJournal.Api/Data.fs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
namespace MyPrayerJournal
|
||||||
|
|
||||||
|
open FSharp.Control.Tasks.V2.ContextInsensitive
|
||||||
|
open Microsoft.EntityFrameworkCore
|
||||||
|
open Microsoft.FSharpLu
|
||||||
|
|
||||||
|
/// Entity Framework configuration for myPrayerJournal
|
||||||
|
module internal EFConfig =
|
||||||
|
|
||||||
|
open FSharp.EFCore.OptionConverter
|
||||||
|
open System.Collections.Generic
|
||||||
|
|
||||||
|
/// Configure EF properties for all entity types
|
||||||
|
let configure (mb : ModelBuilder) =
|
||||||
|
mb.Entity<History> (
|
||||||
|
fun m ->
|
||||||
|
m.ToTable "history" |> ignore
|
||||||
|
m.HasKey ("requestId", "asOf") |> ignore
|
||||||
|
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.asOf).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.status).IsRequired() |> ignore
|
||||||
|
m.Property(fun e -> e.text) |> ignore)
|
||||||
|
|> ignore
|
||||||
|
mb.Model.FindEntityType(typeof<History>).FindProperty("text").SetValueConverter (OptionConverter<string> ())
|
||||||
|
|
||||||
|
mb.Entity<Note> (
|
||||||
|
fun m ->
|
||||||
|
m.ToTable "note" |> ignore
|
||||||
|
m.HasKey ("requestId", "asOf") |> ignore
|
||||||
|
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.asOf).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.notes).IsRequired () |> ignore)
|
||||||
|
|> ignore
|
||||||
|
|
||||||
|
mb.Entity<Request> (
|
||||||
|
fun m ->
|
||||||
|
m.ToTable "request" |> ignore
|
||||||
|
m.HasKey(fun e -> e.requestId :> obj) |> ignore
|
||||||
|
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.enteredOn).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.userId).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.snoozedUntil).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.showAfter).IsRequired () |> ignore
|
||||||
|
m.Property(fun e -> e.recurType).IsRequired() |> ignore
|
||||||
|
m.Property(fun e -> e.recurCount).IsRequired() |> ignore
|
||||||
|
m.HasMany(fun e -> e.history :> IEnumerable<History>)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey(fun e -> e.requestId :> obj)
|
||||||
|
|> ignore
|
||||||
|
m.HasMany(fun e -> e.notes :> IEnumerable<Note>)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey(fun e -> e.requestId :> obj)
|
||||||
|
|> ignore)
|
||||||
|
|> ignore
|
||||||
|
|
||||||
|
mb.Query<JournalRequest> (
|
||||||
|
fun m ->
|
||||||
|
m.ToView "journal" |> ignore
|
||||||
|
m.Ignore(fun e -> e.history :> obj) |> ignore
|
||||||
|
m.Ignore(fun e -> e.notes :> obj) |> ignore)
|
||||||
|
|> ignore
|
||||||
|
|
||||||
|
|
||||||
|
open System.Linq
|
||||||
|
|
||||||
|
/// Data context
|
||||||
|
type AppDbContext (opts : DbContextOptions<AppDbContext>) =
|
||||||
|
inherit DbContext (opts)
|
||||||
|
|
||||||
|
[<DefaultValue>]
|
||||||
|
val mutable private history : DbSet<History>
|
||||||
|
[<DefaultValue>]
|
||||||
|
val mutable private notes : DbSet<Note>
|
||||||
|
[<DefaultValue>]
|
||||||
|
val mutable private requests : DbSet<Request>
|
||||||
|
[<DefaultValue>]
|
||||||
|
val mutable private journal : DbQuery<JournalRequest>
|
||||||
|
|
||||||
|
member this.History
|
||||||
|
with get () = this.history
|
||||||
|
and set v = this.history <- v
|
||||||
|
member this.Notes
|
||||||
|
with get () = this.notes
|
||||||
|
and set v = this.notes <- v
|
||||||
|
member this.Requests
|
||||||
|
with get () = this.requests
|
||||||
|
and set v = this.requests <- v
|
||||||
|
member this.Journal
|
||||||
|
with get () = this.journal
|
||||||
|
and set v = this.journal <- v
|
||||||
|
|
||||||
|
override __.OnModelCreating (mb : ModelBuilder) =
|
||||||
|
base.OnModelCreating mb
|
||||||
|
EFConfig.configure mb
|
||||||
|
|
||||||
|
/// Register a disconnected entity with the context, having the given state
|
||||||
|
member private this.RegisterAs<'TEntity when 'TEntity : not struct> state e =
|
||||||
|
this.Entry<'TEntity>(e).State <- state
|
||||||
|
|
||||||
|
/// Add an entity instance to the context
|
||||||
|
member this.AddEntry e =
|
||||||
|
this.RegisterAs EntityState.Added e
|
||||||
|
|
||||||
|
/// Update the entity instance's values
|
||||||
|
member this.UpdateEntry e =
|
||||||
|
this.RegisterAs EntityState.Modified e
|
||||||
|
|
||||||
|
/// Retrieve all answered requests for the given user
|
||||||
|
member this.AnsweredRequests userId : JournalRequest seq =
|
||||||
|
upcast this.Journal
|
||||||
|
.Where(fun r -> r.userId = userId && r.lastStatus = "Answered")
|
||||||
|
.OrderByDescending(fun r -> r.asOf)
|
||||||
|
|
||||||
|
/// Retrieve the user's current journal
|
||||||
|
member this.JournalByUserId userId : JournalRequest seq =
|
||||||
|
upcast this.Journal
|
||||||
|
.Where(fun r -> r.userId = userId && r.lastStatus <> "Answered")
|
||||||
|
.OrderBy(fun r -> r.showAfter)
|
||||||
|
|
||||||
|
/// Retrieve a request by its ID and user ID
|
||||||
|
member this.TryRequestById reqId userId =
|
||||||
|
task {
|
||||||
|
let! req = this.Requests.AsNoTracking().FirstOrDefaultAsync(fun r -> r.requestId = reqId && r.userId = userId)
|
||||||
|
return Option.fromObject req
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve notes for a request by its ID and user ID
|
||||||
|
member this.NotesById reqId userId =
|
||||||
|
task {
|
||||||
|
match! this.TryRequestById reqId userId with
|
||||||
|
| Some _ -> return this.Notes.AsNoTracking().Where(fun n -> n.requestId = reqId) |> List.ofSeq
|
||||||
|
| None -> return []
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a journal request by its ID and user ID
|
||||||
|
member this.TryJournalById reqId userId =
|
||||||
|
task {
|
||||||
|
let! req = this.Journal.FirstOrDefaultAsync(fun r -> r.requestId = reqId && r.userId = userId)
|
||||||
|
return Option.fromObject req
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a request, including its history and notes, by its ID and user ID
|
||||||
|
member this.TryFullRequestById requestId userId =
|
||||||
|
task {
|
||||||
|
match! this.TryJournalById requestId userId with
|
||||||
|
| Some req ->
|
||||||
|
let! fullReq =
|
||||||
|
this.Requests.AsNoTracking()
|
||||||
|
.Include(fun r -> r.history)
|
||||||
|
.Include(fun r -> r.notes)
|
||||||
|
.FirstOrDefaultAsync(fun r -> r.requestId = requestId && r.userId = userId)
|
||||||
|
match Option.fromObject fullReq with
|
||||||
|
| Some _ -> return Some { req with history = List.ofSeq fullReq.history; notes = List.ofSeq fullReq.notes }
|
||||||
|
| None -> return None
|
||||||
|
| None -> return None
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
@ -30,4 +30,8 @@
|
|||||||
<Folder Include="wwwroot\" />
|
<Folder Include="wwwroot\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MyPrayerJournal.Domain\MyPrayerJournal.Domain.fsproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
114
src/MyPrayerJournal.Domain/Entities.fs
Normal file
114
src/MyPrayerJournal.Domain/Entities.fs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
[<AutoOpen>]
|
||||||
|
/// Entities for use in the data model for myPrayerJournal
|
||||||
|
module MyPrayerJournal.Entities
|
||||||
|
|
||||||
|
open System.Collections.Generic
|
||||||
|
|
||||||
|
/// Type alias for a Collision-resistant Unique IDentifier
|
||||||
|
type Cuid = string
|
||||||
|
|
||||||
|
/// Request ID is a CUID
|
||||||
|
type RequestId = Cuid
|
||||||
|
|
||||||
|
/// User ID is a string (the "sub" part of the JWT)
|
||||||
|
type UserId = string
|
||||||
|
|
||||||
|
/// History is a record of action taken on a prayer request, including updates to its text
|
||||||
|
type [<CLIMutable; NoComparison; NoEquality>] History =
|
||||||
|
{ /// The ID of the request to which this history entry applies
|
||||||
|
requestId : RequestId
|
||||||
|
/// The time when this history entry was made
|
||||||
|
asOf : int64
|
||||||
|
/// The status for this history entry
|
||||||
|
status : string
|
||||||
|
/// The text of the update, if applicable
|
||||||
|
text : string option
|
||||||
|
}
|
||||||
|
with
|
||||||
|
/// An empty history entry
|
||||||
|
static member empty =
|
||||||
|
{ requestId = ""
|
||||||
|
asOf = 0L
|
||||||
|
status = ""
|
||||||
|
text = None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Note is a note regarding a prayer request that does not result in an update to its text
|
||||||
|
and [<CLIMutable; NoComparison; NoEquality>] Note =
|
||||||
|
{ /// The ID of the request to which this note applies
|
||||||
|
requestId : RequestId
|
||||||
|
/// The time when this note was made
|
||||||
|
asOf : int64
|
||||||
|
/// The text of the notes
|
||||||
|
notes : string
|
||||||
|
}
|
||||||
|
with
|
||||||
|
/// An empty note
|
||||||
|
static member empty =
|
||||||
|
{ requestId = ""
|
||||||
|
asOf = 0L
|
||||||
|
notes = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request is the identifying record for a prayer request
|
||||||
|
and [<CLIMutable; NoComparison; NoEquality>] Request =
|
||||||
|
{ /// The ID of the request
|
||||||
|
requestId : RequestId
|
||||||
|
/// The time this request was initially entered
|
||||||
|
enteredOn : int64
|
||||||
|
/// The ID of the user to whom this request belongs ("sub" from the JWT)
|
||||||
|
userId : string
|
||||||
|
/// The time at which this request should reappear in the user's journal by manual user choice
|
||||||
|
snoozedUntil : int64
|
||||||
|
/// The time at which this request should reappear in the user's journal by recurrence
|
||||||
|
showAfter : int64
|
||||||
|
/// The type of recurrence for this request
|
||||||
|
recurType : string
|
||||||
|
/// How many of the recurrence intervals should occur between appearances in the journal
|
||||||
|
recurCount : int16
|
||||||
|
/// The history entries for this request
|
||||||
|
history : ICollection<History>
|
||||||
|
/// The notes for this request
|
||||||
|
notes : ICollection<Note>
|
||||||
|
}
|
||||||
|
with
|
||||||
|
/// An empty request
|
||||||
|
static member empty =
|
||||||
|
{ requestId = ""
|
||||||
|
enteredOn = 0L
|
||||||
|
userId = ""
|
||||||
|
snoozedUntil = 0L
|
||||||
|
showAfter = 0L
|
||||||
|
recurType = "immediate"
|
||||||
|
recurCount = 0s
|
||||||
|
history = List<History> ()
|
||||||
|
notes = List<Note> ()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JournalRequest is the form of a prayer request returned for the request journal display. It also contains
|
||||||
|
/// properties that may be filled for history and notes
|
||||||
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
|
type JournalRequest =
|
||||||
|
{ /// The ID of the request
|
||||||
|
requestId : RequestId
|
||||||
|
/// The ID of the user to whom the request belongs
|
||||||
|
userId : string
|
||||||
|
/// The current text of the request
|
||||||
|
text : string
|
||||||
|
/// The last time action was taken on the request
|
||||||
|
asOf : int64
|
||||||
|
/// The last status for the request
|
||||||
|
lastStatus : string
|
||||||
|
/// The time that this request should reappear in the user's journal
|
||||||
|
snoozedUntil : int64
|
||||||
|
/// The time after which this request should reappear in the user's journal by configured recurrence
|
||||||
|
showAfter : int64
|
||||||
|
/// The type of recurrence for this request
|
||||||
|
recurType : string
|
||||||
|
/// How many of the recurrence intervals should occur between appearances in the journal
|
||||||
|
recurCount : int16
|
||||||
|
/// History entries for the request
|
||||||
|
history : History list
|
||||||
|
/// Note entries for the request
|
||||||
|
notes : Note list
|
||||||
|
}
|
11
src/MyPrayerJournal.Domain/MyPrayerJournal.Domain.fsproj
Normal file
11
src/MyPrayerJournal.Domain/MyPrayerJournal.Domain.fsproj
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Entities.fs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
19
src/MyPrayerJournal.Mobile.Android/Assets/AboutAssets.txt
Normal file
19
src/MyPrayerJournal.Mobile.Android/Assets/AboutAssets.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Any raw assets you want to be deployed with your application can be placed in
|
||||||
|
this directory (and child directories) and given a Build Action of "AndroidAsset".
|
||||||
|
|
||||||
|
These files will be deployed with you package and will be accessible using Android's
|
||||||
|
AssetManager, like this:
|
||||||
|
|
||||||
|
public class ReadAsset : Activity
|
||||||
|
{
|
||||||
|
protected override void OnCreate (Bundle bundle)
|
||||||
|
{
|
||||||
|
base.OnCreate (bundle);
|
||||||
|
|
||||||
|
InputStream input = Assets.Open ("my_asset.txt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Additionally, some Android functions will automatically load asset files:
|
||||||
|
|
||||||
|
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
|
33
src/MyPrayerJournal.Mobile.Android/MainActivity.fs
Normal file
33
src/MyPrayerJournal.Mobile.Android/MainActivity.fs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace MyPrayerJournal.Mobile.Android
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
open Android.App
|
||||||
|
open Android.Content
|
||||||
|
open Android.OS
|
||||||
|
open Android.Runtime
|
||||||
|
open Android.Views
|
||||||
|
open Android.Widget
|
||||||
|
|
||||||
|
type Resources = MyPrayerJournal.Mobile.Android.Resource
|
||||||
|
|
||||||
|
[<Activity (Label = "MyPrayerJournal.Mobile.Android", MainLauncher = true, Icon = "@mipmap/icon")>]
|
||||||
|
type MainActivity () =
|
||||||
|
inherit Activity ()
|
||||||
|
|
||||||
|
let mutable count:int = 1
|
||||||
|
|
||||||
|
override this.OnCreate (bundle) =
|
||||||
|
|
||||||
|
base.OnCreate (bundle)
|
||||||
|
|
||||||
|
// Set our view from the "main" layout resource
|
||||||
|
this.SetContentView (Resources.Layout.Main)
|
||||||
|
|
||||||
|
// Get our button from the layout resource, and attach an event to it
|
||||||
|
let button = this.FindViewById<Button>(Resources.Id.myButton)
|
||||||
|
button.Click.Add (fun args ->
|
||||||
|
button.Text <- sprintf "%d clicks!" count
|
||||||
|
count <- count + 1
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{F2A71F9B-5D33-465A-A702-920D77279786}</ProjectTypeGuids>
|
||||||
|
<ProjectGuid>49245e65-f905-4073-b057-3bceed076a1f</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>MyPrayerJournal.Mobile.Android</RootNamespace>
|
||||||
|
<AssemblyName>MyPrayerJournal.Mobile.Android</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
|
||||||
|
<AndroidApplication>True</AndroidApplication>
|
||||||
|
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
||||||
|
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||||
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
|
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||||
|
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||||
|
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
|
||||||
|
<Name>MyPrayerJournal.Mobile.Android</Name>
|
||||||
|
<SelectedDevice>test-5x</SelectedDevice>
|
||||||
|
<DefaultDevice>test-5x</DefaultDevice>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>True</DebugSymbols>
|
||||||
|
<DebugType>Full</DebugType>
|
||||||
|
<Optimize>False</Optimize>
|
||||||
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
|
<DefineConstants>DEBUG</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
|
<ConsolePause>False</ConsolePause>
|
||||||
|
<Tailcalls>False</Tailcalls>
|
||||||
|
<PlatformTarget>
|
||||||
|
</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>PdbOnly</DebugType>
|
||||||
|
<DebugSymbols>True</DebugSymbols>
|
||||||
|
<Optimize>True</Optimize>
|
||||||
|
<OutputPath>bin\Release</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<PlatformTarget>
|
||||||
|
</PlatformTarget>
|
||||||
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
|
<ConsolePause>False</ConsolePause>
|
||||||
|
<Tailcalls>True</Tailcalls>
|
||||||
|
<DefineConstants>
|
||||||
|
</DefineConstants>
|
||||||
|
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
||||||
|
<GenerateTailCalls>true</GenerateTailCalls>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.FSharp.targets" />
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\AboutResources.txt" />
|
||||||
|
<AndroidResource Include="Resources\layout\Main.axml" />
|
||||||
|
<AndroidResource Include="Resources\values\Strings.xml" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-hdpi\Icon.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-mdpi\Icon.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xhdpi\Icon.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xxhdpi\Icon.png" />
|
||||||
|
<AndroidResource Include="Resources\mipmap-xxxhdpi\Icon.png" />
|
||||||
|
<None Include="Properties\AndroidManifest.xml" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.fs" />
|
||||||
|
<Compile Include="MainActivity.fs" />
|
||||||
|
<AndroidAsset Include="Assets\AboutAssets.txt" />
|
||||||
|
<Content Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="FSharp.Core">
|
||||||
|
<HintPath>..\packages\FSharp.Core.4.0.0.1\lib\portable-net45+monoandroid10+monotouch10+xamarinios10\FSharp.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="mscorlib" />
|
||||||
|
<Reference Include="Mono.Android" />
|
||||||
|
<Reference Include="Xamarin.Android.FSharp.ResourceProvider.Runtime">
|
||||||
|
<HintPath>..\packages\Xamarin.Android.FSharp.ResourceProvider.1.0.0.13\lib\Xamarin.Android.FSharp.ResourceProvider.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MyPrayerJournal.Domain\MyPrayerJournal.Domain.fsproj">
|
||||||
|
<Name>MyPrayerJournal.Domain</Name>
|
||||||
|
<Project>{6236760d-b21e-4187-9d0b-7d5e1c6ad896}</Project>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\MyPrayerJournal.Mobile.Core\MyPrayerJournal.Mobile.Core.fsproj">
|
||||||
|
<Name>MyPrayerJournal.Mobile.Core</Name>
|
||||||
|
<Project>{537d56c5-b55b-4f6c-af80-39f3bf4fd903}</Project>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0"
|
||||||
|
package="me.prayerjournal.android">
|
||||||
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
|
||||||
|
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name">
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -0,0 +1,32 @@
|
|||||||
|
namespace MyPrayerJournal.Mobile.Android
|
||||||
|
module AssemblyInfo =
|
||||||
|
|
||||||
|
open System.Reflection
|
||||||
|
open System.Runtime.CompilerServices
|
||||||
|
open Android.App
|
||||||
|
|
||||||
|
// Information about this assembly is defined by the following attributes.
|
||||||
|
// Change them to the values specific to your project.
|
||||||
|
|
||||||
|
[<assembly: AssemblyTitle("MyPrayerJournal.Mobile.Android")>]
|
||||||
|
[<assembly: AssemblyDescription("")>]
|
||||||
|
[<assembly: AssemblyConfiguration("")>]
|
||||||
|
[<assembly: AssemblyCompany("HP Inc.")>]
|
||||||
|
[<assembly: AssemblyProduct("MyPrayerJournal.Mobile.Android")>]
|
||||||
|
[<assembly: AssemblyCopyright("Copyright © HP Inc. 2019")>]
|
||||||
|
[<assembly: AssemblyTrademark("")>]
|
||||||
|
[<assembly: AssemblyCulture("")>]
|
||||||
|
|
||||||
|
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||||
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||||
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||||
|
|
||||||
|
[<assembly: AssemblyVersion("1.0.0.0")>]
|
||||||
|
|
||||||
|
// The following attributes are used to specify the signing key for the assembly,
|
||||||
|
// if desired. See the Mono documentation for more information about signing.
|
||||||
|
|
||||||
|
//[<assembly: AssemblyDelaySign(false)>]
|
||||||
|
//[<assembly: AssemblyKeyFile("")>]
|
||||||
|
|
||||||
|
()
|
@ -0,0 +1,44 @@
|
|||||||
|
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||||
|
in your application as resource files. Various Android APIs are designed to
|
||||||
|
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||||
|
directly.
|
||||||
|
|
||||||
|
For example, a sample Android app that contains a user interface layout (main.axml),
|
||||||
|
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||||
|
would keep its resources in the "Resources" directory of the application:
|
||||||
|
|
||||||
|
Resources/
|
||||||
|
drawable/
|
||||||
|
icon.png
|
||||||
|
|
||||||
|
layout/
|
||||||
|
main.axml
|
||||||
|
|
||||||
|
values/
|
||||||
|
strings.xml
|
||||||
|
|
||||||
|
In order to get the build system to recognize Android resources, set the build action to
|
||||||
|
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||||
|
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||||
|
the build system will package the resources for distribution and generate a class called "R"
|
||||||
|
(this is an Android convention) that contains the tokens for each one of the resources
|
||||||
|
included. For example, for the above Resources layout, this is what the R class would expose:
|
||||||
|
|
||||||
|
public class R {
|
||||||
|
public class drawable {
|
||||||
|
public const int icon = 0x123;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class layout {
|
||||||
|
public const int main = 0x456;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class strings {
|
||||||
|
public const int first_string = 0xabc;
|
||||||
|
public const int second_string = 0xbcd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
|
||||||
|
to reference the layout/main.axml file, or R.strings.first_string to reference the first
|
||||||
|
string in the dictionary file values/strings.xml.
|
112
src/MyPrayerJournal.Mobile.Android/Resources/Resource.designer.cs
generated
Normal file
112
src/MyPrayerJournal.Mobile.Android/Resources/Resource.designer.cs
generated
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#pragma warning disable 1591
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[assembly: global::Android.Runtime.ResourceDesignerAttribute("MyPrayerJournal.Mobile.Android.Resource", IsApplication=true)]
|
||||||
|
|
||||||
|
namespace MyPrayerJournal.Mobile.Android
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
|
||||||
|
public partial class Resource
|
||||||
|
{
|
||||||
|
|
||||||
|
static Resource()
|
||||||
|
{
|
||||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateIdValues()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
static Attribute()
|
||||||
|
{
|
||||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Attribute()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Id
|
||||||
|
{
|
||||||
|
|
||||||
|
// aapt resource value: 0x7f050000
|
||||||
|
public const int myButton = 2131034112;
|
||||||
|
|
||||||
|
static Id()
|
||||||
|
{
|
||||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Id()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Layout
|
||||||
|
{
|
||||||
|
|
||||||
|
// aapt resource value: 0x7f030000
|
||||||
|
public const int Main = 2130903040;
|
||||||
|
|
||||||
|
static Layout()
|
||||||
|
{
|
||||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Layout()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Mipmap
|
||||||
|
{
|
||||||
|
|
||||||
|
// aapt resource value: 0x7f020000
|
||||||
|
public const int Icon = 2130837504;
|
||||||
|
|
||||||
|
static Mipmap()
|
||||||
|
{
|
||||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mipmap()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class String
|
||||||
|
{
|
||||||
|
|
||||||
|
// aapt resource value: 0x7f040001
|
||||||
|
public const int app_name = 2130968577;
|
||||||
|
|
||||||
|
// aapt resource value: 0x7f040000
|
||||||
|
public const int hello = 2130968576;
|
||||||
|
|
||||||
|
static String()
|
||||||
|
{
|
||||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore 1591
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<Button android:id="@+id/myButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/hello" />
|
||||||
|
</LinearLayout>
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="hello">Hello World, Click Me!</string>
|
||||||
|
<string name="app_name">MyPrayerJournal.Mobile.Android</string>
|
||||||
|
</resources>
|
5
src/MyPrayerJournal.Mobile.Android/packages.config
Normal file
5
src/MyPrayerJournal.Mobile.Android/packages.config
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="FSharp.Core" version="4.0.0.1" targetFramework="monoandroid60" />
|
||||||
|
<package id="Xamarin.Android.FSharp.ResourceProvider" version="1.0.0.13" targetFramework="monoandroid60" />
|
||||||
|
</packages>
|
5
src/MyPrayerJournal.Mobile.Core/Library.fs
Normal file
5
src/MyPrayerJournal.Mobile.Core/Library.fs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace MyPrayerJournal.Mobile.Core
|
||||||
|
|
||||||
|
module Say =
|
||||||
|
let hello name =
|
||||||
|
printfn "Hello %s" name
|
@ -0,0 +1,11 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Library.fs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
85
src/MyPrayerJournal.sln
Normal file
85
src/MyPrayerJournal.sln
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.28721.148
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MyPrayerJournal.Domain", "MyPrayerJournal.Domain\MyPrayerJournal.Domain.fsproj", "{6236760D-B21E-4187-9D0B-7D5E1C6AD896}"
|
||||||
|
EndProject
|
||||||
|
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MyPrayerJournal.Api", "MyPrayerJournal.Api\MyPrayerJournal.Api.fsproj", "{1887D1E1-544A-4F54-B266-38E7867DC842}"
|
||||||
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MyPrayerJournal.Mobile.Core", "MyPrayerJournal.Mobile.Core\MyPrayerJournal.Mobile.Core.fsproj", "{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}"
|
||||||
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MyPrayerJournal.Mobile.Android", "MyPrayerJournal.Mobile.Android\MyPrayerJournal.Mobile.Android.fsproj", "{49245E65-F905-4073-B057-3BCEED076A1F}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|iPhone = Debug|iPhone
|
||||||
|
Debug|iPhoneSimulator = Debug|iPhoneSimulator
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|iPhone = Release|iPhone
|
||||||
|
Release|iPhoneSimulator = Release|iPhoneSimulator
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{6236760D-B21E-4187-9D0B-7D5E1C6AD896}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{1887D1E1-544A-4F54-B266-38E7867DC842}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{537D56C5-B55B-4F6C-AF80-39F3BF4FD903}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|iPhone.Deploy.0 = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|iPhone.Deploy.0 = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{49245E65-F905-4073-B057-3BCEED076A1F}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {8E2447D9-52F0-4A0D-BB61-A83C19353D7C}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
@ -1,275 +0,0 @@
|
|||||||
namespace MyPrayerJournal
|
|
||||||
|
|
||||||
open FSharp.Control.Tasks.V2.ContextInsensitive
|
|
||||||
open Microsoft.EntityFrameworkCore
|
|
||||||
open Microsoft.FSharpLu
|
|
||||||
|
|
||||||
/// Entities for use in the data model for myPrayerJournal
|
|
||||||
[<AutoOpen>]
|
|
||||||
module Entities =
|
|
||||||
|
|
||||||
open FSharp.EFCore.OptionConverter
|
|
||||||
open System.Collections.Generic
|
|
||||||
|
|
||||||
/// Type alias for a Collision-resistant Unique IDentifier
|
|
||||||
type Cuid = string
|
|
||||||
|
|
||||||
/// Request ID is a CUID
|
|
||||||
type RequestId = Cuid
|
|
||||||
|
|
||||||
/// User ID is a string (the "sub" part of the JWT)
|
|
||||||
type UserId = string
|
|
||||||
|
|
||||||
/// History is a record of action taken on a prayer request, including updates to its text
|
|
||||||
type [<CLIMutable; NoComparison; NoEquality>] History =
|
|
||||||
{ /// The ID of the request to which this history entry applies
|
|
||||||
requestId : RequestId
|
|
||||||
/// The time when this history entry was made
|
|
||||||
asOf : int64
|
|
||||||
/// The status for this history entry
|
|
||||||
status : string
|
|
||||||
/// The text of the update, if applicable
|
|
||||||
text : string option
|
|
||||||
}
|
|
||||||
with
|
|
||||||
/// An empty history entry
|
|
||||||
static member empty =
|
|
||||||
{ requestId = ""
|
|
||||||
asOf = 0L
|
|
||||||
status = ""
|
|
||||||
text = None
|
|
||||||
}
|
|
||||||
|
|
||||||
static member configureEF (mb : ModelBuilder) =
|
|
||||||
mb.Entity<History> (
|
|
||||||
fun m ->
|
|
||||||
m.ToTable "history" |> ignore
|
|
||||||
m.HasKey ("requestId", "asOf") |> ignore
|
|
||||||
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.asOf).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.status).IsRequired() |> ignore
|
|
||||||
m.Property(fun e -> e.text) |> ignore)
|
|
||||||
|> ignore
|
|
||||||
let typ = mb.Model.FindEntityType(typeof<History>)
|
|
||||||
let prop = typ.FindProperty("text")
|
|
||||||
mb.Model.FindEntityType(typeof<History>).FindProperty("text").SetValueConverter (OptionConverter<string> ())
|
|
||||||
|
|
||||||
/// Note is a note regarding a prayer request that does not result in an update to its text
|
|
||||||
and [<CLIMutable; NoComparison; NoEquality>] Note =
|
|
||||||
{ /// The ID of the request to which this note applies
|
|
||||||
requestId : RequestId
|
|
||||||
/// The time when this note was made
|
|
||||||
asOf : int64
|
|
||||||
/// The text of the notes
|
|
||||||
notes : string
|
|
||||||
}
|
|
||||||
with
|
|
||||||
/// An empty note
|
|
||||||
static member empty =
|
|
||||||
{ requestId = ""
|
|
||||||
asOf = 0L
|
|
||||||
notes = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
static member configureEF (mb : ModelBuilder) =
|
|
||||||
mb.Entity<Note> (
|
|
||||||
fun m ->
|
|
||||||
m.ToTable "note" |> ignore
|
|
||||||
m.HasKey ("requestId", "asOf") |> ignore
|
|
||||||
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.asOf).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.notes).IsRequired () |> ignore)
|
|
||||||
|> ignore
|
|
||||||
|
|
||||||
/// Request is the identifying record for a prayer request
|
|
||||||
and [<CLIMutable; NoComparison; NoEquality>] Request =
|
|
||||||
{ /// The ID of the request
|
|
||||||
requestId : RequestId
|
|
||||||
/// The time this request was initially entered
|
|
||||||
enteredOn : int64
|
|
||||||
/// The ID of the user to whom this request belongs ("sub" from the JWT)
|
|
||||||
userId : string
|
|
||||||
/// The time at which this request should reappear in the user's journal by manual user choice
|
|
||||||
snoozedUntil : int64
|
|
||||||
/// The time at which this request should reappear in the user's journal by recurrence
|
|
||||||
showAfter : int64
|
|
||||||
/// The type of recurrence for this request
|
|
||||||
recurType : string
|
|
||||||
/// How many of the recurrence intervals should occur between appearances in the journal
|
|
||||||
recurCount : int16
|
|
||||||
/// The history entries for this request
|
|
||||||
history : ICollection<History>
|
|
||||||
/// The notes for this request
|
|
||||||
notes : ICollection<Note>
|
|
||||||
}
|
|
||||||
with
|
|
||||||
/// An empty request
|
|
||||||
static member empty =
|
|
||||||
{ requestId = ""
|
|
||||||
enteredOn = 0L
|
|
||||||
userId = ""
|
|
||||||
snoozedUntil = 0L
|
|
||||||
showAfter = 0L
|
|
||||||
recurType = "immediate"
|
|
||||||
recurCount = 0s
|
|
||||||
history = List<History> ()
|
|
||||||
notes = List<Note> ()
|
|
||||||
}
|
|
||||||
|
|
||||||
static member configureEF (mb : ModelBuilder) =
|
|
||||||
mb.Entity<Request> (
|
|
||||||
fun m ->
|
|
||||||
m.ToTable "request" |> ignore
|
|
||||||
m.HasKey(fun e -> e.requestId :> obj) |> ignore
|
|
||||||
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.enteredOn).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.userId).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.snoozedUntil).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.showAfter).IsRequired () |> ignore
|
|
||||||
m.Property(fun e -> e.recurType).IsRequired() |> ignore
|
|
||||||
m.Property(fun e -> e.recurCount).IsRequired() |> ignore
|
|
||||||
m.HasMany(fun e -> e.history :> IEnumerable<History>)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey(fun e -> e.requestId :> obj)
|
|
||||||
|> ignore
|
|
||||||
m.HasMany(fun e -> e.notes :> IEnumerable<Note>)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey(fun e -> e.requestId :> obj)
|
|
||||||
|> ignore)
|
|
||||||
|> ignore
|
|
||||||
|
|
||||||
/// JournalRequest is the form of a prayer request returned for the request journal display. It also contains
|
|
||||||
/// properties that may be filled for history and notes
|
|
||||||
[<CLIMutable; NoComparison; NoEquality>]
|
|
||||||
type JournalRequest =
|
|
||||||
{ /// The ID of the request
|
|
||||||
requestId : RequestId
|
|
||||||
/// The ID of the user to whom the request belongs
|
|
||||||
userId : string
|
|
||||||
/// The current text of the request
|
|
||||||
text : string
|
|
||||||
/// The last time action was taken on the request
|
|
||||||
asOf : int64
|
|
||||||
/// The last status for the request
|
|
||||||
lastStatus : string
|
|
||||||
/// The time that this request should reappear in the user's journal
|
|
||||||
snoozedUntil : int64
|
|
||||||
/// The time after which this request should reappear in the user's journal by configured recurrence
|
|
||||||
showAfter : int64
|
|
||||||
/// The type of recurrence for this request
|
|
||||||
recurType : string
|
|
||||||
/// How many of the recurrence intervals should occur between appearances in the journal
|
|
||||||
recurCount : int16
|
|
||||||
/// History entries for the request
|
|
||||||
history : History list
|
|
||||||
/// Note entries for the request
|
|
||||||
notes : Note list
|
|
||||||
}
|
|
||||||
with
|
|
||||||
static member configureEF (mb : ModelBuilder) =
|
|
||||||
mb.Query<JournalRequest> (
|
|
||||||
fun m ->
|
|
||||||
m.ToView "journal" |> ignore
|
|
||||||
m.Ignore(fun e -> e.history :> obj) |> ignore
|
|
||||||
m.Ignore(fun e -> e.notes :> obj) |> ignore)
|
|
||||||
|> ignore
|
|
||||||
|
|
||||||
|
|
||||||
open System.Linq
|
|
||||||
|
|
||||||
/// Data context
|
|
||||||
type AppDbContext (opts : DbContextOptions<AppDbContext>) =
|
|
||||||
inherit DbContext (opts)
|
|
||||||
|
|
||||||
[<DefaultValue>]
|
|
||||||
val mutable private history : DbSet<History>
|
|
||||||
[<DefaultValue>]
|
|
||||||
val mutable private notes : DbSet<Note>
|
|
||||||
[<DefaultValue>]
|
|
||||||
val mutable private requests : DbSet<Request>
|
|
||||||
[<DefaultValue>]
|
|
||||||
val mutable private journal : DbQuery<JournalRequest>
|
|
||||||
|
|
||||||
member this.History
|
|
||||||
with get () = this.history
|
|
||||||
and set v = this.history <- v
|
|
||||||
member this.Notes
|
|
||||||
with get () = this.notes
|
|
||||||
and set v = this.notes <- v
|
|
||||||
member this.Requests
|
|
||||||
with get () = this.requests
|
|
||||||
and set v = this.requests <- v
|
|
||||||
member this.Journal
|
|
||||||
with get () = this.journal
|
|
||||||
and set v = this.journal <- v
|
|
||||||
|
|
||||||
override __.OnModelCreating (mb : ModelBuilder) =
|
|
||||||
base.OnModelCreating mb
|
|
||||||
[ History.configureEF
|
|
||||||
Note.configureEF
|
|
||||||
Request.configureEF
|
|
||||||
JournalRequest.configureEF
|
|
||||||
]
|
|
||||||
|> List.iter (fun x -> x mb)
|
|
||||||
|
|
||||||
/// Register a disconnected entity with the context, having the given state
|
|
||||||
member private this.RegisterAs<'TEntity when 'TEntity : not struct> state e =
|
|
||||||
this.Entry<'TEntity>(e).State <- state
|
|
||||||
|
|
||||||
/// Add an entity instance to the context
|
|
||||||
member this.AddEntry e =
|
|
||||||
this.RegisterAs EntityState.Added e
|
|
||||||
|
|
||||||
/// Update the entity instance's values
|
|
||||||
member this.UpdateEntry e =
|
|
||||||
this.RegisterAs EntityState.Modified e
|
|
||||||
|
|
||||||
/// Retrieve all answered requests for the given user
|
|
||||||
member this.AnsweredRequests userId : JournalRequest seq =
|
|
||||||
upcast this.Journal
|
|
||||||
.Where(fun r -> r.userId = userId && r.lastStatus = "Answered")
|
|
||||||
.OrderByDescending(fun r -> r.asOf)
|
|
||||||
|
|
||||||
/// Retrieve the user's current journal
|
|
||||||
member this.JournalByUserId userId : JournalRequest seq =
|
|
||||||
upcast this.Journal
|
|
||||||
.Where(fun r -> r.userId = userId && r.lastStatus <> "Answered")
|
|
||||||
.OrderBy(fun r -> r.showAfter)
|
|
||||||
|
|
||||||
/// Retrieve a request by its ID and user ID
|
|
||||||
member this.TryRequestById reqId userId =
|
|
||||||
task {
|
|
||||||
let! req = this.Requests.AsNoTracking().FirstOrDefaultAsync(fun r -> r.requestId = reqId && r.userId = userId)
|
|
||||||
return Option.fromObject req
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve notes for a request by its ID and user ID
|
|
||||||
member this.NotesById reqId userId =
|
|
||||||
task {
|
|
||||||
match! this.TryRequestById reqId userId with
|
|
||||||
| Some _ -> return this.Notes.AsNoTracking().Where(fun n -> n.requestId = reqId) |> List.ofSeq
|
|
||||||
| None -> return []
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve a journal request by its ID and user ID
|
|
||||||
member this.TryJournalById reqId userId =
|
|
||||||
task {
|
|
||||||
let! req = this.Journal.FirstOrDefaultAsync(fun r -> r.requestId = reqId && r.userId = userId)
|
|
||||||
return Option.fromObject req
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve a request, including its history and notes, by its ID and user ID
|
|
||||||
member this.TryFullRequestById requestId userId =
|
|
||||||
task {
|
|
||||||
match! this.TryJournalById requestId userId with
|
|
||||||
| Some req ->
|
|
||||||
let! fullReq =
|
|
||||||
this.Requests.AsNoTracking()
|
|
||||||
.Include(fun r -> r.history)
|
|
||||||
.Include(fun r -> r.notes)
|
|
||||||
.FirstOrDefaultAsync(fun r -> r.requestId = requestId && r.userId = userId)
|
|
||||||
match Option.fromObject fullReq with
|
|
||||||
| Some _ -> return Some { req with history = List.ofSeq fullReq.history; notes = List.ofSeq fullReq.notes }
|
|
||||||
| None -> return None
|
|
||||||
| None -> return None
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio 15
|
|
||||||
VisualStudioVersion = 15.0.27703.2035
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MyPrayerJournal.Api", "MyPrayerJournal.Api\MyPrayerJournal.Api.fsproj", "{E0E5240C-00DC-428A-899A-DA4F06625B8A}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{E0E5240C-00DC-428A-899A-DA4F06625B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E0E5240C-00DC-428A-899A-DA4F06625B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E0E5240C-00DC-428A-899A-DA4F06625B8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{E0E5240C-00DC-428A-899A-DA4F06625B8A}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {7EAB6243-94B3-49A5-BA64-7F01B8BE7CB9}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
Loading…
Reference in New Issue
Block a user