Pārlūkot izejas kodu

Implement basic post store and list / editor UI components

tags/v0.0.2
Angelo Stavrow pirms 3 gadiem
vecāks
revīzija
896b5f73f7
Šim parakstam datu bāzē netika atrasta zināma atslēga GPG atslēgas ID: 1A49C7064E060EEE
12 mainītis faili ar 360 papildinājumiem un 16 dzēšanām
  1. +3
    -1
      .swiftlint.yml
  2. +0
    -13
      Shared/ContentView.swift
  3. +9
    -0
      Shared/Extensions/View+Keyboard.swift
  4. +27
    -0
      Shared/Navigation/ContentView.swift
  5. +53
    -0
      Shared/Post/Post.swift
  6. +41
    -0
      Shared/Post/PostCell.swift
  7. +41
    -0
      Shared/Post/PostEditor.swift
  8. +21
    -0
      Shared/Post/PostList.swift
  9. +63
    -0
      Shared/Post/PostStatusBadge.swift
  10. +11
    -0
      Shared/Post/PostStore.swift
  11. +2
    -1
      Shared/WriteFreely_MultiPlatformApp.swift
  12. +89
    -1
      WriteFreely-MultiPlatform.xcodeproj/project.pbxproj

+ 3
- 1
.swiftlint.yml Parādīt failu

@@ -1,2 +1,4 @@
type_name:
allowed_symbols: ["_"] # Used in SwiftUI boilerplate naming
allowed_symbols: ["_"] # Used in SwiftUI boilerplate naming
identifier_name:
excluded: ["id"] # Required for Identifiable conformance

+ 0
- 13
Shared/ContentView.swift Parādīt failu

@@ -1,13 +0,0 @@
import SwiftUI

struct ContentView: View {
var body: some View {
Text("Hello, WriteFreely!").padding()
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

+ 9
- 0
Shared/Extensions/View+Keyboard.swift Parādīt failu

@@ -0,0 +1,9 @@
import SwiftUI

#if canImport(UIKit)
extension View {
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
#endif

+ 27
- 0
Shared/Navigation/ContentView.swift Parādīt failu

@@ -0,0 +1,27 @@
import SwiftUI

struct ContentView: View {
@ObservedObject var postStore: PostStore

var body: some View {
NavigationView {
PostList(postStore: postStore)
.frame(maxHeight: .infinity)
.navigationTitle("Posts")
.toolbar {
NavigationLink(destination: PostEditor()) {
Image(systemName: "plus")
}
}

Text("Select a post, or create a new draft.")
.foregroundColor(.secondary)
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(postStore: testPostStore)
}
}

+ 53
- 0
Shared/Post/Post.swift Parādīt failu

@@ -0,0 +1,53 @@
import Foundation
import WriteFreely

struct Post: Identifiable {
var id = UUID()
var title: String
var body: String
var createdDate: Date
var status: PostStatus = .draft
var editableText: String {
return """
# \(self.title)

\(self.body)
"""
}
}

let testPost = Post(
title: "Test Post Title",
body: """
Here's some cool sample body text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ultrices \
posuere dignissim. Vestibulum a libero tempor, lacinia nulla vitae, congue purus. Nunc ac nulla quam. Duis \
tincidunt eros augue, et volutpat tortor pulvinar ut. Nullam sit amet maximus urna. Phasellus non dignissim lacus.\
Nulla ac posuere ex. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec \
non molestie mauris. Suspendisse potenti. Vivamus at erat turpis.

Pellentesque porttitor gravida tincidunt. Sed vitae eros non metus aliquam hendrerit. Aliquam sed risus suscipit \
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)

let testPostData = [
Post(
title: "My First Post",
body: "Look at me, creating a first post! That's cool.",
createdDate: Date(timeIntervalSince1970: 1595429452),
status: .published
),
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
),
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)
)
]

+ 41
- 0
Shared/Post/PostCell.swift Parādīt failu

@@ -0,0 +1,41 @@
import SwiftUI

struct PostCell: View {
var post: Post
var body: some View {
NavigationLink(
destination: PostEditor(
textString: post.editableText,
postStatus: post.status
)
) {
HStack {
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
.lineLimit(1)
Text(buildDateString(from: post.createdDate))
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(1)
}
Spacer()
PostStatusBadge(postStatus: post.status)
}
}
}

func buildDateString(from date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .short

return dateFormatter.string(from: date)
}
}

struct PostCell_Previews: PreviewProvider {
static var previews: some View {
PostCell(post: testPost)
}
}

+ 41
- 0
Shared/Post/PostEditor.swift Parādīt failu

@@ -0,0 +1,41 @@
//
// PostEditor.swift
// WriteFreely-MultiPlatform
//
// Created by Angelo Stavrow on 2020-07-24.
//

import SwiftUI

struct PostEditor: View {
@State var textString: String = ""
@State private var hasUnpublishedChanges: Bool = false
var postStatus: PostStatus = .draft

var body: some View {
TextEditor(text: $textString.animation())
.font(.body)
.padding()
.onChange(of: textString) { _ in
if postStatus == .published {
hasUnpublishedChanges = true
}
}
.toolbar {
if hasUnpublishedChanges {
PostStatusBadge(postStatus: .edited)
} else {
PostStatusBadge(postStatus: postStatus)
}
}
}
}

struct PostEditor_Previews: PreviewProvider {
static var previews: some View {
PostEditor(
textString: testPost.editableText,
postStatus: testPost.status
)
}
}

+ 21
- 0
Shared/Post/PostList.swift Parādīt failu

@@ -0,0 +1,21 @@
import SwiftUI

struct PostList: View {
var postStore: PostStore

var body: some View {
List {
Text("\(postStore.posts.count) Posts")
.foregroundColor(.secondary)
ForEach(postStore.posts) { post in
PostCell(post: post)
}
}
}
}

struct PostList_Previews: PreviewProvider {
static var previews: some View {
PostList(postStore: testPostStore)
}
}

+ 63
- 0
Shared/Post/PostStatusBadge.swift Parādīt failu

@@ -0,0 +1,63 @@
import SwiftUI

enum PostStatus {
case draft
case edited
case published
}

struct PostStatusBadge: View {
@State var postStatus: PostStatus

var body: some View {
let (badgeLabel, badgeColor) = setupBadgeProperties(for: postStatus)
Text(badgeLabel)
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.white)
.textCase(.uppercase)
.lineLimit(1)
.padding(EdgeInsets(top: 2.5, leading: 7.5, bottom: 2.5, trailing: 7.5))
.background(badgeColor)
.clipShape(RoundedRectangle(cornerRadius: 5.0, style: .circular))
}

func setupBadgeProperties(for status: PostStatus) -> (String, Color) {
var badgeLabel: String
var badgeColor: Color

switch status {
case .draft:
badgeLabel = "draft"
badgeColor = Color(red: 0.75, green: 0.5, blue: 0.85, opacity: 1.0)
case .edited:
badgeLabel = "edited"
badgeColor = Color(red: 0.75, green: 0.7, blue: 0.1, opacity: 1.0)
case .published:
badgeLabel = "published"
badgeColor = .gray
}

return (badgeLabel, badgeColor)
}
}

struct PostStatusBadge_DraftPreviews: PreviewProvider {
static var previews: some View {
PostStatusBadge(postStatus: .draft)
}
}

struct PostStatusBadge_EditedPreviews: PreviewProvider {
static var previews: some View {
Group {
PostStatusBadge(postStatus: .edited)
}
}
}

struct PostStatusBadge_PublishedPreviews: PreviewProvider {
static var previews: some View {
PostStatusBadge(postStatus: .published)
}
}

+ 11
- 0
Shared/Post/PostStore.swift Parādīt failu

@@ -0,0 +1,11 @@
import Foundation

class PostStore: ObservableObject {
@Published var posts: [Post]

init(posts: [Post] = []) {
self.posts = posts
}
}

let testPostStore = PostStore(posts: testPostData)

+ 2
- 1
Shared/WriteFreely_MultiPlatformApp.swift Parādīt failu

@@ -2,9 +2,10 @@ import SwiftUI

@main
struct WriteFreely_MultiPlatformApp: App {
@StateObject private var store = PostStore()
var body: some Scene {
WindowGroup {
ContentView()
ContentView(postStore: store)
}
}
}

+ 89
- 1
WriteFreely-MultiPlatform.xcodeproj/project.pbxproj Parādīt failu

@@ -7,6 +7,17 @@
objects = {

/* Begin PBXBuildFile section */
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 */; };
1756AE6F24CB255B00FD7257 /* PostStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE6D24CB255B00FD7257 /* PostStore.swift */; };
1756AE7424CB26FA00FD7257 /* PostCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7324CB26FA00FD7257 /* PostCell.swift */; };
1756AE7524CB26FA00FD7257 /* PostCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7324CB26FA00FD7257 /* PostCell.swift */; };
1756AE7724CB2EDD00FD7257 /* PostEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7624CB2EDD00FD7257 /* PostEditor.swift */; };
1756AE7824CB2EDD00FD7257 /* PostEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7624CB2EDD00FD7257 /* PostEditor.swift */; };
1756AE7A24CB65DF00FD7257 /* PostList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7924CB65DF00FD7257 /* PostList.swift */; };
1756AE7B24CB65DF00FD7257 /* PostList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE7924CB65DF00FD7257 /* PostList.swift */; };
1756AE8124CB844500FD7257 /* View+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756AE8024CB844500FD7257 /* View+Keyboard.swift */; };
17DF329D24C87D3500BCE2E3 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF329C24C87D3500BCE2E3 /* Tests_iOS.swift */; };
17DF32A824C87D3500BCE2E3 /* Tests_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF32A724C87D3500BCE2E3 /* Tests_macOS.swift */; };
17DF32AA24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF328124C87D3300BCE2E3 /* WriteFreely_MultiPlatformApp.swift */; };
@@ -17,6 +28,8 @@
17DF32AF24C87D3500BCE2E3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 17DF328324C87D3500BCE2E3 /* Assets.xcassets */; };
17DF32C024C87D7B00BCE2E3 /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 17DF32BF24C87D7B00BCE2E3 /* WriteFreely */; };
17DF32C324C87D8D00BCE2E3 /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 17DF32C224C87D8D00BCE2E3 /* WriteFreely */; };
17DF32D524C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */; };
17DF32D624C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
@@ -37,6 +50,12 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
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>"; };
1756AE7624CB2EDD00FD7257 /* PostEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostEditor.swift; sourceTree = "<group>"; };
1756AE7924CB65DF00FD7257 /* PostList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostList.swift; sourceTree = "<group>"; };
1756AE8024CB844500FD7257 /* View+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Keyboard.swift"; sourceTree = "<group>"; };
17DF328124C87D3300BCE2E3 /* WriteFreely_MultiPlatformApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteFreely_MultiPlatformApp.swift; sourceTree = "<group>"; };
17DF328224C87D3300BCE2E3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
17DF328324C87D3500BCE2E3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -56,6 +75,7 @@
17DF32C824C8854B00BCE2E3 /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = "<group>"; };
17DF32C924C8855E00BCE2E3 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
17DF32CA24C8856C00BCE2E3 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostStatusBadge.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
@@ -92,6 +112,14 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
1756AE7F24CB841200FD7257 /* Extensions */ = {
isa = PBXGroup;
children = (
1756AE8024CB844500FD7257 /* View+Keyboard.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
17DF327B24C87D3300BCE2E3 = {
isa = PBXGroup;
children = (
@@ -114,8 +142,13 @@
isa = PBXGroup;
children = (
17DF328124C87D3300BCE2E3 /* WriteFreely_MultiPlatformApp.swift */,
17DF328224C87D3300BCE2E3 /* ContentView.swift */,
17DF328324C87D3500BCE2E3 /* Assets.xcassets */,
17DF32D024C8B75C00BCE2E3 /* Model */,
1756AE7F24CB841200FD7257 /* Extensions */,
17DF32CC24C8B72300BCE2E3 /* Navigation */,
17DF32D224C8B78D00BCE2E3 /* Collection */,
17DF32D124C8B78500BCE2E3 /* Post */,
17DF32D324C8C9F600BCE2E3 /* Components */,
);
path = Shared;
sourceTree = "<group>";
@@ -173,6 +206,48 @@
name = Frameworks;
sourceTree = "<group>";
};
17DF32CC24C8B72300BCE2E3 /* Navigation */ = {
isa = PBXGroup;
children = (
17DF328224C87D3300BCE2E3 /* ContentView.swift */,
);
path = Navigation;
sourceTree = "<group>";
};
17DF32D024C8B75C00BCE2E3 /* Model */ = {
isa = PBXGroup;
children = (
);
path = Model;
sourceTree = "<group>";
};
17DF32D124C8B78500BCE2E3 /* Post */ = {
isa = PBXGroup;
children = (
1756AE6A24CB1E4B00FD7257 /* Post.swift */,
1756AE7324CB26FA00FD7257 /* PostCell.swift */,
1756AE7624CB2EDD00FD7257 /* PostEditor.swift */,
1756AE7924CB65DF00FD7257 /* PostList.swift */,
1756AE6D24CB255B00FD7257 /* PostStore.swift */,
17DF32D424C8CA3400BCE2E3 /* PostStatusBadge.swift */,
);
path = Post;
sourceTree = "<group>";
};
17DF32D224C8B78D00BCE2E3 /* Collection */ = {
isa = PBXGroup;
children = (
);
path = Collection;
sourceTree = "<group>";
};
17DF32D324C8C9F600BCE2E3 /* Components */ = {
isa = PBXGroup;
children = (
);
path = Components;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
@@ -379,7 +454,14 @@
buildActionMask = 2147483647;
files = (
17DF32AC24C87D3500BCE2E3 /* ContentView.swift in Sources */,
1756AE8124CB844500FD7257 /* View+Keyboard.swift in Sources */,
1756AE7724CB2EDD00FD7257 /* PostEditor.swift in Sources */,
17DF32D524C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */,
1756AE7A24CB65DF00FD7257 /* PostList.swift in Sources */,
17DF32AA24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */,
1756AE6E24CB255B00FD7257 /* PostStore.swift in Sources */,
1756AE6B24CB1E4B00FD7257 /* Post.swift in Sources */,
1756AE7424CB26FA00FD7257 /* PostCell.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -388,7 +470,13 @@
buildActionMask = 2147483647;
files = (
17DF32AD24C87D3500BCE2E3 /* ContentView.swift in Sources */,
1756AE7824CB2EDD00FD7257 /* PostEditor.swift in Sources */,
17DF32D624C8CA3400BCE2E3 /* PostStatusBadge.swift in Sources */,
1756AE7B24CB65DF00FD7257 /* PostList.swift in Sources */,
17DF32AB24C87D3500BCE2E3 /* WriteFreely_MultiPlatformApp.swift in Sources */,
1756AE6F24CB255B00FD7257 /* PostStore.swift in Sources */,
1756AE6C24CB1E4B00FD7257 /* Post.swift in Sources */,
1756AE7524CB26FA00FD7257 /* PostCell.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};


Notiek ielāde…
Atcelt
Saglabāt