import ActivityKit import WidgetKit import SwiftUI struct LiveActivitiesAppAttributes: ActivityAttributes, Identifiable { public typealias LiveDeliveryData = ContentState // don't forget to add this line, otherwise, live activity will not display it. public struct ContentState: Codable, Hashable { } var id = UUID() } let sharedDefault = UserDefaults(suiteName: "group.alhabib.patientApp")! struct AppWidgetAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { // Dynamic stateful properties about your activity go here! var emoji: String } // Fixed non-changing properties about your activity go here! var name: String } extension Color { static let redColorText = Color(hex: "#ED1C2B") static let yellowColor = Color(hex: "#FFAF15") static let successColor = Color(hex: "#18C273") static let inactiveColor = Color(hex: "#ED1C2B") static let textColor = Color(hex: "#A2A2A2") static let colorWhite = Color(hex: "#FFFFFF") // Helper initializer for hex colors init(hex: String) { let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) var int: UInt64 = 0 Scanner(string: hex).scanHexInt64(&int) let a, r, g, b: UInt64 switch hex.count { case 3: // RGB (12-bit) (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) case 6: // RGB (24-bit) (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) case 8: // ARGB (32-bit) (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) default: (a, r, g, b) = (255, 0, 0, 0) } self.init( .sRGB, red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255, opacity: Double(a) / 255 ) } } // MARK: - Widget Configuration based on nextStepBackground struct WidgetColorConfig { let nextStepBackground: Color let nextStepTextColor: Color let containerBorderColor: Color static func config(for value: Int) -> WidgetColorConfig { switch value { case 0: return WidgetColorConfig( nextStepBackground: .inactiveColor, nextStepTextColor: .textColor, containerBorderColor: .yellowColor ) case 1: return WidgetColorConfig( nextStepBackground: .redColorText, nextStepTextColor: .colorWhite, containerBorderColor: .redColorText ) case 2: return WidgetColorConfig( nextStepBackground: .successColor, nextStepTextColor: .colorWhite, containerBorderColor: .successColor ) default: return WidgetColorConfig( nextStepBackground: .inactiveColor, nextStepTextColor: .textColor, containerBorderColor: .yellowColor ) } } } // MARK: - Widget Data Model struct NotificationWidgetData { let currentTicket: String let currentlyServingHeader: String let currentlyServing: String let status: String let nextStep: String let imageName: String let colorConfig: WidgetColorConfig let progress: Double let yourTicketHeaderText : String } struct NotificationWidgetView: View { private let data: NotificationWidgetData private let backgroundColor: Color let animateProgress = false init(data: NotificationWidgetData, backgroundColor: Color = .white) { print("it is being loaded") self.data = data self.backgroundColor = backgroundColor } var body : some View{ ZStack { // Dark background Color(red: 0.15, green: 0.15, blue: 0.15) .ignoresSafeArea() // Main card VStack(alignment: .leading, spacing: 0) { // Header section HStack(alignment: .top) { HStack( alignment: .center,spacing: 4) { Text(data.currentTicket) .font(.system(size: 32, weight: .bold, design: .default)) .foregroundColor(Color(red: 0, green: 0.2, blue: 0.4)) Text(data.yourTicketHeaderText) .font(.system(size: 14, weight: .regular)) .foregroundColor(Color(hex: "#8F9AA3")) } Spacer() // Hourglass icon Image("clock") .frame(width: 24, height:24 ) .foregroundColor(Color(hex: "#2E3039")) .padding(.top, 4) } .padding(.top,4) .padding(.bottom, 4) // Alert banner HStack(spacing: 4) { Image("profession") Text(data.nextStep) .font(.system(size: 10, weight: .semibold)) .foregroundColor(Color(hex:"#18C273")) } .padding(8) .background(Color(hex:"#1A18C273")) .cornerRadius(8) .padding(.bottom, 10) // Progress bar VStack(alignment: .leading) { ZStack(alignment: .leading) { // Background track Capsule() .fill(Color(red: 0.9, green: 0.9, blue: 0.9)) .frame(height: 4) // Progress fill Capsule() .fill(Color.green) .frame(width: data.progress, height: 4) .animation( Animation.easeInOut(duration: 1.5) .delay(0.3), value: animateProgress ) ZStack{ Circle() .fill(Color.white) .frame(width: 12, height: 12) Circle() .fill(Color.green) .frame(width: 10, height: 10) .animation( Animation.easeInOut(duration: 1.5) .delay(0.3), value: animateProgress ) } .offset(x:data.progress) } } .padding(.bottom, 10) // Footer section HStack { VStack(alignment: .leading, spacing: 0) { HStack(spacing: 4) { Text(data.currentlyServingHeader) .font(.system(size: 14, weight: .regular)) .foregroundColor(Color(hex:"#8F9AA3")) Text(data.currentlyServing) .font(.system(size: 18, weight: .bold)) .foregroundColor(Color(hex:"#2E3039")) } } Spacer() // Visiting Nurse badge HStack(spacing: 8) { Image("profession") .font(.system(size: 10, weight: .semibold)) .foregroundColor(Color(hex: "#18C273")) Text(data.status) .font(.system(size: 10, weight: .semibold)) .foregroundColor(Color(hex: "#18C273")) } .padding(8) .background(Color(hex:"#1A18C273")) .cornerRadius(8) } .padding(.horizontal, 8) .padding(.bottom, 8) } .padding(24) .background(backgroundColor) .cornerRadius(24) } } // var body2 : some View { // Text(data.currentTicket) // .font(.custom("Poppins-Medium", size: 28)) // .fontWeight(.bold) // .foregroundColor(.black) // .padding(.trailing, 10) // .frame(maxWidth: .infinity, alignment: .leading) // } //current one that todo will be handled // var body2: some View { // VStack(alignment: .leading, spacing: 0) { // // Top section with current ticket and image // HStack(alignment: .top, spacing: 0) { // // Current ticket value // Text(data.currentTicket) // .font(.custom("Poppins-Medium", size: 28)) // .fontWeight(.bold) // .foregroundColor(.black) // .padding(.trailing, 10) // .frame(maxWidth: .infinity, alignment: .leading) // // Image(data.imageName) // .resizable() // .scaledToFit() // .frame(width: 34, height: 34) // } // .padding(.top, 16) // .padding(.horizontal, 16) // // // Currently Serving Header // Text(data.currentlyServingHeader) // .font(.custom("Poppins-Medium", size: 14)) // .fontWeight(.bold) // .foregroundColor(.black) // .padding(.trailing, 12) // .padding(.bottom, 10) // .padding(.horizontal, 16) // // // Currently Serving section with status // HStack(alignment: .center, spacing: 0) { // // Currently serving text // Text(data.currentlyServing) // .font(.custom("Poppins-Medium", size: 16)) // .fontWeight(.bold) // .foregroundColor(.black) // .frame(maxWidth: .infinity, alignment: .leading) // // // Status badge // Text(data.status) // .font(.custom("Poppins-Medium", size: 12)) // .foregroundColor(data.colorConfig.nextStepTextColor) // .padding(.horizontal, 12) // .padding(.vertical, 8) // .background( // RoundedRectangle(cornerRadius: 8) // .fill(Color.inactiveColor.opacity(0.2)) // ) // } // .padding(.horizontal, 16) // Spacer() // // Next Step badge // Text(data.nextStep) // .font(.custom("Poppins-Medium", size: 12)) // .foregroundColor(data.colorConfig.nextStepTextColor) // .padding(.horizontal, 12) // .padding(.vertical, 8) // .background( // RoundedRectangle(cornerRadius: 8) // .fill(data.colorConfig.nextStepBackground) // ) // .padding(.horizontal, 16) // .padding(.bottom, 16) // } // .background(Color.black.opacity(0.01)) // .cornerRadius(20) // .border(width: 10, edges: [.top, .bottom], color: data.colorConfig.containerBorderColor) // .border(width: 2, edges: [.leading, .trailing], color: data.colorConfig.containerBorderColor) // .overlay( // RoundedRectangle(cornerRadius: 20) // .stroke(data.colorConfig.containerBorderColor, lineWidth: 20) // ) // } } // MARK: - Live Activity Widget struct AppWidgetLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: LiveActivitiesAppAttributes.self) { context in // Extract parameters from Flutter // print("the queno is \(context.attributes.prefixedKey("title")) ===== \(sharedDefault.string(forKey: context.attributes.prefixedKey("queueNo")) )") // let title = sharedDefault.string(forKey: context.attributes.prefixedKey("title")) ?? "testes" // let contentText = sharedDefault.string(forKey: context.attributes.prefixedKey("contentText")) ?? "tested" // let currentTicket = sharedDefault.string(forKey: context.attributes.prefixedKey("queueNo")) ?? "test1234" // let status = sharedDefault.string(forKey: context.attributes.prefixedKey("callType")) ?? "test3345" // let currentlyServing = sharedDefault.string(forKey: context.attributes.prefixedKey("currentlyServingTitle")) ?? "tes" // let nextStep = sharedDefault.string(forKey: context.attributes.prefixedKey("currentlyServingStatus")) ?? // "test111" // let nextStepBackground = sharedDefault.integer(forKey: context.attributes.prefixedKey("currentlyServingCallType")) // let title = sharedDefault.string(forKey: context.attributes.prefixedKey("title")) ?? "testes" // let contentText = sharedDefault.string(forKey: context.attributes.prefixedKey("contentText")) ?? "tested" let currentTicket = getValueFromSharedDefaults("queueNo") let status = getValueFromSharedDefaults("currentlyServingStatus") ?? "test3345" let currentlyServing = getValueFromSharedDefaults("currentlyServingToken") let currentlyServingHeader = getValueFromSharedDefaults("currentlyServingTitle") let yourTicketHeader = getValueFromSharedDefaults("yourTicketHeader") let nextStep = getValueFromSharedDefaults("nextTicketStatus") let nextStepBackground = Int(sharedDefault.string(forKey: context.attributes.prefixedKey("currentlyServingCallType")) ?? "0") ?? 0 // Get color configuration based on nextStepBackground let colorConfig = WidgetColorConfig.config(for: nextStepBackground) // Create widget data let widgetData = NotificationWidgetData( currentTicket: currentTicket, currentlyServingHeader: currentlyServingHeader, currentlyServing: currentlyServing, status: status, nextStep: nextStep, imageName:"hmg", colorConfig: colorConfig, progress: 33 * Double(nextStepBackground), yourTicketHeaderText : yourTicketHeader, ) // // Lock screen/banner UI NotificationWidgetView(data: widgetData) // Text(widgetData.currentTicket) // .font(.custom("Poppins-Medium", size: 28)) // .fontWeight(.bold) // .foregroundColor(.black) // .padding(.trailing, 10) // .frame(maxWidth: .infinity, alignment: .leading) } dynamicIsland: { context in let currentTicket = getValueFromSharedDefaults("queueNo") let status = getValueFromSharedDefaults("currentlyServingStatus") ?? "test3345" let currentlyServing = getValueFromSharedDefaults("currentlyServingToken") let nextStep = getValueFromSharedDefaults("nextTicketStatus") let nextStepBackground = Int(sharedDefault.string(forKey: context.attributes.prefixedKey("currentlyServingCallType")) ?? "0") ?? 0 let currentlyServingHeader = getValueFromSharedDefaults("currentlyServingTitle") let yourTicketHeader = getValueFromSharedDefaults("yourTicketHeader") let colorConfig = WidgetColorConfig.config(for: nextStepBackground) let widgetData = NotificationWidgetData( currentTicket: currentTicket, currentlyServingHeader: currentlyServingHeader, currentlyServing: currentlyServing, status: status, nextStep: nextStep, imageName:"hmg", colorConfig: colorConfig, progress: 33 * Double(nextStepBackground), yourTicketHeaderText: yourTicketHeader ) return DynamicIsland { // // Lock screen/banner UI // // Expanded UI DynamicIslandExpandedRegion(.center) { NotificationWidgetView(data: widgetData, backgroundColor: .black.opacity(0.4)).padding(-24) } // DynamicIslandExpandedRegion(.trailing) { // Image("hmg") // .resizable() // .scaledToFit() // .frame(width: 34, height: 34) // .foregroundColor(.gray) // } // // DynamicIslandExpandedRegion(.center) { // VStack(spacing: 4) { // Text("Currently Servings:") // .font(.caption2) // .foregroundColor(.secondary) // // Text(currentlyServing) // .font(.caption) // .fontWeight(.semibold) // } // } // // DynamicIslandExpandedRegion(.bottom) { // HStack { // Text(status) // .font(.caption2) // .padding(.horizontal, 8) // .padding(.vertical, 4) // .background( // RoundedRectangle(cornerRadius: 6) // .fill(colorConfig.nextStepBackground.opacity(0.2)) // ) // // Spacer() // // Text(nextStep) // .font(.caption2) // .foregroundColor(colorConfig.nextStepTextColor) // .padding(.horizontal, 8) // .padding(.vertical, 4) // .background( // RoundedRectangle(cornerRadius: 6) // .fill(colorConfig.nextStepBackground) // ) // } // .padding(.horizontal) // } } compactLeading: { // Compact leading view Text(currentTicket) .font(.caption2) .fontWeight(.bold) } compactTrailing: { // Compact trailing view Image("hmg") .resizable() .scaledToFit() .frame(width: 12, height: 12) .foregroundColor(colorConfig.containerBorderColor).padding(4) } minimal: { // Minimal view HStack{ Text(currentTicket) .font(.caption2) .fontWeight(.bold) Spacer() Image("hmg") .resizable() .scaledToFit() .frame(width: 4, height: 4) .foregroundColor(colorConfig.containerBorderColor) }.padding(2) } } } } extension LiveActivitiesAppAttributes { func prefixedKey(_ key: String) -> String { print("the id is \(id)") return "\(id)_\(key)" } } //struct NotificationWidgetView_Previews: PreviewProvider { // static var previews: some View { // Group { // // Preview with nextStepBackground = 0 (Yellow border) // NotificationWidgetView( // data: NotificationWidgetData( // currentTicket: "42", // currentlyServingHeader: "Currently Serving3:", // currentlyServing: "Customer #15", // status: "Pending", // nextStep: "Next: Prepare Order", // imageName: "bell.fill", // colorConfig: .config(for: 0) // ) // ) // .previewDisplayName("Yellow Border (0)") // // // Preview with nextStepBackground = 1 (Red border) // NotificationWidgetView( // data: NotificationWidgetData( // currentTicket: "89", // currentlyServingHeader: "Currently Serving2:", // currentlyServing: "Customer #67", // status: "Active", // nextStep: "Next: Quality Check", // imageName: "bell.fill", // colorConfig: .config(for: 1) // ) // ) // .previewDisplayName("Red Border (1)") // // // Preview with nextStepBackground = 2 (Green border) // NotificationWidgetView( // data: NotificationWidgetData( // currentTicket: "125", // currentlyServingHeader: "Currently Serving1:", // currentlyServing: "Customer #98", // status: "Completed", // nextStep: "Next: Ready for Pickup", // imageName: "bell.fill", // colorConfig: .config(for: 2) // ) // ) // .previewDisplayName("Green Border (2)") // } // .padding() // .previewLayout(.sizeThatFits) // } //} func getValueFromSharedDefaults(_ containingString: String) -> String { print("Function `getValueFromSharedDefaults` is being called") let dictionary = sharedDefault.dictionaryRepresentation() print("Contents of sharedDefault: \(dictionary)") for (key, value) in dictionary { print("Inspecting key: \(key), value: \(value)") if key.contains(containingString) { let components = key.split(separator: "_") if let stringValue = value as? String { return stringValue } else { return "no value" } } } return "no value" }