forked from DroidKaigi/conference-app-2023
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request DroidKaigi#1268 from woxtu/timetable-grid
- Loading branch information
Showing
12 changed files
with
362 additions
and
25 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
app-ios/Modules/Sources/Assets/Resources/Icons.xcassets/list_view.imageset/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"images" : [ | ||
{ | ||
"filename" : "trailing-icon 3.pdf", | ||
"idiom" : "universal" | ||
}, | ||
{ | ||
"appearances" : [ | ||
{ | ||
"appearance" : "luminosity", | ||
"value" : "dark" | ||
} | ||
], | ||
"filename" : "trailing-icon 4.pdf", | ||
"idiom" : "universal" | ||
} | ||
], | ||
"info" : { | ||
"author" : "xcode", | ||
"version" : 1 | ||
} | ||
} |
Binary file added
BIN
+4.35 KB
...os/Modules/Sources/Assets/Resources/Icons.xcassets/list_view.imageset/trailing-icon 3.pdf
Binary file not shown.
Binary file added
BIN
+4.35 KB
...os/Modules/Sources/Assets/Resources/Icons.xcassets/list_view.imageset/trailing-icon 4.pdf
Binary file not shown.
4 changes: 2 additions & 2 deletions
4
app-ios/Modules/Sources/Model/Extension/Kotlinx_datetimeInstant.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import shared | ||
|
||
extension Kotlinx_datetimeInstant { | ||
public func toDate(calendar: Calendar = .current) -> Date { | ||
return Date(timeIntervalSince1970: TimeInterval(toEpochMilliseconds())) | ||
public func toDate() -> Date { | ||
return Date(timeIntervalSince1970: TimeInterval(epochSeconds)) | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
app-ios/Modules/Sources/Model/TimetableRoomGroupItems.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import shared | ||
|
||
public struct TimetableRoomGroupItems: Identifiable, Equatable, Hashable { | ||
public var id: String { | ||
UUID().uuidString | ||
} | ||
|
||
public var room: TimetableRoom | ||
public var items: [TimetableItemWithFavorite] | ||
|
||
public init(room: TimetableRoom, items: [TimetableItemWithFavorite]) { | ||
self.room = room | ||
self.items = items | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
...ules/Sources/Theme/Resources/Colors.xcassets/Error/Error Container.colorset/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"colors" : [ | ||
{ | ||
"color" : { | ||
"color-space" : "srgb", | ||
"components" : { | ||
"alpha" : "1.000", | ||
"blue" : "0xD6", | ||
"green" : "0xDA", | ||
"red" : "0xFF" | ||
} | ||
}, | ||
"idiom" : "universal" | ||
}, | ||
{ | ||
"appearances" : [ | ||
{ | ||
"appearance" : "luminosity", | ||
"value" : "dark" | ||
} | ||
], | ||
"color" : { | ||
"color-space" : "srgb", | ||
"components" : { | ||
"alpha" : "1.000", | ||
"blue" : "0x0A", | ||
"green" : "0x00", | ||
"red" : "0x93" | ||
} | ||
}, | ||
"idiom" : "universal" | ||
} | ||
], | ||
"info" : { | ||
"author" : "xcode", | ||
"version" : 1 | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
app-ios/Modules/Sources/Timetable/RoomType+Extension.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import shared | ||
import SwiftUI | ||
import Theme | ||
|
||
extension RoomType { | ||
func toColor() -> Color { | ||
let colorAsset = switch self { | ||
case .rooma: AssetColors.Custom.hallA | ||
case .roomb: AssetColors.Custom.hallB | ||
case .roomc: AssetColors.Custom.hallC | ||
case .roomd: AssetColors.Custom.hallD | ||
case .roome: AssetColors.Custom.hallE | ||
case .roomde: AssetColors.Custom.hallD | ||
default: AssetColors.Custom.white | ||
} | ||
return colorAsset.swiftUIColor | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
app-ios/Modules/Sources/Timetable/TimetableGridItemView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import Assets | ||
import Component | ||
import shared | ||
import SwiftUI | ||
import Theme | ||
|
||
struct TimetableGridItemView: View { | ||
let timetableItemWithFavorite: TimetableItemWithFavorite | ||
|
||
var timetableItem: TimetableItem { | ||
self.timetableItemWithFavorite.timetableItem | ||
} | ||
|
||
var body: some View { | ||
VStack(alignment: .leading, spacing: 4) { | ||
Text(timetableItem.title.currentLangTitle) | ||
.textStyle(TypographyTokens.labelLarge) | ||
.multilineTextAlignment(.leading) | ||
|
||
HStack(spacing: 3) { | ||
Assets.Icons.schedule.swiftUIImage | ||
.renderingMode(.template) | ||
Text("\(timetableItem.startsTimeString) - \(timetableItem.endsTimeString)") | ||
.textStyle(TypographyTokens.bodySmall) | ||
} | ||
|
||
Spacer() | ||
|
||
if let session = timetableItem as? TimetableItem.Session { | ||
HStack(alignment: .center, spacing: 4) { | ||
if session.speakers.count > 1 { | ||
ForEach(session.speakers, id: \.self) { speaker in | ||
speakerIcon(speaker) | ||
} | ||
} else if let speaker = session.speakers.first { | ||
speakerIcon(speaker) | ||
Text(speaker.name) | ||
.textStyle(TypographyTokens.labelMedium) | ||
.lineLimit(2) | ||
} | ||
|
||
Spacer() | ||
|
||
if session.message != nil { | ||
Assets.Icons.info.swiftUIImage | ||
.resizable() | ||
.renderingMode(.template) | ||
.frame(width: 16, height: 16) | ||
.foregroundStyle(AssetColors.Error.errorContainer.swiftUIColor) | ||
} | ||
} | ||
} | ||
} | ||
.padding(RadiusTokens.s) | ||
.frame(maxWidth: .infinity, alignment: .leading) | ||
.foregroundStyle(AssetColors.Custom.hallText.swiftUIColor) | ||
.background(timetableItem.room.type.toColor()) | ||
.clipShape(RoundedRectangle(cornerRadius: 4)) | ||
} | ||
|
||
private func speakerIcon(_ speaker: TimetableSpeaker) -> some View { | ||
CacheAsyncImage(url: URL(string: speaker.iconUrl)) { image in | ||
image.resizable() | ||
} placeholder: { | ||
Color.gray | ||
} | ||
.frame(width: 32, height: 32) | ||
.scaledToFill() | ||
.clipShape(RoundedRectangle(cornerRadius: RadiusTokens.xs)) | ||
.overlay( | ||
RoundedRectangle(cornerRadius: RadiusTokens.xs) | ||
.stroke(AssetColors.Outline.outline.swiftUIColor, lineWidth: 1) | ||
) | ||
} | ||
} | ||
|
||
#Preview { | ||
TimetableGridItemView( | ||
timetableItemWithFavorite: TimetableItemWithFavorite.companion.fake() | ||
) | ||
} |
147 changes: 147 additions & 0 deletions
147
app-ios/Modules/Sources/Timetable/TimetableGridView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import Model | ||
import shared | ||
import SwiftUI | ||
import Theme | ||
|
||
struct TimetableGridView: View { | ||
let roomHeaderSize: CGSize = .init(width: 194, height: 40) | ||
let gridSize: CGSize = .init(width: 194, height: 310) | ||
|
||
let timetableRoomGroupItems: [TimetableRoomGroupItems] | ||
let hours: [Date] | ||
|
||
init( | ||
day: DroidKaigi2023Day, | ||
timetableRoomGroupItems: [TimetableRoomGroupItems] | ||
) { | ||
self.timetableRoomGroupItems = timetableRoomGroupItems | ||
|
||
var dateComponents = Calendar(identifier: .gregorian) | ||
.dateComponents(in: TimeZone(identifier: "Asia/Tokyo")!, from: day.start.toDate()) | ||
hours = (10 ... 19).compactMap { hour in | ||
dateComponents.hour = hour | ||
dateComponents.minute = 0 | ||
return dateComponents.date | ||
} | ||
} | ||
|
||
var body: some View { | ||
ScrollView(.vertical) { | ||
HStack(alignment: .top, spacing: 0) { | ||
VStack(spacing: 0) { | ||
Text(hourFormatter.string(from: hours[0])) | ||
.textStyle(TypographyTokens.labelMedium) | ||
.frame(height: roomHeaderSize.height + 8, alignment: .bottom) | ||
|
||
ForEach(hours[1...], id: \.self) { hour in | ||
Text(hourFormatter.string(from: hour)) | ||
.textStyle(TypographyTokens.labelMedium) | ||
.frame(height: gridSize.height, alignment: .bottom) | ||
} | ||
} | ||
.padding(.leading, SpacingTokens.m) | ||
.frame(width: 75 - 8, alignment: .leading) | ||
|
||
VStack(spacing: 0) { | ||
Divider().frame(height: roomHeaderSize.height, alignment: .bottom) | ||
|
||
ForEach(hours[1...], id: \.self) { _ in | ||
Divider().frame(height: gridSize.height, alignment: .bottom) | ||
} | ||
} | ||
.frame(width: 8) | ||
|
||
ZStack(alignment: .topLeading) { | ||
VStack(spacing: 0) { | ||
Divider().frame(height: roomHeaderSize.height, alignment: .bottom) | ||
|
||
ForEach(hours[1...], id: \.self) { _ in | ||
Divider().frame(height: gridSize.height, alignment: .bottom) | ||
} | ||
} | ||
|
||
ScrollView(.horizontal) { | ||
HStack(alignment: .top, spacing: 0) { | ||
Divider() | ||
|
||
ForEach(timetableRoomGroupItems) { timetableRoomGroupItem in | ||
VStack(spacing: 0) { | ||
Text(timetableRoomGroupItem.room.name.currentLangTitle) | ||
.textStyle(TypographyTokens.titleSmall) | ||
.frame(height: roomHeaderSize.height) | ||
|
||
ZStack { | ||
ForEach(timetableRoomGroupItem.items, id: \.timetableItem.id.value) { timetableItemWithFavorite in | ||
let frame = itemFrame( | ||
timetableItemWithFavorite: timetableItemWithFavorite, | ||
startTime: hours[0], | ||
gridSize: gridSize | ||
) | ||
|
||
NavigationLink(value: TimetableRouting.session(timetableItemWithFavorite.timetableItem)) { | ||
TimetableGridItemView( | ||
timetableItemWithFavorite: timetableItemWithFavorite | ||
) | ||
} | ||
.frame(width: frame.width, height: frame.height) | ||
.position(x: frame.origin.x, y: frame.origin.y) | ||
} | ||
} | ||
} | ||
.frame(width: gridSize.width) | ||
|
||
Divider() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private func itemFrame( | ||
timetableItemWithFavorite: TimetableItemWithFavorite, | ||
startTime: Date, | ||
gridSize: CGSize | ||
) -> CGRect { | ||
let item = timetableItemWithFavorite.timetableItem | ||
|
||
let heightPerSecond = gridSize.height / (60 * 60) | ||
let itemSpacing = NSDirectionalEdgeInsets(top: 1, leading: 1, bottom: 1, trailing: 1) | ||
|
||
let itemSize = CGSize( | ||
width: gridSize.width - itemSpacing.leading - itemSpacing.trailing, | ||
height: CGFloat(item.endsAt.epochSeconds - item.startsAt.epochSeconds) * heightPerSecond | ||
- itemSpacing.top - itemSpacing.bottom | ||
) | ||
let itemPosition = CGPoint( | ||
x: itemSize.width / 2 + itemSpacing.leading, | ||
y: (CGFloat(item.startsAt.epochSeconds) - startTime.timeIntervalSince1970) * heightPerSecond | ||
+ itemSize.height / 2 + itemSpacing.top | ||
) | ||
return CGRect(origin: itemPosition, size: itemSize) | ||
} | ||
} | ||
|
||
private let hourFormatter: DateFormatter = { | ||
let formatter = DateFormatter() | ||
formatter.calendar = Calendar(identifier: .gregorian) | ||
formatter.locale = Locale(identifier: "Asia/Tokyo") | ||
formatter.dateStyle = .none | ||
formatter.timeStyle = .short | ||
return formatter | ||
}() | ||
|
||
#if DEBUG | ||
#Preview { | ||
TimetableGridView( | ||
day: Timetable.companion.fake().contents.first!.timetableItem.day!, | ||
timetableRoomGroupItems: [ | ||
TimetableRoomGroupItems( | ||
room: Timetable.companion.fake().contents.first!.timetableItem.room, | ||
items: [Timetable.companion.fake().contents.first!] | ||
), | ||
] | ||
) | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.