UX: alarm-status, ablehnen-button, annika-footer, map polish

* Alarm-Status ist jetzt nur 'aktiv' wenn ein echter Push-Channel (Telegram
  mit Token+Chat oder E-Mail mit Adresse) konfiguriert ist. UI-only zählt
  nicht mehr als eingerichteter Alarm.
* Ablehnen-Button in der Wohnungsliste: flat_rejections (migration v4)
  speichert pro-User-Ablehnungen, abgelehnte Flats fallen aus Liste und
  Karte raus. Wiederholbar pro User unabhängig.
* Footer 'Programmiert für Annika ♥' erscheint nur auf Seiten, wenn annika
  angemeldet ist.
* Map: Hinweistext unter leerer Karte entfernt; alle Zoom-Mechanismen
  deaktiviert (Scrollrad, Doppelklick, Box, Touch, Tastatur, +/- Buttons).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Moritz 2026-04-21 12:09:44 +02:00
parent 376551213a
commit 42377f0b67
5 changed files with 97 additions and 18 deletions

View file

@ -185,6 +185,16 @@ MIGRATIONS: list[str] = [
ALTER TABLE flats ADD COLUMN lat REAL;
ALTER TABLE flats ADD COLUMN lng REAL;
""",
# 0004: per-user rejections — flats the user doesn't want in the list anymore
"""
CREATE TABLE IF NOT EXISTS flat_rejections (
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
flat_id TEXT NOT NULL REFERENCES flats(id),
rejected_at TEXT NOT NULL,
PRIMARY KEY (user_id, flat_id)
);
CREATE INDEX IF NOT EXISTS idx_rejections_user ON flat_rejections(user_id);
""",
]
@ -471,6 +481,33 @@ def recent_applications(user_id: Optional[int], limit: int = 50) -> list[sqlite3
).fetchall())
# ---------------------------------------------------------------------------
# Rejections (flats a user doesn't want to see anymore)
# ---------------------------------------------------------------------------
def reject_flat(user_id: int, flat_id: str) -> None:
with _lock:
_conn.execute(
"INSERT OR IGNORE INTO flat_rejections(user_id, flat_id, rejected_at) VALUES (?, ?, ?)",
(user_id, flat_id, now_iso()),
)
def unreject_flat(user_id: int, flat_id: str) -> None:
with _lock:
_conn.execute(
"DELETE FROM flat_rejections WHERE user_id = ? AND flat_id = ?",
(user_id, flat_id),
)
def rejected_flat_ids(user_id: int) -> set[str]:
rows = _conn.execute(
"SELECT flat_id FROM flat_rejections WHERE user_id = ?", (user_id,)
).fetchall()
return {row["flat_id"] for row in rows}
def last_application_for_flat(user_id: int, flat_id: str) -> Optional[sqlite3.Row]:
return _conn.execute(
"""SELECT * FROM applications