Merge pull request #3 from writeas/add-collections-sidebar

Add collections sidebar
This commit is contained in:
Angelo Stavrow 2020-08-14 11:08:52 -04:00 committed by GitHub
commit ea1243b8ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 220 additions and 38 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Post editor now has a Publish button to change post status
- Collections sidebar to choose a specific collection (i.e., blog)
### Changed

View File

@ -5,14 +5,9 @@ struct ContentView: View {
var body: some View {
NavigationView {
PostList()
.frame(maxHeight: .infinity)
.navigationTitle("Posts")
.toolbar {
NavigationLink(destination: PostEditor(post: Post())) {
Image(systemName: "plus")
}
}
CollectionSidebar()
PostList(selectedCollection: allPostsCollection)
Text("Select a post, or create a new draft.")
.foregroundColor(.secondary)

View File

@ -1,5 +1,4 @@
import Foundation
import WriteFreely
enum PostStatus {
case draft
@ -12,6 +11,7 @@ class Post: Identifiable, ObservableObject {
@Published var body: String
@Published var createdDate: Date
@Published var status: PostStatus
@Published var collection: PostCollection
let id = UUID()
@ -19,12 +19,14 @@ class Post: Identifiable, ObservableObject {
title: String = "Title",
body: String = "Write your post here...",
createdDate: Date = Date(),
status: PostStatus = .draft
status: PostStatus = .draft,
collection: PostCollection = defaultDraftCollection
) {
self.title = title
self.body = body
self.createdDate = createdDate
self.status = status
self.collection = collection
}
}
@ -41,25 +43,45 @@ let testPost = Post(
turpis dictum dictum. Duis lacus lectus, dictum vel felis in, rhoncus fringilla felis. Nunc id dolor nisl. Aliquam \
euismod purus elit. Nullam egestas neque leo, sed aliquet ligula ultrices nec.
""",
createdDate: Date(),
status: .published)
createdDate: Date()
)
let testPostData = [
Post(
title: "My First Post",
body: "Look at me, creating a first post! That's cool.",
createdDate: Date(timeIntervalSince1970: 1595429452),
status: .published
status: .published,
collection: userCollections[0]
),
Post(
title: "Post 2: The Quickening",
body: "See, here's the rule about Highlander jokes: _there can be only one_.",
createdDate: Date(timeIntervalSince1970: 1595514125),
status: .edited
status: .edited,
collection: userCollections[0]
),
Post(
title: "The Post Revolutions",
body: "I can never keep the Matrix movie order straight. Why not just call them part 2 and part 3?",
createdDate: Date(timeIntervalSince1970: 1595600006)
),
Post(
title: "Episode IV: A New Post",
body: "How many movies does this person watch? How many movie-title jokes will they make?",
createdDate: Date(timeIntervalSince1970: 1596219877),
status: .published,
collection: userCollections[1]
),
Post(
title: "Fast (Post) Five",
body: "Look, it was either a Fast and the Furious reference, or a Resident Evil reference."
),
Post(
title: "Post: The Final Chapter",
body: "And there you have it, a Resident Evil movie reference.",
createdDate: Date(timeIntervalSince1970: 1596043684),
status: .edited,
collection: userCollections[2]
)
]

View File

@ -5,23 +5,19 @@ struct PostCell: View {
@ObservedObject var post: Post
var body: some View {
NavigationLink(
destination: PostEditor(post: post)
) {
HStack {
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
.lineLimit(1)
Text(buildDateString(from: post.createdDate))
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(1)
}
Spacer()
PostStatusBadge(post: post)
HStack {
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
.lineLimit(1)
Text(buildDateString(from: post.createdDate))
.font(.caption)
.lineLimit(1)
}
Spacer()
PostStatusBadge(post: post)
}
.padding(5)
}
func buildDateString(from date: Date) -> String {

View File

@ -2,13 +2,80 @@ import SwiftUI
struct PostList: View {
@EnvironmentObject var postStore: PostStore
@State var selectedCollection: PostCollection
var body: some View {
#if os(iOS)
List {
Text("\(postStore.posts.count) Posts")
.foregroundColor(.secondary)
ForEach(postStore.posts) { post in
PostCell(post: post)
ForEach(showPosts(for: selectedCollection)) { post in
NavigationLink(
destination: PostEditor(post: post)
) {
PostCell(
post: post
)
}
}
}
.navigationTitle(selectedCollection.title)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: {
let post = Post()
postStore.add(post)
}, label: {
Image(systemName: "square.and.pencil")
})
}
ToolbarItem(placement: .bottomBar) {
Spacer()
}
ToolbarItem(placement: .bottomBar) {
Text(pluralizedPostCount(for: showPosts(for: selectedCollection)))
}
ToolbarItem(placement: .bottomBar) {
Spacer()
}
}
#else //if os(macOS)
List {
ForEach(showPosts(for: selectedCollection)) { post in
NavigationLink(
destination: PostEditor(post: post)
) {
PostCell(
post: post
)
}
}
}
.navigationTitle(selectedCollection.title)
.navigationSubtitle(pluralizedPostCount(for: showPosts(for: selectedCollection)))
.toolbar {
Button(action: {
let post = Post()
postStore.add(post)
}, label: {
Image(systemName: "square.and.pencil")
})
}
#endif
}
private func pluralizedPostCount(for posts: [Post]) -> String {
if posts.count == 1 {
return "1 post"
} else {
return "\(posts.count) posts"
}
}
private func showPosts(for collection: PostCollection) -> [Post] {
if collection == allPostsCollection {
return postStore.posts
} else {
return postStore.posts.filter {
$0.collection.title == collection.title
}
}
}
@ -16,7 +83,9 @@ struct PostList: View {
struct PostList_Previews: PreviewProvider {
static var previews: some View {
PostList()
.environmentObject(testPostStore)
Group {
PostList(selectedCollection: allPostsCollection)
.environmentObject(testPostStore)
}
}
}

View File

@ -7,7 +7,7 @@ struct PostStatusBadge: View {
let (badgeLabel, badgeColor) = setupBadgeProperties(for: post.status)
Text(badgeLabel)
.font(.caption)
.fontWeight(.bold)
.fontWeight(.semibold)
.foregroundColor(.white)
.textCase(.uppercase)
.lineLimit(1)

View File

@ -0,0 +1,36 @@
import SwiftUI
struct CollectionSidebar: View {
@EnvironmentObject var postStore: PostStore
@Binding var selectedCollection: PostCollection?
private let collections = [
allPostsCollection,
defaultDraftCollection,
testPostCollection1,
testPostCollection2,
testPostCollection3
]
var body: some View {
List {
ForEach(collections) { collection in
NavigationLink(
destination: PostList(title: collection.title, posts: showPosts(for: collection)).tag(collection)) {
Text(collection.title)
}
}
}
.listStyle(SidebarListStyle())
}
func showPosts(for collection: PostCollection) -> [Post] {
if collection == allPostsCollection {
return postStore.posts
} else {
return postStore.posts.filter {
$0.collection.title == collection.title
}
}
}
}

View File

@ -0,0 +1,25 @@
import SwiftUI
struct CollectionSidebar: View {
private let collections = postCollections
var body: some View {
List {
ForEach(collections) { collection in
NavigationLink(
destination: PostList(selectedCollection: collection)
) {
Text(collection.title)
}
}
}
.navigationTitle("Collections")
.listStyle(SidebarListStyle())
}
}
struct CollectionSidebar_Previews: PreviewProvider {
static var previews: some View {
CollectionSidebar()
}
}

View File

@ -0,0 +1,22 @@
import Foundation
struct PostCollection: Identifiable, Hashable {
let id = UUID()
let title: String
}
let allPostsCollection = PostCollection(title: "All Posts")
let defaultDraftCollection = PostCollection(title: "Drafts")
let userCollections = [
PostCollection(title: "Collection 1"),
PostCollection(title: "Collection 2"),
PostCollection(title: "Collection 3")
]
let postCollections = [
allPostsCollection,
defaultDraftCollection,
userCollections[0],
userCollections[1],
userCollections[2]
]

View File

@ -2,7 +2,11 @@ import SwiftUI
@main
struct WriteFreely_MultiPlatformApp: App {
#if DEBUG
@StateObject private var store = testPostStore
#else
@StateObject private var store = PostStore()
#endif
var body: some Scene {
WindowGroup {

View File

@ -7,6 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
171BFDF724D49FD400888236 /* PostCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF624D49FD400888236 /* PostCollection.swift */; };
171BFDF824D49FD400888236 /* PostCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF624D49FD400888236 /* PostCollection.swift */; };
171BFDFA24D4AF8300888236 /* CollectionSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionSidebar.swift */; };
171BFDFB24D4AF8300888236 /* CollectionSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionSidebar.swift */; };
1756AE6B24CB1E4B00FD7257 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6A24CB1E4B00FD7257 /* Post.swift */; };
1756AE6C24CB1E4B00FD7257 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6A24CB1E4B00FD7257 /* Post.swift */; };
1756AE6E24CB255B00FD7257 /* PostStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6D24CB255B00FD7257 /* PostStore.swift */; };
@ -50,6 +54,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
171BFDF624D49FD400888236 /* PostCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCollection.swift; sourceTree = "<group>"; };
171BFDF924D4AF8300888236 /* CollectionSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionSidebar.swift; sourceTree = "<group>"; };
1756AE6A24CB1E4B00FD7257 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = "<group>"; };
1756AE6D24CB255B00FD7257 /* PostStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostStore.swift; sourceTree = "<group>"; };
1756AE7324CB26FA00FD7257 /* PostCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCell.swift; sourceTree = "<group>"; };
@ -237,6 +243,8 @@
17DF32D224C8B78D00BCE2E3 /* PostCollection */ = {
isa = PBXGroup;
children = (
171BFDF624D49FD400888236 /* PostCollection.swift */,
171BFDF924D4AF8300888236 /* CollectionSidebar.swift */,
);
path = PostCollection;
sourceTree = "<group>";
@ -455,9 +463,11 @@
files = (
17DF32AC24C87D3500BCE2E3 /* ContentView.swift in Sources */,
1756AE8124CB844500FD7257 /* View+Keyboard.swift in Sources */,
171BFDFA24D4AF8300888236 /* CollectionSidebar.swift in Sources */,
1756AE7724CB2EDD00FD7257 /* PostEditor.swift in Sources */,
17DF32D524C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */,
1756AE7A24CB65DF00FD7257 /* PostList.swift in Sources */,
171BFDF724D49FD400888236 /* PostCollection.swift in Sources */,
17DF32AA24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */,
1756AE6E24CB255B00FD7257 /* PostStore.swift in Sources */,
1756AE6B24CB1E4B00FD7257 /* Post.swift in Sources */,
@ -469,10 +479,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
171BFDF824D49FD400888236 /* PostCollection.swift in Sources */,
17DF32AD24C87D3500BCE2E3 /* ContentView.swift in Sources */,
1756AE7824CB2EDD00FD7257 /* PostEditor.swift in Sources */,
17DF32D624C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */,
1756AE7B24CB65DF00FD7257 /* PostList.swift in Sources */,
171BFDFB24D4AF8300888236 /* CollectionSidebar.swift in Sources */,
17DF32AB24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */,
1756AE6F24CB255B00FD7257 /* PostStore.swift in Sources */,
1756AE6C24CB1E4B00FD7257 /* Post.swift in Sources */,

View File

@ -7,12 +7,12 @@
<key>WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>0</integer>
</dict>
<key>WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>1</integer>
</dict>
</dict>
</dict>