A user can create posts within a site.
That user might be the creator of the blog, or it might be a different user.
A Post
fact refers to the Site
parent, the author User
, and also includes a timestamp to differentiate it from other posts.
[ FactType ( "Blog.Post" ) ]
public record Post ( Site site, User author, DateTime createdAt) { }
Renderer. RenderTypes ( typeof ( Post ) )
%0
Blog.Post
Blog.Post
Blog.Site
Blog.Site
Blog.Post->Blog.Site
site
Jinaga.User
Jinaga.User
Blog.Post->Jinaga.User
author
Blog.Site->Jinaga.User
creator
var post0 = await jinagaClient. Fact ( new Post ( site, user, DateTime. UtcNow) ) ;
var post1 = await jinagaClient. Fact ( new Post ( site, user, DateTime. UtcNow) ) ;
jinagaClient. RenderFacts ( post0, post1)
%0
eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
Jinaga.User
publicKey
--- FAKE USER ---
fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==
Blog.Site
createdAt
2024-05-18T20:32:02....
fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
creator
BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==
Blog.Post
createdAt
2024-05-18T20:32:03....
BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
author
BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==
site
8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==
Blog.Post
createdAt
2024-05-18T20:32:03....
8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
author
8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==
site
When using the application, you will first select a site from the list.
Then you will navigate to a page where you see the posts.
The specification should therefore start from the site.
var postsInSite = Given< Site> . Match ( ( site, facts) =>
from post in facts. OfType < Post> ( )
where post . site == site
select post) ;
var posts = await jinagaClient. Query ( postsInSite, site) ;
posts. Count ( )
Continue the pattern to define deletion, restoration, and mutable properties for the child objects.
[ FactType ( "Blog.Post.Title" ) ]
public record PostTitle ( Post post, string value , PostTitle[ ] prior) { }
[ FactType ( "Blog.Post.Content" ) ]
public record PostContent ( Post post, string markdown, PostContent[ ] prior) { }
[ FactType ( "Blog.Post.Deleted" ) ]
public record PostDeleted ( Post post, DateTime deletedAt) { }
[ FactType ( "Blog.Post.Restored" ) ]
public record PostRestored ( PostDeleted deleted) { }
Renderer. RenderTypes (
typeof ( PostTitle ) ,
typeof ( PostContent ) ,
typeof ( PostDeleted ) ,
typeof ( PostRestored ) )
%0
Blog.Post.Title
Blog.Post.Title
Blog.Post.Title->Blog.Post.Title
prior
Blog.Post
Blog.Post
Blog.Post.Title->Blog.Post
post
Blog.Site
Blog.Site
Blog.Post->Blog.Site
site
Jinaga.User
Jinaga.User
Blog.Post->Jinaga.User
author
Blog.Post.Content
Blog.Post.Content
Blog.Post.Content->Blog.Post
post
Blog.Post.Content->Blog.Post.Content
prior
Blog.Post.Deleted
Blog.Post.Deleted
Blog.Post.Deleted->Blog.Post
post
Blog.Post.Restored
Blog.Post.Restored
Blog.Post.Restored->Blog.Post.Deleted
deleted
Blog.Site->Jinaga.User
creator
var postTitle0 = await jinagaClient. Fact ( new PostTitle (
post0, "Welcome to My Blog" , [ ] ) ) ;
var postContent0 = await jinagaClient. Fact ( new PostContent (
post0, "This is my first post." , [ ] ) ) ;
var postTitle1 = await jinagaClient. Fact ( new PostTitle (
post1, "Interesting Facts" , [ ] ) ) ;
var postContent1 = await jinagaClient. Fact ( new PostContent (
post1, "Let me tell you about this thing that I love." , [ ] ) ) ;
var post0Deleted = await jinagaClient. Fact ( new PostDeleted (
post0, DateTime. UtcNow) ) ;
jinagaClient. RenderFacts (
postTitle0,
postContent0,
postTitle1,
postContent1,
post0Deleted)
%0
eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
Jinaga.User
publicKey
--- FAKE USER ---
fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==
Blog.Site
createdAt
2024-05-18T20:32:02....
fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
creator
BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==
Blog.Post
createdAt
2024-05-18T20:32:03....
BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
author
BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==
site
nO8A3zVvDLx8X6LkD4mea5GbFbWt+hkoJdqelWq6rIcrhmy4yA/lBzZkZuHnc8gqVkWEBrZ4Q9XQE7ZF6QJAYQ==
Blog.Post.Title
value
Welcome to My Blog
nO8A3zVvDLx8X6LkD4mea5GbFbWt+hkoJdqelWq6rIcrhmy4yA/lBzZkZuHnc8gqVkWEBrZ4Q9XQE7ZF6QJAYQ==->BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==
post
vZg30BbPhmhY+EHd4hoAbWex80yeOJbSOXMRPpjTJhQaO/2hFIj+vypnyEI1oTOjcJECphz5emMuJ3jiYgtmhg==
Blog.Post.Content
markdown
This is my first pos...
vZg30BbPhmhY+EHd4hoAbWex80yeOJbSOXMRPpjTJhQaO/2hFIj+vypnyEI1oTOjcJECphz5emMuJ3jiYgtmhg==->BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==
post
8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==
Blog.Post
createdAt
2024-05-18T20:32:03....
8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA==
author
8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==
site
AMe9xpS8GHaCTkyBmnvHESY48H7xtHshMrpi5kjUYaoJZTf9LavJdtFr3DR8pBzC1z3UJtBcxe+eCqd0qqG5Qw==
Blog.Post.Title
value
Interesting Facts
AMe9xpS8GHaCTkyBmnvHESY48H7xtHshMrpi5kjUYaoJZTf9LavJdtFr3DR8pBzC1z3UJtBcxe+eCqd0qqG5Qw==->8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==
post
petnLBBgNIGTKz+tPUctalOq0LgKZC7zfsehdIYy1mG0tQZZKWACchVY/U5CKfogTLgQr19x5k7AN9DUqKU0GQ==
Blog.Post.Content
markdown
Let me tell you abou...
petnLBBgNIGTKz+tPUctalOq0LgKZC7zfsehdIYy1mG0tQZZKWACchVY/U5CKfogTLgQr19x5k7AN9DUqKU0GQ==->8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==
post
Ols1d5hkAP55vmPmTHLtO4bHrkc4HayJ97SQe3OyhZIijRyL/5b79W4LRSKau60gi/lztl+zalDWfP96g8lndg==
Blog.Post.Deleted
deletedAt
2024-05-18T20:32:03....
Ols1d5hkAP55vmPmTHLtO4bHrkc4HayJ97SQe3OyhZIijRyL/5b79W4LRSKau60gi/lztl+zalDWfP96g8lndg==->BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==
post
Extend the specification to filter out deleted posts and project properties such as title.
var postsInSite = Given< Site> . Match ( ( site, facts) =>
from post in facts. OfType < Post> ( )
where post . site == site
where ! facts. Any < PostDeleted> ( pd => pd. post == post &&
! facts. Any < PostRestored> ( pr => pr. deleted == pd) )
select new
{
Post = post,
Title =
from title in facts. OfType < PostTitle> ( )
where title . post == post
where ! facts. Any < PostTitle> ( next => next. prior. Contains ( title) )
select title. value ,
Content =
from content in facts. OfType < PostContent> ( )
where content . post == post
where ! facts. Any < PostContent> ( next => next. prior. Contains ( content) )
select content. markdown
} ) ;
var posts = await jinagaClient. Query ( postsInSite, site) ;
posts
index value 0 { Post = Post { ... }, Title = [ Interesting Facts ], Content = [ Let me ... ] }
Post Post { site = Site { ... }, author = Jinaga.User, createdAt = 5/18/2024 5:54:09 PM }
site Site { creator = Jinaga.User, createdAt = 5/18/2024 5:54:05 PM }
creator Jinaga.User
createdAt 2024-05-18 17:54:05Z
author Jinaga.User
createdAt 2024-05-18 17:54:09Z
Title [ Interesting Facts ]
Content [ Let me tell you about this thing that I love. ]
(values) [ Let me tell you about this thing that I love. ]
Create a view model for the child screen in the same way you did for the parent.
var postsViewModel = posts. Select ( p => new
{
Title = p. Title. FirstOrDefault ( ) ?? "New post" ,
Content = p. Content. FirstOrDefault ( ) ?? ""
} ) ;
postsViewModel
index value 0 { Title = Interesting Facts, Content = Let me ... }
Title Content Let me tell you about this thing that I love.