Skip to content

Commit

Permalink
Extend Migration to Drawer and Notifcations
Browse files Browse the repository at this point in the history
  • Loading branch information
timbms committed Sep 5, 2024
1 parent 75c6fb4 commit bbe4006
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 38 deletions.
8 changes: 8 additions & 0 deletions openHAB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
DA5ED9C02C8509C2004875E0 /* ClientCertificatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5ED9BF2C8509C2004875E0 /* ClientCertificatesView.swift */; };
DA65871F236F83CE007E2E7F /* UserDefaultsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA65871E236F83CD007E2E7F /* UserDefaultsExtension.swift */; };
DA6587222370C9D8007E2E7F /* PreferencesHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA6587212370C9D8007E2E7F /* PreferencesHostingController.swift */; };
DA6B2EEF2C861BC900DF77CF /* DrawerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA6B2EEE2C861BC900DF77CF /* DrawerView.swift */; };
DA6B2EF12C87B59000DF77CF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA6B2EF02C87B59000DF77CF /* NotificationsView.swift */; };
DA7224D223828D3400712D20 /* PreviewConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA7224D123828D3300712D20 /* PreviewConstants.swift */; };
DA72E1B8236DEA0900B8EF3A /* AppMessageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA72E1B5236DEA0900B8EF3A /* AppMessageService.swift */; };
DA7649DE23FC81A20085CE46 /* Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA7649DD23FC81A20085CE46 /* Unwrap.swift */; };
Expand Down Expand Up @@ -392,6 +394,8 @@
DA5ED9BF2C8509C2004875E0 /* ClientCertificatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientCertificatesView.swift; sourceTree = "<group>"; };
DA65871E236F83CD007E2E7F /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = "<group>"; };
DA6587212370C9D8007E2E7F /* PreferencesHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesHostingController.swift; sourceTree = "<group>"; };
DA6B2EEE2C861BC900DF77CF /* DrawerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawerView.swift; sourceTree = "<group>"; };
DA6B2EF02C87B59000DF77CF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
DA7224D123828D3300712D20 /* PreviewConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewConstants.swift; sourceTree = "<group>"; };
DA72E1B0236DE9F200B8EF3A /* AppState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppState.swift; path = "openHABWatch Extension/app/AppState.swift"; sourceTree = "<group>"; };
DA72E1B5236DEA0900B8EF3A /* AppMessageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppMessageService.swift; path = "openHABWatch Extension/external/AppMessageService.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -860,6 +864,8 @@
DA242C612C83588600AFB10D /* SettingsView.swift */,
DA5ED9BF2C8509C2004875E0 /* ClientCertificatesView.swift */,
DA9F81862C85020F00B47B72 /* RTFTextView.swift */,
DA6B2EF02C87B59000DF77CF /* NotificationsView.swift */,
DA6B2EEE2C861BC900DF77CF /* DrawerView.swift */,
DF4B84101886DA9900F34902 /* Widgets */,
DF4A02291CF3157B006C3456 /* Drawer */,
DFFD8FCE18EDBD30003B502A /* Util */,
Expand Down Expand Up @@ -1483,13 +1489,15 @@
DAC65FC7236EDF3900F4501E /* SpinnerViewController.swift in Sources */,
DA9F81872C85020F00B47B72 /* RTFTextView.swift in Sources */,
DF4A02421CF34096006C3456 /* OpenHABDrawerItem.swift in Sources */,
DA6B2EF12C87B59000DF77CF /* NotificationsView.swift in Sources */,
DA50C7BF2B0A65300009F716 /* SliderWithSwitchSupportUITableViewCell.swift in Sources */,
DF4A022C1CF315BA006C3456 /* OpenHABDrawerTableViewController.swift in Sources */,
DA5ED9BE2C850955004875E0 /* ClientCertificatesViewModel.swift in Sources */,
DA21EAE22339621C001AB415 /* Throttler.swift in Sources */,
DAF4F6C0222734D300C24876 /* NewImageUITableViewCell.swift in Sources */,
DAEAA89B21E2611000267EA3 /* OpenHABNotificationsViewController.swift in Sources */,
DF1B302D1CF5C667009C921C /* OpenHABNotification.swift in Sources */,
DA6B2EEF2C861BC900DF77CF /* DrawerView.swift in Sources */,
938BF9D324EFD0B700E6B52F /* UIViewController+Localization.swift in Sources */,
DF06F1F618FE7A160011E7B9 /* OpenHABSelectionTableViewController.swift in Sources */,
DAA42BA821DC97E000244B2A /* NotificationTableViewCell.swift in Sources */,
Expand Down
208 changes: 208 additions & 0 deletions openHAB/DrawerView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Copyright (c) 2010-2024 Contributors to the openHAB project
//
// See the NOTICE file(s) distributed with this work for additional
// information.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0
//
// SPDX-License-Identifier: EPL-2.0

import Kingfisher
import OpenHABCore
import os.log
import SafariServices
import SFSafeSymbols
import SwiftUI

struct DrawerView: View {
@State private var sitemaps: [OpenHABSitemap] = []
@State private var uiTiles: [OpenHABUiTile] = []
@State private var drawerItems: [OpenHABDrawerItem] = []
@State private var selectedSection: Int?

var openHABUsername = ""
var openHABPassword = ""

var onDismiss: (TargetController) -> Void
@Environment(\.dismiss) private var dismiss

// App wide data access
var appData: OpenHABDataObject? {
AppDelegate.appDelegate.appData
}

@ScaledMetric var openHABIconwidth = 20.0
@ScaledMetric var tilesIconwidth = 20.0
@ScaledMetric var sitemapIconwidth = 20.0

var body: some View {
List {
Section(header: Text("Main")) {
HStack {
Image("openHABIcon")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: openHABIconwidth)
Text("Home")
}
.onTapGesture {
dismiss()
onDismiss(.webview)
}
}

Section(header: Text("Tiles")) {
ForEach(uiTiles, id: \.url) { tile in
HStack {
ImageView(url: tile.imageUrl)
.aspectRatio(contentMode: .fit)
.frame(width: tilesIconwidth)
Text(tile.name)
}
.onTapGesture {
dismiss()
onDismiss(.tile(tile.url))
}
}
}

Section(header: Text("Sitemaps")) {
ForEach(sitemaps, id: \.name) { sitemap in
HStack {
let url = Endpoint.iconForDrawer(rootUrl: appData?.openHABRootUrl ?? "", icon: sitemap.icon).url
KFImage(url).placeholder { Image("openHABIcon").resizable() }
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: sitemapIconwidth)
Text(sitemap.label)
}
.onTapGesture {
dismiss()
onDismiss(.sitemap(sitemap.name))
}
}
}

Section(header: Text("System")) {
HStack {
Image(systemSymbol: .gear)
Text("Settings")
}
.onTapGesture {
dismiss()
onDismiss(.settings)
}

// check if we are using my.openHAB, add notifications menu item then
// Actually this should better test whether the host of the remoteUrl is on openhab.org
if Preferences.remoteUrl.contains("openhab.org"), !Preferences.demomode {
HStack {
Image(systemSymbol: .bell)
Text("Notifications")
}
.onTapGesture {
dismiss()
onDismiss(.notifications)
}
}
}
}
.listStyle(.inset)
.onAppear(perform: loadData)
}

private func loadData() {
// TODO: Replace network calls with appropriate @EnvironmentObject or other state management
loadSitemaps()
loadUiTiles()
}

private func loadSitemaps() {
// Perform network call to load sitemaps and decode
// Update the sitemaps state

NetworkConnection.sitemaps(openHABRootUrl: appData?.openHABRootUrl ?? "") { response in
switch response.result {
case let .success(data):
os_log("Sitemap response", log: .viewCycle, type: .info)

sitemaps = deriveSitemaps(data)

if sitemaps.last?.name == "_default", sitemaps.count > 1 {
sitemaps = Array(sitemaps.dropLast())
}

// Sort the sitemaps according to Settings selection.
switch SortSitemapsOrder(rawValue: Preferences.sortSitemapsby) ?? .label {
case .label: sitemaps.sort { $0.label < $1.label }
case .name: sitemaps.sort { $0.name < $1.name }
}

drawerItems.removeAll()
case let .failure(error):
os_log("%{PUBLIC}@", log: .default, type: .error, error.localizedDescription)
drawerItems.removeAll()
}
}
}

private func loadUiTiles() {
// Perform network call to load UI Tiles and decode
// Update the uiTiles state
NetworkConnection.uiTiles(openHABRootUrl: appData?.openHABRootUrl ?? "") { response in
switch response.result {
case .success:
os_log("ui tiles response", log: .viewCycle, type: .info)
guard let responseData = response.data else {
os_log("Error: did not receive data", log: OSLog.remoteAccess, type: .info)
return
}
do {
uiTiles = try JSONDecoder().decode([OpenHABUiTile].self, from: responseData)
} catch {
os_log("Error: did not receive data %{PUBLIC}@", log: OSLog.remoteAccess, type: .info, error.localizedDescription)
}
case let .failure(error):
os_log("%{PUBLIC}@", log: .default, type: .error, error.localizedDescription)
}
}
}

mutating func loadSettings() {
openHABUsername = Preferences.username
openHABPassword = Preferences.password
}
}

struct ImageView: View {
let url: String

// App wide data access
var appData: OpenHABDataObject? {
AppDelegate.appDelegate.appData
}

var body: some View {
if !url.isEmpty {
switch url {
case _ where url.hasPrefix("data:image"):
let provider = Base64ImageDataProvider(base64String: url.deletingPrefix("data:image/png;base64,"), cacheKey: UUID().uuidString)
return KFImage(source: .provider(provider)).resizable()
case _ where url.hasPrefix("http"):
return KFImage(URL(string: url)).resizable()
default:
let builtURL = Endpoint.resource(openHABRootUrl: appData?.openHABRootUrl ?? "", path: url.prepare()).url
return KFImage(builtURL).resizable()
}
} else {
// This will always fallback to placeholder
return KFImage(URL(string: "bundle://openHABIcon")).placeholder { Image("openHABIcon").resizable() }
}
}
}

// #Preview {
// DrawerView(onDismiss: {.webview -> Void})
// }
114 changes: 114 additions & 0 deletions openHAB/NotificationsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (c) 2010-2024 Contributors to the openHAB project
//
// See the NOTICE file(s) distributed with this work for additional
// information.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0
//
// SPDX-License-Identifier: EPL-2.0

import Kingfisher
import OpenHABCore
import os.log
import SwiftUI

struct NotificationsView: View {
@State var notifications: [OpenHABNotification] = []

var body: some View {
List(notifications, id: \.id) { notification in
NotificationRow(notification: notification)
}
.refreshable {
loadNotifications()
}
.navigationTitle("Notifications")
.onAppear {
loadNotifications()
}
}

private func loadNotifications() {
NetworkConnection.notification(urlString: Preferences.remoteUrl) { response in
DispatchQueue.main.async {
switch response.result {
case let .success(data):
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full)
let codingDatas = try data.decoded(as: [OpenHABNotification.CodingData].self, using: decoder)
notifications = codingDatas.map(\.openHABNotification)
} catch {
os_log("%{PUBLIC}@ ", log: .default, type: .error, error.localizedDescription)
}
case let .failure(error):
os_log("%{PUBLIC}@", log: .default, type: .error, error.localizedDescription)
}
}
}
}
}

struct NotificationRow: View {
var notification: OpenHABNotification

// App wide data access
var appData: OpenHABDataObject? {
AppDelegate.appDelegate.appData
}

var body: some View {
HStack {
KFImage(iconUrl)
.placeholder {
Image("openHABIcon").resizable()
}
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(8)
VStack(alignment: .leading) {
Text(notification.message)
.font(.body)
if let timeStamp = notification.created {
Text(dateString(from: timeStamp))
.font(.caption)
.foregroundColor(.gray)
}
}
}

.padding(.vertical, 8)
}

private var iconUrl: URL? {
if let appData {
return Endpoint.icon(
rootUrl: appData.openHABRootUrl,
version: appData.openHABVersion,
icon: notification.icon,
state: "",
iconType: .png,
iconColor: ""
).url
}
return nil
}

private func dateString(from date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
formatter.timeZone = TimeZone.current
return formatter.string(from: date)
}
}

#Preview {
Group {
NotificationsView(notifications: [OpenHABNotification(message: "message1", created: Date.now, id: UUID().uuidString), OpenHABNotification(message: "message2", created: Date.now, id: UUID().uuidString)])

NotificationRow(notification: OpenHABNotification(message: "message3", created: Date.now))
}
}
8 changes: 8 additions & 0 deletions openHAB/OpenHABDrawerItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// SPDX-License-Identifier: EPL-2.0

import Foundation
import SFSafeSymbols

enum OpenHABDrawerItem {
case settings
Expand All @@ -34,4 +35,11 @@ enum OpenHABDrawerItem {
OpenHABDrawerItem.settings
}
}

var icon: SFSymbol {
switch self {
case .notifications: .bell
case .settings: .gear
}
}
}
Loading

0 comments on commit bbe4006

Please sign in to comment.