Skip to content

Commit

Permalink
Add widgets reload button to settings
Browse files Browse the repository at this point in the history
  • Loading branch information
bgoncal committed Apr 22, 2024
1 parent 8127b05 commit d3f87bf
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 13 deletions.
24 changes: 24 additions & 0 deletions HomeAssistant.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,10 @@
420FE84B2B556BB100878E06 /* CarPlayActionsTemplate+Build.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FE84A2B556BB100878E06 /* CarPlayActionsTemplate+Build.swift */; };
420FE84E2B556CE500878E06 /* CarPlayEntitiesListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FE84D2B556CE500878E06 /* CarPlayEntitiesListViewModel.swift */; };
420FE8502B556F7500878E06 /* CarPlayEntitiesListTemplate+Build.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FE84F2B556F7500878E06 /* CarPlayEntitiesListTemplate+Build.swift */; };
421B1C162BD65246001ED18C /* WidgetsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421B1C152BD65246001ED18C /* WidgetsSettingsView.swift */; };
421B1C182BD6524E001ED18C /* WidgetsSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421B1C172BD6524E001ED18C /* WidgetsSettingsViewModel.swift */; };
421B1C1A2BD65255001ED18C /* WidgetsSettingsView+build.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421B1C192BD65255001ED18C /* WidgetsSettingsView+build.swift */; };
421B1C1D2BD65C04001ED18C /* View+ConditionalModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 421B1C1B2BD65BFA001ED18C /* View+ConditionalModifier.swift */; };
42266B112B740E4C00E94A71 /* BarcodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42266B102B740E4C00E94A71 /* BarcodeScannerView.swift */; };
42266B252B7A4BA900E94A71 /* BarcodeScannerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42266B242B7A4BA900E94A71 /* BarcodeScannerViewModel.swift */; };
424A7F462B188946008C8DF3 /* WidgetBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424A7F452B188946008C8DF3 /* WidgetBackground.swift */; };
Expand Down Expand Up @@ -1633,6 +1637,10 @@
420FE84A2B556BB100878E06 /* CarPlayActionsTemplate+Build.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarPlayActionsTemplate+Build.swift"; sourceTree = "<group>"; };
420FE84D2B556CE500878E06 /* CarPlayEntitiesListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlayEntitiesListViewModel.swift; sourceTree = "<group>"; };
420FE84F2B556F7500878E06 /* CarPlayEntitiesListTemplate+Build.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarPlayEntitiesListTemplate+Build.swift"; sourceTree = "<group>"; };
421B1C152BD65246001ED18C /* WidgetsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetsSettingsView.swift; sourceTree = "<group>"; };
421B1C172BD6524E001ED18C /* WidgetsSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetsSettingsViewModel.swift; sourceTree = "<group>"; };
421B1C192BD65255001ED18C /* WidgetsSettingsView+build.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WidgetsSettingsView+build.swift"; sourceTree = "<group>"; };
421B1C1B2BD65BFA001ED18C /* View+ConditionalModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+ConditionalModifier.swift"; sourceTree = "<group>"; };
42266B102B740E4C00E94A71 /* BarcodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarcodeScannerView.swift; sourceTree = "<group>"; };
42266B242B7A4BA900E94A71 /* BarcodeScannerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarcodeScannerViewModel.swift; sourceTree = "<group>"; };
4242A2B12B2B5C8000E9F001 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = en; path = en.lproj/AppIntentVocabulary.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3172,6 +3180,16 @@
path = Entities;
sourceTree = "<group>";
};
421B1C142BD65238001ED18C /* Widgets */ = {
isa = PBXGroup;
children = (
421B1C152BD65246001ED18C /* WidgetsSettingsView.swift */,
421B1C172BD6524E001ED18C /* WidgetsSettingsViewModel.swift */,
421B1C192BD65255001ED18C /* WidgetsSettingsView+build.swift */,
);
path = Widgets;
sourceTree = "<group>";
};
425573C52B55729E00145217 /* Servers */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3227,6 +3245,7 @@
426740A72B17390A00C1DD73 /* Data+Hexadecimal.swift */,
42C3737E2BC415AC00898990 /* UIViewController+Extensions.swift */,
42FCCFA72B9A05400057783F /* View+RoundedCorner.swift */,
421B1C1B2BD65BFA001ED18C /* View+ConditionalModifier.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -3856,6 +3875,7 @@
11F3D74F2495433800C05BBA /* Sensors */,
11AD2E392528FDF800FBC437 /* Connection */,
11AD2E542528FE1300FBC437 /* Notifications */,
421B1C142BD65238001ED18C /* Widgets */,
B626AAF01D8F972800A0D225 /* SettingsDetailViewController.swift */,
B6B2E6A4216ACE4400D39A26 /* ActionConfigurator.swift */,
B661FB69226BBDA900E541DD /* SettingsViewController.swift */,
Expand Down Expand Up @@ -6021,6 +6041,7 @@
1187DE4624D7E1BD00F0A6A6 /* SimulatorNFCManager.swift in Sources */,
1185DF96271FBB9800ED7D9A /* OnboardingAuthLogin.swift in Sources */,
425573E62B5838B600145217 /* MaterialDesignIcons+CarPlay.swift in Sources */,
421B1C1A2BD65255001ED18C /* WidgetsSettingsView+build.swift in Sources */,
42F1DA612B4D4F31002729BC /* CarPlayNoServerAlert.swift in Sources */,
11C590ED24A832CA0066085D /* YamlSection.swift in Sources */,
4296C36D2B90DB640051B63C /* IntentActionAppEntity.swift in Sources */,
Expand Down Expand Up @@ -6062,6 +6083,7 @@
42C08CF72BA31F2700172EE5 /* CMSampleBuffer+AudioSamples.swift in Sources */,
425573ED2B58904000145217 /* CarPlayEntityListItem.swift in Sources */,
11F55ECD25D3A364003977AC /* NotificationRateLimitViewController.swift in Sources */,
421B1C162BD65246001ED18C /* WidgetsSettingsView.swift in Sources */,
117EBC32261D398B00F5334A /* ZoneManagerAccuracyFuzzer.swift in Sources */,
113FB1132515A065000AC680 /* ScaleFactorMutator.swift in Sources */,
4296C37A2B9205450051B63C /* WidgetActionsAppIntent.swift in Sources */,
Expand Down Expand Up @@ -6099,6 +6121,7 @@
425573C92B5572DB00145217 /* CarPlayServerListViewModel.swift in Sources */,
11A71C6F24A4644A00D9565F /* ZoneManagerIgnoreReason.swift in Sources */,
1101568324D770B2009424C9 /* iOSTagManager.swift in Sources */,
421B1C182BD6524E001ED18C /* WidgetsSettingsViewModel.swift in Sources */,
42F5CABC2B10AE1A00409816 /* ServerFixture.swift in Sources */,
4291068C2BA9D79500D452F9 /* AudioPlayer.swift in Sources */,
11B1FFC524CCD72F00F9BCB2 /* VoiceShortcutRow.swift in Sources */,
Expand Down Expand Up @@ -6421,6 +6444,7 @@
11AF4D16249C8083006C74C0 /* With.swift in Sources */,
11B38EEC275C54A200205C7B /* IntentHandlerFactory.swift in Sources */,
1182620124F9C3F7000795C6 /* HACoreBlahObject.swift in Sources */,
421B1C1D2BD65C04001ED18C /* View+ConditionalModifier.swift in Sources */,
B672333E225DB68B0031D629 /* WebSocketMessage.swift in Sources */,
11482AD62505CB6E00C48C58 /* HACoreAudioObjectDevice.swift in Sources */,
11AF4D25249D1931006C74C0 /* LastUpdateSensor.swift in Sources */,
Expand Down
5 changes: 4 additions & 1 deletion Sources/App/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,9 @@ Home Assistant is free and open source home automation software with a focus on
"settings.status_section.version_row.title" = "Version";
"settings.template_edit.title" = "Edit Template";
"settings.whats_new.title" = "What's new?";
"settings.widgets.title" = "Widgets";
"settings_details.widgets.reload_all.title" = "Reload all widgets";
"settings_details.widgets.reload_all.description" = "This will reload all widgets timelines, use this in case your widgets are stuck in a blank state or not updating for some reason.";
"settings_details.actions.actions_synced.empty" = "No Synced Actions";
"settings_details.actions.actions_synced.footer" = "Actions defined in .yaml are not editable on device.";
"settings_details.actions.actions_synced.footer_no_actions" = "Actions may be also defined in the .yaml configuration.";
Expand Down Expand Up @@ -848,4 +851,4 @@ Home Assistant is free and open source home automation software with a focus on
"widgets.open_page.not_configured" = "No Pages Available";
"widgets.open_page.title" = "Open Page";
"widgets.button.reload_timeline" = "Reload all widgets";
"yes_label" = "Yes";
"yes_label" = "Yes";
12 changes: 0 additions & 12 deletions Sources/App/Settings/DebugSettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import RealmSwift
import Shared
import SwiftUI
import WebKit
import WidgetKit
import XCGLogger

class DebugSettingsViewController: HAFormViewController {
Expand Down Expand Up @@ -140,15 +139,6 @@ class DebugSettingsViewController: HAFormViewController {
Section() <<< SettingsRootDataSource.Row.thread.row
}

private var reloadWidgetsButton: ButtonRow {
ButtonRow {
$0.title = L10n.Widgets.Button.reloadTimeline
$0.onCellSelection { _, _ in
WidgetCenter.shared.reloadAllTimelines()
}
}
}

private func logs() -> Eureka.Section {
let section = Section()

Expand Down Expand Up @@ -260,8 +250,6 @@ class DebugSettingsViewController: HAFormViewController {
$0.tag = "developerOptions"
}

section <<< reloadWidgetsButton

section <<< ButtonRow("onboardTest") {
$0.title = "Onboard (Initial)"
$0.presentationMode = .presentModally(controllerProvider: .callback(builder: {
Expand Down
12 changes: 12 additions & 0 deletions Sources/App/Settings/SettingsRootDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum SettingsRootDataSource {
case sensors
case complications
case nfc
case widgets
case help
case privacy
case debugging
Expand All @@ -45,6 +46,7 @@ enum SettingsRootDataSource {
case .sensors: return SettingsRootDataSource.sensors()
case .complications: return SettingsRootDataSource.complications()
case .nfc: return SettingsRootDataSource.nfc()
case .widgets: return SettingsRootDataSource.widgets()
case .help: return SettingsRootDataSource.help()
case .privacy: return SettingsRootDataSource.privacy()
case .debugging: return SettingsRootDataSource.debugging()
Expand Down Expand Up @@ -158,6 +160,16 @@ enum SettingsRootDataSource {
}
}

private static func widgets() -> SettingsButtonRow {
SettingsButtonRow {
$0.title = L10n.Settings.Widgets.title
$0.icon = .widgetsIcon
$0.presentationMode = .show(controllerProvider: ControllerProvider.callback {
UIHostingController(rootView: WidgetsSettingsView.build())
}, onDismiss: nil)
}
}

private static func help() -> SettingsButtonRow {
SettingsButtonRow {
$0.title = L10n.helpLabel
Expand Down
1 change: 1 addition & 0 deletions Sources/App/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class SettingsViewController: HAFormViewController {
<<< SettingsRootDataSource.Row.sensors.row
<<< SettingsRootDataSource.Row.complications.row
<<< SettingsRootDataSource.Row.nfc.row
<<< SettingsRootDataSource.Row.widgets.row
}

if contentSections.contains(.help) {
Expand Down
8 changes: 8 additions & 0 deletions Sources/App/Settings/Widgets/WidgetsSettingsView+build.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation

extension WidgetsSettingsView {
static func build() -> WidgetsSettingsView {
let viewModel = WidgetsSettingsViewModel()
return .init(viewModel: viewModel)
}
}
53 changes: 53 additions & 0 deletions Sources/App/Settings/Widgets/WidgetsSettingsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Shared
import SwiftUI

struct WidgetsSettingsView: View {
@StateObject private var viewModel: WidgetsSettingsViewModel

init(viewModel: WidgetsSettingsViewModel) {
self._viewModel = .init(wrappedValue: viewModel)
}

var body: some View {
content
.navigationTitle(L10n.Settings.Widgets.title)
.navigationBarTitleDisplayMode(.inline)
}

private var content: some View {
List {
Button(action: {
viewModel.reloadWidgets()
}, label: {
HStack {
Label(L10n.SettingsDetails.Widgets.ReloadAll.title, systemImage: "square.text.square.fill")
.frame(maxWidth: .infinity, alignment: .leading)
if viewModel.isLoading {
ProgressView()
.progressViewStyle(.circular)
}
}
})
.listRowSeparator(.hidden)

// New section to avoid list to not round previous item corners
Section {
Text(L10n.SettingsDetails.Widgets.ReloadAll.description)
.font(.footnote)
.foregroundStyle(Color(uiColor: .secondaryLabel))
.listRowBackground(Color.clear)
}
}
.modify {
if #available(iOS 17.0, *) {
$0.listSectionSpacing(.leastNonzeroMagnitude)
}
}
}
}

#Preview {
NavigationView {
WidgetsSettingsView(viewModel: .init())
}
}
16 changes: 16 additions & 0 deletions Sources/App/Settings/Widgets/WidgetsSettingsViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Foundation
import WidgetKit

final class WidgetsSettingsViewModel: ObservableObject {
@Published var isLoading = false

func reloadWidgets() {
isLoading = true
WidgetCenter.shared.reloadAllTimelines()

// Delay to give some sense of execution
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
self?.isLoading = false
}
}
}
13 changes: 13 additions & 0 deletions Sources/Shared/Extensions/View+ConditionalModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Foundation
import SwiftUI

public extension View {
@ViewBuilder
func modify(@ViewBuilder _ transform: (Self) -> (some View)?) -> some View {
if let view = transform(self), !(view is EmptyView) {
view
} else {
self
}
}
}
12 changes: 12 additions & 0 deletions Sources/Shared/Resources/Swiftgen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,10 @@ public enum L10n {
/// What's new?
public static var title: String { return L10n.tr("Localizable", "settings.whats_new.title") }
}
public enum Widgets {
/// Widgets
public static var title: String { return L10n.tr("Localizable", "settings.widgets.title") }
}
}

public enum SettingsDetails {
Expand Down Expand Up @@ -1930,6 +1934,14 @@ public enum L10n {
/// Apple Watch
public static var title: String { return L10n.tr("Localizable", "settings_details.watch.title") }
}
public enum Widgets {
public enum ReloadAll {
/// This will reload all widgets timeline, use this in case your widgets are stuck in a blank state or not updating for some reason.
public static var description: String { return L10n.tr("Localizable", "settings_details.widgets.reload_all.description") }
/// Reload all widgets
public static var title: String { return L10n.tr("Localizable", "settings_details.widgets.reload_all.title") }
}
}
}

public enum SettingsSensors {
Expand Down

0 comments on commit d3f87bf

Please sign in to comment.