Proje: Okul B2B App · Hub: Okul B2B App — Incidents

Toast Modal Arkasında Kalıyor — Çözüm

Semptom

İçerik (duyuru/etkinlik/başarı) form modal’ında kullanıcı görsel eklemeden “Oluştur“‘a basınca:

  • Server 422 dönüyor
  • ApiClient.showToastForError() generic “İstek hatası” toast’u fırlatıyor
  • Toast root’ta (app/_layout.tsx’teki <Toast>) render edildiği için Modal’ın arkasında çıkıyor
  • Kullanıcı hata olduğunu göremiyor, formun kapanmasını bekliyor

Root Cause

React Native <Modal> native olarak açılır ve view hierarchy’nin en üstüne oturur. Kök View’a render edilmiş herhangi bir overlay (Toast, custom tooltip) modal açıkken arkada kalır.

react-native-toast-message tek global ref kullanır. Hangi <Toast> instance mount edilmişse Toast.show() ona düşer.

Çözüm — Modal İçine de <Toast /> Render Et

// components/school-content/ContentFormModal.tsx
import Toast from 'react-native-toast-message';
import { toastConfig } from '@/components/ui/ToastConfig';
 
<Modal visible={visible} presentationStyle="pageSheet">
  <SafeAreaView>
    <KeyboardAvoidingView>
      {/* form content */}
    </KeyboardAvoidingView>
    <Toast config={toastConfig} topOffset={75} />
  </SafeAreaView>
</Modal>

Modal mount olduğunda react-native-toast-message global ref’i bu instance’a döner, Toast.show() çağrıları modal üstünde görünür. Modal kapanınca root’taki <Toast>’a geri düşer.

Destekleyici Değişiklikler

  1. toastConfig ayrı dosyayacomponents/ui/ToastConfig.tsx. Hem root (_layout.tsx) hem modal’lar aynı config’i import eder (DRY).

  2. ApiClient 422’de global toast’ı atlautils/ApiClient.ts:

    const isValidationError = lastError instanceof ServerError && lastError.status === 422;
    if (!this.isPublicEndpoint(endpoint) && !isValidationError) {
      this.showToastForError(lastError, endpoint);
    }

    Validation hatalarını caller inline gösterir (generic “İstek hatası” karışıklığını önler).

  3. Client-side validation — Server 422’yi beklemeden form’da engelle:

    • En az 1 görsel (existingMedia.length + newMediaItems.length === 0 → hata)
    • Validation fail olunca Toast.show() + scrollRef.current?.scrollToEnd() (kullanıcı scroll etmeden bile hatayı görür)

Uygulanan Başka Modal’larda

Aynı sorun başka modal’larda (leads detail, gallery editor, school selection vb.) görülürse <Toast config={toastConfig} topOffset={75} />’i modal içine ekle. Shared config zaten ToastConfig.tsx’te.

Commit

Dev branch, unmerged commit. Dosyalar:

  • components/ui/ToastConfig.tsx (yeni)
  • components/school-content/ContentFormModal.tsx
  • utils/ApiClient.ts
  • app/_layout.tsx