Files
thoughts/ios/NightThoughts/Views/LegalView.swift
T
denshooter 5bc81d5b3b Initial commit: nightly iOS app + Supabase backend
iOS SwiftUI app with Supabase auth/realtime, Node.js backend,
Docker/Supabase self-hosted infrastructure, and APNs scheduler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 23:31:38 +02:00

233 lines
8.6 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
/// Impressum + Datenschutzerklärung
/// Muss vor dem Launch von einem Anwalt geprüft/ergänzt werden!
/// Kosten: ca. 300500 einmalig für Impressum + AGB + DSGVO-Datenschutzerklärung
struct LegalView: View {
@Environment(\.dismiss) var dismiss
@State private var tab = 0
var body: some View {
NavigationStack {
ZStack {
Color.nightBase.ignoresSafeArea()
VStack(spacing: 0) {
// Tab Switcher
HStack(spacing: 0) {
ForEach(["impressum", "datenschutz", "nutzungsbedingungen"], id: \.self) { label in
let idx = ["impressum", "datenschutz", "nutzungsbedingungen"].firstIndex(of: label)!
Button(label) { tab = idx }
.font(.nightLabel(12, weight: tab == idx ? .semibold : .regular))
.foregroundColor(tab == idx ? .nightPrimary : .nightSecondary)
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
.overlay(
Rectangle()
.fill(tab == idx ? Color.nightPurple : .clear)
.frame(height: 2),
alignment: .bottom
)
}
}
.padding(.horizontal, 16)
.overlay(Rectangle().fill(Color.nightBorder).frame(height: 1), alignment: .bottom)
ScrollView {
VStack(alignment: .leading, spacing: 0) {
switch tab {
case 0: ImpressumContent()
case 1: DatenschutzContent()
default: NutzungsbedingungenContent()
}
}
.padding(20)
}
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Schließen") { dismiss() }
.foregroundColor(.nightSecondary)
}
}
}
.preferredColorScheme(.dark)
}
}
// MARK: - Impressum
struct ImpressumContent: View {
var body: some View {
LegalSection(title: "Impressum") {
// PFLICHTANGABEN vor Launch ausfüllen!
LegalParagraph(title: "Angaben gemäß § 5 TMG") {
"""
[DEIN NAME]
[STRASSE HAUSNUMMER]
[PLZ ORT]
Deutschland
⚠️ Vor Launch ausfüllen!
"""
}
LegalParagraph(title: "Kontakt") {
"""
E-Mail: legal@xxx.dk0.dev
⚠️ Vor Launch ausfüllen!
"""
}
LegalParagraph(title: "Verantwortlich für den Inhalt nach § 18 Abs. 2 MStV") {
"[DEIN NAME], [ADRESSE]"
}
LegalParagraph(title: "Hinweis") {
"""
Diese App ist ein privates Projekt. Für die Richtigkeit, \
Vollständigkeit und Aktualität der Inhalte kann keine Gewähr übernommen werden.
"""
}
}
}
}
// MARK: - Datenschutz
struct DatenschutzContent: View {
var body: some View {
LegalSection(title: "Datenschutzerklärung") {
LegalParagraph(title: "⚠️ Hinweis") {
"""
Diese Datenschutzerklärung ist ein Entwurf und muss vor dem Launch \
von einem Datenschutzanwalt geprüft und vervollständigt werden. \
Kosten: ca. 300500€.
"""
}
LegalParagraph(title: "Verantwortlicher") {
"[DEIN NAME], [ADRESSE], [E-MAIL]"
}
LegalParagraph(title: "Welche Daten wir speichern") {
"""
• E-Mail-Adresse (für Account & Passwort-Reset)
• Benutzername und Anzeigename
• Posts, Reaktionen, Kommentare (Inhalte die du selbst erstellst)
• Push-Token (für Benachrichtigungen, optional)
• IP-Adresse in Server-Logs (max. 14 Tage)
"""
}
LegalParagraph(title: "Wofür wir Daten verwenden") {
"""
• Betrieb des Dienstes (Authentifizierung, Feed, Benachrichtigungen)
• Moderation (Meldungen von Inhalten)
• Keine Weitergabe an Dritte außer für den Betrieb notwendige Dienste
"""
}
LegalParagraph(title: "Serverstandort") {
"Alle Daten werden auf Servern in der EU gespeichert."
}
LegalParagraph(title: "Deine Rechte (DSGVO)") {
"""
• Auskunft über gespeicherte Daten: legal@xxx.dk0.dev
• Berichtigung falscher Daten
• Löschung: Account in den Einstellungen löschen — entfernt alle deine Daten sofort
• Datenübertragbarkeit: auf Anfrage per E-Mail
• Widerspruch gegen Verarbeitung: legal@xxx.dk0.dev
• Beschwerde bei der Datenschutzbehörde
"""
}
LegalParagraph(title: "Datenlöschung") {
"""
Posts werden 14 Stunden nach Erstellung aus dem öffentlichen Feed entfernt. \
Dein persönliches Tagebuch behältst du so lange du möchtest. \
Account-Löschung entfernt alle Daten dauerhaft und unwiderruflich.
"""
}
LegalParagraph(title: "Cookies / Tracking") {
"Wir verwenden keine Cookies, keine Tracker, keine Werbenetze."
}
}
}
}
// MARK: - Nutzungsbedingungen
struct NutzungsbedingungenContent: View {
var body: some View {
LegalSection(title: "Nutzungsbedingungen") {
LegalParagraph(title: "⚠️ Entwurf") {
"Diese Nutzungsbedingungen sind ein Entwurf und müssen vor dem Launch von einem Anwalt geprüft werden."
}
LegalParagraph(title: "Nutzung") {
"""
nightly ist ein Dienst für Personen ab 17 Jahren. \
Du bist für die Inhalte die du postest selbst verantwortlich.
"""
}
LegalParagraph(title: "Verbotene Inhalte") {
"""
Folgende Inhalte sind verboten:
• Hassrede, Diskriminierung, Bedrohung
• Belästigung oder Mobbing
• Illegale Inhalte jeglicher Art
• Spam oder kommerzielle Werbung
• Inhalte die andere Personen ohne deren Zustimmung zeigen
"""
}
LegalParagraph(title: "Moderation") {
"""
Gemeldete Inhalte werden geprüft und können ohne Vorankündigung entfernt werden. \
Bei schwerwiegenden Verstößen behalten wir uns die Sperrung des Accounts vor.
"""
}
LegalParagraph(title: "Haftungsausschluss") {
"""
Wir übernehmen keine Haftung für nutzergenerierte Inhalte. \
Der Dienst wird ohne Gewähr für Verfügbarkeit bereitgestellt.
"""
}
}
}
}
// MARK: - Reusable components
struct LegalSection<Content: View>: View {
let title: String
@ViewBuilder let content: Content
var body: some View {
VStack(alignment: .leading, spacing: 20) {
Text(title)
.font(.nightTitle(22))
.foregroundColor(.nightPrimary)
.padding(.bottom, 4)
content
}
}
}
struct LegalParagraph: View {
let title: String
let body: String
init(title: String, _ body: () -> String) {
self.title = title
self.body = body()
}
var body: some View {
VStack(alignment: .leading, spacing: 6) {
Text(title)
.font(.nightLabel(14, weight: .semibold))
.foregroundColor(.nightPrimary)
Text(body)
.font(.nightBody(14))
.foregroundColor(.nightSecondary)
.lineSpacing(4)
.fixedSize(horizontal: false, vertical: true)
}
}
}