Test
This commit is contained in:
parent
b02518850d
commit
be42ea7176
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildLocationStyle</key>
|
||||
<string>UseAppPreferences</string>
|
||||
<key>CompilationCachingSetting</key>
|
||||
<string>Default</string>
|
||||
<key>CustomBuildLocationType</key>
|
||||
<string>RelativeToDerivedData</string>
|
||||
<key>DerivedDataLocationStyle</key>
|
||||
<string>Default</string>
|
||||
<key>ShowSharedSchemesAutomaticallyEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1,24 +1,244 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// PanicButton
|
||||
//
|
||||
// Created by Dendi Yogia Pratama on 27/11/25.
|
||||
//
|
||||
|
||||
// ContentView.swift - Main Screen
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var message = "Klik tombol untuk mengirim notifikasi"
|
||||
@State private var selectedConditions: Set<String> = []
|
||||
@State private var additionalNotes = ""
|
||||
@State private var showEvacuationRoute = false
|
||||
@FocusState private var isNotesFieldFocused: Bool
|
||||
|
||||
let conditions = [
|
||||
"🔥 Kebakaran",
|
||||
"⛈️ Banjir",
|
||||
"🌊 Tsunami",
|
||||
"🌋 Gunung Meletus",
|
||||
"🌏 Gempa Bumi",
|
||||
"👿 Huru hara",
|
||||
"🐍 Binatang Buas",
|
||||
"☢️ Radiasi Nuklir",
|
||||
"☣️ Biohazard"
|
||||
]
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundStyle(.tint)
|
||||
Text("Hello, world!")
|
||||
NavigationView {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
Text("Terjadi Kondisi Darurat")
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.foregroundColor(.red)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
// Daftar kondisi dengan checkbox
|
||||
ForEach(conditions, id: \.self) { condition in
|
||||
HStack {
|
||||
Button(action: {
|
||||
isNotesFieldFocused = false
|
||||
toggleCondition(condition)
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: selectedConditions.contains(condition) ? "checkmark.square.fill" : "square")
|
||||
.foregroundColor(selectedConditions.contains(condition) ? .blue : .gray)
|
||||
Text(condition)
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
isNotesFieldFocused = false
|
||||
toggleCondition(condition)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer().frame(height: 16)
|
||||
|
||||
Text("Catatan tambahan:")
|
||||
.font(.system(size: 16))
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
TextEditor(text: $additionalNotes)
|
||||
.frame(height: 100)
|
||||
.padding(8)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(4)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(Color.gray, lineWidth: 1)
|
||||
)
|
||||
.focused($isNotesFieldFocused)
|
||||
|
||||
// Placeholder text
|
||||
if additionalNotes.isEmpty {
|
||||
Text("Tulis catatan di sini...")
|
||||
.foregroundColor(Color(.placeholderText))
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 16)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer().frame(height: 16)
|
||||
|
||||
Button(action: {
|
||||
isNotesFieldFocused = false
|
||||
sendReport()
|
||||
}) {
|
||||
Text("Kirim Laporan")
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.red)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
|
||||
Spacer().frame(height: 16)
|
||||
|
||||
Text("\"JANGAN PANIK! SEGERA EVAKUASI\nDIRI ANDA KE TITIK KUMPUL\"")
|
||||
.foregroundColor(.red)
|
||||
.font(.system(size: 15, weight: .medium))
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer().frame(height: 16)
|
||||
|
||||
Text(message)
|
||||
.padding(.top, 16)
|
||||
|
||||
Button(action: {
|
||||
isNotesFieldFocused = false
|
||||
showEvacuationRoute = true
|
||||
}) {
|
||||
Text("Lihat Jalur Evakuasi")
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.green)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.sheet(isPresented: $showEvacuationRoute) {
|
||||
EvacuationRouteView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toggleCondition(_ condition: String) {
|
||||
if selectedConditions.contains(condition) {
|
||||
selectedConditions.remove(condition)
|
||||
} else {
|
||||
selectedConditions.insert(condition)
|
||||
}
|
||||
}
|
||||
|
||||
func sendReport() {
|
||||
let conditions = Array(selectedConditions).joined(separator: ", ")
|
||||
let report = "Kondisi: \(conditions)\nCatatan: \(additionalNotes)"
|
||||
|
||||
sendNotification(condition: conditions, report: report) { response in
|
||||
DispatchQueue.main.async {
|
||||
self.message = response
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
// NetworkManager.swift - HTTP Request Handler
|
||||
func sendNotification(condition: String, report: String, completion: @escaping (String) -> Void) {
|
||||
let url = URL(string: "https://ntfy.ubharajaya.ac.id/panic-button")!
|
||||
|
||||
let tagMapping: [String: String] = [
|
||||
"🔥 Kebakaran": "fire",
|
||||
"⛈️ Banjir": "cloud_with_lightning_and_rain",
|
||||
"🌊 Tsunami": "ocean",
|
||||
"🌋 Gunung Meletus": "volcano",
|
||||
"🌏 Gempa Bumi": "earth_asia",
|
||||
"👿 Huru hara": "imp",
|
||||
"🐍 Binatang Buas": "snake",
|
||||
"☢️ Radiasi Nuklir": "radioactive",
|
||||
"☣️ Biohazard": "biohazard"
|
||||
]
|
||||
|
||||
let selectedList = condition
|
||||
.split(separator: ",")
|
||||
.map { $0.trimmingCharacters(in: .whitespaces) }
|
||||
.filter { !$0.isEmpty }
|
||||
|
||||
let cleanConditionText = selectedList.joined(separator: ", ")
|
||||
let emojiTags = selectedList.compactMap { tagMapping[String($0)] }
|
||||
let finalTags = ["Alert"] + emojiTags
|
||||
|
||||
let notesPart = report.components(separatedBy: "Catatan:").last ?? ""
|
||||
let finalReport = "Kondisi: \(cleanConditionText)\nCatatan:\(notesPart)"
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("Alert", forHTTPHeaderField: "Title")
|
||||
request.setValue("urgent", forHTTPHeaderField: "Priority")
|
||||
request.setValue(finalTags.joined(separator: ","), forHTTPHeaderField: "Tags")
|
||||
request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = finalReport.data(using: .utf8)
|
||||
|
||||
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
if let error = error {
|
||||
completion("Error: \(error.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
if let httpResponse = response as? HTTPURLResponse {
|
||||
if httpResponse.statusCode == 200 {
|
||||
completion("Notifikasi berhasil dikirim!")
|
||||
} else {
|
||||
completion("Gagal mengirim notifikasi: \(httpResponse.statusCode)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task.resume()
|
||||
}
|
||||
|
||||
// EvacuationRouteView.swift - Evacuation Route Screen
|
||||
struct EvacuationRouteView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Jalur Evakuasi")
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.padding(.top, 16)
|
||||
|
||||
Spacer().frame(height: 16)
|
||||
|
||||
// Tampilkan gambar jalur evakuasi
|
||||
// Ganti "jalur_evakuasi" dengan nama file gambar Anda
|
||||
Image("jalur_evakuasi")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 450)
|
||||
.padding()
|
||||
|
||||
Spacer().frame(height: 20)
|
||||
|
||||
Button(action: {
|
||||
dismiss()
|
||||
}) {
|
||||
Text("Close")
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.red)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user