Various fixes and improvements

main
Laurent 3 years ago
parent 5e22e43b72
commit 3352cc6e4e
  1. 4
      LeCountdown.xcodeproj/project.pbxproj
  2. 2
      LeCountdown/Subscription/AppGuard.swift
  3. 3
      LeCountdown/Subscription/StoreView.swift
  4. 38
      LeCountdown/Subscription/SubscriptionButtonView.swift
  5. 229
      LeCountdown/Views/ContentView.swift
  6. 22
      LeCountdown/Views/Countdown/NewCountdownView.swift
  7. 37
      LeCountdown/Views/Reusable/SiriTimerView.swift
  8. 4
      LeCountdown/Views/Reusable/SoundFormView.swift
  9. 9
      LeCountdown/fr.lproj/Localizable.strings

@ -74,6 +74,7 @@
C445FA922987CC8A0054D761 /* Sound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C445FA912987CC8A0054D761 /* Sound.swift */; };
C4636D9C29AF46BD00994E31 /* ActivityKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4636D9B29AF46BD00994E31 /* ActivityKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
C4636D9D29AF46D900994E31 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C438C7D02981216200BF3EF9 /* WidgetKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
C46926BD29DDC49E0003E310 /* SubscriptionButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46926BC29DDC49E0003E310 /* SubscriptionButtonView.swift */; };
C473C2F029A8CFFC0056B38A /* TimerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1D7298C0727005C86A5 /* TimerRouter.swift */; };
C473C2F129A8DA0B0056B38A /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; };
C473C2F229A8DA1F0056B38A /* CountdownScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C02980228B00BF3EF9 /* CountdownScheduler.swift */; };
@ -379,6 +380,7 @@
C445FA902987C0CF0054D761 /* LeCountdown.0.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.2.xcdatamodel; sourceTree = "<group>"; };
C445FA912987CC8A0054D761 /* Sound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sound.swift; sourceTree = "<group>"; };
C4636D9B29AF46BD00994E31 /* ActivityKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ActivityKit.framework; path = System/Library/Frameworks/ActivityKit.framework; sourceTree = SDKROOT; };
C46926BC29DDC49E0003E310 /* SubscriptionButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionButtonView.swift; sourceTree = "<group>"; };
C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidgetAttributes.swift; sourceTree = "<group>"; };
C473C32729AA307D0056B38A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/LaunchWidget.intentdefinition; sourceTree = "<group>"; };
C473C32929AA30890056B38A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/LaunchWidget.strings; sourceTree = "<group>"; };
@ -813,6 +815,7 @@
C4BA2B56299FFA4F00CB4FBA /* AppGuard.swift */,
C4E5D67F29B8FD93008E7465 /* Store.swift */,
C4BA2B5E299FFC8300CB4FBA /* StoreView.swift */,
C46926BC29DDC49E0003E310 /* SubscriptionButtonView.swift */,
);
path = Subscription;
sourceTree = "<group>";
@ -1239,6 +1242,7 @@
C4F8B164298A9A92005C86A5 /* AlarmFormView.swift in Sources */,
C40FDB622992985C0042A390 /* TextToSpeechRecorder.swift in Sources */,
C4BA2B43299FCB2B00CB4FBA /* RecordsView.swift in Sources */,
C46926BD29DDC49E0003E310 /* SubscriptionButtonView.swift in Sources */,
C4BA2B2F299E69A000CB4FBA /* View+Extension.swift in Sources */,
C4E5D68629BB369E008E7465 /* TimersView.swift in Sources */,
C4E5D68A29BB7953008E7465 /* SettingsView.swift in Sources */,

@ -126,7 +126,7 @@ extension Notification.Name {
}
var isSubscriber: Bool {
return true //self.currentPlan != .none
return self.currentPlan != .none
}
var currentPlan: StorePlan {

@ -123,6 +123,7 @@ struct PlanView: View {
VStack {
if let plan = StorePlan(rawValue: product.id) {
Text("\(product.displayPrice) / \(plan.formattedPeriod)").font(.title3)
.foregroundColor(.white)
} else {
Text("Plan not found")
}
@ -137,6 +138,8 @@ struct PlanView: View {
}
}.padding()
.foregroundColor(.black)
.background(Color(red: 1.0, green: 0.8, blue: 0.9))
}
fileprivate func _purchase(product: Product) {

@ -0,0 +1,38 @@
//
// SubscriptionButtonView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 05/04/2023.
//
import SwiftUI
struct SubscriptionButtonView: View {
@State private var showSubscriptionSheet: Bool = false
var body: some View {
Button {
self.showSubscriptionSheet = true
} label: {
Text("Get fully enchanted")
.frame(maxWidth: .infinity)
}
.sheet(isPresented: self.$showSubscriptionSheet, content: {
StoreView(isPresented: self.$showSubscriptionSheet)
})
.frame(height: 50.0)
.background(Color.accentColor)
.cornerRadius(8.0)
.padding(.horizontal)
.buttonStyle(.bordered)
.foregroundColor(.white)
}
}
struct SubscriptionButtonView_Previews: PreviewProvider {
static var previews: some View {
SubscriptionButtonView()
}
}

@ -17,6 +17,11 @@ class BoringContext : ObservableObject {
@Published var siriTimer: AbstractTimer? = nil
}
//enum TimerType {
// case timer
// case stopwatch
//}
struct ContentView<T : AbstractTimer>: View {
@Environment(\.managedObjectContext) private var viewContext
@ -32,25 +37,18 @@ struct ContentView<T : AbstractTimer>: View {
@State private var showSettingsSheet: Bool = false
@State private var showStatsSheet: Bool = false
@State private var showAddSheet: Bool = false
// @State private var showAddSheet: Bool = false
// @State private var showTimerSheet: Bool = false
// @State private var showStopwatchSheet: Bool = false
@State private var showSubscriptionSheet: Bool = false
var body: some View {
VStack {
if !AppGuard.main.isSubscriber {
Button {
self.showSubscriptionSheet = true
} label: {
Text("Get fully enchanted")
.frame(maxWidth: .infinity)
} .background(Color.accentColor)
.cornerRadius(8.0)
.padding(.horizontal)
.buttonStyle(.bordered)
.foregroundColor(.white)
SubscriptionButtonView()
}
TimersView(isEditing: self.$isEditing,
@ -61,41 +59,12 @@ struct ContentView<T : AbstractTimer>: View {
.environment(\.managedObjectContext, viewContext)
.environmentObject(self.boringContext)
// if !self.tipsShown, let tip = Preferences.tipToShow {
// TipView(tip: tip) {
// self._hideTip(tip)
// }.padding()
// }
if !conductor.liveTimers.isEmpty {
Spacer()
if UIDevice.isPhoneIdiom {
SiriTimerView(timer: self.boringContext.siriTimer, isVisible: self.$siriTipShown)
HStack(alignment: .center) {
VolumeView(changeVolume: true)
.padding(12.0)
}.frame(width: 300.0, height: 40.0)
.background(Color(white: 0.9))
.cornerRadius(16.0)
} else {
HStack() {
if self.siriTipShown {
SiriTimerView(timer: self.boringContext.siriTimer, isVisible: self.$siriTipShown)
Spacer()
}
VolumeView()
.padding(12.0)
.frame(width: 300.0, height: 40.0)
.background(Color(white: 0.9))
.cornerRadius(16.0)
}.padding(.horizontal)
}
SiriVolumeView(timer: self.boringContext.siriTimer, siriTipShown: self.$siriTipShown)
LiveTimerListView()
.environment(\.managedObjectContext, viewContext)
.environmentObject(conductor)
@ -105,8 +74,7 @@ struct ContentView<T : AbstractTimer>: View {
.cornerRadius(32.0, corners: [.topRight, .topLeft])
}
}
.navigationTitle("Home")
.navigationTitle(Bundle.main.applicationName)
.alert(boringContext.error?.localizedDescription ?? "missing error", isPresented: $boringContext.showDefaultAlert) {
Button("OK", role: .cancel) { }
}
@ -115,59 +83,12 @@ struct ContentView<T : AbstractTimer>: View {
}
.sheet(isPresented: self.$boringContext.isShowingNewData, content: {
})
.sheet(isPresented: self.$showSettingsSheet, content: {
SettingsView()
.presentationDetents([.height(240.0)])
})
.sheet(isPresented: self.$showSubscriptionSheet, content: {
StoreView(isPresented: self.$showSubscriptionSheet)
})
.sheet(isPresented: self.$showStatsSheet, content: {
NavigationStack {
ActivitiesView()
}
})
.sheet(isPresented: self.$showAddSheet, content: {
NavigationStack {
NewCountdownView(isPresented: $showAddSheet, tabSelection: .constant(0))
.environment(\.managedObjectContext, viewContext)
}
})
// .sheet(isPresented: self.$showAddSheet, content: {
// NewCountdownView(isPresented: $showAddSheet, tabSelection: .constant(0))
// .environment(\.managedObjectContext, viewContext)
// })
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
self.isEditing.toggle()
}
} label: {
Text(self.isEditing ? "Done" : "Edit")
}
}
ToolbarItemGroup(placement: .navigationBarTrailing) {
if self.viewContext.count(entityName: "Record") > 0 {
Button {
withAnimation {
self.showStatsSheet.toggle()
}
} label: {
Image(systemName: "chart.bar.doc.horizontal")
}
}
Button {
withAnimation {
self.showSettingsSheet.toggle()
}
} label: {
Image(systemName: "gearshape.fill")
}
Button {
withAnimation {
self.showAddSheet.toggle()
}
} label: {
Image(systemName: "plus")
}
}
MainToolbarView(isEditing: self.$isEditing)
}
.onAppear {
self._askPermissions()
@ -243,37 +164,87 @@ struct ContentView<T : AbstractTimer>: View {
}
struct MainToolbarView: View {
struct MainToolbarView: ToolbarContent {
@Environment(\.managedObjectContext) private var viewContext
@Binding var isEditing: Bool
// @State var type: TimerType? = nil
var isShowingNewData: Binding<Bool>
@State var showSettingsSheet: Bool = false
@State var showStatsSheet: Bool = false
@State var showAddSheet: Bool = false
@State var showTimerSheet: Bool = false
@State var showStopwatchSheet: Bool = false
var body: some View {
Button {
self.isShowingNewData.wrappedValue = true
} label: {
HStack {
Image(systemName: "timer")
Text("countdown")
var body: some ToolbarContent {
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
self.isEditing.toggle()
}
} label: {
Text(self.isEditing ? "Done" : "Edit")
}
}
Button {
self.isShowingNewData.wrappedValue = true
} label: {
HStack {
Image(systemName: "stopwatch")
Text("stopwatch")
ToolbarItemGroup(placement: .navigationBarTrailing) {
if self.haveRecords() {
Button {
withAnimation {
self.showStatsSheet.toggle()
}
} label: {
Image(systemName: "chart.bar.doc.horizontal")
}
.sheet(isPresented: self.$showStatsSheet, content: {
NavigationStack {
ActivitiesView()
}
})
}
}
Button {
self.isShowingNewData.wrappedValue = true
} label: {
HStack {
Image(systemName: "alarm")
Text("alarm")
Button {
withAnimation {
self.showSettingsSheet.toggle()
}
} label: {
Image(systemName: "gearshape.fill")
}
.sheet(isPresented: self.$showSettingsSheet, content: {
SettingsView()
.presentationDetents([.height(240.0)])
})
Button {
withAnimation {
self.showAddSheet.toggle()
}
} label: {
Image(systemName: "plus")
}
.confirmationDialog("Please select", isPresented: self.$showAddSheet) {
Button("Timer") {
self.showAddSheet = false
self.showTimerSheet.toggle()
}
Button("Stopwatch") {
self.showAddSheet = false
self.showStopwatchSheet.toggle()
}
}
.sheet(isPresented: self.$showStopwatchSheet, content: {
NewStopwatchView(isPresented: $showStopwatchSheet, tabSelection: .constant(0))
})
.sheet(isPresented: self.$showTimerSheet, content: {
NewCountdownView(isPresented: $showTimerSheet, tabSelection: .constant(0))
})
}
}
func haveRecords() -> Bool {
return self.viewContext.count(entityName: "Record") > 0
}
}
fileprivate extension Countdown {
@ -311,10 +282,24 @@ class TimerSpot : Identifiable, Equatable {
}
struct ContentView_Previews: PreviewProvider {
//struct ContentView_Previews: PreviewProvider {
// static var previews: some View {
// ContentView<Countdown>()
// .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
// .environmentObject(Conductor.maestro)
//
// }
//}
struct Toolbar_Previews: PreviewProvider {
static var previews: some View {
ContentView<Countdown>()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext).environmentObject(Conductor.maestro)
NavigationStack {
Text("Hello")
}
.navigationTitle("Title")
.toolbar {
MainToolbarView(isEditing: .constant(false))
}
}
}

@ -27,15 +27,16 @@ struct NewCountdownView : View {
}
var body: some View {
CountdownEditView(isPresented: $isPresented, tabSelection: self.tabSelection)
.environment(\.managedObjectContext, viewContext)
.navigationTitle("New countdown")
.onAppear {
self.userActivity.becomeCurrent()
}
.onDisappear {
self.userActivity.resignCurrent()
}
NavigationStack {
CountdownEditView(isPresented: $isPresented, tabSelection: self.tabSelection)
.environment(\.managedObjectContext, viewContext)
.onAppear {
self.userActivity.becomeCurrent()
}
.onDisappear {
self.userActivity.resignCurrent()
}
}
}
}
@ -155,8 +156,7 @@ struct CountdownEditView : View {
}
}
.navigationTitle("Edit countdown")
.navigationTitle(self.countdown?.name ?? NSLocalizedString("New timer", comment: ""))
}
.onAppear {
self._onAppear()

@ -8,6 +8,43 @@
import SwiftUI
import _AppIntents_SwiftUI
struct SiriVolumeView: View {
var timer: AbstractTimer? = nil
@Binding var siriTipShown: Bool
var body: some View {
if UIDevice.isPhoneIdiom {
SiriTimerView(timer: self.timer,
isVisible: self.$siriTipShown)
HStack(alignment: .center) {
VolumeView(changeVolume: true)
.padding(12.0)
}.frame(width: 300.0, height: 40.0)
.background(Color(white: 0.9))
.cornerRadius(16.0)
} else {
HStack() {
if self.siriTipShown {
SiriTimerView(timer: self.timer,
isVisible: self.$siriTipShown)
Spacer()
}
VolumeView()
.padding(12.0)
.frame(width: 300.0, height: 40.0)
.background(Color(white: 0.9))
.cornerRadius(16.0)
}.padding(.horizontal)
}
}
}
struct SiriTimerView: View {
var timer: AbstractTimer? = nil

@ -35,7 +35,7 @@ struct SoundFormView : View {
SoundLinkView(soundModel: self.model.soundModel,
catalog: .ring,
title: "Sound")
title: NSLocalizedString("Sound", comment: "") )
if self.repeatCountBinding != nil {
Picker("Repeat Count", selection: self.repeatCountBinding!) {
@ -49,7 +49,7 @@ struct SoundFormView : View {
SoundLinkView(soundModel: self.model.confirmationSoundModel,
catalog: .confirmation,
title: "Confirmation Sound")
title: NSLocalizedString("Confirmation Sound", comment: ""))
}.sheet(isPresented: self.$imageSelectionSheetShown) {
ImageSelectionView(showBinding: self.$imageSelectionSheetShown, imageBinding: self.imageBinding)

@ -137,7 +137,7 @@
"New alarm" = "Nouvelle alarme";
/* No comment provided by engineer. */
"New countdown" = "Nouveau minuteur";
"New timer" = "Nouveau minuteur";
/* No comment provided by engineer. */
"New stopwatch" = "Nouveau chrono";
@ -259,3 +259,10 @@
"year" = "an";
"Get fully enchanted" = "Enchantement permanent";
"Ask Siri: %@ %@!" = "Dites à Siri: %@ %@!";
"Timer" = "Minuteur";
"Stopwatch" = "Chronomètre";
"Duration" = "Durée";
"Confirmation Sound" = "Son de démarrage";
"Name" = "Nom";
"Hours" = "Heures";
"Default Volume" = "Volume par défaut";

Loading…
Cancel
Save