From 97f1a50dea61436b19daef34173cdc04ce0e54ae Mon Sep 17 00:00:00 2001 From: Daiki Matsudate Date: Wed, 20 Mar 2024 03:09:35 +0900 Subject: [PATCH] Add venue tab Signed-off-by: Daiki Matsudate --- MyLibrary/Package.swift | 2 +- MyLibrary/Sources/AppFeature/AppView.swift | 26 +- .../Sources/AppFeature/Localizable.xcstrings | 30 +++ .../GuidanceFeature/Localizable.xcstrings | 231 ++++++++++++++++-- .../ScheduleFeature/Localizable.xcstrings | 40 --- .../Sources/ScheduleFeature/Schedule.swift | 37 ++- 6 files changed, 285 insertions(+), 81 deletions(-) diff --git a/MyLibrary/Package.swift b/MyLibrary/Package.swift index b6f6d0f..dbae88a 100644 --- a/MyLibrary/Package.swift +++ b/MyLibrary/Package.swift @@ -23,6 +23,7 @@ let package = Package( .target( name: "AppFeature", dependencies: [ + "GuidanceFeature", "ScheduleFeature", "SponsorFeature", "trySwiftFeature", @@ -63,7 +64,6 @@ let package = Package( name: "ScheduleFeature", dependencies: [ "DataClient", - "GuidanceFeature", "Safari", .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), ] diff --git a/MyLibrary/Sources/AppFeature/AppView.swift b/MyLibrary/Sources/AppFeature/AppView.swift index f06f96e..635973d 100644 --- a/MyLibrary/Sources/AppFeature/AppView.swift +++ b/MyLibrary/Sources/AppFeature/AppView.swift @@ -1,8 +1,10 @@ import ComposableArchitecture import Foundation +import GuidanceFeature import ScheduleFeature import SponsorFeature import SwiftUI +import TipKit import trySwiftFeature @Reducer @@ -10,14 +12,20 @@ public struct AppReducer { @ObservableState public struct State: Equatable { var schedule = Schedule.State() + var guidance = Guidance.State() var sponsors = SponsorsList.State() var trySwift = TrySwift.State() - public init() {} + let mapTip: MapTip = .init() + + public init() { + try? Tips.configure([.displayFrequency(.immediate)]) + } } public enum Action { case schedule(Schedule.Action) + case guidance(Guidance.Action) case sponsors(SponsorsList.Action) case trySwift(TrySwift.Action) } @@ -28,6 +36,9 @@ public struct AppReducer { Scope(state: \.schedule, action: \.schedule) { Schedule() } + Scope(state: \.guidance, action: \.guidance) { + Guidance() + } Scope(state: \.sponsors, action: \.sponsors) { SponsorsList() } @@ -50,6 +61,11 @@ public struct AppView: View { .tabItem { Label(String(localized: "Schedule", bundle: .module), systemImage: "calendar") } + GuidanceView(store: store.scope(state: \.guidance, action: \.guidance)) + .tabItem { + Label(String(localized: "Venue", bundle: .module), systemImage: "map") + } + .popoverTip(store.mapTip) SponsorsListView(store: store.scope(state: \.sponsors, action: \.sponsors)) .tabItem { Label(String(localized: "Sponsors", bundle: .module), systemImage: "building.2") @@ -63,6 +79,14 @@ public struct AppView: View { } } +struct MapTip: Tip, Equatable { + var title: Text = Text("Go Shibuya First, NOT Garden", bundle: .module) + var message: Text? = Text( + "There are two kinds of Bellesalle in Shibuya. Learn how to get from Shibuya Station to \"Bellesalle Shibuya FIRST\". ", + bundle: .module) + var image: Image? = .init(systemName: "map.circle.fill") +} + #Preview { AppView( store: .init( diff --git a/MyLibrary/Sources/AppFeature/Localizable.xcstrings b/MyLibrary/Sources/AppFeature/Localizable.xcstrings index 4bcb3f6..c8032f1 100644 --- a/MyLibrary/Sources/AppFeature/Localizable.xcstrings +++ b/MyLibrary/Sources/AppFeature/Localizable.xcstrings @@ -11,6 +11,16 @@ } } }, + "Go Shibuya First, NOT Garden" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "渋谷ファーストです!ガーデンではありません!" + } + } + } + }, "Schedule" : { "localizations" : { "ja" : { @@ -30,6 +40,26 @@ } } } + }, + "There are two kinds of Bellesalle in Shibuya. Learn how to get from Shibuya Station to \"Bellesalle Shibuya FIRST\". " : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "渋谷にはベルサールが2つあります。渋谷駅からベルサール渋谷ファーストへの行き方を確認しましょう。" + } + } + } + }, + "Venue" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "会場" + } + } + } } }, "version" : "1.0" diff --git a/MyLibrary/Sources/GuidanceFeature/Localizable.xcstrings b/MyLibrary/Sources/GuidanceFeature/Localizable.xcstrings index 8dc0b57..96caccd 100644 --- a/MyLibrary/Sources/GuidanceFeature/Localizable.xcstrings +++ b/MyLibrary/Sources/GuidanceFeature/Localizable.xcstrings @@ -5,25 +5,74 @@ }, "Belle Salle Shibuya First" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベルサール渋谷ファースト" + } + } + } }, "Belle Salle Shibuya First address" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "〒150-0011\n東京都渋谷区東1-2-20 住友不動産渋谷ファーストタワーB1・2F ベルサール渋谷ファースト" + } + } + } }, "Directions" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "道順" + } + } + } }, "Exit B1" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "B1出口" + } + } + } }, "Exit C1" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CI出口" + } + } + } }, "JR Shibuya" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "JR渋谷駅" + } + } + } }, "JR Shibuya Station East Exit" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "JR渋谷駅東口" + } + } + } }, "jr-1" : { "localizations" : { @@ -32,6 +81,12 @@ "state" : "translated", "value" : "After get off train, exit from Shibuya station South gate on 1st floor (Downstair), then go to East exit." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "電車を降りたら、階段を降りて南改札から出てください。そのあと東口から出ます。" + } } } }, @@ -42,6 +97,12 @@ "state" : "translated", "value" : "After exit from East Exit, pass through left side of downstairs." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "東口を出たら、エスカレータの左側を抜けます。" + } } } }, @@ -52,6 +113,12 @@ "state" : "translated", "value" : "Then, turn right at behind of pillar." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "柱の後ろで右折します。" + } } } }, @@ -62,6 +129,12 @@ "state" : "translated", "value" : "Go straight along with bus stops." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "バス停の横を直進します。" + } } } }, @@ -72,6 +145,12 @@ "state" : "translated", "value" : "Go upstairs on the left hand." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "左手の階段を登ります。" + } } } }, @@ -82,6 +161,12 @@ "state" : "translated", "value" : "Go diagonally right, then turn left." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "斜め右方向へ進み、そのあと左折します。" + } } } }, @@ -92,6 +177,12 @@ "state" : "translated", "value" : "Go downstairs left of police office." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "渋谷警察署横の階段を降ります。" + } } } }, @@ -102,6 +193,12 @@ "state" : "translated", "value" : "Go straight." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "直進します。" + } } } }, @@ -112,6 +209,12 @@ "state" : "translated", "value" : "Keep going. You can get something to eat and drink at convenience store." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "そのまま直進します。途中コンビニがあるので食料を調達しても良いでしょう。" + } } } }, @@ -122,6 +225,12 @@ "state" : "translated", "value" : "Almost there!" } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "もうすぐ到着です!" + } } } }, @@ -132,14 +241,34 @@ "state" : "translated", "value" : "You arrived and enter to entrance." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "到着しました。入り口からお入りください。" + } } } }, "Lines" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "路線" + } + } + } }, "Metro Shibuya" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "東京メトロ渋谷" + } + } + } }, "metro-1" : { "localizations" : { @@ -148,14 +277,34 @@ "state" : "translated", "value" : "After getting off Tokyo Metro, head to C1 Exit. Then turn left" } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "地下鉄を降りたらC1出口に向かいます。地上に出たら左折します。" + } } } }, "Omote-sando" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "表参道駅" + } + } + } }, "Omote-sando Station B1 Exit" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "表参道駅B1出口" + } + } + } }, "omotesando-1" : { "localizations" : { @@ -164,26 +313,74 @@ "state" : "translated", "value" : "Highly recommend using map with button above." } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "上のボタンからマップを開くことをお勧めします。" + } } } }, "Open Map" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapを開く" + } + } + } }, "Our venue is Belle Salle Shibuya FIRST, not garden. Make sure there are two belle salle hall in Shibuya." : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "会場はベルサール渋谷ファーストです。渋谷ガーデンもあるのでお間違えないようにしてください。" + } + } + } }, "Shibuya Station C1 Exit" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "渋谷駅C1出口" + } + } + } }, "Shibuya Station East Exit" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "渋谷駅東口" + } + } + } }, "Venue" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "会場" + } + } + } }, "Warning" : { - + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "注意" + } + } + } } }, "version" : "1.0" diff --git a/MyLibrary/Sources/ScheduleFeature/Localizable.xcstrings b/MyLibrary/Sources/ScheduleFeature/Localizable.xcstrings index 0473252..faa0460 100644 --- a/MyLibrary/Sources/ScheduleFeature/Localizable.xcstrings +++ b/MyLibrary/Sources/ScheduleFeature/Localizable.xcstrings @@ -370,33 +370,6 @@ } } }, - "Go Shibuya First, NOT Garden" : { - "localizations" : { - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "渋谷ガーデンではありません!渋谷ファーストに向かいましょう!" - } - } - } - }, - "Guidance URL" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "https://twitter.com/tryswiftconf/status/1108474796788977664" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "https://twitter.com/tryswiftconf/status/1108474796788977664" - } - } - } - }, "Have you ever built a socket communication app? The sense of accomplishment when you delve into layers that aren't typically touched in your everyday app development and actually utilize them in an app is truly exceptional. However, creating a socket communication app requires knowledge of POSIX sockets, and for complex communications, you need to implement parallel processing.\nIn this talk, we'll implement socket communication and parallel processing in Swift. With Swift, which we're all familiar with, you can easily venture into unfamiliar territories, and there are several instances where you can leverage Swift's capabilities through implementing socket communication and parallel processing. Take this opportunity to enjoy learning socket communication and rediscover the charm of Swift!" : { "extractionState" : "manual", "localizations" : { @@ -1056,16 +1029,6 @@ } } }, - "There are two kinds of Bellesalle in Shibuya. Learn how to get from Shibuya Station to \"Bellesalle Shibuya FIRST\". " : { - "localizations" : { - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "渋谷にはベルサールが2つあります。ベルサール渋谷ファーストへの行き方を確認しましょう。" - } - } - } - }, "This talk will take you through a handful of topics that either make a big or a small win for you or your application, mostly with the new iOS, SwiftUI and Xcode features. Want to give your app some extra sparkle? Let's chat about cool shortcuts, playful animations, and the new tricks from SwiftUI 5 like observation framework, enhanced phased animations, updates to scrollview and new gestures. Come join this session and take home some big and small wins with you." : { "extractionState" : "manual", "localizations" : { @@ -1142,9 +1105,6 @@ } } } - }, - "Venue" : { - }, "visionOS represents a new paradigm known as Spatial Computing, which is expected to significantly increase the number of experiences available on headset devices. This talk will introduce the basic concepts of visionOS and provide explanations on implementing sample apps using Swift. Given that many may not have developed for visionOS or have experience with SwiftUI or RealityKit, understanding every aspect is not necessary. By learning the basic architecture, you can start implementing spatial applications. The session will cover the process from project creation to operation, with spatial expressions explained through demos and videos. Let's create experiences in Spatial Computing together!" : { "extractionState" : "manual", diff --git a/MyLibrary/Sources/ScheduleFeature/Schedule.swift b/MyLibrary/Sources/ScheduleFeature/Schedule.swift index 95266fd..c45438a 100644 --- a/MyLibrary/Sources/ScheduleFeature/Schedule.swift +++ b/MyLibrary/Sources/ScheduleFeature/Schedule.swift @@ -1,7 +1,6 @@ import ComposableArchitecture import DataClient import Foundation -import GuidanceFeature import Safari import SharedModels import SwiftUI @@ -35,9 +34,7 @@ public struct Schedule { var workshop: Conference? @Presents var destination: Destination.State? - public init() { - try? Tips.configure([.displayFrequency(.immediate)]) - } + public init() {} } public enum Action: BindableAction, ViewAction { @@ -59,9 +56,7 @@ public struct Schedule { } @Reducer(state: .equatable) - public enum Destination { - case guidance(Guidance) - } + public enum Destination {} @Dependency(DataClient.self) var dataClient @Dependency(\.openURL) var openURL @@ -81,7 +76,6 @@ public struct Schedule { let workshop = try dataClient.fetchWorkshop() return .init(day1: day1, day2: day2, workshop: workshop) })) - return .none case let .view(.disclosureTapped(session)): guard let description = session.description, let speakers = session.speakers else { return .none @@ -97,6 +91,19 @@ public struct Schedule { ) ) return .none + case let .fetchResponse(.success(response)): + state.day1 = response.day1 + state.day2 = response.day2 + state.workshop = response.workshop + return .none + case let .fetchResponse(.failure(error as DecodingError)): + assertionFailure(error.localizedDescription) + return .none + case let .fetchResponse(.failure(error)): + print(error) // TODO: replace to Logger API + return .none + case .binding, .path, .destination: + return .none } } .forEach(\.path, action: \.path) @@ -109,8 +116,6 @@ public struct ScheduleView: View { @Bindable public var store: StoreOf - let mapTip: MapTip = .init() - public init(store: StoreOf) { self.store = store } @@ -126,10 +131,6 @@ public struct ScheduleView: View { } } } - .sheet(item: $store.scope(state: \.destination?.guidance, action: \.destination.guidance)) { - sheetStore in - GuidanceView(store: sheetStore) - } } @ViewBuilder @@ -281,14 +282,6 @@ public struct ScheduleView: View { } } -struct MapTip: Tip, Equatable { - var title: Text = Text("Go Shibuya First, NOT Garden", bundle: .module) - var message: Text? = Text( - "There are two kinds of Bellesalle in Shibuya. Learn how to get from Shibuya Station to \"Bellesalle Shibuya FIRST\". ", - bundle: .module) - var image: Image? = .init(systemName: "map.circle.fill") -} - #Preview { ScheduleView( store: .init(