diff --git a/src/app/index.tsx b/src/app/index.tsx index 1e788f3..55b912c 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -381,6 +381,14 @@ export default function Tokens() { label: "Spawn Sample 10", onPress: () => devMenu.spawnSampleTokens(10), }, + { + androidIcon: CodeSymbol, + disabled: devMenu.tokenActionDisabled, + iosIcon: "list.bullet.rectangle.fill", + key: "tokens-spawn-activity", + label: "Spawn Activity Log", + onPress: devMenu.spawnAllActivityTypes, + }, { androidIcon: DeleteSymbol, destructive: true, diff --git a/src/components/notification-handler.tsx b/src/components/notification-handler.tsx index fe09f9e..0c16de2 100644 --- a/src/components/notification-handler.tsx +++ b/src/components/notification-handler.tsx @@ -5,6 +5,8 @@ import { handlePushAuthRequest, } from "@/services/push-auth"; import { usePushRequestStore } from "@/stores/push-request"; +import { useActivityStore } from "@/stores/activity"; +import { ActivityType } from "@/types/activity"; import type { PushRequest } from "@/types/push-request"; import { PushRequestStatus } from "@/types/push-request"; import { useCallback } from "react"; @@ -17,6 +19,7 @@ import { PushRequestPopup } from "./push-request-popup"; export function NotificationHandler() { const { tokens } = useToken(); const { pushRequests, updatePushRequestStatus } = usePushRequestStore(); + const addActivity = useActivityStore((state) => state.addActivity); const handleRequest = useCallback( async (request: PushRequest): Promise => { @@ -39,6 +42,14 @@ export function NotificationHandler() { if (action === "ACCEPT") { console.log("User accepted the push authentication"); updatePushRequestStatus(pushRequest.id, PushRequestStatus.Accepted); + addActivity({ + id: `push-approved-${pushRequest.id}`, + tokenId: pushRequest.serial, + type: ActivityType.PushApproved, + timestamp: Date.now(), + pushRequestId: pushRequest.id, + title: pushRequest.title || undefined, + }); void handleRequest({ ...pushRequest, status: PushRequestStatus.Accepted, @@ -46,6 +57,14 @@ export function NotificationHandler() { } else if (action === "DECLINE") { console.log("User declined the push authentication"); updatePushRequestStatus(pushRequest.id, PushRequestStatus.Declined); + addActivity({ + id: `push-denied-${pushRequest.id}`, + tokenId: pushRequest.serial, + type: ActivityType.PushDenied, + timestamp: Date.now(), + pushRequestId: pushRequest.id, + title: pushRequest.title || undefined, + }); void handleRequest({ ...pushRequest, status: PushRequestStatus.Declined, diff --git a/src/components/push-request-popup.tsx b/src/components/push-request-popup.tsx index 972e841..a73a251 100644 --- a/src/components/push-request-popup.tsx +++ b/src/components/push-request-popup.tsx @@ -1,7 +1,9 @@ import { Radii, Spacing, Typography } from "@/constants/theme"; import { useTheme } from "@/hooks/use-theme"; import { usePushRequestStore } from "@/stores/push-request"; +import { useActivityStore } from "@/stores/activity"; import { useTokenStore } from "@/stores/token"; +import { ActivityType } from "@/types/activity"; import type { PushRequest } from "@/types/push-request"; import { PushRequestStatus } from "@/types/push-request"; import { playImpactLightHaptic, playImpactMediumHaptic } from "@/utils/haptics"; @@ -44,6 +46,7 @@ export function PushRequestPopup({ const [isAnimatingOut, setIsAnimatingOut] = useState(false); const colorScheme = useColorScheme(); const { updatePushRequestStatus } = usePushRequestStore(); + const addActivity = useActivityStore((state) => state.addActivity); const pendingRequests = requests.filter( (request) => request.status === PushRequestStatus.Pending, ); @@ -68,11 +71,28 @@ export function PushRequestPopup({ setTimeout(() => { const updatedRequest = { ...currentRequest, status }; updatePushRequestStatus(updatedRequest.id, status); + addActivity({ + id: `push-${action === "accept" ? "approved" : "denied"}-${updatedRequest.id}`, + tokenId: updatedRequest.serial, + type: + action === "accept" + ? ActivityType.PushApproved + : ActivityType.PushDenied, + timestamp: Date.now(), + pushRequestId: updatedRequest.id, + title: updatedRequest.title || undefined, + }); onAction(updatedRequest); setIsAnimatingOut(false); }, 200); }, - [currentRequest, isAnimatingOut, onAction, updatePushRequestStatus], + [ + addActivity, + currentRequest, + isAnimatingOut, + onAction, + updatePushRequestStatus, + ], ); if (!currentRequest) return null; diff --git a/src/components/token-detail/token-overview-content.tsx b/src/components/token-detail/token-overview-content.tsx index 2f31c4b..67199c5 100644 --- a/src/components/token-detail/token-overview-content.tsx +++ b/src/components/token-detail/token-overview-content.tsx @@ -1,9 +1,12 @@ import { StatusCard } from "@/components/status-card"; +import { ThemedPressable } from "@/components/themed-pressable"; import { ThemedText } from "@/components/themed-text"; import { ThemedView } from "@/components/themed-view"; import { TokenImage } from "@/components/token-image"; import { Radii, Spacing, Typography } from "@/constants/theme"; import { useTheme } from "@/hooks/use-theme"; +import { useActivityStore } from "@/stores/activity"; +import { ActivityType, type Activity } from "@/types/activity"; import { PushTokenRefreshStatus, PushTokenRolloutState, @@ -13,7 +16,8 @@ import ForwardMediaSymbol from "@expo/material-symbols/forward_media.xml"; import { Button, Host, Icon, Row, Text } from "@expo/ui"; import { buttonStyle, controlSize } from "@expo/ui/swift-ui/modifiers"; import { useLingui } from "@lingui/react/macro"; -import { StyleSheet, View } from "react-native"; +import { useCallback, useMemo, useState } from "react"; +import { Alert, StyleSheet, View, type ColorValue } from "react-native"; import { formatTimestamp, getRolloutFailureDetails, @@ -22,6 +26,8 @@ import { refreshErrorMessages, } from "./token-detail-utils"; +const ACTIVITY_PAGE_SIZE = 20; + function DetailRow({ label, value }: { label: string; value: string }) { return ( @@ -39,6 +45,65 @@ function DetailRow({ label, value }: { label: string; value: string }) { ); } +function ActivityRow({ activity }: { activity: Activity }) { + const { t } = useLingui(); + const theme = useTheme(); + const content = { + [ActivityType.EnrollmentStarted]: { + label: t`Enrollment started`, + color: theme.branding, + }, + [ActivityType.EnrollmentCompleted]: { + label: t`Enrollment completed`, + color: theme.successBar, + }, + [ActivityType.EnrollmentFailed]: { + label: t`Enrollment failed`, + color: theme.error, + }, + [ActivityType.PushReceived]: { + label: t`Push request received`, + color: theme.branding, + }, + [ActivityType.PushApproved]: { + label: t`Push request approved`, + color: theme.successBar, + }, + [ActivityType.PushDenied]: { + label: t`Push request denied`, + color: theme.error, + }, + } satisfies Record; + const event = content[activity.type]; + const timestamp = new Date(activity.timestamp).toLocaleString(undefined, { + dateStyle: "medium", + timeStyle: "short", + }); + + return ( + + + + + {event.label} + + {activity.title ? ( + + {activity.title} + + ) : null} + + {timestamp} + + + + ); +} + export function TokenOverviewContent({ token, onRetryRollout, @@ -48,6 +113,18 @@ export function TokenOverviewContent({ }) { const { i18n, t } = useLingui(); const theme = useTheme(); + const allActivities = useActivityStore((state) => state.activities); + const clearTokenActivities = useActivityStore( + (state) => state.clearTokenActivities, + ); + const [visibleActivityCount, setVisibleActivityCount] = + useState(ACTIVITY_PAGE_SIZE); + const activities = useMemo( + () => allActivities.filter((activity) => activity.tokenId === token.id), + [allActivities, token.id], + ); + const visibleActivities = activities.slice(0, visibleActivityCount); + const hasOlderActivities = visibleActivities.length < activities.length; const isRolloutFailed = PushTokenRolloutState.isFailed(token.rolloutState); const rolloutFailureDetails = getRolloutFailureDetails(token.rolloutState); const rolloutStateLabel = getRolloutStateLabel(token.rolloutState); @@ -63,6 +140,23 @@ export function TokenOverviewContent({ networkMessage: i18n._(refreshErrorMessages.networkMessage), }, ); + const confirmClearActivityLog = useCallback(() => { + Alert.alert( + t`Clear activity log?`, + t`This permanently removes all activity for this token.`, + [ + { text: t`Cancel`, style: "cancel" }, + { + text: t`Clear`, + style: "destructive", + onPress: () => { + clearTokenActivities(token.id); + setVisibleActivityCount(ACTIVITY_PAGE_SIZE); + }, + }, + ], + ); + }, [clearTokenActivities, t, token.id]); return ( <> @@ -169,11 +263,126 @@ export function TokenOverviewContent({ + + + + + {t`Activity Log`} + + {activities.length > 0 ? ( + [ + styles.clearActivityButton, + { opacity: pressed ? 0.65 : 1 }, + ]} + type="errorBackground" + > + + {t`Clear`} + + + ) : null} + + + {activities.length > 0 ? ( + <> + {visibleActivities.map((activity) => ( + + ))} + {hasOlderActivities ? ( + + setVisibleActivityCount((count) => + Math.min(count + ACTIVITY_PAGE_SIZE, activities.length), + ) + } + style={({ pressed }) => [ + styles.showOlderButton, + { opacity: pressed ? 0.65 : 1 }, + ]} + type="fill" + > + + {t`Show older activity`} + + + ) : null} + + ) : ( + + + {t`No interactions yet`} + + + {t`Token approvals, denials, push requests, and enrollment activity will appear here.`} + + + )} + + ); } const styles = StyleSheet.create({ + activityCard: { + borderRadius: Radii.xl, + paddingHorizontal: Spacing.lg, + }, + activityContent: { + flex: 1, + gap: Spacing.xxs, + }, + activityDot: { + borderRadius: 5, + height: 10, + marginTop: Spacing.xs, + width: 10, + }, + activityHeader: { + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + }, + activityRow: { + flexDirection: "row", + gap: Spacing.md, + paddingVertical: Spacing.md, + }, + auditDescription: { + lineHeight: 20, + marginTop: Spacing.xs, + textAlign: "center", + }, + auditPlaceholder: { + alignItems: "center", + justifyContent: "center", + minHeight: 132, + padding: Spacing.xl, + }, + clearActivityButton: { + alignItems: "center", + borderRadius: Radii.pill, + paddingHorizontal: Spacing.md, + paddingVertical: Spacing.sm, + }, detailRow: { gap: Spacing.xs, paddingVertical: Spacing.md, @@ -216,6 +425,13 @@ const styles = StyleSheet.create({ serverErrorMessage: { lineHeight: 20, }, + showOlderButton: { + alignItems: "center", + borderRadius: Radii.lg, + marginBottom: Spacing.lg, + marginTop: Spacing.sm, + padding: Spacing.md, + }, statusMeta: { marginTop: Spacing.md, }, diff --git a/src/hooks/use-dev-menu.ts b/src/hooks/use-dev-menu.ts index f0cd39a..d1de020 100644 --- a/src/hooks/use-dev-menu.ts +++ b/src/hooks/use-dev-menu.ts @@ -1,6 +1,8 @@ import { useToken } from "@/hooks/use-token"; +import { useActivityStore } from "@/stores/activity"; import { usePushRequestStore } from "@/stores/push-request"; import { useSettingsStore } from "@/stores/settings"; +import { ActivityType } from "@/types/activity"; import { PushRequestStatus, type PushRequest } from "@/types/push-request"; import { PushTokenRolloutState, type PushToken } from "@/types/token"; import { Alert } from "react-native"; @@ -30,6 +32,7 @@ export function useDevMenu() { const { tokens, addToken, deleteToken, updateToken, rolloutToken } = useToken(); const { addPushRequest, clearPushRequests } = usePushRequestStore(); + const addActivity = useActivityStore((state) => state.addActivity); const token = tokens[0]; const tokenActionDisabled = !token; const resetOnboarding = useSettingsStore((state) => state.resetOnboarding); @@ -132,12 +135,39 @@ export function useDevMenu() { addPushRequest(request); }; + const spawnAllActivityTypes = () => { + if (!token) { + return; + } + + const batchId = Date.now(); + const activities = [ + ActivityType.EnrollmentStarted, + ActivityType.EnrollmentCompleted, + ActivityType.EnrollmentFailed, + ActivityType.PushReceived, + ActivityType.PushApproved, + ActivityType.PushDenied, + ] as const; + + activities.forEach((type, index) => { + addActivity({ + id: `sample-activity-${batchId}-${type}`, + tokenId: token.id, + type, + timestamp: batchId + index, + title: type.startsWith("push_") ? "Sample sign-in request" : undefined, + }); + }); + }; + return { clearAllTokens, clearPushRequests, demoRolloutFailure, demoRolloutSuccess, rolloutFirstToken, + spawnAllActivityTypes, spawnSamplePushRequest, spawnSampleTokens, tokenActionDisabled, diff --git a/src/hooks/use-notifications.ts b/src/hooks/use-notifications.ts index 761229d..58366d0 100644 --- a/src/hooks/use-notifications.ts +++ b/src/hooks/use-notifications.ts @@ -1,10 +1,12 @@ import { useNotificationStore } from "@/stores/notification"; +import { useActivityStore } from "@/stores/activity"; import { usePushRequestStore } from "@/stores/push-request"; import type { NotificationAction, NotificationResponseData, } from "@/types/notification"; import type { PushRequest } from "@/types/push-request"; +import { ActivityType } from "@/types/activity"; import { base32ToBase64 } from "@/utils/crypto"; import { addBackgroundMessageHandler, @@ -72,6 +74,7 @@ export function useNotifications(onAction?: NotificationActionHandler) { const { addPushRequest, getPushRequestByNonce, getPushRequestById } = usePushRequestStore(); + const addActivity = useActivityStore((state) => state.addActivity); const handlePushRequest = useCallback( async (pushRequest: PushRequest) => { @@ -108,9 +111,19 @@ export function useNotifications(onAction?: NotificationActionHandler) { return; } - addPushRequest(pushRequest); + const wasAdded = addPushRequest(pushRequest); + if (wasAdded) { + addActivity({ + id: `push-received-${pushRequest.id}`, + tokenId: pushRequest.serial, + type: ActivityType.PushReceived, + timestamp: pushRequest.sentAt, + pushRequestId: pushRequest.id, + title: pushRequest.title || undefined, + }); + } }, - [addPushRequest, tokens], + [addActivity, addPushRequest, tokens], ); const handleNotificationAction = useCallback( diff --git a/src/hooks/use-token.ts b/src/hooks/use-token.ts index 2a02434..ec6d00d 100644 --- a/src/hooks/use-token.ts +++ b/src/hooks/use-token.ts @@ -1,5 +1,7 @@ import { deleteTokenPrivateKey } from "@/services/token-rollout"; +import { useActivityStore } from "@/stores/activity"; import { useTokenStore } from "@/stores/token"; +import { ActivityType } from "@/types/activity"; import { PushTokenRolloutState } from "@/types/token"; import { parseTokenFromUri } from "@/utils/token"; import { useCallback } from "react"; @@ -22,22 +24,46 @@ export function useToken() { const updateToken = useTokenStore(selectUpdateToken); const removeToken = useTokenStore(selectRemoveToken); const rolloutTokenFromStore = useTokenStore(selectRolloutToken); + const addActivity = useActivityStore((state) => state.addActivity); + const clearTokenActivities = useActivityStore( + (state) => state.clearTokenActivities, + ); const createTokenFromURI = useCallback( - (uri: string) => { + async (uri: string) => { const newToken = parseTokenFromUri(uri); + if (tokens.some((token) => token.id === newToken.id)) { + return; + } + addToken(newToken); + addActivity({ + tokenId: newToken.id, + type: ActivityType.EnrollmentStarted, + timestamp: Date.now(), + }); - // Trigger rollout for the newly added token - rolloutTokenFromStore(newToken.id).catch((error) => { + try { + await rolloutTokenFromStore(newToken.id); + addActivity({ + tokenId: newToken.id, + type: ActivityType.EnrollmentCompleted, + timestamp: Date.now(), + }); + } catch (error) { + addActivity({ + tokenId: newToken.id, + type: ActivityType.EnrollmentFailed, + timestamp: Date.now(), + }); console.error( `Failed to rollout newly added token ${newToken.id}:`, error, ); - }); + } }, - [addToken, rolloutTokenFromStore], + [addActivity, addToken, rolloutTokenFromStore, tokens], ); const rolloutToken = useCallback( @@ -56,9 +82,29 @@ export function useToken() { updateToken(id, { rolloutState: PushTokenRolloutState.Pending }); } - await rolloutTokenFromStore(id); + addActivity({ + tokenId: id, + type: ActivityType.EnrollmentStarted, + timestamp: Date.now(), + }); + + try { + await rolloutTokenFromStore(id); + addActivity({ + tokenId: id, + type: ActivityType.EnrollmentCompleted, + timestamp: Date.now(), + }); + } catch (error) { + addActivity({ + tokenId: id, + type: ActivityType.EnrollmentFailed, + timestamp: Date.now(), + }); + throw error; + } }, - [tokens, updateToken, rolloutTokenFromStore], + [addActivity, tokens, updateToken, rolloutTokenFromStore], ); const deleteToken = useCallback( @@ -79,8 +125,9 @@ export function useToken() { } removeToken(id); + clearTokenActivities(id); }, - [tokens, removeToken], + [clearTokenActivities, tokens, removeToken], ); return { diff --git a/src/locales/de/messages.js b/src/locales/de/messages.js index 55c5a71..85c75dd 100644 --- a/src/locales/de/messages.js +++ b/src/locales/de/messages.js @@ -1 +1 @@ -/*eslint-disable*/module.exports={messages:JSON.parse("{\"0DlQl0\":[\"Noch keine Token vorhanden\"],\"0EvA_s\":[\"Möchtest du diesen Token wirklich löschen?\"],\"0VZTWA\":[\"Genehmige die Anmeldung nur, wenn du sie selbst gestartet hast. Lehne sie im Zweifel ab.\"],\"0_bbfl\":[\"Bewahre deine Authentifizierungs-Token an einem Ort auf und genehmige Anmeldungen sicher von diesem Gerät aus.\"],\"1QfxQT\":[\"Schließen\"],\"1m41_U\":[\"Feedback senden\"],\"1njn7W\":[\"Hell\"],\"2EKhU_\":[\"Protokolle liefern mehr Kontext und helfen uns, die Ursache des Problems zu finden.\"],\"2b4lV4\":[\"Aktualisierung fehlgeschlagen\"],\"3DRYd7\":[\"Mit dem Senden des Feedbacks stimmst du der Verarbeitung deiner Daten gemäß unserer [Datenschutzerklärung](https://edumfa.io/app-privacy) zu.\"],\"3OQxEV\":[\"Servernachricht\"],\"47yaVS\":[\"Google Play-Dienste sind nicht verfügbar, daher können Anfragen nicht automatisch eingehen. Ziehe die Token-Liste nach unten, um nach ausstehenden Anfragen zu suchen.\"],\"4aqCBZ\":[\"Keine Anmeldeanfrage verpassen\"],\"4xUhXP\":[\"Benachrichtigungseinstellungen öffnen\"],\"6jfS51\":[\"Willkommen\"],\"6mYeNy\":[\"Serverregistrierung fehlgeschlagen\"],\"79FlZ7\":[\"Benachrichtigungen sind aktiviert\"],\"7GD6En\":[\"Anmeldeanfragen können unbemerkt bleiben\"],\"7yOnZx\":[\"Das Gerät konnte das für diesen Token erforderliche RSA-Schlüsselpaar nicht erstellen. Versuche den Rollout erneut und lasse die App währenddessen geöffnet.\"],\"858Cvj\":[\"Der QR-Code wird von dieser App nicht unterstützt. Bitte verwenden Sie eine allgemeine Authenticator-App.\"],\"87a_t_\":[\"Bezeichnung\"],\"88u4WE\":[\"Feedback-Typ\"],\"8ZFiCO\":[\"Dein Feedback wurde erfolgreich übermittelt.\"],\"A9KSfI\":[\"Hilf, die App zu verbessern\"],\"BrFR00\":[\"Registrierung wird abgeschlossen\"],\"CVIHyt\":[\"Bitte beschreibe dein Feedback.\"],\"CsH7i4\":[\"Anonymisierte Absturz- und Fehlerdiagnosen senden\"],\"DEKI9D\":[\"Projektwebsite\"],\"DPfwMq\":[\"Fertig\"],\"Dnn2XG\":[\"Automatisch\"],\"DvOF3m\":[\"Teile Absturz- und Fehlerberichte, um die Zuverlässigkeit zu verbessern. Sie enthalten niemals Token-Geheimnisse, Passwörter oder Namen von Einrichtungen.\"],\"DyF_C_\":[\"Lizenzinformationen sind nicht verfügbar.\"],\"EAyh56\":[\"Anmeldung prüfen\"],\"EBRcBj\":[\"Haptik\"],\"ED9KJC\":[\"Was möchtest du uns mitteilen?\"],\"ET7frA\":[\"Kamerazugriff erlauben\"],\"EfBEmE\":[\"Rollout läuft...\"],\"F37c1s\":[\"Einstellungen öffnen\"],\"FEr96N\":[\"Erscheinungsbild\"],\"FSVCou\":[\"Nach unten ziehen, um Anfragen abzurufen\"],\"G0TPBU\":[\"QR-Code-Scanner\"],\"I92Z-b\":[\"Benachrichtigungen aktivieren\"],\"IPFr7d\":[\"Wähle, ob du anonymisierte Absturz- und Fehlerberichte teilen möchtest.\"],\"IRRc6m\":[\"Anonyme Berichte teilen\"],\"IT-f_-\":[\"Schlüssel werden generiert\"],\"Ijsgym\":[\"Token löschen\"],\"JG7Ls-\":[\"Aktiviere den Kamerazugriff in den Einstellungen, um QR-Codes zu scannen.\"],\"Lu4tTO\":[\"Platziere den QR-Code im Rahmen\"],\"Mi0UmV\":[\"Registrierungsantwort fehlgeschlagen\"],\"NkQliA\":[\"Du kannst jetzt Anmeldeanfragen empfangen und freigeben.\"],\"NsiA8m\":[\"Rollout fehlgeschlagen\"],\"O7obZD\":[\"Dieses Token konnte nicht aktualisiert werden, weil der Push-Dienst nicht erreichbar war. Prüfe deine Verbindung oder versuche es später erneut.\"],\"On0aF2\":[\"Webseite\"],\"OugAQq\":[\"QR-Code hochladen\"],\"PBxg_E\":[\"Nicht jetzt\"],\"PvN-FE\":[\"Die Token-Version wird von dieser App nicht unterstützt. Bitte aktualisiere die App auf die neueste Version oder kontaktiere deine Institution für Unterstützung.\"],\"Q__-1-\":[\"Fehlerbericht\"],\"QchA_G\":[\"E-Mail (optional)\"],\"Qm1NmK\":[\"ODER\"],\"QmIEhK\":[\"Authenticator-Apps suchen\"],\"QoroAc\":[\"Füge deinen ersten eduMFA Push-Token hinzu, um Anmeldungen sicher von diesem Gerät aus zu genehmigen.\"],\"R1QFqA\":[\"Funktionswunsch\"],\"R8Gqnt\":[\"Open-Source-Lizenzen\"],\"ROheaS\":[\"Version \",[\"itemVersion\"],\" · \",[\"itemLicense\"]],\"RkXlPZ\":[\"GitHub\"],\"Ro1gox\":[\"Token bearbeiten\"],\"RoafuO\":[\"Feedback senden\"],\"SWOmlM\":[\"Aktualisiere den Anzeigenamen für diesen Token.\"],\"ScJ9fj\":[\"Datenschutzerklärung\"],\"TP9_K5\":[\"Token\"],\"TfpFBG\":[\"Deine Wahl\"],\"TqCsD2\":[[\"totalPending\"],\" Anfragen warten. Prüfe jede einzeln.\"],\"Tz0i8g\":[\"Einstellungen\"],\"UMeccq\":[\"Schlüsselgenerierung fehlgeschlagen\"],\"UO-3EH\":[\"Dieses Token hat die Registrierung nicht abgeschlossen und kann keine Push-Anfragen empfangen, bis der Rollout erfolgreich ist.\"],\"URmyfc\":[\"Details\"],\"UbRKMZ\":[\"Ausstehend\"],\"VIZNLO\":[\"Öffentlicher Schlüssel wird registriert\"],\"VSWXlh\":[\"Feedback-Details\"],\"WRdUP2\":[\"Kamerazugriff ist erforderlich, um den QR-Code zu scannen und einen Token zu koppeln.\"],\"Weq9zb\":[\"Allgemein\"],\"YYVGS5\":[\"Protokolle einschließen\"],\"YirHq7\":[\"Feedback\"],\"Z2CsKg\":[\"Anonyme Berichte nicht teilen\"],\"Z7ZXbT\":[\"Freigeben\"],\"ZDIydz\":[\"Loslegen\"],\"ZYIECH\":[\"Benachrichtigungen sind deaktiviert\"],\"_ijUgq\":[\"Lass dich benachrichtigen, wenn eine Anmeldung deine Genehmigung erfordert.\"],\"aAIQg2\":[\"Darstellung\"],\"agAz3d\":[\"eduMFA Push bewerten\"],\"b75KIN\":[\"App-Sprache in den Systemeinstellungen ändern\"],\"bfECx6\":[\"Dieses Token konnte nicht beim Registrierungsserver registriert werden. Prüfe deine Verbindung oder versuche es später erneut.\"],\"c7uz-G\":[\"Aktiviere Benachrichtigungen in den Einstellungen, damit du reagieren kannst, sobald eine Anfrage eingeht.\"],\"cFCKYZ\":[\"Ablehnen\"],\"cnGeoo\":[\"Löschen\"],\"dEgA5A\":[\"Abbrechen\"],\"dN-1Th\":[\"Öffnen mit deiner Authenticator-App\"],\"dum6Mb\":[\"Aktualisiere...\"],\"eMwMfT\":[\"Aktiviere Benachrichtigungen, um Push-Genehmigungsanfragen auf diesem Gerät zu empfangen.\"],\"ePK91l\":[\"Bearbeiten\"],\"g0aJ_D\":[\"Name und E-Mail-Adresse sind optional. Sie ermöglichen es uns, dich bei Rückfragen zu deinem Feedback zu kontaktieren.\"],\"g3UF2V\":[\"Akzeptieren\"],\"g7oOIY\":[\"Token suchen\"],\"g7yB5h\":[\"Die Serverantwort konnte nicht verarbeitet werden oder enthielt nicht das erwartete Tokenmaterial.\"],\"hevXA2\":[\"Zuletzt fehlgeschlagen\"],\"iAqBor\":[\"Gib eine Bezeichnung für dieses Token ein.\"],\"iDNBZe\":[\"Benachrichtigungen\"],\"iQmbPb\":[\"Lizenz\"],\"jbq7j2\":[\"Ablehnen\"],\"jiR92q\":[\"Neues Push-Token koppeln\"],\"k4nIkP\":[\"Keine Token gefunden\"],\"kLttbL\":[\"Registrierung fehlgeschlagen\"],\"lF4FiS\":[\"Kein QR-Code gefunden\"],\"liw26p\":[\"Anonyme Berichte\"],\"mRd7qL\":[\"Feedback konnte nicht gesendet werden. Bitte versuche es erneut.\"],\"mVSlBU\":[\"Registriert\"],\"ml5TCa\":[\"Der bereitgestellte QR-Code ist kein gültiger eduMFA-Token.\"],\"o4_Dwt\":[\"Kamerazugriff ist deaktiviert\"],\"pphVNl\":[\"Dein Bild enthielt keinen QR-Code.\"],\"pvnfJD\":[\"Dunkel\"],\"q9XKrW\":[\"Antwortverarbeitung fehlgeschlagen\"],\"rjGI_Q\":[\"Datenschutz\"],\"roN3CK\":[\"eduMFA Push verwendet \",[\"licenseCount\"],\" Open-Source-Pakete. Die Paketdetails werden aus den installierten Produktionsabhängigkeiten generiert.\"],\"tfDRzk\":[\"Speichern\"],\"tqFPnN\":[\"Mit dem Senden des Feedbacks akzeptierst du die Verarbeitung deiner Daten gemäß unserer\"],\"tsg9Ze\":[\"Dieses Token konnte nicht aktualisiert werden. Es wurde möglicherweise auf dem Server entfernt oder die Institution, die den Push-Dienst betreibt, hat ein technisches Problem.\"],\"u-uuOt\":[\"Willkommen bei <0/>\"],\"u5FNLt\":[\"Absturz- und Fehlerberichte\"],\"uAQUqI\":[\"Status\"],\"uI1l6M\":[\"Bezeichnung erforderlich\"],\"uZ25pH\":[\"Kontakt (optional)\"],\"uh8i5u\":[\"Rollout erneut versuchen\"],\"uyJsf6\":[\"Über\"],\"vXIe7J\":[\"Sprache\"],\"wJAhPZ\":[\"Name (optional)\"],\"xGVfLh\":[\"Weiter\"],\"xPl9LS\":[\"Versuche einen anderen Token-Namen, Aussteller oder eine andere Seriennummer.\"],\"xkjgVR\":[\"Token konnte nicht hinzugefügt werden\"],\"xoqD_n\":[\"Gib eine gültige E-Mail-Adresse ein.\"],\"yOrM5-\":[\"Vielen Dank für dein Feedback\"],\"ypfsn_\":[\"Seriennummer\"],\"yz7wBu\":[\"Schließen\"],\"zXZJoN\":[\"Token hinzufügen\"]}")}; \ No newline at end of file +/*eslint-disable*/module.exports={messages:JSON.parse("{\"0DlQl0\":[\"Noch keine Token vorhanden\"],\"0EvA_s\":[\"Möchtest du diesen Token wirklich löschen?\"],\"0VZTWA\":[\"Genehmige die Anmeldung nur, wenn du sie selbst gestartet hast. Lehne sie im Zweifel ab.\"],\"0_bbfl\":[\"Bewahre deine Authentifizierungs-Token an einem Ort auf und genehmige Anmeldungen sicher von diesem Gerät aus.\"],\"1QfxQT\":[\"Schließen\"],\"1m41_U\":[\"Feedback senden\"],\"1njn7W\":[\"Hell\"],\"2EKhU_\":[\"Protokolle liefern mehr Kontext und helfen uns, die Ursache des Problems zu finden.\"],\"2b4lV4\":[\"Aktualisierung fehlgeschlagen\"],\"3DRYd7\":[\"Mit dem Senden des Feedbacks stimmst du der Verarbeitung deiner Daten gemäß unserer [Datenschutzerklärung](https://edumfa.io/app-privacy) zu.\"],\"3OQxEV\":[\"Servernachricht\"],\"3ovE9o\":[\"Token-Genehmigungen, Ablehnungen, Push-Anfragen und Registrierungsaktivitäten werden hier angezeigt.\"],\"47yaVS\":[\"Google Play-Dienste sind nicht verfügbar, daher können Anfragen nicht automatisch eingehen. Ziehe die Token-Liste nach unten, um nach ausstehenden Anfragen zu suchen.\"],\"4aqCBZ\":[\"Keine Anmeldeanfrage verpassen\"],\"4xUhXP\":[\"Benachrichtigungseinstellungen öffnen\"],\"6jfS51\":[\"Willkommen\"],\"6mYeNy\":[\"Serverregistrierung fehlgeschlagen\"],\"79FlZ7\":[\"Benachrichtigungen sind aktiviert\"],\"7GD6En\":[\"Anmeldeanfragen können unbemerkt bleiben\"],\"7yOnZx\":[\"Das Gerät konnte das für diesen Token erforderliche RSA-Schlüsselpaar nicht erstellen. Versuche den Rollout erneut und lasse die App währenddessen geöffnet.\"],\"858Cvj\":[\"Der QR-Code wird von dieser App nicht unterstützt. Bitte verwenden Sie eine allgemeine Authenticator-App.\"],\"87a_t_\":[\"Bezeichnung\"],\"88u4WE\":[\"Feedback-Typ\"],\"8DuEDH\":[\"Noch keine Interaktionen\"],\"8ZFiCO\":[\"Dein Feedback wurde erfolgreich übermittelt.\"],\"A9KSfI\":[\"Hilf, die App zu verbessern\"],\"BlvmX5\":[\"Registrierung fehlgeschlagen\"],\"BrFR00\":[\"Registrierung wird abgeschlossen\"],\"CVIHyt\":[\"Bitte beschreibe dein Feedback.\"],\"CsH7i4\":[\"Anonymisierte Absturz- und Fehlerdiagnosen senden\"],\"DEKI9D\":[\"Projektwebsite\"],\"DPfwMq\":[\"Fertig\"],\"Dnn2XG\":[\"Automatisch\"],\"DvOF3m\":[\"Teile Absturz- und Fehlerberichte, um die Zuverlässigkeit zu verbessern. Sie enthalten niemals Token-Geheimnisse, Passwörter oder Namen von Einrichtungen.\"],\"DyF_C_\":[\"Lizenzinformationen sind nicht verfügbar.\"],\"EAyh56\":[\"Anmeldung prüfen\"],\"EBRcBj\":[\"Haptik\"],\"ED9KJC\":[\"Was möchtest du uns mitteilen?\"],\"ET7frA\":[\"Kamerazugriff erlauben\"],\"EfBEmE\":[\"Rollout läuft...\"],\"F37c1s\":[\"Einstellungen öffnen\"],\"FEr96N\":[\"Erscheinungsbild\"],\"FSVCou\":[\"Nach unten ziehen, um Anfragen abzurufen\"],\"G0TPBU\":[\"QR-Code-Scanner\"],\"Hrlil0\":[\"Registrierung gestartet\"],\"I5kL4f\":[\"Aktivitätsprotokoll\"],\"I92Z-b\":[\"Benachrichtigungen aktivieren\"],\"IPFr7d\":[\"Wähle, ob du anonymisierte Absturz- und Fehlerberichte teilen möchtest.\"],\"IRRc6m\":[\"Anonyme Berichte teilen\"],\"IT-f_-\":[\"Schlüssel werden generiert\"],\"Ijsgym\":[\"Token löschen\"],\"JG7Ls-\":[\"Aktiviere den Kamerazugriff in den Einstellungen, um QR-Codes zu scannen.\"],\"Lu4tTO\":[\"Platziere den QR-Code im Rahmen\"],\"Mi0UmV\":[\"Registrierungsantwort fehlgeschlagen\"],\"NkQliA\":[\"Du kannst jetzt Anmeldeanfragen empfangen und freigeben.\"],\"NsiA8m\":[\"Rollout fehlgeschlagen\"],\"O7obZD\":[\"Dieses Token konnte nicht aktualisiert werden, weil der Push-Dienst nicht erreichbar war. Prüfe deine Verbindung oder versuche es später erneut.\"],\"OSBdo6\":[\"Ältere Aktivitäten anzeigen\"],\"On0aF2\":[\"Webseite\"],\"OugAQq\":[\"QR-Code hochladen\"],\"PBxg_E\":[\"Nicht jetzt\"],\"PvN-FE\":[\"Die Token-Version wird von dieser App nicht unterstützt. Bitte aktualisiere die App auf die neueste Version oder kontaktiere deine Institution für Unterstützung.\"],\"Q__-1-\":[\"Fehlerbericht\"],\"QchA_G\":[\"E-Mail (optional)\"],\"Qm1NmK\":[\"ODER\"],\"QmIEhK\":[\"Authenticator-Apps suchen\"],\"QoroAc\":[\"Füge deinen ersten eduMFA Push-Token hinzu, um Anmeldungen sicher von diesem Gerät aus zu genehmigen.\"],\"R1QFqA\":[\"Funktionswunsch\"],\"R8Gqnt\":[\"Open-Source-Lizenzen\"],\"ROheaS\":[\"Version \",[\"itemVersion\"],\" · \",[\"itemLicense\"]],\"RkXlPZ\":[\"GitHub\"],\"Ro1gox\":[\"Token bearbeiten\"],\"RoafuO\":[\"Feedback senden\"],\"SWOmlM\":[\"Aktualisiere den Anzeigenamen für diesen Token.\"],\"ScJ9fj\":[\"Datenschutzerklärung\"],\"TP9_K5\":[\"Token\"],\"TfpFBG\":[\"Deine Wahl\"],\"TqCsD2\":[[\"totalPending\"],\" Anfragen warten. Prüfe jede einzeln.\"],\"Tz0i8g\":[\"Einstellungen\"],\"UMeccq\":[\"Schlüsselgenerierung fehlgeschlagen\"],\"UO-3EH\":[\"Dieses Token hat die Registrierung nicht abgeschlossen und kann keine Push-Anfragen empfangen, bis der Rollout erfolgreich ist.\"],\"URmyfc\":[\"Details\"],\"UbRKMZ\":[\"Ausstehend\"],\"VIZNLO\":[\"Öffentlicher Schlüssel wird registriert\"],\"VSWXlh\":[\"Feedback-Details\"],\"WRdUP2\":[\"Kamerazugriff ist erforderlich, um den QR-Code zu scannen und einen Token zu koppeln.\"],\"Weq9zb\":[\"Allgemein\"],\"YXukeV\":[\"Push-Anfrage empfangen\"],\"YYVGS5\":[\"Protokolle einschließen\"],\"YirHq7\":[\"Feedback\"],\"Z2CsKg\":[\"Anonyme Berichte nicht teilen\"],\"Z7ZXbT\":[\"Freigeben\"],\"ZDIydz\":[\"Loslegen\"],\"ZYIECH\":[\"Benachrichtigungen sind deaktiviert\"],\"_ijUgq\":[\"Lass dich benachrichtigen, wenn eine Anmeldung deine Genehmigung erfordert.\"],\"aAIQg2\":[\"Darstellung\"],\"agAz3d\":[\"eduMFA Push bewerten\"],\"b75KIN\":[\"App-Sprache in den Systemeinstellungen ändern\"],\"bfECx6\":[\"Dieses Token konnte nicht beim Registrierungsserver registriert werden. Prüfe deine Verbindung oder versuche es später erneut.\"],\"c7uz-G\":[\"Aktiviere Benachrichtigungen in den Einstellungen, damit du reagieren kannst, sobald eine Anfrage eingeht.\"],\"cFCKYZ\":[\"Ablehnen\"],\"cnGeoo\":[\"Löschen\"],\"d3Vukm\":[\"Push-Anfrage abgelehnt\"],\"dEgA5A\":[\"Abbrechen\"],\"dN-1Th\":[\"Öffnen mit deiner Authenticator-App\"],\"dum6Mb\":[\"Aktualisiere...\"],\"eMwMfT\":[\"Aktiviere Benachrichtigungen, um Push-Genehmigungsanfragen auf diesem Gerät zu empfangen.\"],\"ePK91l\":[\"Bearbeiten\"],\"g0aJ_D\":[\"Name und E-Mail-Adresse sind optional. Sie ermöglichen es uns, dich bei Rückfragen zu deinem Feedback zu kontaktieren.\"],\"g3UF2V\":[\"Akzeptieren\"],\"g7oOIY\":[\"Token suchen\"],\"g7yB5h\":[\"Die Serverantwort konnte nicht verarbeitet werden oder enthielt nicht das erwartete Tokenmaterial.\"],\"hevXA2\":[\"Zuletzt fehlgeschlagen\"],\"iAqBor\":[\"Gib eine Bezeichnung für dieses Token ein.\"],\"iDNBZe\":[\"Benachrichtigungen\"],\"iDbChF\":[\"Registrierung abgeschlossen\"],\"iQmbPb\":[\"Lizenz\"],\"jbq7j2\":[\"Ablehnen\"],\"jiR92q\":[\"Neues Push-Token koppeln\"],\"k4nIkP\":[\"Keine Token gefunden\"],\"kLttbL\":[\"Registrierung fehlgeschlagen\"],\"ktAc5i\":[\"Aktivitätsprotokoll leeren?\"],\"lAOkhV\":[\"Push-Anfrage genehmigt\"],\"lF4FiS\":[\"Kein QR-Code gefunden\"],\"liw26p\":[\"Anonyme Berichte\"],\"mRd7qL\":[\"Feedback konnte nicht gesendet werden. Bitte versuche es erneut.\"],\"mVSlBU\":[\"Registriert\"],\"ml5TCa\":[\"Der bereitgestellte QR-Code ist kein gültiger eduMFA-Token.\"],\"moNf7g\":[\"Dadurch werden alle Aktivitäten für dieses Token dauerhaft entfernt.\"],\"o4_Dwt\":[\"Kamerazugriff ist deaktiviert\"],\"pphVNl\":[\"Dein Bild enthielt keinen QR-Code.\"],\"pvnfJD\":[\"Dunkel\"],\"q9XKrW\":[\"Antwortverarbeitung fehlgeschlagen\"],\"rjGI_Q\":[\"Datenschutz\"],\"roN3CK\":[\"eduMFA Push verwendet \",[\"licenseCount\"],\" Open-Source-Pakete. Die Paketdetails werden aus den installierten Produktionsabhängigkeiten generiert.\"],\"tfDRzk\":[\"Speichern\"],\"tqFPnN\":[\"Mit dem Senden des Feedbacks akzeptierst du die Verarbeitung deiner Daten gemäß unserer\"],\"tsg9Ze\":[\"Dieses Token konnte nicht aktualisiert werden. Es wurde möglicherweise auf dem Server entfernt oder die Institution, die den Push-Dienst betreibt, hat ein technisches Problem.\"],\"u-uuOt\":[\"Willkommen bei <0/>\"],\"u5FNLt\":[\"Absturz- und Fehlerberichte\"],\"uAQUqI\":[\"Status\"],\"uI1l6M\":[\"Bezeichnung erforderlich\"],\"uZ25pH\":[\"Kontakt (optional)\"],\"uh8i5u\":[\"Rollout erneut versuchen\"],\"uyJsf6\":[\"Über\"],\"vXIe7J\":[\"Sprache\"],\"wJAhPZ\":[\"Name (optional)\"],\"xCJdfg\":[\"Leeren\"],\"xGVfLh\":[\"Weiter\"],\"xPl9LS\":[\"Versuche einen anderen Token-Namen, Aussteller oder eine andere Seriennummer.\"],\"xkjgVR\":[\"Token konnte nicht hinzugefügt werden\"],\"xoqD_n\":[\"Gib eine gültige E-Mail-Adresse ein.\"],\"yOrM5-\":[\"Vielen Dank für dein Feedback\"],\"ypfsn_\":[\"Seriennummer\"],\"yz7wBu\":[\"Schließen\"],\"zXZJoN\":[\"Token hinzufügen\"]}")}; \ No newline at end of file diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 8e9ccf8..2d1d260 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -13,7 +13,7 @@ msgstr "" "Plural-Forms: \n" "X-Generator: Poedit 3.8\n" -#: src/components/push-request-popup.tsx:273 +#: src/components/push-request-popup.tsx:293 msgid "{totalPending} requests are waiting. Review each one separately." msgstr "{totalPending} Anfragen warten. Prüfe jede einzeln." @@ -25,13 +25,17 @@ msgstr "Über" msgid "Accept" msgstr "Akzeptieren" -#: src/app/index.tsx:590 -#: src/app/index.tsx:653 -#: src/app/index.tsx:655 +#: src/components/token-detail/token-overview-content.tsx:270 +msgid "Activity Log" +msgstr "Aktivitätsprotokoll" + +#: src/app/index.tsx:598 +#: src/app/index.tsx:661 +#: src/app/index.tsx:663 msgid "Add token" msgstr "Token hinzufügen" -#: src/app/index.tsx:625 +#: src/app/index.tsx:633 msgid "Add your first eduMFA Push token to approve sign-ins securely from this device." msgstr "Füge deinen ersten eduMFA Push-Token hinzu, um Anmeldungen sicher von diesem Gerät aus zu genehmigen." @@ -47,7 +51,7 @@ msgstr "Anonyme Berichte" msgid "Appearance" msgstr "Darstellung" -#: src/components/push-request-popup.tsx:284 +#: src/components/push-request-popup.tsx:304 msgid "Approve" msgstr "Freigeben" @@ -79,6 +83,7 @@ msgstr "Kamerazugriff ist erforderlich, um den QR-Code zu scannen und einen Toke msgid "Camera access is turned off" msgstr "Kamerazugriff ist deaktiviert" +#: src/components/token-detail/token-overview-content.tsx:148 #: src/hooks/use-delete-token-confirmation.ts:20 msgid "Cancel" msgstr "Abbrechen" @@ -91,6 +96,15 @@ msgstr "App-Sprache in den Systemeinstellungen ändern" msgid "Choose whether to share anonymous crash and error reports." msgstr "Wähle, ob du anonymisierte Absturz- und Fehlerberichte teilen möchtest." +#: src/components/token-detail/token-overview-content.tsx:150 +#: src/components/token-detail/token-overview-content.tsx:287 +msgid "Clear" +msgstr "Leeren" + +#: src/components/token-detail/token-overview-content.tsx:145 +msgid "Clear activity log?" +msgstr "Aktivitätsprotokoll leeren?" + #: src/components/feedback-form.android.tsx:95 #: src/components/feedback-form.ios.tsx:84 msgid "Close" @@ -130,11 +144,11 @@ msgstr "Löschen" msgid "Delete token" msgstr "Token löschen" -#: src/components/push-request-popup.tsx:279 +#: src/components/push-request-popup.tsx:299 msgid "Deny" msgstr "Ablehnen" -#: src/components/token-detail/token-overview-content.tsx:165 +#: src/components/token-detail/token-overview-content.tsx:259 msgid "Details" msgstr "Details" @@ -177,7 +191,7 @@ msgstr "Aktiviere den Kamerazugriff in den Einstellungen, um QR-Codes zu scannen msgid "Enable notifications" msgstr "Benachrichtigungen aktivieren" -#: src/app/index.tsx:763 +#: src/app/index.tsx:771 msgid "Enable notifications to receive push approval requests on this device." msgstr "Aktiviere Benachrichtigungen, um Push-Genehmigungsanfragen auf diesem Gerät zu empfangen." @@ -185,10 +199,22 @@ msgstr "Aktiviere Benachrichtigungen, um Push-Genehmigungsanfragen auf diesem Ge msgid "Enrolled" msgstr "Registriert" +#: src/components/token-detail/token-overview-content.tsx:57 +msgid "Enrollment completed" +msgstr "Registrierung abgeschlossen" + +#: src/components/token-detail/token-overview-content.tsx:61 +msgid "Enrollment failed" +msgstr "Registrierung fehlgeschlagen" + #: src/components/token-detail/token-detail-utils.ts:60 msgid "Enrollment response failed" msgstr "Registrierungsantwort fehlgeschlagen" +#: src/components/token-detail/token-overview-content.tsx:53 +msgid "Enrollment started" +msgstr "Registrierung gestartet" + #: src/app/token/[tokenId].tsx:130 msgid "Enter a label for this token." msgstr "Gib eine Bezeichnung für dieses Token ein." @@ -251,7 +277,7 @@ msgstr "Loslegen" msgid "GitHub" msgstr "GitHub" -#: src/app/index.tsx:762 +#: src/app/index.tsx:770 #: src/components/onboarding-sequence/step-actions.tsx:75 msgid "Google Play services are unavailable, so requests cannot arrive automatically. Pull down on the token list to check for pending requests." msgstr "Google Play-Dienste sind nicht verfügbar, daher können Anfragen nicht automatisch eingehen. Ziehe die Token-Liste nach unten, um nach ausstehenden Anfragen zu suchen." @@ -296,7 +322,7 @@ msgstr "Bezeichnung erforderlich" msgid "Language" msgstr "Sprache" -#: src/components/token-detail/token-overview-content.tsx:157 +#: src/components/token-detail/token-overview-content.tsx:251 msgid "Last failed" msgstr "Zuletzt fehlgeschlagen" @@ -331,15 +357,19 @@ msgstr "Name und E-Mail-Adresse sind optional. Sie ermöglichen es uns, dich bei msgid "Never miss a sign-in request" msgstr "Keine Anmeldeanfrage verpassen" +#: src/components/token-detail/token-overview-content.tsx:327 +msgid "No interactions yet" +msgstr "Noch keine Interaktionen" + #: src/hooks/use-handle-token-uri.ts:35 msgid "No QR code found" msgstr "Kein QR-Code gefunden" -#: src/app/index.tsx:716 +#: src/app/index.tsx:724 msgid "No tokens found" msgstr "Keine Token gefunden" -#: src/app/index.tsx:617 +#: src/app/index.tsx:625 msgid "No tokens yet" msgstr "Noch keine Token vorhanden" @@ -351,7 +381,7 @@ msgstr "Nicht jetzt" msgid "Notifications" msgstr "Benachrichtigungen" -#: src/app/index.tsx:758 +#: src/app/index.tsx:766 msgid "Notifications are disabled" msgstr "Benachrichtigungen sind deaktiviert" @@ -359,7 +389,7 @@ msgstr "Benachrichtigungen sind deaktiviert" msgid "Notifications are enabled" msgstr "Benachrichtigungen sind aktiviert" -#: src/components/push-request-popup.tsx:262 +#: src/components/push-request-popup.tsx:282 msgid "Only approve if you started this sign-in. If you are unsure, deny it." msgstr "Genehmige die Anmeldung nur, wenn du sie selbst gestartet hast. Lehne sie im Zweifel ab." @@ -413,21 +443,33 @@ msgstr "Datenschutzerklärung" msgid "Project website" msgstr "Projektwebsite" -#: src/app/index.tsx:757 +#: src/app/index.tsx:765 #: src/components/onboarding-sequence/step-actions.tsx:79 msgid "Pull to refresh for requests" msgstr "Nach unten ziehen, um Anfragen abzurufen" +#: src/components/token-detail/token-overview-content.tsx:69 +msgid "Push request approved" +msgstr "Push-Anfrage genehmigt" + +#: src/components/token-detail/token-overview-content.tsx:73 +msgid "Push request denied" +msgstr "Push-Anfrage abgelehnt" + +#: src/components/token-detail/token-overview-content.tsx:65 +msgid "Push request received" +msgstr "Push-Anfrage empfangen" + #: src/components/token-add/upload-qr-code-button.tsx:70 msgid "QR code scanner" msgstr "QR-Code-Scanner" -#: src/components/token-detail/token-overview-content.tsx:128 +#: src/components/token-detail/token-overview-content.tsx:222 #: src/components/token-list-item.tsx:234 msgid "Refresh failed" msgstr "Aktualisierung fehlgeschlagen" -#: src/app/index.tsx:779 +#: src/app/index.tsx:787 msgid "Refreshing..." msgstr "Aktualisiere..." @@ -444,9 +486,9 @@ msgid "Response parsing failed" msgstr "Antwortverarbeitung fehlgeschlagen" #: src/app/index.tsx:281 -#: src/components/token-detail/token-overview-content.tsx:105 -#: src/components/token-detail/token-overview-content.tsx:115 -#: src/components/token-detail/token-overview-content.tsx:117 +#: src/components/token-detail/token-overview-content.tsx:199 +#: src/components/token-detail/token-overview-content.tsx:209 +#: src/components/token-detail/token-overview-content.tsx:211 msgid "Retry Rollout" msgstr "Rollout erneut versuchen" @@ -454,7 +496,7 @@ msgstr "Rollout erneut versuchen" msgid "Review eduMFA Push" msgstr "eduMFA Push bewerten" -#: src/components/push-request-popup.tsx:197 +#: src/components/push-request-popup.tsx:217 msgid "Review sign-in" msgstr "Anmeldung prüfen" @@ -475,8 +517,8 @@ msgstr "Speichern" msgid "Search Authenticator Apps" msgstr "Authenticator-Apps suchen" -#: src/app/index.tsx:451 -#: src/app/index.tsx:522 +#: src/app/index.tsx:459 +#: src/app/index.tsx:530 msgid "Search tokens" msgstr "Token suchen" @@ -490,11 +532,11 @@ msgstr "Anonymisierte Absturz- und Fehlerdiagnosen senden" msgid "Send feedback" msgstr "Feedback senden" -#: src/components/token-detail/token-overview-content.tsx:169 +#: src/components/token-detail/token-overview-content.tsx:263 msgid "Serial" msgstr "Seriennummer" -#: src/components/token-detail/token-overview-content.tsx:140 +#: src/components/token-detail/token-overview-content.tsx:234 msgid "Server message" msgstr "Servernachricht" @@ -510,11 +552,15 @@ msgstr "Einstellungen" msgid "Share anonymous reports" msgstr "Anonyme Berichte teilen" +#: src/components/token-detail/token-overview-content.tsx:316 +msgid "Show older activity" +msgstr "Ältere Aktivitäten anzeigen" + #: src/components/onboarding-sequence/step-actions.tsx:120 msgid "Sign-in requests may go unnoticed" msgstr "Anmeldeanfragen können unbemerkt bleiben" -#: src/components/token-detail/token-overview-content.tsx:168 +#: src/components/token-detail/token-overview-content.tsx:262 msgid "Status" msgstr "Status" @@ -552,6 +598,10 @@ msgstr "Die Token-Version wird von dieser App nicht unterstützt. Bitte aktualis msgid "Theme" msgstr "Erscheinungsbild" +#: src/components/token-detail/token-overview-content.tsx:146 +msgid "This permanently removes all activity for this token." +msgstr "Dadurch werden alle Aktivitäten für dieses Token dauerhaft entfernt." + #: src/components/token-detail/token-detail-utils.ts:21 msgid "This token could not be refreshed because the push service could not be reached. Check your connection or try again later." msgstr "Dieses Token konnte nicht aktualisiert werden, weil der Push-Dienst nicht erreichbar war. Prüfe deine Verbindung oder versuche es später erneut." @@ -572,7 +622,11 @@ msgstr "Dieses Token hat die Registrierung nicht abgeschlossen und kann keine Pu msgid "Token" msgstr "Token" -#: src/app/index.tsx:723 +#: src/components/token-detail/token-overview-content.tsx:334 +msgid "Token approvals, denials, push requests, and enrollment activity will appear here." +msgstr "Token-Genehmigungen, Ablehnungen, Push-Anfragen und Registrierungsaktivitäten werden hier angezeigt." + +#: src/app/index.tsx:731 msgid "Try another token name, issuer, or serial number." msgstr "Versuche einen anderen Token-Namen, Aussteller oder eine andere Seriennummer." diff --git a/src/locales/en/messages.js b/src/locales/en/messages.js index 5a8951f..13783fa 100644 --- a/src/locales/en/messages.js +++ b/src/locales/en/messages.js @@ -1 +1 @@ -/*eslint-disable*/module.exports={messages:JSON.parse("{\"0DlQl0\":[\"No tokens yet\"],\"0EvA_s\":[\"Are you sure you want to delete this token?\"],\"0VZTWA\":[\"Only approve if you started this sign-in. If you are unsure, deny it.\"],\"0_bbfl\":[\"Keep your authentication tokens in one place and approve sign-ins securely from this device.\"],\"1QfxQT\":[\"Dismiss\"],\"1m41_U\":[\"Submit feedback\"],\"1njn7W\":[\"Light\"],\"2EKhU_\":[\"Logs provide more context and help us find the cause of the issue.\"],\"2b4lV4\":[\"Refresh failed\"],\"3DRYd7\":[\"By sending feedback, you agree to the processing of your data under our [Privacy policy](https://edumfa.io/app-privacy).\"],\"3OQxEV\":[\"Server message\"],\"47yaVS\":[\"Google Play services are unavailable, so requests cannot arrive automatically. Pull down on the token list to check for pending requests.\"],\"4aqCBZ\":[\"Never miss a sign-in request\"],\"4xUhXP\":[\"Open notification settings\"],\"6jfS51\":[\"Welcome\"],\"6mYeNy\":[\"Server registration failed\"],\"79FlZ7\":[\"Notifications are enabled\"],\"7GD6En\":[\"Sign-in requests may go unnoticed\"],\"7yOnZx\":[\"The device could not create the RSA key pair required for this token. Retry rollout and keep the app open while it runs.\"],\"858Cvj\":[\"The QR code isn't supported by this app. Please use a general Authenticator app.\"],\"87a_t_\":[\"Label\"],\"88u4WE\":[\"Feedback type\"],\"8ZFiCO\":[\"Your feedback was submitted successfully.\"],\"A9KSfI\":[\"Help improve the app\"],\"BrFR00\":[\"Finalizing enrollment\"],\"CVIHyt\":[\"Please describe your feedback.\"],\"CsH7i4\":[\"Send anonymized crash and error diagnostics\"],\"DEKI9D\":[\"Project website\"],\"DPfwMq\":[\"Done\"],\"Dnn2XG\":[\"Automatic\"],\"DvOF3m\":[\"Help improve reliability by sharing crash and error reports. They never include token secrets, passwords, or institution names.\"],\"DyF_C_\":[\"License information is unavailable.\"],\"EAyh56\":[\"Review sign-in\"],\"EBRcBj\":[\"Haptics\"],\"ED9KJC\":[\"What would you like us to know?\"],\"ET7frA\":[\"Allow camera access\"],\"EfBEmE\":[\"Rolling out...\"],\"F37c1s\":[\"Open Settings\"],\"FEr96N\":[\"Theme\"],\"FSVCou\":[\"Pull to refresh for requests\"],\"G0TPBU\":[\"QR code scanner\"],\"I92Z-b\":[\"Enable notifications\"],\"IPFr7d\":[\"Choose whether to share anonymous crash and error reports.\"],\"IRRc6m\":[\"Share anonymous reports\"],\"IT-f_-\":[\"Generating keys\"],\"Ijsgym\":[\"Delete token\"],\"JG7Ls-\":[\"Enable camera access in Settings to scan QR codes.\"],\"Lu4tTO\":[\"Place the QR code inside the frame\"],\"Mi0UmV\":[\"Enrollment response failed\"],\"NkQliA\":[\"You’re ready to receive and approve sign-in requests.\"],\"NsiA8m\":[\"Rollout Failed\"],\"O7obZD\":[\"This token could not be refreshed because the push service could not be reached. Check your connection or try again later.\"],\"On0aF2\":[\"Website\"],\"OugAQq\":[\"Upload QR Code\"],\"PBxg_E\":[\"Not now\"],\"PvN-FE\":[\"The token version is not supported by this app. Please update the app to the latest version or contact your institution for assistance.\"],\"Q__-1-\":[\"Bug report\"],\"QchA_G\":[\"Email (optional)\"],\"Qm1NmK\":[\"OR\"],\"QmIEhK\":[\"Search Authenticator Apps\"],\"QoroAc\":[\"Add your first eduMFA Push token to approve sign-ins securely from this device.\"],\"R1QFqA\":[\"Feature request\"],\"R8Gqnt\":[\"Open-source licenses\"],\"ROheaS\":[\"Version \",[\"itemVersion\"],\" · \",[\"itemLicense\"]],\"RkXlPZ\":[\"GitHub\"],\"Ro1gox\":[\"Edit token\"],\"RoafuO\":[\"Send feedback\"],\"SWOmlM\":[\"Update the display name for this token.\"],\"ScJ9fj\":[\"Privacy policy\"],\"TP9_K5\":[\"Token\"],\"TfpFBG\":[\"Your choice\"],\"TqCsD2\":[[\"totalPending\"],\" requests are waiting. Review each one separately.\"],\"Tz0i8g\":[\"Settings\"],\"UMeccq\":[\"Key generation failed\"],\"UO-3EH\":[\"This token did not finish enrollment and cannot receive push requests until rollout succeeds.\"],\"URmyfc\":[\"Details\"],\"UbRKMZ\":[\"Pending\"],\"VIZNLO\":[\"Registering public key\"],\"VSWXlh\":[\"Feedback details\"],\"WRdUP2\":[\"Camera access is required to scan the QR code and pair a token.\"],\"Weq9zb\":[\"General\"],\"YYVGS5\":[\"Include logs\"],\"YirHq7\":[\"Feedback\"],\"Z2CsKg\":[\"Don't share anonymous reports\"],\"Z7ZXbT\":[\"Approve\"],\"ZDIydz\":[\"Get started\"],\"ZYIECH\":[\"Notifications are disabled\"],\"_ijUgq\":[\"Get notified when a sign-in needs your approval.\"],\"aAIQg2\":[\"Appearance\"],\"agAz3d\":[\"Review eduMFA Push\"],\"b75KIN\":[\"Change the app language in system settings\"],\"bfECx6\":[\"This token could not be registered with the enrollment server. Check your connection or try again later.\"],\"c7uz-G\":[\"Turn on notifications in Settings so you can respond when a request arrives.\"],\"cFCKYZ\":[\"Deny\"],\"cnGeoo\":[\"Delete\"],\"dEgA5A\":[\"Cancel\"],\"dN-1Th\":[\"Open with your Authenticator App\"],\"dum6Mb\":[\"Refreshing...\"],\"eMwMfT\":[\"Enable notifications to receive push approval requests on this device.\"],\"ePK91l\":[\"Edit\"],\"g0aJ_D\":[\"Name and email are optional. They allow us to contact you with follow-up questions about your feedback.\"],\"g3UF2V\":[\"Accept\"],\"g7oOIY\":[\"Search tokens\"],\"g7yB5h\":[\"The server response could not be parsed or did not include the expected token material.\"],\"hevXA2\":[\"Last failed\"],\"iAqBor\":[\"Enter a label for this token.\"],\"iDNBZe\":[\"Notifications\"],\"iQmbPb\":[\"License\"],\"jbq7j2\":[\"Decline\"],\"jiR92q\":[\"Pair new Push Token\"],\"k4nIkP\":[\"No tokens found\"],\"kLttbL\":[\"Registration failed\"],\"lF4FiS\":[\"No QR code found\"],\"liw26p\":[\"Anonymous reports\"],\"mRd7qL\":[\"Feedback could not be sent. Please try again.\"],\"mVSlBU\":[\"Enrolled\"],\"ml5TCa\":[\"The provided QR code isn't a valid eduMFA token.\"],\"o4_Dwt\":[\"Camera access is turned off\"],\"pphVNl\":[\"Your image did not contain a QR code.\"],\"pvnfJD\":[\"Dark\"],\"q9XKrW\":[\"Response parsing failed\"],\"rjGI_Q\":[\"Privacy\"],\"roN3CK\":[\"eduMFA Push uses \",[\"licenseCount\"],\" open-source packages. Package details are generated from the installed production dependency graph.\"],\"tfDRzk\":[\"Save\"],\"tqFPnN\":[\"By sending feedback, you agree to the processing of your data under our\"],\"tsg9Ze\":[\"This token could not be refreshed. It may have been removed on the server or the institution hosting the push service may be having a technical issue.\"],\"u-uuOt\":[\"Welcome to <0/>\"],\"u5FNLt\":[\"Crash and error reports\"],\"uAQUqI\":[\"Status\"],\"uI1l6M\":[\"Label required\"],\"uZ25pH\":[\"Contact (optional)\"],\"uh8i5u\":[\"Retry Rollout\"],\"uyJsf6\":[\"About\"],\"vXIe7J\":[\"Language\"],\"wJAhPZ\":[\"Name (optional)\"],\"xGVfLh\":[\"Continue\"],\"xPl9LS\":[\"Try another token name, issuer, or serial number.\"],\"xkjgVR\":[\"Failed to add token\"],\"xoqD_n\":[\"Enter a valid email address.\"],\"yOrM5-\":[\"Thank you for your feedback\"],\"ypfsn_\":[\"Serial\"],\"yz7wBu\":[\"Close\"],\"zXZJoN\":[\"Add token\"]}")}; \ No newline at end of file +/*eslint-disable*/module.exports={messages:JSON.parse("{\"0DlQl0\":[\"No tokens yet\"],\"0EvA_s\":[\"Are you sure you want to delete this token?\"],\"0VZTWA\":[\"Only approve if you started this sign-in. If you are unsure, deny it.\"],\"0_bbfl\":[\"Keep your authentication tokens in one place and approve sign-ins securely from this device.\"],\"1QfxQT\":[\"Dismiss\"],\"1m41_U\":[\"Submit feedback\"],\"1njn7W\":[\"Light\"],\"2EKhU_\":[\"Logs provide more context and help us find the cause of the issue.\"],\"2b4lV4\":[\"Refresh failed\"],\"3DRYd7\":[\"By sending feedback, you agree to the processing of your data under our [Privacy policy](https://edumfa.io/app-privacy).\"],\"3OQxEV\":[\"Server message\"],\"3ovE9o\":[\"Token approvals, denials, push requests, and enrollment activity will appear here.\"],\"47yaVS\":[\"Google Play services are unavailable, so requests cannot arrive automatically. Pull down on the token list to check for pending requests.\"],\"4aqCBZ\":[\"Never miss a sign-in request\"],\"4xUhXP\":[\"Open notification settings\"],\"6jfS51\":[\"Welcome\"],\"6mYeNy\":[\"Server registration failed\"],\"79FlZ7\":[\"Notifications are enabled\"],\"7GD6En\":[\"Sign-in requests may go unnoticed\"],\"7yOnZx\":[\"The device could not create the RSA key pair required for this token. Retry rollout and keep the app open while it runs.\"],\"858Cvj\":[\"The QR code isn't supported by this app. Please use a general Authenticator app.\"],\"87a_t_\":[\"Label\"],\"88u4WE\":[\"Feedback type\"],\"8DuEDH\":[\"No interactions yet\"],\"8ZFiCO\":[\"Your feedback was submitted successfully.\"],\"A9KSfI\":[\"Help improve the app\"],\"BlvmX5\":[\"Enrollment failed\"],\"BrFR00\":[\"Finalizing enrollment\"],\"CVIHyt\":[\"Please describe your feedback.\"],\"CsH7i4\":[\"Send anonymized crash and error diagnostics\"],\"DEKI9D\":[\"Project website\"],\"DPfwMq\":[\"Done\"],\"Dnn2XG\":[\"Automatic\"],\"DvOF3m\":[\"Help improve reliability by sharing crash and error reports. They never include token secrets, passwords, or institution names.\"],\"DyF_C_\":[\"License information is unavailable.\"],\"EAyh56\":[\"Review sign-in\"],\"EBRcBj\":[\"Haptics\"],\"ED9KJC\":[\"What would you like us to know?\"],\"ET7frA\":[\"Allow camera access\"],\"EfBEmE\":[\"Rolling out...\"],\"F37c1s\":[\"Open Settings\"],\"FEr96N\":[\"Theme\"],\"FSVCou\":[\"Pull to refresh for requests\"],\"G0TPBU\":[\"QR code scanner\"],\"Hrlil0\":[\"Enrollment started\"],\"I5kL4f\":[\"Activity Log\"],\"I92Z-b\":[\"Enable notifications\"],\"IPFr7d\":[\"Choose whether to share anonymous crash and error reports.\"],\"IRRc6m\":[\"Share anonymous reports\"],\"IT-f_-\":[\"Generating keys\"],\"Ijsgym\":[\"Delete token\"],\"JG7Ls-\":[\"Enable camera access in Settings to scan QR codes.\"],\"Lu4tTO\":[\"Place the QR code inside the frame\"],\"Mi0UmV\":[\"Enrollment response failed\"],\"NkQliA\":[\"You’re ready to receive and approve sign-in requests.\"],\"NsiA8m\":[\"Rollout Failed\"],\"O7obZD\":[\"This token could not be refreshed because the push service could not be reached. Check your connection or try again later.\"],\"OSBdo6\":[\"Show older activity\"],\"On0aF2\":[\"Website\"],\"OugAQq\":[\"Upload QR Code\"],\"PBxg_E\":[\"Not now\"],\"PvN-FE\":[\"The token version is not supported by this app. Please update the app to the latest version or contact your institution for assistance.\"],\"Q__-1-\":[\"Bug report\"],\"QchA_G\":[\"Email (optional)\"],\"Qm1NmK\":[\"OR\"],\"QmIEhK\":[\"Search Authenticator Apps\"],\"QoroAc\":[\"Add your first eduMFA Push token to approve sign-ins securely from this device.\"],\"R1QFqA\":[\"Feature request\"],\"R8Gqnt\":[\"Open-source licenses\"],\"ROheaS\":[\"Version \",[\"itemVersion\"],\" · \",[\"itemLicense\"]],\"RkXlPZ\":[\"GitHub\"],\"Ro1gox\":[\"Edit token\"],\"RoafuO\":[\"Send feedback\"],\"SWOmlM\":[\"Update the display name for this token.\"],\"ScJ9fj\":[\"Privacy policy\"],\"TP9_K5\":[\"Token\"],\"TfpFBG\":[\"Your choice\"],\"TqCsD2\":[[\"totalPending\"],\" requests are waiting. Review each one separately.\"],\"Tz0i8g\":[\"Settings\"],\"UMeccq\":[\"Key generation failed\"],\"UO-3EH\":[\"This token did not finish enrollment and cannot receive push requests until rollout succeeds.\"],\"URmyfc\":[\"Details\"],\"UbRKMZ\":[\"Pending\"],\"VIZNLO\":[\"Registering public key\"],\"VSWXlh\":[\"Feedback details\"],\"WRdUP2\":[\"Camera access is required to scan the QR code and pair a token.\"],\"Weq9zb\":[\"General\"],\"YXukeV\":[\"Push request received\"],\"YYVGS5\":[\"Include logs\"],\"YirHq7\":[\"Feedback\"],\"Z2CsKg\":[\"Don't share anonymous reports\"],\"Z7ZXbT\":[\"Approve\"],\"ZDIydz\":[\"Get started\"],\"ZYIECH\":[\"Notifications are disabled\"],\"_ijUgq\":[\"Get notified when a sign-in needs your approval.\"],\"aAIQg2\":[\"Appearance\"],\"agAz3d\":[\"Review eduMFA Push\"],\"b75KIN\":[\"Change the app language in system settings\"],\"bfECx6\":[\"This token could not be registered with the enrollment server. Check your connection or try again later.\"],\"c7uz-G\":[\"Turn on notifications in Settings so you can respond when a request arrives.\"],\"cFCKYZ\":[\"Deny\"],\"cnGeoo\":[\"Delete\"],\"d3Vukm\":[\"Push request denied\"],\"dEgA5A\":[\"Cancel\"],\"dN-1Th\":[\"Open with your Authenticator App\"],\"dum6Mb\":[\"Refreshing...\"],\"eMwMfT\":[\"Enable notifications to receive push approval requests on this device.\"],\"ePK91l\":[\"Edit\"],\"g0aJ_D\":[\"Name and email are optional. They allow us to contact you with follow-up questions about your feedback.\"],\"g3UF2V\":[\"Accept\"],\"g7oOIY\":[\"Search tokens\"],\"g7yB5h\":[\"The server response could not be parsed or did not include the expected token material.\"],\"hevXA2\":[\"Last failed\"],\"iAqBor\":[\"Enter a label for this token.\"],\"iDNBZe\":[\"Notifications\"],\"iDbChF\":[\"Enrollment completed\"],\"iQmbPb\":[\"License\"],\"jbq7j2\":[\"Decline\"],\"jiR92q\":[\"Pair new Push Token\"],\"k4nIkP\":[\"No tokens found\"],\"kLttbL\":[\"Registration failed\"],\"ktAc5i\":[\"Clear activity log?\"],\"lAOkhV\":[\"Push request approved\"],\"lF4FiS\":[\"No QR code found\"],\"liw26p\":[\"Anonymous reports\"],\"mRd7qL\":[\"Feedback could not be sent. Please try again.\"],\"mVSlBU\":[\"Enrolled\"],\"ml5TCa\":[\"The provided QR code isn't a valid eduMFA token.\"],\"moNf7g\":[\"This permanently removes all activity for this token.\"],\"o4_Dwt\":[\"Camera access is turned off\"],\"pphVNl\":[\"Your image did not contain a QR code.\"],\"pvnfJD\":[\"Dark\"],\"q9XKrW\":[\"Response parsing failed\"],\"rjGI_Q\":[\"Privacy\"],\"roN3CK\":[\"eduMFA Push uses \",[\"licenseCount\"],\" open-source packages. Package details are generated from the installed production dependency graph.\"],\"tfDRzk\":[\"Save\"],\"tqFPnN\":[\"By sending feedback, you agree to the processing of your data under our\"],\"tsg9Ze\":[\"This token could not be refreshed. It may have been removed on the server or the institution hosting the push service may be having a technical issue.\"],\"u-uuOt\":[\"Welcome to <0/>\"],\"u5FNLt\":[\"Crash and error reports\"],\"uAQUqI\":[\"Status\"],\"uI1l6M\":[\"Label required\"],\"uZ25pH\":[\"Contact (optional)\"],\"uh8i5u\":[\"Retry Rollout\"],\"uyJsf6\":[\"About\"],\"vXIe7J\":[\"Language\"],\"wJAhPZ\":[\"Name (optional)\"],\"xCJdfg\":[\"Clear\"],\"xGVfLh\":[\"Continue\"],\"xPl9LS\":[\"Try another token name, issuer, or serial number.\"],\"xkjgVR\":[\"Failed to add token\"],\"xoqD_n\":[\"Enter a valid email address.\"],\"yOrM5-\":[\"Thank you for your feedback\"],\"ypfsn_\":[\"Serial\"],\"yz7wBu\":[\"Close\"],\"zXZJoN\":[\"Add token\"]}")}; \ No newline at end of file diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 9b0b5bd..fd9bed1 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -13,7 +13,7 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/components/push-request-popup.tsx:273 +#: src/components/push-request-popup.tsx:293 msgid "{totalPending} requests are waiting. Review each one separately." msgstr "{totalPending} requests are waiting. Review each one separately." @@ -25,13 +25,17 @@ msgstr "About" msgid "Accept" msgstr "Accept" -#: src/app/index.tsx:590 -#: src/app/index.tsx:653 -#: src/app/index.tsx:655 +#: src/components/token-detail/token-overview-content.tsx:270 +msgid "Activity Log" +msgstr "Activity Log" + +#: src/app/index.tsx:598 +#: src/app/index.tsx:661 +#: src/app/index.tsx:663 msgid "Add token" msgstr "Add token" -#: src/app/index.tsx:625 +#: src/app/index.tsx:633 msgid "Add your first eduMFA Push token to approve sign-ins securely from this device." msgstr "Add your first eduMFA Push token to approve sign-ins securely from this device." @@ -47,7 +51,7 @@ msgstr "Anonymous reports" msgid "Appearance" msgstr "Appearance" -#: src/components/push-request-popup.tsx:284 +#: src/components/push-request-popup.tsx:304 msgid "Approve" msgstr "Approve" @@ -79,6 +83,7 @@ msgstr "Camera access is required to scan the QR code and pair a token." msgid "Camera access is turned off" msgstr "Camera access is turned off" +#: src/components/token-detail/token-overview-content.tsx:148 #: src/hooks/use-delete-token-confirmation.ts:20 msgid "Cancel" msgstr "Cancel" @@ -91,6 +96,15 @@ msgstr "Change the app language in system settings" msgid "Choose whether to share anonymous crash and error reports." msgstr "Choose whether to share anonymous crash and error reports." +#: src/components/token-detail/token-overview-content.tsx:150 +#: src/components/token-detail/token-overview-content.tsx:287 +msgid "Clear" +msgstr "Clear" + +#: src/components/token-detail/token-overview-content.tsx:145 +msgid "Clear activity log?" +msgstr "Clear activity log?" + #: src/components/feedback-form.android.tsx:95 #: src/components/feedback-form.ios.tsx:84 msgid "Close" @@ -130,11 +144,11 @@ msgstr "Delete" msgid "Delete token" msgstr "Delete token" -#: src/components/push-request-popup.tsx:279 +#: src/components/push-request-popup.tsx:299 msgid "Deny" msgstr "Deny" -#: src/components/token-detail/token-overview-content.tsx:165 +#: src/components/token-detail/token-overview-content.tsx:259 msgid "Details" msgstr "Details" @@ -177,7 +191,7 @@ msgstr "Enable camera access in Settings to scan QR codes." msgid "Enable notifications" msgstr "Enable notifications" -#: src/app/index.tsx:763 +#: src/app/index.tsx:771 msgid "Enable notifications to receive push approval requests on this device." msgstr "Enable notifications to receive push approval requests on this device." @@ -185,10 +199,22 @@ msgstr "Enable notifications to receive push approval requests on this device." msgid "Enrolled" msgstr "Enrolled" +#: src/components/token-detail/token-overview-content.tsx:57 +msgid "Enrollment completed" +msgstr "Enrollment completed" + +#: src/components/token-detail/token-overview-content.tsx:61 +msgid "Enrollment failed" +msgstr "Enrollment failed" + #: src/components/token-detail/token-detail-utils.ts:60 msgid "Enrollment response failed" msgstr "Enrollment response failed" +#: src/components/token-detail/token-overview-content.tsx:53 +msgid "Enrollment started" +msgstr "Enrollment started" + #: src/app/token/[tokenId].tsx:130 msgid "Enter a label for this token." msgstr "Enter a label for this token." @@ -251,7 +277,7 @@ msgstr "Get started" msgid "GitHub" msgstr "GitHub" -#: src/app/index.tsx:762 +#: src/app/index.tsx:770 #: src/components/onboarding-sequence/step-actions.tsx:75 msgid "Google Play services are unavailable, so requests cannot arrive automatically. Pull down on the token list to check for pending requests." msgstr "Google Play services are unavailable, so requests cannot arrive automatically. Pull down on the token list to check for pending requests." @@ -296,7 +322,7 @@ msgstr "Label required" msgid "Language" msgstr "Language" -#: src/components/token-detail/token-overview-content.tsx:157 +#: src/components/token-detail/token-overview-content.tsx:251 msgid "Last failed" msgstr "Last failed" @@ -331,15 +357,19 @@ msgstr "Name and email are optional. They allow us to contact you with follow-up msgid "Never miss a sign-in request" msgstr "Never miss a sign-in request" +#: src/components/token-detail/token-overview-content.tsx:327 +msgid "No interactions yet" +msgstr "No interactions yet" + #: src/hooks/use-handle-token-uri.ts:35 msgid "No QR code found" msgstr "No QR code found" -#: src/app/index.tsx:716 +#: src/app/index.tsx:724 msgid "No tokens found" msgstr "No tokens found" -#: src/app/index.tsx:617 +#: src/app/index.tsx:625 msgid "No tokens yet" msgstr "No tokens yet" @@ -351,7 +381,7 @@ msgstr "Not now" msgid "Notifications" msgstr "Notifications" -#: src/app/index.tsx:758 +#: src/app/index.tsx:766 msgid "Notifications are disabled" msgstr "Notifications are disabled" @@ -359,7 +389,7 @@ msgstr "Notifications are disabled" msgid "Notifications are enabled" msgstr "Notifications are enabled" -#: src/components/push-request-popup.tsx:262 +#: src/components/push-request-popup.tsx:282 msgid "Only approve if you started this sign-in. If you are unsure, deny it." msgstr "Only approve if you started this sign-in. If you are unsure, deny it." @@ -413,21 +443,33 @@ msgstr "Privacy policy" msgid "Project website" msgstr "Project website" -#: src/app/index.tsx:757 +#: src/app/index.tsx:765 #: src/components/onboarding-sequence/step-actions.tsx:79 msgid "Pull to refresh for requests" msgstr "Pull to refresh for requests" +#: src/components/token-detail/token-overview-content.tsx:69 +msgid "Push request approved" +msgstr "Push request approved" + +#: src/components/token-detail/token-overview-content.tsx:73 +msgid "Push request denied" +msgstr "Push request denied" + +#: src/components/token-detail/token-overview-content.tsx:65 +msgid "Push request received" +msgstr "Push request received" + #: src/components/token-add/upload-qr-code-button.tsx:70 msgid "QR code scanner" msgstr "QR code scanner" -#: src/components/token-detail/token-overview-content.tsx:128 +#: src/components/token-detail/token-overview-content.tsx:222 #: src/components/token-list-item.tsx:234 msgid "Refresh failed" msgstr "Refresh failed" -#: src/app/index.tsx:779 +#: src/app/index.tsx:787 msgid "Refreshing..." msgstr "Refreshing..." @@ -444,9 +486,9 @@ msgid "Response parsing failed" msgstr "Response parsing failed" #: src/app/index.tsx:281 -#: src/components/token-detail/token-overview-content.tsx:105 -#: src/components/token-detail/token-overview-content.tsx:115 -#: src/components/token-detail/token-overview-content.tsx:117 +#: src/components/token-detail/token-overview-content.tsx:199 +#: src/components/token-detail/token-overview-content.tsx:209 +#: src/components/token-detail/token-overview-content.tsx:211 msgid "Retry Rollout" msgstr "Retry Rollout" @@ -454,7 +496,7 @@ msgstr "Retry Rollout" msgid "Review eduMFA Push" msgstr "Review eduMFA Push" -#: src/components/push-request-popup.tsx:197 +#: src/components/push-request-popup.tsx:217 msgid "Review sign-in" msgstr "Review sign-in" @@ -475,8 +517,8 @@ msgstr "Save" msgid "Search Authenticator Apps" msgstr "Search Authenticator Apps" -#: src/app/index.tsx:451 -#: src/app/index.tsx:522 +#: src/app/index.tsx:459 +#: src/app/index.tsx:530 msgid "Search tokens" msgstr "Search tokens" @@ -490,11 +532,11 @@ msgstr "Send anonymized crash and error diagnostics" msgid "Send feedback" msgstr "Send feedback" -#: src/components/token-detail/token-overview-content.tsx:169 +#: src/components/token-detail/token-overview-content.tsx:263 msgid "Serial" msgstr "Serial" -#: src/components/token-detail/token-overview-content.tsx:140 +#: src/components/token-detail/token-overview-content.tsx:234 msgid "Server message" msgstr "Server message" @@ -510,11 +552,15 @@ msgstr "Settings" msgid "Share anonymous reports" msgstr "Share anonymous reports" +#: src/components/token-detail/token-overview-content.tsx:316 +msgid "Show older activity" +msgstr "Show older activity" + #: src/components/onboarding-sequence/step-actions.tsx:120 msgid "Sign-in requests may go unnoticed" msgstr "Sign-in requests may go unnoticed" -#: src/components/token-detail/token-overview-content.tsx:168 +#: src/components/token-detail/token-overview-content.tsx:262 msgid "Status" msgstr "Status" @@ -552,6 +598,10 @@ msgstr "The token version is not supported by this app. Please update the app to msgid "Theme" msgstr "Theme" +#: src/components/token-detail/token-overview-content.tsx:146 +msgid "This permanently removes all activity for this token." +msgstr "This permanently removes all activity for this token." + #: src/components/token-detail/token-detail-utils.ts:21 msgid "This token could not be refreshed because the push service could not be reached. Check your connection or try again later." msgstr "This token could not be refreshed because the push service could not be reached. Check your connection or try again later." @@ -572,7 +622,11 @@ msgstr "This token did not finish enrollment and cannot receive push requests un msgid "Token" msgstr "Token" -#: src/app/index.tsx:723 +#: src/components/token-detail/token-overview-content.tsx:334 +msgid "Token approvals, denials, push requests, and enrollment activity will appear here." +msgstr "Token approvals, denials, push requests, and enrollment activity will appear here." + +#: src/app/index.tsx:731 msgid "Try another token name, issuer, or serial number." msgstr "Try another token name, issuer, or serial number." diff --git a/src/stores/activity.ts b/src/stores/activity.ts new file mode 100644 index 0000000..331bc72 --- /dev/null +++ b/src/stores/activity.ts @@ -0,0 +1,49 @@ +import type { Activity } from "@/types/activity"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { create } from "zustand"; +import { createJSONStorage, persist } from "zustand/middleware"; + +type NewActivity = Omit & { id?: string }; + +type ActivityState = { + activities: Activity[]; + addActivity: (activity: NewActivity) => void; + clearTokenActivities: (tokenId: string) => void; +}; + +const MAX_ACTIVITIES = 500; + +const createActivityId = () => + `activity-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; + +export const useActivityStore = create( + persist( + (set) => ({ + activities: [], + + addActivity: (activity) => + set((state) => { + const entry = { ...activity, id: activity.id ?? createActivityId() }; + + if (state.activities.some((item) => item.id === entry.id)) { + return state; + } + + return { + activities: [entry, ...state.activities].slice(0, MAX_ACTIVITIES), + }; + }), + + clearTokenActivities: (tokenId) => + set((state) => ({ + activities: state.activities.filter( + (activity) => activity.tokenId !== tokenId, + ), + })), + }), + { + name: "activity-storage", + storage: createJSONStorage(() => AsyncStorage), + }, + ), +); diff --git a/src/types/activity.ts b/src/types/activity.ts new file mode 100644 index 0000000..0c69e6b4 --- /dev/null +++ b/src/types/activity.ts @@ -0,0 +1,17 @@ +export enum ActivityType { + EnrollmentStarted = "enrollment_started", + EnrollmentCompleted = "enrollment_completed", + EnrollmentFailed = "enrollment_failed", + PushReceived = "push_received", + PushApproved = "push_approved", + PushDenied = "push_denied", +} + +export type Activity = { + id: string; + tokenId: string; + type: ActivityType; + timestamp: number; + pushRequestId?: string; + title?: string; +};