Skip to content

Commit

Permalink
List emails
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurentTreguier committed Nov 12, 2024
1 parent eb408d7 commit f250378
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 7 deletions.
16 changes: 16 additions & 0 deletions Fyreplace.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
4D13AF752C492F4500845FDB /* Config.sh in Resources */ = {isa = PBXBuildFile; fileRef = 4D13AF742C492F4500845FDB /* Config.sh */; };
4D13AF7B2C4E8F4200845FDB /* EnvironmentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D13AF7A2C4E8F4200845FDB /* EnvironmentPicker.swift */; };
4D13AF812C4E907200845FDB /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D13AF802C4E907200845FDB /* Environment.swift */; };
4D30D7232CCFAF4E0071B03F /* EmailsScreenProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D30D7222CCFAF440071B03F /* EmailsScreenProtocol.swift */; };
4D30D7262CCFB01B0071B03F /* EmailsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D30D7252CCFB0170071B03F /* EmailsScreen.swift */; };
4D30DA5F2C986B6C00499450 /* Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D30DA5E2C986B6C00499450 /* Avatar.swift */; };
4D30DA612C98706C00499450 /* Placeholders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D30DA602C98706C00499450 /* Placeholders.swift */; };
4D351AEB2CA6BD45002EEB8F /* SettingsScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D351AEA2CA6BD3A002EEB8F /* SettingsScreenTests.swift */; };
Expand Down Expand Up @@ -55,6 +57,7 @@
4DB10B502C4FEBFC00634BF6 /* HelpCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB10B4F2C4FEBFC00634BF6 /* HelpCommands.swift */; };
4DB2E36D2C416611007F958D /* SubmitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB2E36C2C416611007F958D /* SubmitButton.swift */; };
4DB2E36F2C418F5C007F958D /* DynamicForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB2E36E2C418F5C007F958D /* DynamicForm.swift */; };
4DB367BC2CE364C300853CFE /* EmailsScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB367BB2CE364BC00853CFE /* EmailsScreenTests.swift */; };
4DC5B1CA2C6FA23000B75A07 /* LoginScreenProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC5B1C92C6FA23000B75A07 /* LoginScreenProtocol.swift */; };
4DC5B1CD2C6FA28E00B75A07 /* RegisterScreenProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC5B1CC2C6FA28E00B75A07 /* RegisterScreenProtocol.swift */; };
4DC5B1CF2C6FA2BE00B75A07 /* MainViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DC5B1CE2C6FA2BE00B75A07 /* MainViewProtocol.swift */; };
Expand All @@ -71,6 +74,7 @@
4DE785952C8B17AE000EC4E5 /* SubmitOrCancel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DE785942C8B17AE000EC4E5 /* SubmitOrCancel.swift */; };
4DFB90702C5908DE00D4DABF /* LoginScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DFB906F2C5908DE00D4DABF /* LoginScreenTests.swift */; };
4DFB90762C59173C00D4DABF /* RegisterScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DFB90752C59173C00D4DABF /* RegisterScreenTests.swift */; };
4DFDBC102CD1080000CCDA4A /* DynamicList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DFDBC0E2CD107FC00CCDA4A /* DynamicList.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -102,6 +106,8 @@
4D13AF742C492F4500845FDB /* Config.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = Config.sh; sourceTree = "<group>"; };
4D13AF7A2C4E8F4200845FDB /* EnvironmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentPicker.swift; sourceTree = "<group>"; };
4D13AF802C4E907200845FDB /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; };
4D30D7222CCFAF440071B03F /* EmailsScreenProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailsScreenProtocol.swift; sourceTree = "<group>"; };
4D30D7252CCFB0170071B03F /* EmailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailsScreen.swift; sourceTree = "<group>"; };
4D30DA5E2C986B6C00499450 /* Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatar.swift; sourceTree = "<group>"; };
4D30DA602C98706C00499450 /* Placeholders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Placeholders.swift; sourceTree = "<group>"; };
4D351AEA2CA6BD3A002EEB8F /* SettingsScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -153,6 +159,7 @@
4DB10B4F2C4FEBFC00634BF6 /* HelpCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpCommands.swift; sourceTree = "<group>"; };
4DB2E36C2C416611007F958D /* SubmitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmitButton.swift; sourceTree = "<group>"; };
4DB2E36E2C418F5C007F958D /* DynamicForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicForm.swift; sourceTree = "<group>"; };
4DB367BB2CE364BC00853CFE /* EmailsScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailsScreenTests.swift; sourceTree = "<group>"; };
4DBF0BD32C9DB2E500E797BF /* .ios-test-model */ = {isa = PBXFileReference; lastKnownFileType = text; path = ".ios-test-model"; sourceTree = "<group>"; };
4DC5B1C92C6FA23000B75A07 /* LoginScreenProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenProtocol.swift; sourceTree = "<group>"; };
4DC5B1CC2C6FA28E00B75A07 /* RegisterScreenProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterScreenProtocol.swift; sourceTree = "<group>"; };
Expand All @@ -175,6 +182,7 @@
4DF3737F2C99C23D0008AB04 /* .swift-format */ = {isa = PBXFileReference; explicitFileType = text.json; path = ".swift-format"; sourceTree = "<group>"; };
4DFB906F2C5908DE00D4DABF /* LoginScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenTests.swift; sourceTree = "<group>"; };
4DFB90752C59173C00D4DABF /* RegisterScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterScreenTests.swift; sourceTree = "<group>"; };
4DFDBC0E2CD107FC00CCDA4A /* DynamicList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicList.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -250,6 +258,8 @@
4D4D39492C086DA2007196D2 /* PublishedScreen.swift */,
4DC5B1F32C7A137B00B75A07 /* SettingsScreenProtocol.swift */,
4D54C9702BF4EA15001DE071 /* SettingsScreen.swift */,
4D30D7222CCFAF440071B03F /* EmailsScreenProtocol.swift */,
4D30D7252CCFB0170071B03F /* EmailsScreen.swift */,
4DC5B1C92C6FA23000B75A07 /* LoginScreenProtocol.swift */,
4D9B3B372C334B3A00A8F7AD /* LoginScreen.swift */,
4DC5B1CC2C6FA28E00B75A07 /* RegisterScreenProtocol.swift */,
Expand Down Expand Up @@ -401,6 +411,7 @@
isa = PBXGroup;
children = (
4DB2E36E2C418F5C007F958D /* DynamicForm.swift */,
4DFDBC0E2CD107FC00CCDA4A /* DynamicList.swift */,
4DB2E36C2C416611007F958D /* SubmitButton.swift */,
4DE785942C8B17AE000EC4E5 /* SubmitOrCancel.swift */,
4D13AF7A2C4E8F4200845FDB /* EnvironmentPicker.swift */,
Expand Down Expand Up @@ -457,6 +468,7 @@
children = (
4D351AEC2CA6BE27002EEB8F /* FakeScreenBase.swift */,
4D351AEA2CA6BD3A002EEB8F /* SettingsScreenTests.swift */,
4DB367BB2CE364BC00853CFE /* EmailsScreenTests.swift */,
4DFB906F2C5908DE00D4DABF /* LoginScreenTests.swift */,
4DFB90752C59173C00D4DABF /* RegisterScreenTests.swift */,
);
Expand Down Expand Up @@ -637,6 +649,7 @@
files = (
4D40ACB42CC3ECBC00B26FDF /* OpenAPI.swift in Sources */,
4DA04EE22CAEEAD800B70D73 /* Foundation.swift in Sources */,
4D30D7232CCFAF4E0071B03F /* EmailsScreenProtocol.swift in Sources */,
4D9B3B3D2C34B13E00A8F7AD /* LogoHeader.swift in Sources */,
4DA7BFBB2C5FDEC1005CC4FF /* FakeClient.swift in Sources */,
4D4AF71C2C7CE72900621FF3 /* Tokens.swift in Sources */,
Expand All @@ -653,6 +666,7 @@
4DE785952C8B17AE000EC4E5 /* SubmitOrCancel.swift in Sources */,
4DE785882C88F392000EC4E5 /* HTTPTypes.swift in Sources */,
4D6A24DD2CAC193F001B4435 /* EditableAvatar.swift in Sources */,
4DFDBC102CD1080000CCDA4A /* DynamicList.swift in Sources */,
4D51F2802C621ADB0018E76E /* ViewProtocol.swift in Sources */,
4DC5B1D92C720B9800B75A07 /* AuthenticationMiddleware.swift in Sources */,
4D9B3B382C334B3A00A8F7AD /* LoginScreen.swift in Sources */,
Expand All @@ -667,6 +681,7 @@
4DB2E36F2C418F5C007F958D /* DynamicForm.swift in Sources */,
4DCE062B2C08E5E200F69AF1 /* CompactNavigation.swift in Sources */,
4DCE062D2C08E65300F69AF1 /* RegularNavigation.swift in Sources */,
4D30D7262CCFB01B0071B03F /* EmailsScreen.swift in Sources */,
4D54C9692BF4E8F4001DE071 /* FeedScreen.swift in Sources */,
4DC5B1F42C7A137B00B75A07 /* SettingsScreenProtocol.swift in Sources */,
4DC5B1D62C6FEA2100B75A07 /* Keychain.swift in Sources */,
Expand All @@ -691,6 +706,7 @@
files = (
4D351AEB2CA6BD45002EEB8F /* SettingsScreenTests.swift in Sources */,
4D351AED2CA6BE2D002EEB8F /* FakeScreenBase.swift in Sources */,
4DB367BC2CE364C300853CFE /* EmailsScreenTests.swift in Sources */,
4D5348612C6646F80001EFDE /* StoringEventBus.swift in Sources */,
4DFB90702C5908DE00D4DABF /* LoginScreenTests.swift in Sources */,
4DFB90762C59173C00D4DABF /* RegisterScreenTests.swift in Sources */,
Expand Down
3 changes: 3 additions & 0 deletions Fyreplace/Extensions/OpenAPI.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
extension Components.Schemas.User {
static let maxBioSize = 3000
}

extension Components.Schemas.Email: Identifiable {
}
13 changes: 10 additions & 3 deletions Fyreplace/Fakes/FakeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ extension FakeClient {
func countEmails(_: Operations.countEmails.Input) async throws
-> Operations.countEmails.Output
{
fatalError("Not implemented")
let emails = try await listEmails(.init()).ok.body.json
return .ok(.init(body: .json(Int64(emails.count))))
}

func createEmail(_: Operations.createEmail.Input) async throws
Expand All @@ -108,10 +109,16 @@ extension FakeClient {
fatalError("Not implemented")
}

func listEmails(_: Operations.listEmails.Input) async throws
func listEmails(_ input: Operations.listEmails.Input) async throws
-> Operations.listEmails.Output
{
fatalError("Not implemented")
return switch input.query.page {
case nil, 0:
.ok(.init(body: .json([.make(main: true), .make(), .make()])))

default:
.ok(.init(body: .json([])))
}
}

func setMainEmail(_: Operations.setMainEmail.Input) async throws
Expand Down
14 changes: 13 additions & 1 deletion Fyreplace/Fakes/Placeholders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extension Components.Schemas.User {
}

static func make(named username: String) -> Self {
return Self(
return .init(
id: .randomUuid,
dateCreated: .now,
username: username,
Expand All @@ -17,3 +17,15 @@ extension Components.Schemas.User {
)
}
}

extension Components.Schemas.Email {
static func make(verified: Bool = true, main: Bool = false) -> Self {
let id = String.randomUuid
return .init(
id: id,
email: "\(id)@example.org",
verified: verified,
main: main
)
}
}
40 changes: 40 additions & 0 deletions Fyreplace/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@
}
}
},
"Emails.Main" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Main address"
}
}
}
},
"Environment.Default" : {
"localizations" : {
"en" : {
Expand Down Expand Up @@ -364,6 +374,16 @@
}
}
},
"Main.Emails" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Emails"
}
}
}
},
"Main.Feed" : {
"localizations" : {
"en" : {
Expand Down Expand Up @@ -694,6 +714,26 @@
}
}
},
"Settings.Emails" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add or remove email addresses"
}
}
}
},
"Settings.Emails.Header" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Emails"
}
}
}
},
"Settings.Error.ContentTooLarge.Message" : {
"localizations" : {
"en" : {
Expand Down
20 changes: 20 additions & 0 deletions Fyreplace/Views/Forms/DynamicList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import SwiftUI

struct DynamicList<Content>: View where Content: View {
@ViewBuilder
let content: () -> Content

var body: some View {
#if os(macOS)
DynamicForm {
List {
content()
}
}
#else
List {
content()
}
#endif
}
}
7 changes: 5 additions & 2 deletions Fyreplace/Views/Navigation/Destination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ enum Destination: String, Identifiable {
case drafts
case published
case settings
case emails
case login
case register

Expand Down Expand Up @@ -39,6 +40,8 @@ enum Destination: String, Identifiable {
"Main.Published"
case .settings:
"Main.Settings"
case .emails:
"Main.Emails"
case .login:
"Main.Login"
case .register:
Expand All @@ -60,7 +63,7 @@ enum Destination: String, Identifiable {
"archivebox"
case .settings:
"person.crop.circle"
case .login, .register:
default:
""
}
}
Expand Down Expand Up @@ -102,7 +105,7 @@ enum Destination: String, Identifiable {
.init("5")
case .settings:
.init("6")
case .login, .register:
default:
nil
}
}
Expand Down
41 changes: 41 additions & 0 deletions Fyreplace/Views/Screens/EmailsScreen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import SwiftUI

struct EmailsScreen: View, EmailsScreenProtocol {
@EnvironmentObject
var eventBus: EventBus

@Environment(\.api)
var api: any APIProtocol

@State
var emails: [Components.Schemas.Email] = []

var body: some View {
DynamicList {
ForEach(emails) { email in
LabeledContent {
} label: {
Text(verbatim: email.email)

if email.main {
Text("Emails.Main")
} else {
Spacer()
}
}
}
}
.navigationTitle(Destination.emails.titleKey)
.onAppear {
Task {
await loadEmails()
}
}
}
}

#Preview {
NavigationStack {
EmailsScreen()
}
}
43 changes: 43 additions & 0 deletions Fyreplace/Views/Screens/EmailsScreenProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@MainActor
protocol EmailsScreenProtocol: APIViewProtocol {
var emails: [Components.Schemas.Email] { get nonmutating set }
}

@MainActor
extension EmailsScreenProtocol {
func loadEmails() async {
emails.removeAll()
var page: Int32 = 0

while await loadEmails(at: page) {
page += 1
}
}

func loadEmails(at page: Int32) async -> Bool {
var hasMore = false

await call {
let response = try await api.listEmails(query: .init(page: page))

switch response {
case let .ok(ok):
switch ok.body {
case let .json(json):
hasMore = !json.isEmpty
emails.append(contentsOf: json)
}

return nil

case .unauthorized:
return .authorizationIssue()

case .forbidden, .default:
return .error()
}
}

return hasMore
}
}
2 changes: 2 additions & 0 deletions Fyreplace/Views/Screens/Screen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ struct Screen: View {
PublishedScreen()
case .settings:
SettingsScreen()
case .emails:
EmailsScreen()
case .login:
LoginScreen()
case .register:
Expand Down
6 changes: 6 additions & 0 deletions Fyreplace/Views/Screens/SettingsScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ struct SettingsScreen: View, SettingsScreenProtocol {
Text("Settings.Bio.Footer:\(bio.count),\(Components.Schemas.User.maxBioSize)")
}

Section("Settings.Emails.Header") {
NavigationLink("Settings.Emails") {
Screen(destination: .emails)
}
}

Section("Settings.About.Header") {
Link(destination: config.app.info.website) {
Label("App.Help.Website", systemImage: "safari")
Expand Down
Loading

0 comments on commit f250378

Please sign in to comment.