Sync birthday to umbral contacts on DOB or share_birthday change

When a user updates their date of birth or toggles share_birthday,
all linked Person records (where linked_user_id matches) are updated.
If share_birthday is off, the birthday is cleared on linked records.
Virtual birthday events auto-reflect the change on next calendar poll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-07 06:01:35 +08:00
parent f0e8f450f3
commit 8aec5a5078
3 changed files with 26 additions and 0 deletions

View File

@ -25,6 +25,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func from sqlalchemy import select, func
from app.database import get_db from app.database import get_db
from app.services.connection import sync_birthday_to_contacts
from app.models.user import User from app.models.user import User
from app.models.session import UserSession from app.models.session import UserSession
from app.models.settings import Settings from app.models.settings import Settings
@ -686,6 +687,7 @@ async def update_profile(
current_user.email = update_data["email"] current_user.email = update_data["email"]
if "date_of_birth" in update_data: if "date_of_birth" in update_data:
current_user.date_of_birth = update_data["date_of_birth"] current_user.date_of_birth = update_data["date_of_birth"]
await sync_birthday_to_contacts(db, current_user.id)
if "umbral_name" in update_data: if "umbral_name" in update_data:
current_user.umbral_name = update_data["umbral_name"] current_user.umbral_name = update_data["umbral_name"]

View File

@ -7,6 +7,7 @@ from app.models.settings import Settings
from app.models.user import User from app.models.user import User
from app.schemas.settings import SettingsUpdate, SettingsResponse from app.schemas.settings import SettingsUpdate, SettingsResponse
from app.routers.auth import get_current_user, get_current_settings from app.routers.auth import get_current_user, get_current_settings
from app.services.connection import sync_birthday_to_contacts
router = APIRouter() router = APIRouter()
@ -94,6 +95,9 @@ async def update_settings(
for key, value in update_data.items(): for key, value in update_data.items():
setattr(current_settings, key, value) setattr(current_settings, key, value)
if "share_birthday" in update_data:
await sync_birthday_to_contacts(db, current_settings.user_id)
await db.commit() await db.commit()
await db.refresh(current_settings) await db.refresh(current_settings)

View File

@ -9,6 +9,7 @@ from datetime import date as date_type
from types import SimpleNamespace from types import SimpleNamespace
from typing import Optional from typing import Optional
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.models.person import Person from app.models.person import Person
@ -134,6 +135,25 @@ def create_person_from_connection(
) )
async def sync_birthday_to_contacts(db: AsyncSession, user_id: int) -> None:
"""Sync user's DOB to all Person records where linked_user_id == user_id.
Respects share_birthday setting if disabled, clears birthday on linked records."""
user = await db.execute(select(User).where(User.id == user_id))
user_obj = user.scalar_one()
settings_result = await db.execute(select(Settings).where(Settings.user_id == user_id))
settings_obj = settings_result.scalar_one_or_none()
share = settings_obj.share_birthday if settings_obj else False
new_birthday = user_obj.date_of_birth if share else None
await db.execute(
update(Person)
.where(Person.linked_user_id == user_id)
.values(birthday=new_birthday)
)
async def detach_umbral_contact(person: Person) -> None: async def detach_umbral_contact(person: Person) -> None:
"""Convert an umbral contact back to a standard contact. Does NOT commit. """Convert an umbral contact back to a standard contact. Does NOT commit.