Phase 3.1 WS2: FCM fallback Flutter + CC, unread badges, dynamic ping
- Control center: add mitra ping config UI (require ping toggle + interval)
- Mitra app StatusNotifier: honor require_ping and ping_interval_seconds
from API; skip heartbeat when ping not required
- Both apps: update notification services for FCM deep-linking
- mitra_app: handle chat_request (open_accept), session_closing
- client_app: handle session_closing, paired
- Unread badge providers:
- mitra_app: UnreadSessions provider (polls active-with-unread, badge
on active sessions button)
- client_app: UnreadCount provider (polls active-with-unread, badge
on _ActiveSessionCard)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,17 @@ const updateExtensionTimeoutConfig = async (extension_timeout_seconds) => {
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
// Phase 3.1: Mitra Ping Config
|
||||
const fetchMitraPingConfig = async () => {
|
||||
const res = await apiClient.get('/internal/config/mitra-ping')
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
const updateMitraPingConfig = async (data) => {
|
||||
const res = await apiClient.patch('/internal/config/mitra-ping', data)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
const fetchEarlyEndConfig = async () => {
|
||||
const res = await apiClient.get('/internal/config/early-end')
|
||||
return res.data.data
|
||||
@@ -101,7 +112,17 @@ export default function SettingsPage() {
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['config-early-end'] }),
|
||||
})
|
||||
|
||||
if (isLoading || maxLoading || ftLoading || etLoading || eeLoading) return <div>Loading...</div>
|
||||
// Phase 3.1: Mitra Ping
|
||||
const { data: mpData, isLoading: mpLoading } = useQuery({
|
||||
queryKey: ['config-mitra-ping'],
|
||||
queryFn: fetchMitraPingConfig,
|
||||
})
|
||||
const mpMutation = useMutation({
|
||||
mutationFn: updateMitraPingConfig,
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['config-mitra-ping'] }),
|
||||
})
|
||||
|
||||
if (isLoading || maxLoading || ftLoading || etLoading || eeLoading || mpLoading) return <div>Loading...</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -215,6 +236,39 @@ export default function SettingsPage() {
|
||||
</label>
|
||||
{eeMutation.isError && <p style={{ color: 'red' }}>Gagal menyimpan.</p>}
|
||||
</section>
|
||||
|
||||
<section style={{ marginBottom: 24 }}>
|
||||
<h2>Mitra Online Status (Ping)</h2>
|
||||
<p>Konfigurasi apakah mitra harus mengirim ping (heartbeat) untuk tetap online.</p>
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={mpData?.require_ping ?? true}
|
||||
onChange={e => mpMutation.mutate({ require_ping: e.target.checked })}
|
||||
disabled={mpMutation.isPending}
|
||||
/>
|
||||
Wajibkan Mitra Ping (Heartbeat)
|
||||
</label>
|
||||
<p style={{ fontSize: 12, color: '#666', marginBottom: 8 }}>
|
||||
Jika dinonaktifkan, mitra akan tetap online tanpa perlu mengirim ping. QC bertanggung jawab atas kualitas layanan mitra.
|
||||
</p>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<label>Interval Ping:</label>
|
||||
<input
|
||||
type="number"
|
||||
min="5"
|
||||
value={mpData?.ping_interval_seconds ?? 15}
|
||||
onChange={e => {
|
||||
const val = parseInt(e.target.value, 10)
|
||||
if (val >= 5) mpMutation.mutate({ ping_interval_seconds: val })
|
||||
}}
|
||||
disabled={mpMutation.isPending}
|
||||
style={{ width: 80 }}
|
||||
/>
|
||||
<span>detik</span>
|
||||
</div>
|
||||
{mpMutation.isError && <p style={{ color: 'red' }}>Gagal menyimpan.</p>}
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user