Skip to content

Commit ced6c7f

Browse files
committed
Convert selectedTimeZone to a TimeZone instead of String. Add Copy button to Myke Mode
1 parent 0a49191 commit ced6c7f

8 files changed

Lines changed: 119 additions & 56 deletions

Elsewhen/ContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct ContentView: View {
1717
@State private var selectedTab: Int = 1
1818

1919
@State private var selectedDate = Date()
20-
@State private var selectedTimeZone: String = TimeZone.current.identifier
20+
@State private var selectedTimeZone: TimeZone = TimeZone.current
2121

2222
var body: some View {
2323
TabView(selection: $selectedTab) {

Elsewhen/Elements/DateTimeSelection.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import SwiftUI
1010
struct DateTimeSelection: View {
1111
@Binding var selectedFormatStyle: DateFormat
1212
@Binding var selectedDate: Date
13-
@Binding var selectedTimeZone: String
13+
@Binding var selectedTimeZone: TimeZone
1414

1515
var body: some View {
1616
Group {
@@ -38,7 +38,7 @@ struct DateTimeSelection: View {
3838

3939
Button(action: {
4040
self.selectedDate = Date()
41-
self.selectedTimeZone = TimeZone.current.identifier
41+
self.selectedTimeZone = TimeZone.current
4242
}) {
4343
Text("Reset")
4444
}

Elsewhen/Elements/DateTimeZonePicker.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,34 @@ import SwiftUI
1010
struct DateTimeZonePicker: View {
1111

1212
@Binding var selectedDate: Date
13-
@Binding var selectedTimeZone: String
13+
@Binding var selectedTimeZone: TimeZone
14+
15+
var showDate: Bool = true
1416

1517
var body: some View {
1618
Group {
17-
DatePicker("Date", selection: $selectedDate)
18-
.datePickerStyle(.graphical)
19-
.padding(.top)
19+
20+
if showDate {
21+
DatePicker("Date", selection: $selectedDate)
22+
.datePickerStyle(.graphical)
23+
.padding(.top)
24+
} else {
25+
HStack {
26+
Text("Time")
27+
.fontWeight(.semibold)
28+
.padding(.horizontal, 8)
29+
Spacer()
30+
DatePicker("Time", selection: $selectedDate, displayedComponents: [.hourAndMinute])
31+
.datePickerStyle(.graphical)
32+
}
33+
}
2034

2135
HStack {
2236
Text("Time zone")
2337
.fontWeight(.semibold)
2438
Spacer()
2539
NavigationLink(destination: TimezoneChoiceView(selectedTimeZone: $selectedTimeZone)) {
26-
Text(selectedTimeZone.replacingOccurrences(of: "_", with: " "))
40+
Text(selectedTimeZone.identifier.replacingOccurrences(of: "_", with: " "))
2741
.foregroundColor(.primary)
2842
.padding(.vertical, 8)
2943
.padding(.horizontal, 10)
@@ -42,6 +56,6 @@ struct DateTimeZonePicker: View {
4256

4357
struct DateTimeZonePicker_Previews: PreviewProvider {
4458
static var previews: some View {
45-
DateTimeZonePicker(selectedDate: .constant(Date()), selectedTimeZone: .constant("Europe/London"))
59+
DateTimeZonePicker(selectedDate: .constant(Date()), selectedTimeZone: .constant(TimeZone(identifier: "Europe/London")!), showDate: false)
4660
}
4761
}

Elsewhen/Elements/ResultSheet.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import MobileCoreServices
1111
struct ResultSheet: View {
1212
// MARK: Parameters
1313
let selectedDate: Date
14-
let selectedTimeZone: String
14+
let selectedTimeZone: TimeZone
1515
let discordFormat: String
1616
@Binding var showLocalTimeInstead: Bool
1717
@Binding var selectedFormatStyle: DateFormat
@@ -27,27 +27,26 @@ struct ResultSheet: View {
2727
var body: some View {
2828
VStack {
2929
VStack {
30-
let selectedTimezoneIdentifier = TimeZone(identifier: selectedTimeZone)!
3130

3231
if !showLocalTimeInstead {
3332

34-
Text(format(date: convertSelectedDate(from: selectedTimezoneIdentifier, to: selectedTimezoneIdentifier), in: selectedTimezoneIdentifier, with: selectedFormatStyle.code))
33+
Text(format(date: convertSelectedDate(from: selectedTimeZone, to: selectedTimeZone), in: selectedTimeZone, with: selectedFormatStyle.code))
3534
.multilineTextAlignment(.center)
3635
.font(.headline)
3736
.foregroundColor(.primary)
3837

3938
} else {
4039

41-
Text(format(date: convertSelectedDate(from: selectedTimezoneIdentifier, to: TimeZone.current), in: TimeZone.current, with: selectedFormatStyle.code))
40+
Text(format(date: convertSelectedDate(from: selectedTimeZone, to: TimeZone.current), in: TimeZone.current, with: selectedFormatStyle.code))
4241
.multilineTextAlignment(.center)
4342
.font(.headline)
4443
.foregroundColor(.primary)
4544

4645
}
4746

48-
if selectedTimezoneIdentifier != TimeZone.current {
47+
if selectedTimeZone != TimeZone.current {
4948

50-
Text(showLocalTimeInstead ? "for you (\(formatTimeZoneName(TimeZone.current.identifier)))" : "in \(formatTimeZoneName(selectedTimeZone))")
49+
Text(showLocalTimeInstead ? "for you (\(TimeZone.current.friendlyName))" : "in \(selectedTimeZone.friendlyName)")
5150
.foregroundColor(.secondary)
5251

5352
}

Elsewhen/Elements/TimezoneChoiceView.swift

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,13 @@ struct TimezoneChoiceView: View {
1111

1212
@Environment(\.presentationMode) private var presentationMode
1313

14-
@Binding var selectedTimeZone: String
14+
@Binding var selectedTimeZone: TimeZone
1515
@State private var searchTerm: String = ""
1616

1717
private var filteredTimeZones: [TimeZone] {
18-
let st = searchTerm.trimmingCharacters(in: .whitespaces).lowercased().replacingOccurrences(of: " ", with: "_")
1918
return TimeZone.knownTimeZoneIdentifiers.compactMap { tz in
2019
TimeZone(identifier: tz)
21-
}.filter { tz in
22-
if st == "" {
23-
return true
24-
}
25-
if tz.identifier.lowercased().contains(st) {
26-
return true
27-
}
28-
if let abbreviation = tz.abbreviation(), abbreviation.lowercased().contains(st) {
29-
return true
30-
}
31-
return false
32-
}
20+
}.filter { $0.matches(searchTerm: searchTerm) }
3321
}
3422

3523
var body: some View {
@@ -38,18 +26,18 @@ struct TimezoneChoiceView: View {
3826
.padding(.horizontal, -10)
3927
ForEach(filteredTimeZones, id: \.self) { tz in
4028
Button(action: {
41-
self.selectedTimeZone = tz.identifier
29+
self.selectedTimeZone = tz
4230
presentationMode.wrappedValue.dismiss()
4331
}) {
4432
HStack {
45-
Text(tz.identifier.replacingOccurrences(of: "_", with: " "))
33+
Text(tz.friendlyName)
4634
Spacer()
4735
if let abbreviation = tz.abbreviation() {
4836
Text(abbreviation)
49-
.foregroundColor(selectedTimeZone == tz.identifier ? .accentColor : .secondary)
37+
.foregroundColor(selectedTimeZone == tz ? .accentColor : .secondary)
5038
}
5139
}
52-
.foregroundColor(selectedTimeZone == tz.identifier ? .accentColor : .primary)
40+
.foregroundColor(selectedTimeZone == tz ? .accentColor : .primary)
5341
}
5442
}
5543
}
@@ -60,6 +48,6 @@ struct TimezoneChoiceView: View {
6048

6149
struct TimezoneChoiceView_Previews: PreviewProvider {
6250
static var previews: some View {
63-
TimezoneChoiceView(selectedTimeZone: .constant("Europe/London"))
51+
TimezoneChoiceView(selectedTimeZone: .constant(TimeZone(identifier: "Europe/London")!))
6452
}
6553
}

Elsewhen/Helpers/DateHelpers.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,26 @@ func convert(date: Date, from initialTimezone: TimeZone, to targetTimezone: Time
3838
return date.addingTimeInterval(offset)
3939
}
4040

41-
func formatTimeZoneName(_ zone: String) -> String {
42-
zone.replacingOccurrences(of: "_", with: " ")
41+
extension TimeZone {
42+
43+
var friendlyName: String {
44+
identifier.replacingOccurrences(of: "_", with: " ")
45+
}
46+
47+
func matches(searchTerm: String) -> Bool {
48+
let st = searchTerm.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
49+
if st == "" {
50+
return true
51+
}
52+
if identifier.lowercased().contains(st) || friendlyName.lowercased().contains(st) {
53+
return true
54+
}
55+
if let abbreviation = abbreviation(), abbreviation.lowercased().contains(st) {
56+
return true
57+
}
58+
return false
59+
}
60+
4361
}
4462

4563
func format(date: Date, in timezone: TimeZone, with formatCode: FormatCode) -> String {

Elsewhen/Views/MykeMode.swift

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
//
77

88
import SwiftUI
9+
import MobileCoreServices
910

1011
struct MykeMode: View {
1112

12-
// @State private var selectedDate: Date = Date(timeIntervalSince1970: TimeInterval(1639239835))
13-
// @State private var selectedDate: Date = Date(timeIntervalSince1970: TimeInterval(1631892600))
13+
// @State private var selectedDate: Date = Date(timeIntervalSince1970: TimeInterval(1639239835))
14+
// @State private var selectedDate: Date = Date(timeIntervalSince1970: TimeInterval(1631892600))
1415

1516
@Binding var selectedDate: Date
16-
@Binding var selectedTimeZone: String
17+
@Binding var selectedTimeZone: TimeZone
1718

1819
@State private var selectedFormatStyle: DateFormat = dateFormats[0]
1920

@@ -24,25 +25,43 @@ struct MykeMode: View {
2425
TimeZone(identifier: "Europe/Budapest")!,
2526
]
2627

28+
@State private var showCopied: Bool = false
29+
30+
#if os(iOS)
31+
// @State private var selectionFeedbackGenerator: UISelectionFeedbackGenerator? = nil
32+
@State private var notificationFeedbackGenerator: UINotificationFeedbackGenerator? = nil
33+
#endif
34+
2735
func selectedTimeInZone(_ zone: TimeZone) -> String {
2836
let df = DateFormatter()
2937
df.dateStyle = .none
3038
df.timeStyle = .short
3139
df.timeZone = zone
3240
df.locale = Locale(identifier: "en/us")
33-
return df.string(from: convert(date: selectedDate, from: TimeZone(identifier: selectedTimeZone)!, to: TimeZone.current))
41+
return df.string(from: convert(date: selectedDate, from: selectedTimeZone, to: TimeZone.current))
42+
}
43+
44+
func generateTimesAndFlagsText() -> String {
45+
var text = "\n"
46+
for tz in selectedTimeZones {
47+
let abbr = fudgedAbbreviation(for: tz) ?? ""
48+
text += "\(flagForTimeZone(tz)) - \(selectedTimeInZone(tz)) \(abbr)\n"
49+
}
50+
return text
3451
}
3552

3653
var body: some View {
3754

3855
NavigationView {
3956

4057
ScrollView(showsIndicators: true) {
41-
DateTimeZonePicker(selectedDate: $selectedDate, selectedTimeZone: $selectedTimeZone)
4258

43-
Text("Time Zones:")
44-
.font(.headline)
45-
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
59+
Group {
60+
61+
DateTimeZonePicker(selectedDate: $selectedDate, selectedTimeZone: $selectedTimeZone, showDate: false)
62+
63+
}
64+
.padding(.horizontal, 8)
4665

4766
List {
4867
ForEach(selectedTimeZones, id: \.self) { tz in
@@ -66,8 +85,40 @@ struct MykeMode: View {
6685
.listStyle(PlainListStyle())
6786
.scaledToFill()
6887

88+
Button(action: {
89+
#if os(iOS)
90+
notificationFeedbackGenerator = UINotificationFeedbackGenerator()
91+
notificationFeedbackGenerator?.prepare()
92+
#endif
93+
UIPasteboard.general.setValue(generateTimesAndFlagsText(),
94+
forPasteboardType: kUTTypePlainText as String)
95+
withAnimation {
96+
showCopied = true
97+
#if os(iOS)
98+
notificationFeedbackGenerator?.notificationOccurred(.success)
99+
#endif
100+
}
101+
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
102+
withAnimation {
103+
showCopied = false
104+
}
105+
#if os(iOS)
106+
notificationFeedbackGenerator = nil
107+
#endif
108+
}
109+
}) {
110+
Text(showCopied ? "Copied ✓" : "Copy Times & Flags")
111+
.font(.headline)
112+
.foregroundColor(.white)
113+
}
114+
.padding(.horizontal)
115+
.padding(.vertical, 5)
116+
.background(
117+
RoundedRectangle(cornerRadius: 15, style: .continuous)
118+
.fill(Color.accentColor)
119+
)
120+
69121
}
70-
.padding(.horizontal)
71122
.navigationTitle("Myke Mode")
72123
.navigationBarTitleDisplayMode(.inline)
73124
}
@@ -97,6 +148,6 @@ struct MykeMode: View {
97148

98149
struct MykeMode_Previews: PreviewProvider {
99150
static var previews: some View {
100-
MykeMode(selectedDate: .constant(Date()), selectedTimeZone: .constant("America/Los_Angeles"))
151+
MykeMode(selectedDate: .constant(Date()), selectedTimeZone: .constant(TimeZone(identifier: "America/Los_Angeles")!))
101152
}
102153
}

Elsewhen/Views/TimeCodeGeneratorView.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,12 @@ import UniformTypeIdentifiers
1212
struct TimeCodeGeneratorView: View {
1313

1414
@Binding var selectedDate: Date
15-
@Binding var selectedTimeZone: String
15+
@Binding var selectedTimeZone: TimeZone
1616
@State private var selectedFormatStyle: DateFormat = dateFormats[0]
1717
@State private var showLocalTimeInstead: Bool = false
1818

1919
private var discordFormat: String {
20-
var timeIntervalSince1970 = Int(selectedDate.timeIntervalSince1970)
21-
22-
if let tz = TimeZone(identifier: selectedTimeZone) {
23-
timeIntervalSince1970 = Int(convertSelectedDate(from: tz, to: TimeZone.current).timeIntervalSince1970)
24-
} else {
25-
logger.warning("\(selectedTimeZone, privacy: .public) is not a valid timezone identifier!")
26-
}
27-
20+
let timeIntervalSince1970 = Int(convertSelectedDate(from: selectedTimeZone, to: TimeZone.current).timeIntervalSince1970)
2821
return "<t:\(timeIntervalSince1970):\(selectedFormatStyle.code.rawValue)>"
2922
}
3023

@@ -56,6 +49,6 @@ struct TimeCodeGeneratorView: View {
5649

5750
struct TimeCodeGeneratorView_Previews: PreviewProvider {
5851
static var previews: some View {
59-
TimeCodeGeneratorView(selectedDate: .constant(Date()), selectedTimeZone: .constant("Europe/London"))
52+
TimeCodeGeneratorView(selectedDate: .constant(Date()), selectedTimeZone: .constant(TimeZone(identifier: "Europe/London")!))
6053
}
6154
}

0 commit comments

Comments
 (0)