mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +00:00
finishing #327
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
This commit is contained in:
parent
36e11c206c
commit
bc1c61c8f8
22 changed files with 584 additions and 980 deletions
|
|
@ -26,6 +26,13 @@
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter android:autoVerify="true">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="http" android:host="me.twonly.eu" />
|
||||||
|
<data android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
|
|
||||||
|
|
@ -1,785 +1,397 @@
|
||||||
{
|
{
|
||||||
"@@locale": "de",
|
"@@locale": "de",
|
||||||
"registerTitle": "Willkommen bei twonly!",
|
"registerTitle": "Willkommen bei twonly!",
|
||||||
"@registerTitle": {},
|
|
||||||
"registerSlogan": "twonly, eine private und sichere Möglichkeit um mit Freunden in Kontakt zu bleiben.",
|
"registerSlogan": "twonly, eine private und sichere Möglichkeit um mit Freunden in Kontakt zu bleiben.",
|
||||||
"@registerSlogan": {},
|
|
||||||
"onboardingWelcomeTitle": "Willkommen bei twonly!",
|
"onboardingWelcomeTitle": "Willkommen bei twonly!",
|
||||||
"@onboardingWelcomeTitle": {},
|
|
||||||
"onboardingWelcomeBody": "Erlebe eine private und sichere Möglichkeit mit Freunden in Kontakt zu bleiben, indem du spontane Bilder teilst.",
|
"onboardingWelcomeBody": "Erlebe eine private und sichere Möglichkeit mit Freunden in Kontakt zu bleiben, indem du spontane Bilder teilst.",
|
||||||
"@onboardingWelcomeBody": {},
|
|
||||||
"onboardingE2eTitle": "Unbekümmert teilen",
|
"onboardingE2eTitle": "Unbekümmert teilen",
|
||||||
"@onboardingE2eTitle": {},
|
|
||||||
"onboardingE2eBody": "Genieße durch die Ende-zu-Ende-Verschlüsselung die Gewissheit, dass nur du und deine Freunde die geteilten Momente sehen können.",
|
"onboardingE2eBody": "Genieße durch die Ende-zu-Ende-Verschlüsselung die Gewissheit, dass nur du und deine Freunde die geteilten Momente sehen können.",
|
||||||
"@onboardingE2eBody": {},
|
|
||||||
"onboardingFocusTitle": "Fokussiere dich auf das Teilen von Momenten",
|
"onboardingFocusTitle": "Fokussiere dich auf das Teilen von Momenten",
|
||||||
"@onboardingFocusTitle": {},
|
|
||||||
"onboardingFocusBody": "Verabschiede dich von süchtig machenden Funktionen! twonly wurde für das Teilen von Momenten ohne nutzlose Ablenkungen oder Werbung entwickelt.",
|
"onboardingFocusBody": "Verabschiede dich von süchtig machenden Funktionen! twonly wurde für das Teilen von Momenten ohne nutzlose Ablenkungen oder Werbung entwickelt.",
|
||||||
"@onboardingFocusBody": {},
|
|
||||||
"onboardingSendTwonliesTitle": "twonlies senden",
|
"onboardingSendTwonliesTitle": "twonlies senden",
|
||||||
"@onboardingSendTwonliesTitle": {},
|
|
||||||
"onboardingSendTwonliesBody": "Teile Momente sicher mit deinem Partner. twonly stellt sicher, dass nur dein Partner sie öffnen kann, sodass deine Momente mit deinem Partner eine two(o)nly Sache bleiben!",
|
"onboardingSendTwonliesBody": "Teile Momente sicher mit deinem Partner. twonly stellt sicher, dass nur dein Partner sie öffnen kann, sodass deine Momente mit deinem Partner eine two(o)nly Sache bleiben!",
|
||||||
"@onboardingSendTwonliesBody": {},
|
|
||||||
"onboardingNotProductTitle": "Du bist nicht das Produkt!",
|
"onboardingNotProductTitle": "Du bist nicht das Produkt!",
|
||||||
"@onboardingNotProductTitle": {},
|
|
||||||
"onboardingNotProductBody": "twonly wird durch Spenden und ein optionales Abonnement finanziert. Deine Daten werden niemals verkauft.",
|
"onboardingNotProductBody": "twonly wird durch Spenden und ein optionales Abonnement finanziert. Deine Daten werden niemals verkauft.",
|
||||||
"@onboardingNotProductBody": {},
|
|
||||||
"onboardingBuyOneGetTwoTitle": "Kaufe eins, bekomme zwei",
|
"onboardingBuyOneGetTwoTitle": "Kaufe eins, bekomme zwei",
|
||||||
"@onboardingBuyOneGetTwoTitle": {},
|
|
||||||
"onboardingBuyOneGetTwoBody": "twonly benötigt immer mindestens zwei Personen, daher erhältst du beim Kauf eine zweite kostenlose Lizenz für deinen twonly-Partner.",
|
"onboardingBuyOneGetTwoBody": "twonly benötigt immer mindestens zwei Personen, daher erhältst du beim Kauf eine zweite kostenlose Lizenz für deinen twonly-Partner.",
|
||||||
"@onboardingBuyOneGetTwoBody": {},
|
|
||||||
"onboardingGetStartedTitle": "Auf geht's",
|
"onboardingGetStartedTitle": "Auf geht's",
|
||||||
"@onboardingGetStartedTitle": {},
|
|
||||||
"onboardingGetStartedBody": "Du kannst twonly kostenlos im Preview-Modus testen. In diesem Modus kannst du von anderen gefunden werden und Bilder oder Videos empfangen, aber du kannst selbst keine senden.",
|
"onboardingGetStartedBody": "Du kannst twonly kostenlos im Preview-Modus testen. In diesem Modus kannst du von anderen gefunden werden und Bilder oder Videos empfangen, aber du kannst selbst keine senden.",
|
||||||
"@onboardingGetStartedBody": {},
|
|
||||||
"onboardingTryForFree": "Jetzt registrieren",
|
"onboardingTryForFree": "Jetzt registrieren",
|
||||||
"@onboardingTryForFree": {},
|
|
||||||
"registerUsernameSlogan": "Bitte wähle einen Benutzernamen, damit dich andere finden können!",
|
"registerUsernameSlogan": "Bitte wähle einen Benutzernamen, damit dich andere finden können!",
|
||||||
"@registerUsernameSlogan": {},
|
|
||||||
"registerUsernameDecoration": "Benutzername",
|
"registerUsernameDecoration": "Benutzername",
|
||||||
"@registerUsernameDecoration": {},
|
|
||||||
"registerUsernameLimits": "Der Benutzername muss mindestens 3 Zeichen lang sein.",
|
"registerUsernameLimits": "Der Benutzername muss mindestens 3 Zeichen lang sein.",
|
||||||
"@registerUsernameLimits": {},
|
|
||||||
"registerSubmitButton": "Jetzt registrieren!",
|
"registerSubmitButton": "Jetzt registrieren!",
|
||||||
"@registerSubmitButton": {},
|
|
||||||
"registerTwonlyCodeText": "Hast du einen twonly-Code erhalten? Dann löse ihn entweder direkt hier oder später ein!",
|
"registerTwonlyCodeText": "Hast du einen twonly-Code erhalten? Dann löse ihn entweder direkt hier oder später ein!",
|
||||||
"@registerTwonlyCodeText": {},
|
|
||||||
"registerTwonlyCodeLabel": "twonly-Code",
|
"registerTwonlyCodeLabel": "twonly-Code",
|
||||||
"@registerTwonlyCodeLabel": {},
|
|
||||||
"newMessageTitle": "Neue Nachricht",
|
"newMessageTitle": "Neue Nachricht",
|
||||||
"@newMessageTitle": {},
|
|
||||||
"chatsTapToSend": "Klicke, um dein erstes Bild zu teilen.",
|
"chatsTapToSend": "Klicke, um dein erstes Bild zu teilen.",
|
||||||
"@chatsTapToSend": {},
|
|
||||||
"cameraPreviewSendTo": "Senden an",
|
"cameraPreviewSendTo": "Senden an",
|
||||||
"@cameraPreviewSendTo": {},
|
|
||||||
"shareImageTitle": "Teilen mit",
|
"shareImageTitle": "Teilen mit",
|
||||||
"@shareImageTitle": {},
|
|
||||||
"shareImageBestFriends": "Beste Freunde",
|
"shareImageBestFriends": "Beste Freunde",
|
||||||
"@shareImageBestFriends": {},
|
|
||||||
"shareImagePinnedContacts": "Angeheftet",
|
"shareImagePinnedContacts": "Angeheftet",
|
||||||
"@shareImagePinnedContacts": {},
|
|
||||||
"shareImagedEditorSendImage": "Senden",
|
"shareImagedEditorSendImage": "Senden",
|
||||||
"@shareImagedEditorSendImage": {},
|
|
||||||
"shareImagedEditorShareWith": "Teilen mit",
|
"shareImagedEditorShareWith": "Teilen mit",
|
||||||
"@shareImagedEditorShareWith": {},
|
|
||||||
"shareImagedEditorSaveImage": "Speichern",
|
"shareImagedEditorSaveImage": "Speichern",
|
||||||
"@shareImagedEditorSaveImage": {},
|
|
||||||
"shareImagedEditorSavedImage": "Gespeichert",
|
"shareImagedEditorSavedImage": "Gespeichert",
|
||||||
"@shareImagedEditorSavedImage": {},
|
|
||||||
"shareImagedSelectAll": "Alle auswählen",
|
"shareImagedSelectAll": "Alle auswählen",
|
||||||
"@shareImagedSelectAll": {},
|
|
||||||
"shareImageAllUsers": "Alle Kontakte",
|
"shareImageAllUsers": "Alle Kontakte",
|
||||||
"@shareImageAllUsers": {},
|
|
||||||
"shareImageAllTwonlyWarning": "twonlies können nur an verifizierte Kontakte gesendet werden!",
|
"shareImageAllTwonlyWarning": "twonlies können nur an verifizierte Kontakte gesendet werden!",
|
||||||
"@shareImageAllTwonlyWarning": {},
|
|
||||||
"shareImageSearchAllContacts": "Alle Kontakte durchsuchen",
|
"shareImageSearchAllContacts": "Alle Kontakte durchsuchen",
|
||||||
"@shareImageSearchAllContacts": {},
|
|
||||||
"shareImageUserNotVerified": "Benutzer ist nicht verifiziert",
|
"shareImageUserNotVerified": "Benutzer ist nicht verifiziert",
|
||||||
"@shareImageUserNotVerified": {},
|
|
||||||
"shareImageUserNotVerifiedDesc": "twonlies können nur an verifizierte Nutzer gesendet werden. Um einen Nutzer zu verifizieren, gehe auf sein Profil und auf „Sicherheitsnummer verifizieren“.",
|
"shareImageUserNotVerifiedDesc": "twonlies können nur an verifizierte Nutzer gesendet werden. Um einen Nutzer zu verifizieren, gehe auf sein Profil und auf „Sicherheitsnummer verifizieren“.",
|
||||||
"@shareImageUserNotVerifiedDesc": {},
|
|
||||||
"shareImageShowArchived": "Archivierte Benutzer anzeigen",
|
"shareImageShowArchived": "Archivierte Benutzer anzeigen",
|
||||||
"@shareImageShowArchived": {},
|
|
||||||
"startNewChatSearchHint": "Name, Benutzername oder Gruppenname",
|
"startNewChatSearchHint": "Name, Benutzername oder Gruppenname",
|
||||||
"searchUsernameInput": "Benutzername",
|
"searchUsernameInput": "Benutzername",
|
||||||
"@searchUsernameInput": {},
|
|
||||||
"searchUsernameTitle": "Benutzernamen suchen",
|
"searchUsernameTitle": "Benutzernamen suchen",
|
||||||
"@searchUsernameTitle": {},
|
|
||||||
"searchUserNamePreview": "Um dich und andere twonly Benutzer vor Spam und Missbrauch zu schützen, ist es nicht möglich, im Preview-Modus nach anderen Personen zu suchen. Andere Benutzer können dich finden und deren Anfragen werden dann hier angezeigt!",
|
"searchUserNamePreview": "Um dich und andere twonly Benutzer vor Spam und Missbrauch zu schützen, ist es nicht möglich, im Preview-Modus nach anderen Personen zu suchen. Andere Benutzer können dich finden und deren Anfragen werden dann hier angezeigt!",
|
||||||
"@searchUserNamePreview": {},
|
|
||||||
"selectSubscription": "Abo auswählen",
|
"selectSubscription": "Abo auswählen",
|
||||||
"@selectSubscription": {},
|
|
||||||
"searchUsernameNotFound": "Benutzername nicht gefunden",
|
"searchUsernameNotFound": "Benutzername nicht gefunden",
|
||||||
"@searchUsernameNotFound": {},
|
|
||||||
"searchUsernameNotFoundBody": "Es wurde kein Benutzer mit dem Benutzernamen \"{username}\" gefunden.",
|
"searchUsernameNotFoundBody": "Es wurde kein Benutzer mit dem Benutzernamen \"{username}\" gefunden.",
|
||||||
"@searchUsernameNotFoundBody": {},
|
|
||||||
"searchUsernameNewFollowerTitle": "Folgeanfragen",
|
"searchUsernameNewFollowerTitle": "Folgeanfragen",
|
||||||
"@searchUsernameNewFollowerTitle": {},
|
|
||||||
"searchUsernameQrCodeBtn": "QR-Code scannen",
|
"searchUsernameQrCodeBtn": "QR-Code scannen",
|
||||||
"@searchUsernameQrCodeBtn": {},
|
|
||||||
"searchUserNamePending": "Ausstehend",
|
"searchUserNamePending": "Ausstehend",
|
||||||
"@searchUserNamePending": {},
|
|
||||||
"searchUserNameBlockUserTooltip": "Benutzer ohne Benachrichtigung blockieren.",
|
"searchUserNameBlockUserTooltip": "Benutzer ohne Benachrichtigung blockieren.",
|
||||||
"@searchUserNameBlockUserTooltip": {},
|
|
||||||
"searchUserNameRejectUserTooltip": "Die Anfrage ablehnen und den Anfragenden informieren.",
|
"searchUserNameRejectUserTooltip": "Die Anfrage ablehnen und den Anfragenden informieren.",
|
||||||
"@searchUserNameRejectUserTooltip": {},
|
|
||||||
"searchUserNameArchiveUserTooltip": "Benutzer archivieren. Du wirst informiert sobald er deine Anfrage akzeptiert.",
|
"searchUserNameArchiveUserTooltip": "Benutzer archivieren. Du wirst informiert sobald er deine Anfrage akzeptiert.",
|
||||||
"@searchUserNameArchiveUserTooltip": {},
|
|
||||||
"userFound": "Benutzer gefunden",
|
"userFound": "Benutzer gefunden",
|
||||||
"@userFound": {},
|
|
||||||
"userFoundBody": "Möchtest du eine Folgeanfrage stellen?",
|
"userFoundBody": "Möchtest du eine Folgeanfrage stellen?",
|
||||||
"@userFoundBody": {},
|
|
||||||
"chatListViewSearchUserNameBtn": "Füge deinen ersten twonly-Kontakt hinzu!",
|
"chatListViewSearchUserNameBtn": "Füge deinen ersten twonly-Kontakt hinzu!",
|
||||||
"@chatListViewSearchUserNameBtn": {},
|
|
||||||
"chatListViewSendFirstTwonly": "Sende dein erstes twonly!",
|
"chatListViewSendFirstTwonly": "Sende dein erstes twonly!",
|
||||||
"@chatListViewSendFirstTwonly": {},
|
|
||||||
"chatListDetailInput": "Nachricht eingeben",
|
"chatListDetailInput": "Nachricht eingeben",
|
||||||
"@chatListDetailInput": {},
|
|
||||||
"userDeletedAccount": "Der Nutzer hat sein Konto gelöscht.",
|
"userDeletedAccount": "Der Nutzer hat sein Konto gelöscht.",
|
||||||
"@userDeletedAccount": {},
|
|
||||||
"contextMenuUserProfile": "Userprofil",
|
"contextMenuUserProfile": "Userprofil",
|
||||||
"@contextMenuUserProfile": {},
|
|
||||||
"contextMenuVerifyUser": "Verifizieren",
|
"contextMenuVerifyUser": "Verifizieren",
|
||||||
"@contextMenuVerifyUser": {},
|
|
||||||
"contextMenuArchiveUser": "Archivieren",
|
"contextMenuArchiveUser": "Archivieren",
|
||||||
"@contextMenuArchiveUser": {},
|
|
||||||
"contextMenuUndoArchiveUser": "Archivierung aufheben",
|
"contextMenuUndoArchiveUser": "Archivierung aufheben",
|
||||||
"@contextMenuUndoArchiveUser": {},
|
|
||||||
"startNewChatTitle": "Kontakt wählen",
|
"startNewChatTitle": "Kontakt wählen",
|
||||||
"@startNewChatTitle": {},
|
|
||||||
"startNewChatNewContact": "Neuer Kontakt",
|
"startNewChatNewContact": "Neuer Kontakt",
|
||||||
"@startNewChatNewContact": {},
|
|
||||||
"startNewChatYourContacts": "Deine Kontakte",
|
"startNewChatYourContacts": "Deine Kontakte",
|
||||||
"@startNewChatYourContacts": {},
|
|
||||||
"contextMenuOpenChat": "Chat",
|
"contextMenuOpenChat": "Chat",
|
||||||
"@contextMenuOpenChat": {},
|
|
||||||
"contextMenuPin": "Anheften",
|
"contextMenuPin": "Anheften",
|
||||||
"@contextMenuPin": {},
|
|
||||||
"contextMenuUnpin": "Lösen",
|
"contextMenuUnpin": "Lösen",
|
||||||
"@contextMenuUnpin": {},
|
|
||||||
"mediaViewerAuthReason": "Bitte authentifiziere dich, um diesen twonly zu sehen!",
|
"mediaViewerAuthReason": "Bitte authentifiziere dich, um diesen twonly zu sehen!",
|
||||||
"@mediaViewerAuthReason": {},
|
|
||||||
"mediaViewerTwonlyTapToOpen": "Tippe um den twonly zu öffnen!",
|
"mediaViewerTwonlyTapToOpen": "Tippe um den twonly zu öffnen!",
|
||||||
"@mediaViewerTwonlyTapToOpen": {},
|
|
||||||
"messageSendState_Received": "Empfangen",
|
"messageSendState_Received": "Empfangen",
|
||||||
"@messageSendState_Received": {},
|
|
||||||
"messageSendState_Opened": "Geöffnet",
|
"messageSendState_Opened": "Geöffnet",
|
||||||
"@messageSendState_Opened": {},
|
|
||||||
"messageSendState_Send": "Gesendet",
|
"messageSendState_Send": "Gesendet",
|
||||||
"@messageSendState_Send": {},
|
|
||||||
"messageSendState_Sending": "Wird gesendet",
|
"messageSendState_Sending": "Wird gesendet",
|
||||||
"@messageSendState_Sending": {},
|
|
||||||
"messageSendState_TapToLoad": "Tippe zum Laden",
|
"messageSendState_TapToLoad": "Tippe zum Laden",
|
||||||
"@messageSendState_TapToLoad": {},
|
|
||||||
"messageSendState_Loading": "Herunterladen",
|
"messageSendState_Loading": "Herunterladen",
|
||||||
"@messageSendState_Loading": {},
|
|
||||||
"messageStoredInGallery": "Gespeichert",
|
"messageStoredInGallery": "Gespeichert",
|
||||||
"@messageStoredInGallery": {},
|
|
||||||
"messageReopened": "Erneut geöffnet",
|
"messageReopened": "Erneut geöffnet",
|
||||||
"@messageReopened": {},
|
|
||||||
"imageEditorDrawOk": "Zeichnung machen",
|
"imageEditorDrawOk": "Zeichnung machen",
|
||||||
"@imageEditorDrawOk": {},
|
|
||||||
"settingsTitle": "Einstellungen",
|
"settingsTitle": "Einstellungen",
|
||||||
"@settingsTitle": {},
|
|
||||||
"settingsChats": "Chats",
|
"settingsChats": "Chats",
|
||||||
"@settingsChats": {},
|
|
||||||
"settingsStorageData": "Daten und Speicher",
|
"settingsStorageData": "Daten und Speicher",
|
||||||
"@settingsStorageData": {},
|
|
||||||
"settingsStorageDataStoreInGTitle": "In der Galerie speichern",
|
"settingsStorageDataStoreInGTitle": "In der Galerie speichern",
|
||||||
"@settingsStorageDataStoreInGTitle": {},
|
|
||||||
"settingsStorageDataStoreInGSubtitle": "Speichere Bilder zusätzlich in der Systemgalerie.",
|
"settingsStorageDataStoreInGSubtitle": "Speichere Bilder zusätzlich in der Systemgalerie.",
|
||||||
"@settingsStorageDataStoreInGSubtitle": {},
|
|
||||||
"settingsStorageDataMediaAutoDownload": "Automatischer Mediendownload",
|
"settingsStorageDataMediaAutoDownload": "Automatischer Mediendownload",
|
||||||
"@settingsStorageDataMediaAutoDownload": {},
|
|
||||||
"settingsStorageDataAutoDownMobile": "Bei Nutzung mobiler Daten",
|
"settingsStorageDataAutoDownMobile": "Bei Nutzung mobiler Daten",
|
||||||
"@settingsStorageDataAutoDownMobile": {},
|
|
||||||
"settingsStorageDataAutoDownWifi": "Bei Nutzung von WLAN",
|
"settingsStorageDataAutoDownWifi": "Bei Nutzung von WLAN",
|
||||||
"@settingsStorageDataAutoDownWifi": {},
|
|
||||||
"settingsPreSelectedReactions": "Vorgewählte Reaktions-Emojis",
|
"settingsPreSelectedReactions": "Vorgewählte Reaktions-Emojis",
|
||||||
"@settingsPreSelectedReactions": {},
|
|
||||||
"settingsPreSelectedReactionsError": "Es können maximal 12 Reaktionen ausgewählt werden.",
|
"settingsPreSelectedReactionsError": "Es können maximal 12 Reaktionen ausgewählt werden.",
|
||||||
"@settingsPreSelectedReactionsError": {},
|
|
||||||
"settingsProfile": "Profil",
|
"settingsProfile": "Profil",
|
||||||
"@settingsProfile": {},
|
|
||||||
"settingsProfileCustomizeAvatar": "Avatar anpassen",
|
"settingsProfileCustomizeAvatar": "Avatar anpassen",
|
||||||
"@settingsProfileCustomizeAvatar": {},
|
|
||||||
"settingsProfileEditDisplayName": "Anzeigename",
|
"settingsProfileEditDisplayName": "Anzeigename",
|
||||||
"@settingsProfileEditDisplayName": {},
|
|
||||||
"settingsProfileEditDisplayNameNew": "Neuer Anzeigename",
|
"settingsProfileEditDisplayNameNew": "Neuer Anzeigename",
|
||||||
"@settingsProfileEditDisplayNameNew": {},
|
|
||||||
"settingsAccount": "Konto",
|
"settingsAccount": "Konto",
|
||||||
"@settingsAccount": {},
|
|
||||||
"settingsSubscription": "Abonnement",
|
"settingsSubscription": "Abonnement",
|
||||||
"@settingsSubscription": {},
|
|
||||||
"settingsAppearance": "Erscheinungsbild",
|
"settingsAppearance": "Erscheinungsbild",
|
||||||
"@settingsAppearance": {},
|
|
||||||
"settingsPrivacy": "Datenschutz",
|
"settingsPrivacy": "Datenschutz",
|
||||||
"@settingsPrivacy": {},
|
|
||||||
"settingsPrivacyBlockUsers": "Benutzer blockieren",
|
"settingsPrivacyBlockUsers": "Benutzer blockieren",
|
||||||
"@settingsPrivacyBlockUsers": {},
|
|
||||||
"settingsPrivacyBlockUsersDesc": "Blockierte Benutzer können nicht mit dir kommunizieren. Du kannst einen blockierten Benutzer jederzeit wieder entsperren.",
|
"settingsPrivacyBlockUsersDesc": "Blockierte Benutzer können nicht mit dir kommunizieren. Du kannst einen blockierten Benutzer jederzeit wieder entsperren.",
|
||||||
"@settingsPrivacyBlockUsersDesc": {},
|
|
||||||
"settingsPrivacyBlockUsersCount": "{len} Kontakt(e)",
|
"settingsPrivacyBlockUsersCount": "{len} Kontakt(e)",
|
||||||
"@settingsPrivacyBlockUsersCount": {},
|
|
||||||
"settingsNotification": "Benachrichtigung",
|
"settingsNotification": "Benachrichtigung",
|
||||||
"@settingsNotification": {},
|
|
||||||
"settingsNotifyTroubleshooting": "Fehlersuche",
|
"settingsNotifyTroubleshooting": "Fehlersuche",
|
||||||
"@settingsNotifyTroubleshooting": {},
|
|
||||||
"settingsNotifyTroubleshootingDesc": "Hier klicken, wenn Probleme beim Empfang von Push-Benachrichtigungen auftreten.",
|
"settingsNotifyTroubleshootingDesc": "Hier klicken, wenn Probleme beim Empfang von Push-Benachrichtigungen auftreten.",
|
||||||
"@settingsNotifyTroubleshootingDesc": {},
|
|
||||||
"settingsNotifyTroubleshootingNoProblem": "Kein Problem festgestellt",
|
"settingsNotifyTroubleshootingNoProblem": "Kein Problem festgestellt",
|
||||||
"@settingsNotifyTroubleshootingNoProblem": {},
|
|
||||||
"settingsNotifyTroubleshootingNoProblemDesc": "Klicke auf OK, um eine Testbenachrichtigung zu erhalten. Wenn du auch nach 10 Minuten warten keine Nachricht erhältst, sende uns bitte dein Diagnoseprotokoll unter Einstellungen > Hilfe > Diagnoseprotokoll, damit wir uns das Problem ansehen können.",
|
"settingsNotifyTroubleshootingNoProblemDesc": "Klicke auf OK, um eine Testbenachrichtigung zu erhalten. Wenn du auch nach 10 Minuten warten keine Nachricht erhältst, sende uns bitte dein Diagnoseprotokoll unter Einstellungen > Hilfe > Diagnoseprotokoll, damit wir uns das Problem ansehen können.",
|
||||||
"@settingsNotifyTroubleshootingNoProblemDesc": {},
|
|
||||||
"settingsHelp": "Hilfe",
|
"settingsHelp": "Hilfe",
|
||||||
"@settingsHelp": {},
|
|
||||||
"settingsHelpFAQ": "FAQ",
|
"settingsHelpFAQ": "FAQ",
|
||||||
"@settingsHelpFAQ": {},
|
|
||||||
"feedbackTooltip": "Feedback zur Verbesserung von twonly geben.",
|
"feedbackTooltip": "Feedback zur Verbesserung von twonly geben.",
|
||||||
"@feedbackTooltip": {},
|
|
||||||
"settingsHelpContactUs": "Kontaktiere uns",
|
"settingsHelpContactUs": "Kontaktiere uns",
|
||||||
"@settingsHelpContactUs": {},
|
|
||||||
"contactUsFaq": "FAQ schon gelesen?",
|
"contactUsFaq": "FAQ schon gelesen?",
|
||||||
"@contactUsFaq": {},
|
|
||||||
"contactUsEmojis": "Wie fühlst du dich? (optional)",
|
"contactUsEmojis": "Wie fühlst du dich? (optional)",
|
||||||
"@contactUsEmojis": {},
|
|
||||||
"contactUsSelectOption": "Bitte wähle eine Option",
|
"contactUsSelectOption": "Bitte wähle eine Option",
|
||||||
"@contactUsSelectOption": {},
|
|
||||||
"contactUsReason": "Sag uns, warum du uns kontaktierst",
|
"contactUsReason": "Sag uns, warum du uns kontaktierst",
|
||||||
"@contactUsReason": {},
|
|
||||||
"contactUsMessage": "Wenn du eine Antwort erhalten möchtest, füge bitte deine E-Mail-Adresse hinzu, damit wir dich kontaktieren können.",
|
"contactUsMessage": "Wenn du eine Antwort erhalten möchtest, füge bitte deine E-Mail-Adresse hinzu, damit wir dich kontaktieren können.",
|
||||||
"@contactUsMessage": {},
|
|
||||||
"contactUsYourMessage": "Deine Nachricht",
|
"contactUsYourMessage": "Deine Nachricht",
|
||||||
"@contactUsYourMessage": {},
|
|
||||||
"contactUsMessageTitle": "Erzähl uns, was los ist",
|
"contactUsMessageTitle": "Erzähl uns, was los ist",
|
||||||
"@contactUsMessageTitle": {},
|
|
||||||
"contactUsReasonNotWorking": "Etwas funktioniert nicht",
|
"contactUsReasonNotWorking": "Etwas funktioniert nicht",
|
||||||
"@contactUsReasonNotWorking": {},
|
|
||||||
"contactUsReasonFeatureRequest": "Funktionsanfrage",
|
"contactUsReasonFeatureRequest": "Funktionsanfrage",
|
||||||
"@contactUsReasonFeatureRequest": {},
|
|
||||||
"contactUsReasonQuestion": "Frage",
|
"contactUsReasonQuestion": "Frage",
|
||||||
"@contactUsReasonQuestion": {},
|
|
||||||
"contactUsReasonFeedback": "Feedback",
|
"contactUsReasonFeedback": "Feedback",
|
||||||
"@contactUsReasonFeedback": {},
|
|
||||||
"contactUsReasonOther": "Sonstiges",
|
"contactUsReasonOther": "Sonstiges",
|
||||||
"@contactUsReasonOther": {},
|
|
||||||
"contactUsIncludeLog": "Debug-Protokoll anhängen.",
|
"contactUsIncludeLog": "Debug-Protokoll anhängen.",
|
||||||
"@contactUsIncludeLog": {},
|
|
||||||
"contactUsWhatsThat": "Was ist das?",
|
"contactUsWhatsThat": "Was ist das?",
|
||||||
"@contactUsWhatsThat": {},
|
|
||||||
"contactUsLastWarning": "Dies sind die Informationen, die an uns gesendet werden. Bitte prüfen Sie sie und klicke dann auf „Abschicken“.",
|
"contactUsLastWarning": "Dies sind die Informationen, die an uns gesendet werden. Bitte prüfen Sie sie und klicke dann auf „Abschicken“.",
|
||||||
"@contactUsLastWarning": {},
|
|
||||||
"contactUsSuccess": "Feedback erfolgreich übermittelt!",
|
"contactUsSuccess": "Feedback erfolgreich übermittelt!",
|
||||||
"@contactUsSuccess": {},
|
|
||||||
"contactUsShortcut": "Feedback-Symbol ausblenden",
|
"contactUsShortcut": "Feedback-Symbol ausblenden",
|
||||||
"@contactUsShortcut": {},
|
|
||||||
"settingsHelpDiagnostics": "Diagnoseprotokoll",
|
"settingsHelpDiagnostics": "Diagnoseprotokoll",
|
||||||
"@settingsHelpDiagnostics": {},
|
|
||||||
"settingsHelpVersion": "Version",
|
"settingsHelpVersion": "Version",
|
||||||
"@settingsHelpVersion": {},
|
|
||||||
"settingsHelpLicenses": "Lizenzen (Source-Code)",
|
"settingsHelpLicenses": "Lizenzen (Source-Code)",
|
||||||
"@settingsHelpLicenses": {},
|
|
||||||
"settingsHelpCredits": "Lizenzen (Bilder)",
|
"settingsHelpCredits": "Lizenzen (Bilder)",
|
||||||
"@settingsHelpCredits": {},
|
|
||||||
"settingsHelpImprint": "Impressum & Datenschutzrichtlinie",
|
"settingsHelpImprint": "Impressum & Datenschutzrichtlinie",
|
||||||
"@settingsHelpImprint": {},
|
|
||||||
"settingsHelpTerms": "Nutzungsbedingungen",
|
"settingsHelpTerms": "Nutzungsbedingungen",
|
||||||
"@settingsHelpTerms": {},
|
|
||||||
"settingsAppearanceTheme": "Theme",
|
"settingsAppearanceTheme": "Theme",
|
||||||
"@settingsAppearanceTheme": {},
|
|
||||||
"settingsAccountDeleteAccount": "Konto löschen",
|
"settingsAccountDeleteAccount": "Konto löschen",
|
||||||
"@settingsAccountDeleteAccount": {},
|
|
||||||
"settingsAccountDeleteAccountWithBallance": "Im nächsten Schritt kannst du auswählen, was du mit dem Restguthaben ({credit}) machen willst.",
|
"settingsAccountDeleteAccountWithBallance": "Im nächsten Schritt kannst du auswählen, was du mit dem Restguthaben ({credit}) machen willst.",
|
||||||
"@settingsAccountDeleteAccountWithBallance": {},
|
|
||||||
"settingsAccountDeleteAccountNoInternet": "Zum Löschen deines Accounts ist eine Internetverbindung erforderlich.",
|
"settingsAccountDeleteAccountNoInternet": "Zum Löschen deines Accounts ist eine Internetverbindung erforderlich.",
|
||||||
"@settingsAccountDeleteAccountNoInternet": {},
|
|
||||||
"settingsAccountDeleteAccountNoBallance": "Wenn du dein Konto gelöscht hast, gibt es keinen Weg zurück.",
|
"settingsAccountDeleteAccountNoBallance": "Wenn du dein Konto gelöscht hast, gibt es keinen Weg zurück.",
|
||||||
"@settingsAccountDeleteAccountNoBallance": {},
|
|
||||||
"settingsAccountDeleteModalTitle": "Bist du sicher?",
|
"settingsAccountDeleteModalTitle": "Bist du sicher?",
|
||||||
"@settingsAccountDeleteModalTitle": {},
|
|
||||||
"settingsAccountDeleteModalBody": "Dein Konto wird gelöscht. Es gibt keine Möglichkeit, es wiederherzustellen.",
|
"settingsAccountDeleteModalBody": "Dein Konto wird gelöscht. Es gibt keine Möglichkeit, es wiederherzustellen.",
|
||||||
"@settingsAccountDeleteModalBody": {},
|
|
||||||
"contactVerifyNumberTitle": "Sicherheitsnummer verifizieren",
|
"contactVerifyNumberTitle": "Sicherheitsnummer verifizieren",
|
||||||
"@contactVerifyNumberTitle": {},
|
|
||||||
"contactVerifyNumberTapToScan": "Zum Scannen tippen",
|
"contactVerifyNumberTapToScan": "Zum Scannen tippen",
|
||||||
"@contactVerifyNumberTapToScan": {},
|
|
||||||
"contactVerifyNumberMarkAsVerified": "Als verifiziert markieren",
|
"contactVerifyNumberMarkAsVerified": "Als verifiziert markieren",
|
||||||
"@contactVerifyNumberMarkAsVerified": {},
|
|
||||||
"contactVerifyNumberClearVerification": "Verifizierung aufheben",
|
"contactVerifyNumberClearVerification": "Verifizierung aufheben",
|
||||||
"@contactVerifyNumberClearVerification": {},
|
|
||||||
"contactVerifyNumberLongDesc": "Um die Ende-zu-Ende-Verschlüsselung mit {username} zu verifizieren, vergleiche die Zahlen mit ihrem Gerät. Die Person kann auch deinen Code mit ihrem Gerät scannen.",
|
"contactVerifyNumberLongDesc": "Um die Ende-zu-Ende-Verschlüsselung mit {username} zu verifizieren, vergleiche die Zahlen mit ihrem Gerät. Die Person kann auch deinen Code mit ihrem Gerät scannen.",
|
||||||
"@contactVerifyNumberLongDesc": {},
|
|
||||||
"contactNickname": "Spitzname",
|
"contactNickname": "Spitzname",
|
||||||
"@contactNickname": {},
|
|
||||||
"contactNicknameNew": "Neuer Spitzname",
|
"contactNicknameNew": "Neuer Spitzname",
|
||||||
"@contactNicknameNew": {},
|
|
||||||
"contactBlock": "Blockieren",
|
"contactBlock": "Blockieren",
|
||||||
"@contactBlock": {},
|
|
||||||
"contactRemove": "Benutzer löschen",
|
"contactRemove": "Benutzer löschen",
|
||||||
"@contactRemove": {},
|
|
||||||
"contactRemoveTitle": "{username} löschen?",
|
"contactRemoveTitle": "{username} löschen?",
|
||||||
"@contactRemoveTitle": {},
|
|
||||||
"contactRemoveBody": "Entferne den Benutzer und lösche den Chat sowie alle zugehörigen Mediendateien dauerhaft. Dadurch wird auch DEIN KONTO VON DEM TELEFON DEINES KONTAKTS gelöscht.",
|
"contactRemoveBody": "Entferne den Benutzer und lösche den Chat sowie alle zugehörigen Mediendateien dauerhaft. Dadurch wird auch DEIN KONTO VON DEM TELEFON DEINES KONTAKTS gelöscht.",
|
||||||
"@contactRemoveBody": {},
|
|
||||||
"deleteAllContactMessages": "Textnachrichten löschen",
|
"deleteAllContactMessages": "Textnachrichten löschen",
|
||||||
"@deleteAllContactMessages": {},
|
|
||||||
"deleteAllContactMessagesBody": "Dadurch werden alle Nachrichten, ausgenommen gespeicherte Mediendateien, in deinem Chat mit {username} gelöscht. Dies löscht NICHT die auf dem Gerät von {username} gespeicherten Nachrichten!",
|
"deleteAllContactMessagesBody": "Dadurch werden alle Nachrichten, ausgenommen gespeicherte Mediendateien, in deinem Chat mit {username} gelöscht. Dies löscht NICHT die auf dem Gerät von {username} gespeicherten Nachrichten!",
|
||||||
"@deleteAllContactMessagesBody": {},
|
|
||||||
"contactBlockTitle": "Blockiere {username}",
|
"contactBlockTitle": "Blockiere {username}",
|
||||||
"@contactBlockTitle": {},
|
|
||||||
"contactBlockBody": "Ein blockierter Benutzer kann dir keine Nachrichten mehr senden, und sein Profil ist nicht mehr sichtbar. Um die Blockierung eines Benutzers aufzuheben, navigiere einfach zu Einstellungen > Datenschutz > Blockierte Benutzer.",
|
"contactBlockBody": "Ein blockierter Benutzer kann dir keine Nachrichten mehr senden, und sein Profil ist nicht mehr sichtbar. Um die Blockierung eines Benutzers aufzuheben, navigiere einfach zu Einstellungen > Datenschutz > Blockierte Benutzer.",
|
||||||
"@contactBlockBody": {},
|
|
||||||
"undo": "Rückgängig",
|
"undo": "Rückgängig",
|
||||||
"@undo": {},
|
|
||||||
"redo": "Wiederholen",
|
"redo": "Wiederholen",
|
||||||
"@redo": {},
|
|
||||||
"next": "Weiter",
|
"next": "Weiter",
|
||||||
"@next": {},
|
|
||||||
"submit": "Abschicken",
|
"submit": "Abschicken",
|
||||||
"@submit": {},
|
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"@close": {},
|
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"@cancel": {},
|
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"@edit": {},
|
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"@ok": {},
|
|
||||||
"now": "Jetzt",
|
"now": "Jetzt",
|
||||||
"@now": {},
|
|
||||||
"you": "Du",
|
"you": "Du",
|
||||||
"@you": {},
|
|
||||||
"minutesShort": "Min.",
|
"minutesShort": "Min.",
|
||||||
"@minutesShort": {},
|
|
||||||
"image": "Bild",
|
"image": "Bild",
|
||||||
"@image": {},
|
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"@video": {},
|
|
||||||
"react": "Reagieren",
|
"react": "Reagieren",
|
||||||
"@react": {},
|
|
||||||
"reply": "Antworten",
|
"reply": "Antworten",
|
||||||
"@reply": {},
|
|
||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
"@copy": {},
|
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"@delete": {},
|
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"@info": {},
|
|
||||||
"disable": "Deaktiviern",
|
"disable": "Deaktiviern",
|
||||||
"@disable": {},
|
|
||||||
"enable": "Aktivieren",
|
"enable": "Aktivieren",
|
||||||
"@enable": {},
|
|
||||||
"switchFrontAndBackCamera": "Zwischen Front- und Rückkamera wechseln.",
|
"switchFrontAndBackCamera": "Zwischen Front- und Rückkamera wechseln.",
|
||||||
"@switchFrontAndBackCamera": {},
|
|
||||||
"addTextItem": "Text",
|
"addTextItem": "Text",
|
||||||
"@addTextItem": {},
|
|
||||||
"protectAsARealTwonly": "Als echtes twonly senden!",
|
"protectAsARealTwonly": "Als echtes twonly senden!",
|
||||||
"@protectAsARealTwonly": {},
|
|
||||||
"addDrawing": "Zeichnung",
|
"addDrawing": "Zeichnung",
|
||||||
"@addDrawing": {},
|
|
||||||
"addEmoji": "Emoji",
|
"addEmoji": "Emoji",
|
||||||
"@addEmoji": {},
|
|
||||||
"toggleFlashLight": "Taschenlampe umschalten",
|
"toggleFlashLight": "Taschenlampe umschalten",
|
||||||
"@toggleFlashLight": {},
|
|
||||||
"toggleHighQuality": "Bessere Auflösung umschalten",
|
"toggleHighQuality": "Bessere Auflösung umschalten",
|
||||||
"@toggleHighQuality": {},
|
|
||||||
"searchUsernameNotFoundLong": "\"{username}\" ist kein twonly-Benutzer. Bitte überprüfe den Benutzernamen und versuche es erneut.",
|
"searchUsernameNotFoundLong": "\"{username}\" ist kein twonly-Benutzer. Bitte überprüfe den Benutzernamen und versuche es erneut.",
|
||||||
"@searchUsernameNotFoundLong": {},
|
|
||||||
"errorUnknown": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es später erneut.",
|
"errorUnknown": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es später erneut.",
|
||||||
"@errorUnknown": {},
|
|
||||||
"errorBadRequest": "Die Anfrage konnte vom Server aufgrund einer fehlerhaften Syntax nicht verstanden werden. Bitte überprüfe deine Eingabe und versuche es erneut.",
|
"errorBadRequest": "Die Anfrage konnte vom Server aufgrund einer fehlerhaften Syntax nicht verstanden werden. Bitte überprüfe deine Eingabe und versuche es erneut.",
|
||||||
"@errorBadRequest": {},
|
|
||||||
"errorTooManyRequests": "Du hast in kurzer Zeit zu viele Anfragen gestellt. Bitte warte einen Moment, bevor du es erneut versuchst.",
|
"errorTooManyRequests": "Du hast in kurzer Zeit zu viele Anfragen gestellt. Bitte warte einen Moment, bevor du es erneut versuchst.",
|
||||||
"@errorTooManyRequests": {},
|
|
||||||
"errorInternalError": "Der Server ist derzeit nicht verfügbar. Bitte versuche es später erneut.",
|
"errorInternalError": "Der Server ist derzeit nicht verfügbar. Bitte versuche es später erneut.",
|
||||||
"@errorInternalError": {},
|
|
||||||
"errorInvalidInvitationCode": "Der von dir angegebene Einladungscode ist ungültig. Bitte überprüfe den Code und versuche es erneut.",
|
"errorInvalidInvitationCode": "Der von dir angegebene Einladungscode ist ungültig. Bitte überprüfe den Code und versuche es erneut.",
|
||||||
"@errorInvalidInvitationCode": {},
|
|
||||||
"errorUsernameAlreadyTaken": "Der Benutzername ist bereits vergeben.",
|
"errorUsernameAlreadyTaken": "Der Benutzername ist bereits vergeben.",
|
||||||
"@errorUsernameAlreadyTaken": {},
|
|
||||||
"errorSignatureNotValid": "Die bereitgestellte Signatur ist nicht gültig. Bitte überprüfe deine Anmeldeinformationen und versuche es erneut.",
|
"errorSignatureNotValid": "Die bereitgestellte Signatur ist nicht gültig. Bitte überprüfe deine Anmeldeinformationen und versuche es erneut.",
|
||||||
"@errorSignatureNotValid": {},
|
|
||||||
"errorUsernameNotFound": "Der eingegebene Benutzername existiert nicht. Bitte überprüfe die Schreibweise oder erstelle ein neues Konto.",
|
"errorUsernameNotFound": "Der eingegebene Benutzername existiert nicht. Bitte überprüfe die Schreibweise oder erstelle ein neues Konto.",
|
||||||
"@errorUsernameNotFound": {},
|
|
||||||
"errorUsernameNotValid": "Der von dir angegebene Benutzername entspricht nicht den erforderlichen Kriterien. Bitte wähle einen gültigen Benutzernamen.",
|
"errorUsernameNotValid": "Der von dir angegebene Benutzername entspricht nicht den erforderlichen Kriterien. Bitte wähle einen gültigen Benutzernamen.",
|
||||||
"@errorUsernameNotValid": {},
|
|
||||||
"errorInvalidPublicKey": "Der von dir angegebene öffentliche Schlüssel ist ungültig. Bitte überprüfe den Schlüssel und versuche es erneut.",
|
"errorInvalidPublicKey": "Der von dir angegebene öffentliche Schlüssel ist ungültig. Bitte überprüfe den Schlüssel und versuche es erneut.",
|
||||||
"@errorInvalidPublicKey": {},
|
|
||||||
"errorSessionAlreadyAuthenticated": "Du bist bereits angemeldet. Bitte melde dich ab, wenn du dich mit einem anderen Konto anmelden möchtest.",
|
"errorSessionAlreadyAuthenticated": "Du bist bereits angemeldet. Bitte melde dich ab, wenn du dich mit einem anderen Konto anmelden möchtest.",
|
||||||
"@errorSessionAlreadyAuthenticated": {},
|
|
||||||
"errorSessionNotAuthenticated": "Deine Sitzung ist nicht authentifiziert. Bitte melde dich an, um fortzufahren.",
|
"errorSessionNotAuthenticated": "Deine Sitzung ist nicht authentifiziert. Bitte melde dich an, um fortzufahren.",
|
||||||
"@errorSessionNotAuthenticated": {},
|
|
||||||
"errorOnlyOneSessionAllowed": "Es ist nur eine aktive Sitzung pro Benutzer erlaubt. Bitte melde dich von anderen Geräten ab, um fortzufahren.",
|
"errorOnlyOneSessionAllowed": "Es ist nur eine aktive Sitzung pro Benutzer erlaubt. Bitte melde dich von anderen Geräten ab, um fortzufahren.",
|
||||||
"@errorOnlyOneSessionAllowed": {},
|
|
||||||
"upgradeToPaidPlan": "Upgrade auf einen kostenpflichtigen Plan.",
|
"upgradeToPaidPlan": "Upgrade auf einen kostenpflichtigen Plan.",
|
||||||
"@upgradeToPaidPlan": {},
|
|
||||||
"upgradeToPaidPlanButton": "Auf {planId} upgraden",
|
"upgradeToPaidPlanButton": "Auf {planId} upgraden",
|
||||||
"@upgradeToPaidPlanButton": {},
|
|
||||||
"partOfPaidPlanOf": "Du bist Teil des bezahlten Plans von {username}!",
|
"partOfPaidPlanOf": "Du bist Teil des bezahlten Plans von {username}!",
|
||||||
"@partOfPaidPlanOf": {},
|
|
||||||
"errorNotEnoughCredit": "Du hast nicht genügend twonly-Guthaben.",
|
"errorNotEnoughCredit": "Du hast nicht genügend twonly-Guthaben.",
|
||||||
"@errorNotEnoughCredit": {},
|
|
||||||
"errorPlanLimitReached": "Du hast das Limit deines Plans erreicht. Bitte upgrade deinen Plan.",
|
"errorPlanLimitReached": "Du hast das Limit deines Plans erreicht. Bitte upgrade deinen Plan.",
|
||||||
"@errorPlanLimitReached": {},
|
|
||||||
"errorPlanNotAllowed": "Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.",
|
"errorPlanNotAllowed": "Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.",
|
||||||
"@errorPlanNotAllowed": {},
|
|
||||||
"errorVoucherInvalid": "Der eingegebene Gutschein-Code ist nicht gültig.",
|
"errorVoucherInvalid": "Der eingegebene Gutschein-Code ist nicht gültig.",
|
||||||
"@errorVoucherInvalid": {},
|
|
||||||
"errorPlanUpgradeNotYearly": "Das Upgrade des Plans muss jährlich bezahlt werden, da der aktuelle Plan ebenfalls jährlich abgerechnet wird.",
|
"errorPlanUpgradeNotYearly": "Das Upgrade des Plans muss jährlich bezahlt werden, da der aktuelle Plan ebenfalls jährlich abgerechnet wird.",
|
||||||
"@errorPlanUpgradeNotYearly": {},
|
|
||||||
"proFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
"proFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||||
"@proFeature1": {},
|
|
||||||
"proFeature2": "1 zusätzlicher Plus Benutzer",
|
"proFeature2": "1 zusätzlicher Plus Benutzer",
|
||||||
"@proFeature2": {},
|
|
||||||
"proFeature3": "Flammen wiederherstellen",
|
"proFeature3": "Flammen wiederherstellen",
|
||||||
"@proFeature3": {},
|
|
||||||
"proFeature4": "Cloud-Backup verschlüsselt (coming-soon)",
|
"proFeature4": "Cloud-Backup verschlüsselt (coming-soon)",
|
||||||
"@proFeature4": {},
|
|
||||||
"year": "year",
|
"year": "year",
|
||||||
"@year": {},
|
|
||||||
"month": "month",
|
"month": "month",
|
||||||
"@month": {},
|
|
||||||
"familyFeature1": "✓ Alles von Pro",
|
"familyFeature1": "✓ Alles von Pro",
|
||||||
"@familyFeature1": {},
|
|
||||||
"familyFeature2": "4 zusätzliche Plus Benutzer",
|
"familyFeature2": "4 zusätzliche Plus Benutzer",
|
||||||
"@familyFeature2": {},
|
|
||||||
"redeemUserInviteCode": "Oder löse einen twonly-Code ein.",
|
"redeemUserInviteCode": "Oder löse einen twonly-Code ein.",
|
||||||
"@redeemUserInviteCode": {},
|
|
||||||
"freeFeature1": "10 Medien-Datei-Uploads pro Tag",
|
"freeFeature1": "10 Medien-Datei-Uploads pro Tag",
|
||||||
"@freeFeature1": {},
|
|
||||||
"plusFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
"plusFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||||
"@plusFeature1": {},
|
|
||||||
"plusFeature2": "Zusatzfunktionen (coming-soon)",
|
"plusFeature2": "Zusatzfunktionen (coming-soon)",
|
||||||
"@plusFeature2": {},
|
|
||||||
"transactionHistory": "Transaktionshistorie",
|
"transactionHistory": "Transaktionshistorie",
|
||||||
"@transactionHistory": {},
|
|
||||||
"currentBalance": "Dein Guthaben",
|
"currentBalance": "Dein Guthaben",
|
||||||
"@currentBalance": {},
|
|
||||||
"manageAdditionalUsers": "Zusätzliche Benutzer verwalten",
|
"manageAdditionalUsers": "Zusätzliche Benutzer verwalten",
|
||||||
"@manageAdditionalUsers": {},
|
|
||||||
"manageSubscription": "Abonnement verwalten",
|
"manageSubscription": "Abonnement verwalten",
|
||||||
"@manageSubscription": {},
|
|
||||||
"nextPayment": "Nächste Zahlung",
|
"nextPayment": "Nächste Zahlung",
|
||||||
"@nextPayment": {},
|
|
||||||
"open": "Offene",
|
"open": "Offene",
|
||||||
"@open": {},
|
|
||||||
"buy": "Kaufen",
|
"buy": "Kaufen",
|
||||||
"@buy": {},
|
|
||||||
"createOrRedeemVoucher": "Gutschein erstellen oder einlösen",
|
"createOrRedeemVoucher": "Gutschein erstellen oder einlösen",
|
||||||
"@createOrRedeemVoucher": {},
|
|
||||||
"subscriptionRefund": "Wenn du ein Upgrade durchführst, erhältst du eine Rückerstattung von {refund} für dein aktuelles Abonnement.",
|
"subscriptionRefund": "Wenn du ein Upgrade durchführst, erhältst du eine Rückerstattung von {refund} für dein aktuelles Abonnement.",
|
||||||
"@subscriptionRefund": {},
|
|
||||||
"createVoucher": "Gutschein kaufen",
|
"createVoucher": "Gutschein kaufen",
|
||||||
"@createVoucher": {},
|
|
||||||
"createVoucherDesc": "Wähle den Wert des Gutscheins. Der Wert des Gutschein wird von deinem twonly-Guthaben abgezogen.",
|
"createVoucherDesc": "Wähle den Wert des Gutscheins. Der Wert des Gutschein wird von deinem twonly-Guthaben abgezogen.",
|
||||||
"@createVoucherDesc": {},
|
|
||||||
"redeemVoucher": "Gutschein einlösen",
|
"redeemVoucher": "Gutschein einlösen",
|
||||||
"@redeemVoucher": {},
|
|
||||||
"redeemUserInviteCodeTitle": "twonly-Code einlösen",
|
"redeemUserInviteCodeTitle": "twonly-Code einlösen",
|
||||||
"@redeemUserInviteCodeTitle": {},
|
|
||||||
"redeemUserInviteCodeSuccess": "Dein Plan wurde erfolgreich angepasst.",
|
"redeemUserInviteCodeSuccess": "Dein Plan wurde erfolgreich angepasst.",
|
||||||
"@redeemUserInviteCodeSuccess": {},
|
|
||||||
"voucherCreated": "Gutschein wurde erstellt",
|
"voucherCreated": "Gutschein wurde erstellt",
|
||||||
"@voucherCreated": {},
|
|
||||||
"openVouchers": "Offene Gutscheine",
|
"openVouchers": "Offene Gutscheine",
|
||||||
"@openVouchers": {},
|
|
||||||
"enterVoucherCode": "Gutschein Code eingeben",
|
"enterVoucherCode": "Gutschein Code eingeben",
|
||||||
"@enterVoucherCode": {},
|
|
||||||
"voucherRedeemed": "Gutschein eingelöst",
|
"voucherRedeemed": "Gutschein eingelöst",
|
||||||
"@voucherRedeemed": {},
|
|
||||||
"requestedVouchers": "Beantragte Gutscheine",
|
"requestedVouchers": "Beantragte Gutscheine",
|
||||||
"@requestedVouchers": {},
|
|
||||||
"redeemedVouchers": "Eingelöste Gutscheine",
|
"redeemedVouchers": "Eingelöste Gutscheine",
|
||||||
"@redeemedVouchers": {},
|
|
||||||
"transactionCash": "Bargeldtransaktion",
|
"transactionCash": "Bargeldtransaktion",
|
||||||
"@transactionCash": {},
|
|
||||||
"transactionPlanUpgrade": "Planupgrade",
|
"transactionPlanUpgrade": "Planupgrade",
|
||||||
"@transactionPlanUpgrade": {},
|
|
||||||
"transactionRefund": "Rückerstattung",
|
"transactionRefund": "Rückerstattung",
|
||||||
"@transactionRefund": {},
|
|
||||||
"transactionAutoRenewal": "Automatische Verlängerung",
|
"transactionAutoRenewal": "Automatische Verlängerung",
|
||||||
"@transactionAutoRenewal": {},
|
|
||||||
"refund": "Rückerstattung",
|
"refund": "Rückerstattung",
|
||||||
"@refund": {},
|
|
||||||
"transactionThanksForTesting": "Danke fürs Testen",
|
"transactionThanksForTesting": "Danke fürs Testen",
|
||||||
"@transactionThanksForTesting": {},
|
|
||||||
"transactionUnknown": "Unbekannte Transaktion",
|
"transactionUnknown": "Unbekannte Transaktion",
|
||||||
"@transactionUnknown": {},
|
|
||||||
"transactionVoucherCreated": "Gutschein erstellt",
|
"transactionVoucherCreated": "Gutschein erstellt",
|
||||||
"@transactionVoucherCreated": {},
|
|
||||||
"transactionVoucherRedeemed": "Gutschein eingelöst",
|
"transactionVoucherRedeemed": "Gutschein eingelöst",
|
||||||
"@transactionVoucherRedeemed": {},
|
|
||||||
"checkoutOptions": "Optionen",
|
"checkoutOptions": "Optionen",
|
||||||
"@checkoutOptions": {},
|
|
||||||
"checkoutPayYearly": "Jährlich bezahlen",
|
"checkoutPayYearly": "Jährlich bezahlen",
|
||||||
"@checkoutPayYearly": {},
|
|
||||||
"checkoutTotal": "Gesamt",
|
"checkoutTotal": "Gesamt",
|
||||||
"@checkoutTotal": {},
|
|
||||||
"selectPaymentMethod": "Zahlungsmethode auswählen",
|
"selectPaymentMethod": "Zahlungsmethode auswählen",
|
||||||
"@selectPaymentMethod": {},
|
|
||||||
"twonlyCredit": "twonly-Guthaben",
|
"twonlyCredit": "twonly-Guthaben",
|
||||||
"@twonlyCredit": {},
|
|
||||||
"notEnoughCredit": "Du hast nicht genügend Guthaben!",
|
"notEnoughCredit": "Du hast nicht genügend Guthaben!",
|
||||||
"@notEnoughCredit": {},
|
|
||||||
"chargeCredit": "Guthaben aufladen",
|
"chargeCredit": "Guthaben aufladen",
|
||||||
"@chargeCredit": {},
|
|
||||||
"autoRenewal": "Automatische Verlängerung",
|
"autoRenewal": "Automatische Verlängerung",
|
||||||
"@autoRenewal": {},
|
|
||||||
"autoRenewalDesc": "Du kannst dies jederzeit ändern.",
|
"autoRenewalDesc": "Du kannst dies jederzeit ändern.",
|
||||||
"@autoRenewalDesc": {},
|
|
||||||
"autoRenewalLongDesc": "Wenn dein Abonnement ausläuft, wirst du automatisch auf den Preview-Plan zurückgestuft. Wenn du die automatische Verlängerung aktivierst, vergewissere dich bitte, dass du über genügend Guthaben für die automatische Erneuerung verfügst. Wir werden dich rechtzeitig vor der automatischen Erneuerung benachrichtigen.",
|
"autoRenewalLongDesc": "Wenn dein Abonnement ausläuft, wirst du automatisch auf den Preview-Plan zurückgestuft. Wenn du die automatische Verlängerung aktivierst, vergewissere dich bitte, dass du über genügend Guthaben für die automatische Erneuerung verfügst. Wir werden dich rechtzeitig vor der automatischen Erneuerung benachrichtigen.",
|
||||||
"@autoRenewalLongDesc": {},
|
|
||||||
"planSuccessUpgraded": "Dein Plan wurde erfolgreich aktualisiert.",
|
"planSuccessUpgraded": "Dein Plan wurde erfolgreich aktualisiert.",
|
||||||
"@planSuccessUpgraded": {},
|
|
||||||
"checkoutSubmit": "Kostenpflichtig bestellen",
|
"checkoutSubmit": "Kostenpflichtig bestellen",
|
||||||
"@checkoutSubmit": {},
|
|
||||||
"additionalUsersList": "Ihre zusätzlichen Benutzer",
|
"additionalUsersList": "Ihre zusätzlichen Benutzer",
|
||||||
"@additionalUsersList": {},
|
|
||||||
"additionalUsersPlusTokens": "twonly-Codes für \"Plus\"-Benutzer",
|
"additionalUsersPlusTokens": "twonly-Codes für \"Plus\"-Benutzer",
|
||||||
"@additionalUsersPlusTokens": {},
|
|
||||||
"additionalUsersFreeTokens": "twonly-Codes für \"Free\"-Benutzer",
|
"additionalUsersFreeTokens": "twonly-Codes für \"Free\"-Benutzer",
|
||||||
"@additionalUsersFreeTokens": {},
|
|
||||||
"planNotAllowed": "In deinem aktuellen Plan kannst du keine Mediendateien versenden. Aktualisiere deinen Plan jetzt, um die Mediendatei zu senden.",
|
"planNotAllowed": "In deinem aktuellen Plan kannst du keine Mediendateien versenden. Aktualisiere deinen Plan jetzt, um die Mediendatei zu senden.",
|
||||||
"@planNotAllowed": {},
|
|
||||||
"planLimitReached": "Du hast dein Planlimit für heute erreicht. Aktualisiere deinen Plan jetzt, um die Mediendatei zu senden.",
|
"planLimitReached": "Du hast dein Planlimit für heute erreicht. Aktualisiere deinen Plan jetzt, um die Mediendatei zu senden.",
|
||||||
"@planLimitReached": {},
|
|
||||||
"galleryDelete": "Datei löschen",
|
"galleryDelete": "Datei löschen",
|
||||||
"@galleryDelete": {},
|
|
||||||
"galleryExport": "In Galerie exportieren",
|
"galleryExport": "In Galerie exportieren",
|
||||||
"@galleryExport": {},
|
|
||||||
"galleryExportSuccess": "Erfolgreich in der Gallery gespeichert.",
|
"galleryExportSuccess": "Erfolgreich in der Gallery gespeichert.",
|
||||||
"@galleryExportSuccess": {},
|
|
||||||
"galleryDetails": "Details anzeigen",
|
"galleryDetails": "Details anzeigen",
|
||||||
"@galleryDetails": {},
|
|
||||||
"settingsResetTutorials": "Tutorials erneut anzeigen",
|
"settingsResetTutorials": "Tutorials erneut anzeigen",
|
||||||
"@settingsResetTutorials": {},
|
|
||||||
"settingsResetTutorialsDesc": "Klicke hier, um bereits angezeigte Tutorials erneut anzuzeigen.",
|
"settingsResetTutorialsDesc": "Klicke hier, um bereits angezeigte Tutorials erneut anzuzeigen.",
|
||||||
"@settingsResetTutorialsDesc": {},
|
|
||||||
"settingsResetTutorialsSuccess": "Tutorials werden erneut angezeigt.",
|
"settingsResetTutorialsSuccess": "Tutorials werden erneut angezeigt.",
|
||||||
"@settingsResetTutorialsSuccess": {},
|
|
||||||
"tutorialChatListSearchUsersTitle": "Freunde finden und Freundschaftsanfragen verwalten",
|
"tutorialChatListSearchUsersTitle": "Freunde finden und Freundschaftsanfragen verwalten",
|
||||||
"@tutorialChatListSearchUsersTitle": {},
|
|
||||||
"tutorialChatListSearchUsersDesc": "Wenn du die Benutzernamen deiner Freunde kennst, kannst du sie hier suchen und eine Freundschaftsanfrage senden. Außerdem siehst du hier alle Anfragen von anderen Nutzern, die du annehmen oder blockieren kannst.",
|
"tutorialChatListSearchUsersDesc": "Wenn du die Benutzernamen deiner Freunde kennst, kannst du sie hier suchen und eine Freundschaftsanfrage senden. Außerdem siehst du hier alle Anfragen von anderen Nutzern, die du annehmen oder blockieren kannst.",
|
||||||
"@tutorialChatListSearchUsersDesc": {},
|
|
||||||
"tutorialChatListContextMenuTitle": "Klicke lange auf den Kontakt, um das Kontextmenü zu öffnen.",
|
"tutorialChatListContextMenuTitle": "Klicke lange auf den Kontakt, um das Kontextmenü zu öffnen.",
|
||||||
"@tutorialChatListContextMenuTitle": {},
|
|
||||||
"tutorialChatListContextMenuDesc": "Mit dem Kontextmenü kannst du deine Kontakte anheften, archivieren und verschiedene Aktionen durchführen. Halte dazu einfach den Kontakt lange gedrückt und bewege dann deinen Finger auf die gewünschte Option oder tippe direkt darauf.",
|
"tutorialChatListContextMenuDesc": "Mit dem Kontextmenü kannst du deine Kontakte anheften, archivieren und verschiedene Aktionen durchführen. Halte dazu einfach den Kontakt lange gedrückt und bewege dann deinen Finger auf die gewünschte Option oder tippe direkt darauf.",
|
||||||
"@tutorialChatListContextMenuDesc": {},
|
|
||||||
"tutorialChatMessagesVerifyShieldTitle": "Verifiziere deine Kontakte!",
|
"tutorialChatMessagesVerifyShieldTitle": "Verifiziere deine Kontakte!",
|
||||||
"@tutorialChatMessagesVerifyShieldTitle": {},
|
|
||||||
"tutorialChatMessagesVerifyShieldDesc": "twonly nutzt das Signal-Protokoll für eine sichere Ende-zu-Ende Verschlüsselung. Bei der ersten Kontaktaufnahme wird dafür der öffentliche Identitätsschlüssel von deinem Kontakt heruntergeladen. Um sicherzustellen, dass dieser Schlüssel nicht von Dritten ausgetauscht wurde, solltest du ihn mit deinem Freund vergleichen, wenn ihr euch persönlich trefft. Sobald du den Benutzer verifiziert hast, kannst du auch beim verschicken von Bildern und Videos den twonly-Modus aktivieren.",
|
"tutorialChatMessagesVerifyShieldDesc": "twonly nutzt das Signal-Protokoll für eine sichere Ende-zu-Ende Verschlüsselung. Bei der ersten Kontaktaufnahme wird dafür der öffentliche Identitätsschlüssel von deinem Kontakt heruntergeladen. Um sicherzustellen, dass dieser Schlüssel nicht von Dritten ausgetauscht wurde, solltest du ihn mit deinem Freund vergleichen, wenn ihr euch persönlich trefft. Sobald du den Benutzer verifiziert hast, kannst du auch beim verschicken von Bildern und Videos den twonly-Modus aktivieren.",
|
||||||
"@tutorialChatMessagesVerifyShieldDesc": {},
|
|
||||||
"tutorialChatMessagesReopenMessageTitle": "Bilder und Videos erneut öffnen",
|
"tutorialChatMessagesReopenMessageTitle": "Bilder und Videos erneut öffnen",
|
||||||
"@tutorialChatMessagesReopenMessageTitle": {},
|
|
||||||
"tutorialChatMessagesReopenMessageDesc": "Wenn dein Freund dir ein Bild oder Video mit unendlicher Anzeigezeit gesendet hat, kannst du es bis zum Neustart der App jederzeit erneut öffnen. Um dies zu tun, musst du einfach doppelt auf die Nachricht klicken. Dein Freund erhält dann eine Benachrichtigung, dass du das Bild erneut angesehen hast.",
|
"tutorialChatMessagesReopenMessageDesc": "Wenn dein Freund dir ein Bild oder Video mit unendlicher Anzeigezeit gesendet hat, kannst du es bis zum Neustart der App jederzeit erneut öffnen. Um dies zu tun, musst du einfach doppelt auf die Nachricht klicken. Dein Freund erhält dann eine Benachrichtigung, dass du das Bild erneut angesehen hast.",
|
||||||
"@tutorialChatMessagesReopenMessageDesc": {},
|
|
||||||
"memoriesEmpty": "Sobald du Bilder oder Videos speicherst, landen sie hier in deinen Erinnerungen.",
|
"memoriesEmpty": "Sobald du Bilder oder Videos speicherst, landen sie hier in deinen Erinnerungen.",
|
||||||
"@memoriesEmpty": {},
|
|
||||||
"deleteTitle": "Bist du dir sicher?",
|
"deleteTitle": "Bist du dir sicher?",
|
||||||
"@deleteTitle": {},
|
|
||||||
"deleteOkBtnForAll": "Für alle löschen",
|
"deleteOkBtnForAll": "Für alle löschen",
|
||||||
"@deleteOkBtnForAll": {},
|
|
||||||
"deleteOkBtnForMe": "Für mich löschen",
|
"deleteOkBtnForMe": "Für mich löschen",
|
||||||
"@deleteOkBtnForMe": {},
|
|
||||||
"deleteImageTitle": "Bist du dir sicher?",
|
"deleteImageTitle": "Bist du dir sicher?",
|
||||||
"@deleteImageTitle": {},
|
|
||||||
"deleteImageBody": "Das Bild wird unwiderruflich gelöscht.",
|
"deleteImageBody": "Das Bild wird unwiderruflich gelöscht.",
|
||||||
"@deleteImageBody": {},
|
|
||||||
"backupNoticeTitle": "Kein Backup konfiguriert",
|
"backupNoticeTitle": "Kein Backup konfiguriert",
|
||||||
"@backupNoticeTitle": {},
|
|
||||||
"backupNoticeDesc": "Wenn du dein Gerät wechselst oder verlierst, kann ohne Backup niemand dein Account wiederherstellen. Sichere deshalb deine Daten.",
|
"backupNoticeDesc": "Wenn du dein Gerät wechselst oder verlierst, kann ohne Backup niemand dein Account wiederherstellen. Sichere deshalb deine Daten.",
|
||||||
"@backupNoticeDesc": {},
|
|
||||||
"backupNoticeLater": "Später erinnern",
|
"backupNoticeLater": "Später erinnern",
|
||||||
"@backupNoticeLater": {},
|
|
||||||
"backupNoticeOpenBackup": "Backup erstellen",
|
"backupNoticeOpenBackup": "Backup erstellen",
|
||||||
"@backupNoticeOpenBackup": {},
|
|
||||||
"backupPending": "Ausstehend",
|
"backupPending": "Ausstehend",
|
||||||
"@backupPending": {},
|
|
||||||
"backupFailed": "Fehlgeschlagen",
|
"backupFailed": "Fehlgeschlagen",
|
||||||
"@backupFailed": {},
|
|
||||||
"backupSuccess": "Erfolgreich",
|
"backupSuccess": "Erfolgreich",
|
||||||
"@backupSuccess": {},
|
|
||||||
"backupTwonlySafeDesc": "Sichere deine twonly-Identität, da dies die einzige Möglichkeit ist, dein Konto wiederherzustellen, wenn du die App deinstallierst oder dein Handy verlierst.",
|
"backupTwonlySafeDesc": "Sichere deine twonly-Identität, da dies die einzige Möglichkeit ist, dein Konto wiederherzustellen, wenn du die App deinstallierst oder dein Handy verlierst.",
|
||||||
"@backupTwonlySafeDesc": {},
|
|
||||||
"backupServer": "Server",
|
"backupServer": "Server",
|
||||||
"@backupServer": {},
|
|
||||||
"backupMaxBackupSize": "max. Backup-Größe",
|
"backupMaxBackupSize": "max. Backup-Größe",
|
||||||
"@backupMaxBackupSize": {},
|
|
||||||
"backupStorageRetention": "Speicheraufbewahrung",
|
"backupStorageRetention": "Speicheraufbewahrung",
|
||||||
"@backupStorageRetention": {},
|
|
||||||
"backupLastBackupDate": "Letztes Backup",
|
"backupLastBackupDate": "Letztes Backup",
|
||||||
"@backupLastBackupDate": {},
|
|
||||||
"backupLastBackupSize": "Backup-Größe",
|
"backupLastBackupSize": "Backup-Größe",
|
||||||
"@backupLastBackupSize": {},
|
|
||||||
"backupLastBackupResult": "Ergebnis",
|
"backupLastBackupResult": "Ergebnis",
|
||||||
"@backupLastBackupResult": {},
|
|
||||||
"deleteBackupTitle": "Bist du sicher?",
|
"deleteBackupTitle": "Bist du sicher?",
|
||||||
"@deleteBackupTitle": {},
|
|
||||||
"backupNoPasswordRecovery": "Aufgrund des Sicherheitssystems von twonly gibt es (derzeit) keine Funktion zur Wiederherstellung des Passworts. Daher musst du dir dein Passwort merken oder, besser noch, aufschreiben.",
|
"backupNoPasswordRecovery": "Aufgrund des Sicherheitssystems von twonly gibt es (derzeit) keine Funktion zur Wiederherstellung des Passworts. Daher musst du dir dein Passwort merken oder, besser noch, aufschreiben.",
|
||||||
"@backupNoPasswordRecovery": {},
|
|
||||||
"deleteBackupBody": "Ohne ein Backup kannst du dein Benutzerkonto nicht wiederherstellen.",
|
"deleteBackupBody": "Ohne ein Backup kannst du dein Benutzerkonto nicht wiederherstellen.",
|
||||||
"@deleteBackupBody": {},
|
|
||||||
"backupData": "Daten-Backup",
|
"backupData": "Daten-Backup",
|
||||||
"@backupData": {},
|
|
||||||
"backupDataDesc": "Das Daten-Backup enthält neben deiner twonly-Identität auch alle deine Mediendateien. Dieses Backup ist ebenfalls verschlüsselt, wird jedoch lokal gespeichert. Du musst es dann manuell auf deinen Laptop oder ein Gerät deiner Wahl kopieren.",
|
"backupDataDesc": "Das Daten-Backup enthält neben deiner twonly-Identität auch alle deine Mediendateien. Dieses Backup ist ebenfalls verschlüsselt, wird jedoch lokal gespeichert. Du musst es dann manuell auf deinen Laptop oder ein Gerät deiner Wahl kopieren.",
|
||||||
"@backupDataDesc": {},
|
|
||||||
"backupInsecurePassword": "Unsicheres Passwort",
|
"backupInsecurePassword": "Unsicheres Passwort",
|
||||||
"@backupInsecurePassword": {},
|
|
||||||
"backupInsecurePasswordDesc": "Das gewählte Passwort ist sehr unsicher und kann daher leicht von Angreifern erraten werden. Bitte wähle ein sicheres Passwort.",
|
"backupInsecurePasswordDesc": "Das gewählte Passwort ist sehr unsicher und kann daher leicht von Angreifern erraten werden. Bitte wähle ein sicheres Passwort.",
|
||||||
"@backupInsecurePasswordDesc": {},
|
|
||||||
"backupInsecurePasswordOk": "Trotzdem fortfahren",
|
"backupInsecurePasswordOk": "Trotzdem fortfahren",
|
||||||
"@backupInsecurePasswordOk": {},
|
|
||||||
"backupInsecurePasswordCancel": "Erneut versuchen",
|
"backupInsecurePasswordCancel": "Erneut versuchen",
|
||||||
"@backupInsecurePasswordCancel": {},
|
|
||||||
"backupTwonlySafeLongDesc": "twonly hat keine zentralen Benutzerkonten. Während der Installation wird ein Schlüsselpaar erstellt, das aus einem öffentlichen und einem privaten Schlüssel besteht. Der private Schlüssel wird nur auf deinem Gerät gespeichert, um ihn vor unbefugtem Zugriff zu schützen. Der öffentliche Schlüssel wird auf den Server hochgeladen und mit deinem gewählten Benutzernamen verknüpft, damit andere dich finden können.\n\ntwonly Backup erstellt regelmäßig ein verschlüsseltes, anonymes Backup deines privaten Schlüssels zusammen mit deinen Kontakten und Einstellungen. Dein Benutzername und das gewählte Passwort reichen aus, um diese Daten auf einem anderen Gerät wiederherzustellen.",
|
"backupTwonlySafeLongDesc": "twonly hat keine zentralen Benutzerkonten. Während der Installation wird ein Schlüsselpaar erstellt, das aus einem öffentlichen und einem privaten Schlüssel besteht. Der private Schlüssel wird nur auf deinem Gerät gespeichert, um ihn vor unbefugtem Zugriff zu schützen. Der öffentliche Schlüssel wird auf den Server hochgeladen und mit deinem gewählten Benutzernamen verknüpft, damit andere dich finden können.\n\ntwonly Backup erstellt regelmäßig ein verschlüsseltes, anonymes Backup deines privaten Schlüssels zusammen mit deinen Kontakten und Einstellungen. Dein Benutzername und das gewählte Passwort reichen aus, um diese Daten auf einem anderen Gerät wiederherzustellen.",
|
||||||
"@backupTwonlySafeLongDesc": {},
|
|
||||||
"backupSelectStrongPassword": "Wähle ein sicheres Passwort. Dies ist erforderlich, wenn du dein twonly Backup wiederherstellen möchtest.",
|
"backupSelectStrongPassword": "Wähle ein sicheres Passwort. Dies ist erforderlich, wenn du dein twonly Backup wiederherstellen möchtest.",
|
||||||
"@backupSelectStrongPassword": {},
|
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"@password": {},
|
|
||||||
"passwordRepeated": "Passwort wiederholen",
|
"passwordRepeated": "Passwort wiederholen",
|
||||||
"@passwordRepeated": {},
|
|
||||||
"passwordRepeatedNotEqual": "Passwörter stimmen nicht überein.",
|
"passwordRepeatedNotEqual": "Passwörter stimmen nicht überein.",
|
||||||
"@passwordRepeatedNotEqual": {},
|
|
||||||
"backupPasswordRequirement": "Das Passwort muss mindestens 8 Zeichen lang sein.",
|
"backupPasswordRequirement": "Das Passwort muss mindestens 8 Zeichen lang sein.",
|
||||||
"@backupPasswordRequirement": {},
|
|
||||||
"backupExpertSettings": "Experteneinstellungen",
|
"backupExpertSettings": "Experteneinstellungen",
|
||||||
"@backupExpertSettings": {},
|
|
||||||
"backupEnableBackup": "Automatische Sicherung aktivieren",
|
"backupEnableBackup": "Automatische Sicherung aktivieren",
|
||||||
"@backupEnableBackup": {},
|
|
||||||
"backupOwnServerDesc": "Speichere dein twonly Backup auf einem Server deiner Wahl.",
|
"backupOwnServerDesc": "Speichere dein twonly Backup auf einem Server deiner Wahl.",
|
||||||
"@backupOwnServerDesc": {},
|
|
||||||
"backupUseOwnServer": "Server verwenden",
|
"backupUseOwnServer": "Server verwenden",
|
||||||
"@backupUseOwnServer": {},
|
|
||||||
"backupResetServer": "Standardserver verwenden",
|
"backupResetServer": "Standardserver verwenden",
|
||||||
"@backupResetServer": {},
|
|
||||||
"backupTwonlySaveNow": "Jetzt speichern",
|
"backupTwonlySaveNow": "Jetzt speichern",
|
||||||
"@backupTwonlySaveNow": {},
|
|
||||||
"backupChangePassword": "Password ändern",
|
"backupChangePassword": "Password ändern",
|
||||||
"@backupChangePassword": {},
|
|
||||||
"inviteFriends": "Freunde einladen",
|
"inviteFriends": "Freunde einladen",
|
||||||
"@inviteFriends": {},
|
|
||||||
"inviteFriendsShareBtn": "Teilen",
|
"inviteFriendsShareBtn": "Teilen",
|
||||||
"@inviteFriendsShareBtn": {},
|
|
||||||
"inviteFriendsShareText": "Wechseln wir zu twonly: {url}",
|
"inviteFriendsShareText": "Wechseln wir zu twonly: {url}",
|
||||||
"@inviteFriendsShareText": {},
|
|
||||||
"appOutdated": "Deine Version von twonly ist veraltet.",
|
"appOutdated": "Deine Version von twonly ist veraltet.",
|
||||||
"@appOutdated": {},
|
|
||||||
"appOutdatedBtn": "Jetzt aktualisieren.",
|
"appOutdatedBtn": "Jetzt aktualisieren.",
|
||||||
"@appOutdatedBtn": {},
|
|
||||||
"doubleClickToReopen": "Doppelklicken zum\nerneuten Öffnen.",
|
"doubleClickToReopen": "Doppelklicken zum\nerneuten Öffnen.",
|
||||||
"uploadLimitReached": "Das Upload-Limit wurde\nerreicht. Upgrade auf Pro\noder warte bis morgen.",
|
"uploadLimitReached": "Das Upload-Limit wurde\nerreicht. Upgrade auf Pro\noder warte bis morgen.",
|
||||||
"@doubleClickToReopen": {},
|
|
||||||
"retransmissionRequested": "Wird erneut versucht.",
|
"retransmissionRequested": "Wird erneut versucht.",
|
||||||
"@retransmissionRequested": {},
|
|
||||||
"testPaymentMethod": "Vielen Dank für dein Interesse an einem kostenpflichtigen Tarif. Die kostenpflichtigen Pläne sind derzeit noch deaktiviert. Sie werden aber bald aktiviert!",
|
"testPaymentMethod": "Vielen Dank für dein Interesse an einem kostenpflichtigen Tarif. Die kostenpflichtigen Pläne sind derzeit noch deaktiviert. Sie werden aber bald aktiviert!",
|
||||||
"@testPaymentMethod": {},
|
|
||||||
"openChangeLog": "Changelog automatisch öffnen",
|
"openChangeLog": "Changelog automatisch öffnen",
|
||||||
"@openChangeLog": {},
|
|
||||||
"reportUserTitle": "Melde {username}",
|
"reportUserTitle": "Melde {username}",
|
||||||
"@reportUserTitle": {},
|
|
||||||
"reportUserReason": "Meldegrund",
|
"reportUserReason": "Meldegrund",
|
||||||
"@reportUserReason": {},
|
|
||||||
"reportUser": "Benutzer melden",
|
"reportUser": "Benutzer melden",
|
||||||
"@reportUser": {},
|
|
||||||
"newDeviceRegistered": "Du hast dich auf einem anderen Gerät angemeldet. Daher wurdest du hier abgemeldet.",
|
"newDeviceRegistered": "Du hast dich auf einem anderen Gerät angemeldet. Daher wurdest du hier abgemeldet.",
|
||||||
"@newDeviceRegistered": {},
|
|
||||||
"tabToRemoveEmoji": "Tippen um zu entfernen",
|
"tabToRemoveEmoji": "Tippen um zu entfernen",
|
||||||
"@tabToRemoveEmoji": {},
|
|
||||||
"quotedMessageWasDeleted": "Die zitierte Nachricht wurde gelöscht.",
|
"quotedMessageWasDeleted": "Die zitierte Nachricht wurde gelöscht.",
|
||||||
"@quotedMessageWasDeleted": {},
|
|
||||||
"messageWasDeleted": "Nachricht wurde gelöscht.",
|
"messageWasDeleted": "Nachricht wurde gelöscht.",
|
||||||
"@messageWasDeleted": {},
|
|
||||||
"messageWasDeletedShort": "Gelöscht",
|
"messageWasDeletedShort": "Gelöscht",
|
||||||
"@messageWasDeletedShort": {},
|
|
||||||
"sent": "Versendet",
|
"sent": "Versendet",
|
||||||
"@sent": {},
|
|
||||||
"sentTo": "Zugestellt an",
|
"sentTo": "Zugestellt an",
|
||||||
"@sentTo": {},
|
|
||||||
"received": "Empfangen",
|
"received": "Empfangen",
|
||||||
"@received": {},
|
|
||||||
"opened": "Geöffnet",
|
"opened": "Geöffnet",
|
||||||
"@opened": {},
|
|
||||||
"waitingForInternet": "Warten auf Internet",
|
"waitingForInternet": "Warten auf Internet",
|
||||||
"@waitingForInternet": {},
|
|
||||||
"editHistory": "Bearbeitungshistorie",
|
"editHistory": "Bearbeitungshistorie",
|
||||||
"@editHistory": {},
|
|
||||||
"archivedChats": "Archivierte Chats",
|
"archivedChats": "Archivierte Chats",
|
||||||
"@archivedChats": {},
|
|
||||||
"durationShortSecond": "Sek.",
|
"durationShortSecond": "Sek.",
|
||||||
"@durationShortSecond": {},
|
|
||||||
"durationShortMinute": "Min.",
|
"durationShortMinute": "Min.",
|
||||||
"@durationShortMinute": {},
|
|
||||||
"durationShortHour": "Std.",
|
"durationShortHour": "Std.",
|
||||||
"@durationShortHour": {},
|
|
||||||
"durationShortDays": "{count, plural, =1{1 Tag} other{{count} Tage}}",
|
"durationShortDays": "{count, plural, =1{1 Tag} other{{count} Tage}}",
|
||||||
"@durationShortDays": {},
|
|
||||||
"contacts": "Kontakte",
|
"contacts": "Kontakte",
|
||||||
"groups": "Gruppen",
|
"groups": "Gruppen",
|
||||||
"@groups": {},
|
|
||||||
"newGroup": "Neue Gruppe",
|
"newGroup": "Neue Gruppe",
|
||||||
"@newGroup": {},
|
|
||||||
"selectMembers": "Mitglieder auswählen",
|
"selectMembers": "Mitglieder auswählen",
|
||||||
"@selectMembers": {},
|
|
||||||
"selectGroupName": "Gruppennamen wählen",
|
"selectGroupName": "Gruppennamen wählen",
|
||||||
"@selectGroupName": {},
|
|
||||||
"groupNameInput": "Gruppennamen",
|
"groupNameInput": "Gruppennamen",
|
||||||
"@groupNameInput": {},
|
|
||||||
"groupMembers": "Mitglieder",
|
"groupMembers": "Mitglieder",
|
||||||
"@groupMembers": {},
|
|
||||||
"createGroup": "Gruppe erstellen",
|
"createGroup": "Gruppe erstellen",
|
||||||
"@createGroup": {},
|
|
||||||
"addMember": "Mitglied hinzufügen",
|
"addMember": "Mitglied hinzufügen",
|
||||||
"@addMember": {},
|
|
||||||
"leaveGroup": "Gruppe verlassen",
|
"leaveGroup": "Gruppe verlassen",
|
||||||
"@leaveGroup": {},
|
|
||||||
"createContactRequest": "Kontaktanfrage erstellen",
|
"createContactRequest": "Kontaktanfrage erstellen",
|
||||||
"@createContactRequest": {},
|
|
||||||
"contactRequestSend": "Kontakanfrage gesendet",
|
"contactRequestSend": "Kontakanfrage gesendet",
|
||||||
"makeAdmin": "Zum Admin machen",
|
"makeAdmin": "Zum Admin machen",
|
||||||
"@makeAdmin": {},
|
|
||||||
"removeAdmin": "Als Admin entfernen",
|
"removeAdmin": "Als Admin entfernen",
|
||||||
"@removeAdmin": {},
|
|
||||||
"removeFromGroup": "Aus Gruppe entfernen",
|
"removeFromGroup": "Aus Gruppe entfernen",
|
||||||
"@removeFromGroup": {},
|
|
||||||
"admin": "Admin",
|
"admin": "Admin",
|
||||||
"@admin": {},
|
|
||||||
"revokeAdminRightsTitle": "Adminrechte von {username} entfernen?",
|
"revokeAdminRightsTitle": "Adminrechte von {username} entfernen?",
|
||||||
"@revokeAdminRightsTitle": {},
|
|
||||||
"revokeAdminRightsOkBtn": "Als Admin entfernen",
|
"revokeAdminRightsOkBtn": "Als Admin entfernen",
|
||||||
"@revokeAdminRightsOkBtn": {},
|
|
||||||
"makeAdminRightsTitle": "{username} zum Admin machen?",
|
"makeAdminRightsTitle": "{username} zum Admin machen?",
|
||||||
"@makeAdminRightsTitle": {},
|
|
||||||
"makeAdminRightsBody": "{username} wird diese Gruppe und ihre Mitglieder bearbeiten können.",
|
"makeAdminRightsBody": "{username} wird diese Gruppe und ihre Mitglieder bearbeiten können.",
|
||||||
"@makeAdminRightsBody": {},
|
|
||||||
"makeAdminRightsOkBtn": "Zum Admin machen",
|
"makeAdminRightsOkBtn": "Zum Admin machen",
|
||||||
"@makeAdminRightsOkBtn": {},
|
|
||||||
"updateGroup": "Gruppe aktualisieren",
|
"updateGroup": "Gruppe aktualisieren",
|
||||||
"@updateGroup": {},
|
|
||||||
"alreadyInGroup": "Bereits Mitglied",
|
"alreadyInGroup": "Bereits Mitglied",
|
||||||
"@alreadyInGroup": {},
|
|
||||||
"removeContactFromGroupTitle": "{username} aus dieser Gruppe entfernen?",
|
"removeContactFromGroupTitle": "{username} aus dieser Gruppe entfernen?",
|
||||||
"@removeContactFromGroupTitle": {},
|
|
||||||
"youChangedGroupName": "Du hast den Gruppennamen zu „{newGroupName}“ geändert.",
|
"youChangedGroupName": "Du hast den Gruppennamen zu „{newGroupName}“ geändert.",
|
||||||
"@youChangedGroupName": {},
|
|
||||||
"makerChangedGroupName": "{maker} hat den Gruppennamen zu „{newGroupName}“ geändert.",
|
"makerChangedGroupName": "{maker} hat den Gruppennamen zu „{newGroupName}“ geändert.",
|
||||||
"@makerChangedGroupName": {},
|
|
||||||
"youCreatedGroup": "Du hast die Gruppe erstellt.",
|
"youCreatedGroup": "Du hast die Gruppe erstellt.",
|
||||||
"@youCreatedGroup": {},
|
|
||||||
"makerCreatedGroup": "{maker} hat die Gruppe erstellt.",
|
"makerCreatedGroup": "{maker} hat die Gruppe erstellt.",
|
||||||
"@makerCreatedGroup": {},
|
|
||||||
"youRemovedMember": "Du hast {affected} aus der Gruppe entfernt.",
|
"youRemovedMember": "Du hast {affected} aus der Gruppe entfernt.",
|
||||||
"@youRemovedMember": {},
|
|
||||||
"makerRemovedMember": "{maker} hat {affected} aus der Gruppe entfernt.",
|
"makerRemovedMember": "{maker} hat {affected} aus der Gruppe entfernt.",
|
||||||
"@makerRemovedMember": {},
|
|
||||||
"youAddedMember": "Du hast {affected} zur Gruppe hinzugefügt.",
|
"youAddedMember": "Du hast {affected} zur Gruppe hinzugefügt.",
|
||||||
"@youAddedMember": {},
|
|
||||||
"makerAddedMember": "{maker} hat {affected} zur Gruppe hinzugefügt.",
|
"makerAddedMember": "{maker} hat {affected} zur Gruppe hinzugefügt.",
|
||||||
"@makerAddedMember": {},
|
|
||||||
"youMadeAdmin": "Du hast {affected} zum Administrator gemacht.",
|
"youMadeAdmin": "Du hast {affected} zum Administrator gemacht.",
|
||||||
"@youMadeAdmin": {},
|
|
||||||
"makerMadeAdmin": "{maker} hat {affected} zum Administrator gemacht.",
|
"makerMadeAdmin": "{maker} hat {affected} zum Administrator gemacht.",
|
||||||
"@makerMadeAdmin": {},
|
|
||||||
"youRevokedAdminRights": "Du hast {affectedR} die Administratorrechte entzogen.",
|
"youRevokedAdminRights": "Du hast {affectedR} die Administratorrechte entzogen.",
|
||||||
"@youRevokedAdminRights": {},
|
|
||||||
"makerRevokedAdminRights": "{maker} hat {affectedR} die Administratorrechte entzogen.",
|
"makerRevokedAdminRights": "{maker} hat {affectedR} die Administratorrechte entzogen.",
|
||||||
"@makerRevokedAdminRights": {},
|
|
||||||
"youLeftGroup": "Du hast die Gruppe verlassen.",
|
"youLeftGroup": "Du hast die Gruppe verlassen.",
|
||||||
"@youLeftGroup": {},
|
|
||||||
"makerLeftGroup": "{maker} hat die Gruppe verlassen.",
|
"makerLeftGroup": "{maker} hat die Gruppe verlassen.",
|
||||||
"@makerLeftGroup": {},
|
|
||||||
"groupActionYou": "dich",
|
"groupActionYou": "dich",
|
||||||
"@groupActionYou": {},
|
|
||||||
"groupActionYour": "deine",
|
"groupActionYour": "deine",
|
||||||
"@groupActionYour": {},
|
|
||||||
"settingsBackup": "Backup",
|
"settingsBackup": "Backup",
|
||||||
"@settingsBackup": {},
|
|
||||||
"twonlySafeRecoverTitle": "Recovery",
|
"twonlySafeRecoverTitle": "Recovery",
|
||||||
"@twonlySafeRecoverTitle": {},
|
|
||||||
"twonlySafeRecoverDesc": "Wenn du ein Backup mit twonly Backup erstellt hast, kannst du es hier wiederherstellen.",
|
"twonlySafeRecoverDesc": "Wenn du ein Backup mit twonly Backup erstellt hast, kannst du es hier wiederherstellen.",
|
||||||
"@twonlySafeRecoverDesc": {},
|
|
||||||
"twonlySafeRecoverBtn": "Backup wiederherstellen",
|
"twonlySafeRecoverBtn": "Backup wiederherstellen",
|
||||||
"@twonlySafeRecoverBtn": {},
|
|
||||||
"notificationFillerIn": "in",
|
"notificationFillerIn": "in",
|
||||||
"notificationText": "hat eine Nachricht{inGroup} gesendet.",
|
"notificationText": "hat eine Nachricht{inGroup} gesendet.",
|
||||||
"notificationTwonly": "hat ein twonly{inGroup} gesendet.",
|
"notificationTwonly": "hat ein twonly{inGroup} gesendet.",
|
||||||
|
|
@ -831,5 +443,7 @@
|
||||||
"exportMemories": "Memories exportieren (Beta)",
|
"exportMemories": "Memories exportieren (Beta)",
|
||||||
"importMemories": "Memories importieren (Beta)",
|
"importMemories": "Memories importieren (Beta)",
|
||||||
"voiceMessageSlideToCancel": "Zum Abbrechen ziehen",
|
"voiceMessageSlideToCancel": "Zum Abbrechen ziehen",
|
||||||
"voiceMessageCancel": "Abbrechen"
|
"voiceMessageCancel": "Abbrechen",
|
||||||
|
"shareYourProfile": "Teile dein Profil",
|
||||||
|
"scanOtherProfile": "Scanne ein anderes Profil"
|
||||||
}
|
}
|
||||||
|
|
@ -1,105 +1,58 @@
|
||||||
{
|
{
|
||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
"registerTitle": "Welcome to twonly!",
|
"registerTitle": "Welcome to twonly!",
|
||||||
"@registerTitle": {},
|
|
||||||
"registerSlogan": "twonly, a privacy friendly way to connect with friends through secure, spontaneous image sharing",
|
"registerSlogan": "twonly, a privacy friendly way to connect with friends through secure, spontaneous image sharing",
|
||||||
"@registerSlogan": {},
|
|
||||||
"onboardingWelcomeTitle": "Welcome to twonly!",
|
"onboardingWelcomeTitle": "Welcome to twonly!",
|
||||||
"@onboardingWelcomeTitle": {},
|
|
||||||
"onboardingWelcomeBody": "Experience a private and secure way to stay in touch with friends by sharing instant pictures.",
|
"onboardingWelcomeBody": "Experience a private and secure way to stay in touch with friends by sharing instant pictures.",
|
||||||
"@onboardingWelcomeBody": {},
|
|
||||||
"onboardingE2eTitle": "Carefree sharing",
|
"onboardingE2eTitle": "Carefree sharing",
|
||||||
"@onboardingE2eTitle": {},
|
|
||||||
"onboardingE2eBody": "With end-to-end encryption, enjoy the peace of mind that only you and your friends can see the moments you share.",
|
"onboardingE2eBody": "With end-to-end encryption, enjoy the peace of mind that only you and your friends can see the moments you share.",
|
||||||
"@onboardingE2eBody": {},
|
|
||||||
"onboardingFocusTitle": "Focus on sharing moments",
|
"onboardingFocusTitle": "Focus on sharing moments",
|
||||||
"@onboardingFocusTitle": {},
|
|
||||||
"onboardingFocusBody": "Say goodbye to addictive features! twonly was created for sharing moments, free from useless distractions or ads.",
|
"onboardingFocusBody": "Say goodbye to addictive features! twonly was created for sharing moments, free from useless distractions or ads.",
|
||||||
"@onboardingFocusBody": {},
|
|
||||||
"onboardingSendTwonliesTitle": "Send twonlies",
|
"onboardingSendTwonliesTitle": "Send twonlies",
|
||||||
"@onboardingSendTwonliesTitle": {},
|
|
||||||
"onboardingSendTwonliesBody": "Share moments securely with your partner. twonly ensures that only your partner can open it, keeping your moments with your partner a two(o)nly thing!",
|
"onboardingSendTwonliesBody": "Share moments securely with your partner. twonly ensures that only your partner can open it, keeping your moments with your partner a two(o)nly thing!",
|
||||||
"@onboardingSendTwonliesBody": {},
|
|
||||||
"onboardingNotProductTitle": "You are not the product!",
|
"onboardingNotProductTitle": "You are not the product!",
|
||||||
"@onboardingNotProductTitle": {},
|
|
||||||
"onboardingNotProductBody": "twonly is financed by donations and an optional subscription. Your data will never be sold.",
|
"onboardingNotProductBody": "twonly is financed by donations and an optional subscription. Your data will never be sold.",
|
||||||
"@onboardingNotProductBody": {},
|
|
||||||
"onboardingBuyOneGetTwoTitle": "Buy one get two",
|
"onboardingBuyOneGetTwoTitle": "Buy one get two",
|
||||||
"@onboardingBuyOneGetTwoTitle": {},
|
|
||||||
"onboardingBuyOneGetTwoBody": "twonly always requires at least two people, which is why you receive a second free license for your twonly partner with your purchase.",
|
"onboardingBuyOneGetTwoBody": "twonly always requires at least two people, which is why you receive a second free license for your twonly partner with your purchase.",
|
||||||
"@onboardingBuyOneGetTwoBody": {},
|
|
||||||
"onboardingGetStartedTitle": "Let's go!",
|
"onboardingGetStartedTitle": "Let's go!",
|
||||||
"@onboardingGetStartedTitle": {},
|
|
||||||
"onboardingGetStartedBody": "You can test twonly free of charge in preview mode. In this mode you can be found by others and receive pictures or videos but you cannot send any yourself.",
|
"onboardingGetStartedBody": "You can test twonly free of charge in preview mode. In this mode you can be found by others and receive pictures or videos but you cannot send any yourself.",
|
||||||
"@onboardingGetStartedBody": {},
|
|
||||||
"onboardingTryForFree": "Try for free",
|
"onboardingTryForFree": "Try for free",
|
||||||
"@onboardingTryForFree": {},
|
|
||||||
"registerUsernameSlogan": "Please select a username so others can find you!",
|
"registerUsernameSlogan": "Please select a username so others can find you!",
|
||||||
"@registerUsernameSlogan": {},
|
|
||||||
"registerUsernameDecoration": "Username",
|
"registerUsernameDecoration": "Username",
|
||||||
"@registerUsernameDecoration": {},
|
|
||||||
"registerUsernameLimits": "Your username must be at least 3 characters long.",
|
"registerUsernameLimits": "Your username must be at least 3 characters long.",
|
||||||
"@registerUsernameLimits": {},
|
|
||||||
"registerSubmitButton": "Register now!",
|
"registerSubmitButton": "Register now!",
|
||||||
"@registerSubmitButton": {},
|
|
||||||
"registerTwonlyCodeText": "Have you received a twonly code? Then redeem it either directly here or later!",
|
"registerTwonlyCodeText": "Have you received a twonly code? Then redeem it either directly here or later!",
|
||||||
"registerTwonlyCodeLabel": "twonly-Code",
|
"registerTwonlyCodeLabel": "twonly-Code",
|
||||||
"newMessageTitle": "New message",
|
"newMessageTitle": "New message",
|
||||||
"@newMessageTitle": {},
|
|
||||||
"chatsTapToSend": "Click to send your first image",
|
"chatsTapToSend": "Click to send your first image",
|
||||||
"@chatsTapToSend": {},
|
|
||||||
"cameraPreviewSendTo": "Send to",
|
"cameraPreviewSendTo": "Send to",
|
||||||
"@cameraPreviewSendTo": {},
|
|
||||||
"shareImageTitle": "Share with",
|
"shareImageTitle": "Share with",
|
||||||
"@shareImageTitle": {},
|
|
||||||
"shareImageBestFriends": "Best friends",
|
"shareImageBestFriends": "Best friends",
|
||||||
"@shareImageBestFriends": {},
|
|
||||||
"shareImagePinnedContacts": "Pinnded",
|
"shareImagePinnedContacts": "Pinnded",
|
||||||
"@shareImagePinnedContacts": {},
|
|
||||||
"shareImagedEditorSendImage": "Send",
|
"shareImagedEditorSendImage": "Send",
|
||||||
"@shareImagedEditorSendImage": {},
|
|
||||||
"shareImagedEditorShareWith": "Share with",
|
"shareImagedEditorShareWith": "Share with",
|
||||||
"@shareImagedEditorShareWith": {},
|
|
||||||
"shareImagedEditorSaveImage": "Save",
|
"shareImagedEditorSaveImage": "Save",
|
||||||
"@shareImagedEditorSaveImage": {},
|
|
||||||
"shareImagedEditorSavedImage": "Saved",
|
"shareImagedEditorSavedImage": "Saved",
|
||||||
"@shareImagedEditorSavedImage": {},
|
|
||||||
"shareImageSearchAllContacts": "Search all contacts",
|
"shareImageSearchAllContacts": "Search all contacts",
|
||||||
"@shareImageSearchAllContacts": {},
|
|
||||||
"startNewChatSearchHint": "Name, username or groupname",
|
"startNewChatSearchHint": "Name, username or groupname",
|
||||||
"shareImagedSelectAll": "Select all",
|
"shareImagedSelectAll": "Select all",
|
||||||
"@shareImagedSelectAll": {},
|
|
||||||
"startNewChatTitle": "Select Contact",
|
"startNewChatTitle": "Select Contact",
|
||||||
"@startNewChatTitle": {},
|
|
||||||
"startNewChatNewContact": "New Contact",
|
"startNewChatNewContact": "New Contact",
|
||||||
"@startNewChatNewContact": {},
|
|
||||||
"startNewChatYourContacts": "Your Contacts",
|
"startNewChatYourContacts": "Your Contacts",
|
||||||
"@startNewChatYourContacts": {},
|
|
||||||
"shareImageAllUsers": "All contacts",
|
"shareImageAllUsers": "All contacts",
|
||||||
"@shareImageAllUsers": {},
|
|
||||||
"shareImageAllTwonlyWarning": "twonlies can only be send to verified contacts!",
|
"shareImageAllTwonlyWarning": "twonlies can only be send to verified contacts!",
|
||||||
"@shareImageAllTwonlyWarning": {},
|
|
||||||
"shareImageUserNotVerified": "User is not verified",
|
"shareImageUserNotVerified": "User is not verified",
|
||||||
"shareImageUserNotVerifiedDesc": "twonlies can only be sent to verified users. To verify a user, go to their profile and to verify security number.",
|
"shareImageUserNotVerifiedDesc": "twonlies can only be sent to verified users. To verify a user, go to their profile and to verify security number.",
|
||||||
"shareImageShowArchived": "Show archived users",
|
"shareImageShowArchived": "Show archived users",
|
||||||
"searchUsernameInput": "Username",
|
"searchUsernameInput": "Username",
|
||||||
"@searchUsernameInput": {},
|
|
||||||
"searchUsernameTitle": "Search username",
|
"searchUsernameTitle": "Search username",
|
||||||
"searchUserNamePreview": "To protect you and other twonly users from spam and abuse, it is not possible to search for other people in preview mode. Other users can find you and their requests will be displayed here!",
|
"searchUserNamePreview": "To protect you and other twonly users from spam and abuse, it is not possible to search for other people in preview mode. Other users can find you and their requests will be displayed here!",
|
||||||
"@searchUserNamePreview": {},
|
|
||||||
"@searchUsernameTitle": {},
|
|
||||||
"selectSubscription": "Select subscription",
|
"selectSubscription": "Select subscription",
|
||||||
"@selectSubscription": {},
|
|
||||||
"searchUserNamePending": "Pending",
|
"searchUserNamePending": "Pending",
|
||||||
"@searchUserNamePending": {},
|
|
||||||
"searchUserNameBlockUserTooltip": "Block the user without informing.",
|
"searchUserNameBlockUserTooltip": "Block the user without informing.",
|
||||||
"@searchUserNameBlockUserTooltip": {},
|
|
||||||
"searchUserNameRejectUserTooltip": "Reject the request and let the requester know.",
|
"searchUserNameRejectUserTooltip": "Reject the request and let the requester know.",
|
||||||
"@searchUserNameRejectUserTooltip": {},
|
|
||||||
"searchUserNameArchiveUserTooltip": "Archive the user. He will appear again as soon as he accepts your request.",
|
"searchUserNameArchiveUserTooltip": "Archive the user. He will appear again as soon as he accepts your request.",
|
||||||
"@searchUserNameArchiveUserTooltip": {},
|
|
||||||
"searchUsernameNotFound": "Username not found",
|
"searchUsernameNotFound": "Username not found",
|
||||||
"@searchUsernameNotFound": {},
|
|
||||||
"searchUsernameNotFoundBody": "There is no user with the username \"{username}\" registered",
|
"searchUsernameNotFoundBody": "There is no user with the username \"{username}\" registered",
|
||||||
"@searchUsernameNotFoundBody": {
|
"@searchUsernameNotFoundBody": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -107,91 +60,49 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"searchUsernameNewFollowerTitle": "Follow requests",
|
"searchUsernameNewFollowerTitle": "Follow requests",
|
||||||
"@searchUsernameNewFollowerTitle": {},
|
|
||||||
"searchUsernameQrCodeBtn": "Scan QR code",
|
"searchUsernameQrCodeBtn": "Scan QR code",
|
||||||
"@searchUsernameQrCodeBtn": {},
|
|
||||||
"chatListViewSearchUserNameBtn": "Add your first twonly contact!",
|
"chatListViewSearchUserNameBtn": "Add your first twonly contact!",
|
||||||
"@chatListViewSearchUserNameBtn": {},
|
|
||||||
"chatListViewSendFirstTwonly": "Send your first twonly!",
|
"chatListViewSendFirstTwonly": "Send your first twonly!",
|
||||||
"@chatListViewSendFirstTwonly": {},
|
|
||||||
"chatListDetailInput": "Type a message",
|
"chatListDetailInput": "Type a message",
|
||||||
"@chatListDetailInput": {},
|
|
||||||
"userDeletedAccount": "The user has deleted its account.",
|
"userDeletedAccount": "The user has deleted its account.",
|
||||||
"contextMenuUserProfile": "User profile",
|
"contextMenuUserProfile": "User profile",
|
||||||
"contextMenuVerifyUser": "Verify",
|
"contextMenuVerifyUser": "Verify",
|
||||||
"@contextMenuVerifyUser": {},
|
|
||||||
"contextMenuArchiveUser": "Archive",
|
"contextMenuArchiveUser": "Archive",
|
||||||
"@contextMenuArchiveUser": {},
|
|
||||||
"contextMenuUndoArchiveUser": "Undo archiving",
|
"contextMenuUndoArchiveUser": "Undo archiving",
|
||||||
"@contextMenuUndoArchiveUser": {},
|
|
||||||
"contextMenuOpenChat": "Open chat",
|
"contextMenuOpenChat": "Open chat",
|
||||||
"@contextMenuOpenChat": {},
|
|
||||||
"contextMenuPin": "Pin",
|
"contextMenuPin": "Pin",
|
||||||
"@contextMenuPin": {},
|
|
||||||
"contextMenuUnpin": "Unpin",
|
"contextMenuUnpin": "Unpin",
|
||||||
"@contextMenuUnpin": {},
|
|
||||||
"mediaViewerAuthReason": "Please authenticate to see this twonly!",
|
"mediaViewerAuthReason": "Please authenticate to see this twonly!",
|
||||||
"@mediaViewerAuthReason": {},
|
|
||||||
"mediaViewerTwonlyTapToOpen": "Tap to open your twonly!",
|
"mediaViewerTwonlyTapToOpen": "Tap to open your twonly!",
|
||||||
"@mediaViewerTwonlyTapToOpen": {},
|
|
||||||
"messageSendState_Received": "Received",
|
"messageSendState_Received": "Received",
|
||||||
"@messageSendState_Received": {},
|
|
||||||
"messageSendState_Opened": "Opened",
|
"messageSendState_Opened": "Opened",
|
||||||
"@messageSendState_Opened": {},
|
|
||||||
"messageSendState_Send": "Sent",
|
"messageSendState_Send": "Sent",
|
||||||
"@messageSendState_Send": {},
|
|
||||||
"messageSendState_Sending": "Sending",
|
"messageSendState_Sending": "Sending",
|
||||||
"@messageSendState_Sending": {},
|
|
||||||
"messageSendState_TapToLoad": "Tap to load",
|
"messageSendState_TapToLoad": "Tap to load",
|
||||||
"@messageSendState_TapToLoad": {},
|
|
||||||
"messageSendState_Loading": "Downloading",
|
"messageSendState_Loading": "Downloading",
|
||||||
"@messageSendState_Loading": {},
|
|
||||||
"messageStoredInGallery": "Stored in gallery",
|
"messageStoredInGallery": "Stored in gallery",
|
||||||
"@messageStoredInGallery": {},
|
|
||||||
"messageReopened": "Re-opened",
|
"messageReopened": "Re-opened",
|
||||||
"@messageReopened": {},
|
|
||||||
"imageEditorDrawOk": "Take drawing",
|
"imageEditorDrawOk": "Take drawing",
|
||||||
"@imageEditorDrawOk": {},
|
|
||||||
"settingsTitle": "Settings",
|
"settingsTitle": "Settings",
|
||||||
"@settingsTitle": {},
|
|
||||||
"settingsChats": "Chats",
|
"settingsChats": "Chats",
|
||||||
"@settingsChats": {},
|
|
||||||
"settingsPreSelectedReactions": "Preselected reaction emojis",
|
"settingsPreSelectedReactions": "Preselected reaction emojis",
|
||||||
"@settingsPreSelectedReactions": {},
|
|
||||||
"settingsPreSelectedReactionsError": "A maximum of 12 reactions can be selected.",
|
"settingsPreSelectedReactionsError": "A maximum of 12 reactions can be selected.",
|
||||||
"@settingsPreSelectedReactionsError": {},
|
|
||||||
"settingsProfile": "Profile",
|
"settingsProfile": "Profile",
|
||||||
"@settingsProfile": {},
|
|
||||||
"settingsStorageData": "Data and storage",
|
"settingsStorageData": "Data and storage",
|
||||||
"@settingsStorageData": {},
|
|
||||||
"settingsStorageDataStoreInGTitle": "Store in Gallery",
|
"settingsStorageDataStoreInGTitle": "Store in Gallery",
|
||||||
"@settingsStorageDataStoreInGTitle": {},
|
|
||||||
"settingsStorageDataStoreInGSubtitle": "Store saved images additional in the systems gallery.",
|
"settingsStorageDataStoreInGSubtitle": "Store saved images additional in the systems gallery.",
|
||||||
"@settingsStorageDataStoreInGSubtitle": {},
|
|
||||||
"settingsStorageDataMediaAutoDownload": "Media auto-download",
|
"settingsStorageDataMediaAutoDownload": "Media auto-download",
|
||||||
"@settingsStorageDataMediaAutoDownload": {},
|
|
||||||
"settingsStorageDataAutoDownMobile": "When using mobile data",
|
"settingsStorageDataAutoDownMobile": "When using mobile data",
|
||||||
"@settingsStorageDataAutoDownMobile": {},
|
|
||||||
"settingsStorageDataAutoDownWifi": "When using WI-FI",
|
"settingsStorageDataAutoDownWifi": "When using WI-FI",
|
||||||
"@settingsStorageDataAutoDownWifi": {},
|
|
||||||
"settingsProfileCustomizeAvatar": "Customize your avatar",
|
"settingsProfileCustomizeAvatar": "Customize your avatar",
|
||||||
"@settingsProfileCustomizeAvatar": {},
|
|
||||||
"settingsProfileEditDisplayName": "Displayname",
|
"settingsProfileEditDisplayName": "Displayname",
|
||||||
"@settingsProfileEditDisplayName": {},
|
|
||||||
"settingsProfileEditDisplayNameNew": "New Displayname",
|
"settingsProfileEditDisplayNameNew": "New Displayname",
|
||||||
"@settingsProfileEditDisplayNameNew": {},
|
|
||||||
"settingsAccount": "Konto",
|
"settingsAccount": "Konto",
|
||||||
"@settingsAccount": {},
|
|
||||||
"settingsSubscription": "Subscription",
|
"settingsSubscription": "Subscription",
|
||||||
"@settingsSubscription": {},
|
|
||||||
"settingsAppearance": "Appearance",
|
"settingsAppearance": "Appearance",
|
||||||
"@settingsAppearance": {},
|
|
||||||
"settingsPrivacy": "Privacy",
|
"settingsPrivacy": "Privacy",
|
||||||
"@settingsPrivacy": {},
|
|
||||||
"settingsPrivacyBlockUsers": "Block users",
|
"settingsPrivacyBlockUsers": "Block users",
|
||||||
"@settingsPrivacyBlockUsers": {},
|
|
||||||
"settingsPrivacyBlockUsersDesc": "Blocked users will not be able to communicate with you. You can unblock a blocked user at any time.",
|
"settingsPrivacyBlockUsersDesc": "Blocked users will not be able to communicate with you. You can unblock a blocked user at any time.",
|
||||||
"@settingsPrivacyBlockUsersDesc": {},
|
|
||||||
"settingsPrivacyBlockUsersCount": "{len} contact(s)",
|
"settingsPrivacyBlockUsersCount": "{len} contact(s)",
|
||||||
"@settingsPrivacyBlockUsersCount": {
|
"@settingsPrivacyBlockUsersCount": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -199,30 +110,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settingsNotification": "Notification",
|
"settingsNotification": "Notification",
|
||||||
"@settingsNotification": {},
|
|
||||||
"settingsNotifyTroubleshooting": "Troubleshooting",
|
"settingsNotifyTroubleshooting": "Troubleshooting",
|
||||||
"@settingsNotifyTroubleshooting": {},
|
|
||||||
"settingsNotifyTroubleshootingDesc": "Click here if you have problems receiving push notifications.",
|
"settingsNotifyTroubleshootingDesc": "Click here if you have problems receiving push notifications.",
|
||||||
"@settingsNotifyTroubleshootingDesc": {},
|
|
||||||
"settingsNotifyTroubleshootingNoProblem": "No problem detected",
|
"settingsNotifyTroubleshootingNoProblem": "No problem detected",
|
||||||
"@settingsNotifyTroubleshootingNoProblem": {},
|
|
||||||
"settingsNotifyTroubleshootingNoProblemDesc": "Press OK to receive a test notification. When you receive no message even after waiting for 10 minutes, please send us your debug log in Settings > Help > Debug log, so we can look at that issue.",
|
"settingsNotifyTroubleshootingNoProblemDesc": "Press OK to receive a test notification. When you receive no message even after waiting for 10 minutes, please send us your debug log in Settings > Help > Debug log, so we can look at that issue.",
|
||||||
"@settingsNotifyTroubleshootingNoProblemDesc": {},
|
|
||||||
"settingsHelp": "Help",
|
"settingsHelp": "Help",
|
||||||
"@settingsHelp": {},
|
|
||||||
"settingsHelpDiagnostics": "Diagnostic protocol",
|
"settingsHelpDiagnostics": "Diagnostic protocol",
|
||||||
"@settingsHelpDiagnostics": {},
|
|
||||||
"settingsHelpFAQ": "FAQ",
|
"settingsHelpFAQ": "FAQ",
|
||||||
"@settingsHelpFAQ": {},
|
|
||||||
"feedbackTooltip": "Give Feedback to improve twonly.",
|
"feedbackTooltip": "Give Feedback to improve twonly.",
|
||||||
"settingsHelpContactUs": "Contact us",
|
"settingsHelpContactUs": "Contact us",
|
||||||
"@settingsHelpContactUs": {},
|
|
||||||
"settingsHelpVersion": "Version",
|
"settingsHelpVersion": "Version",
|
||||||
"@settingsHelpVersion": {},
|
|
||||||
"settingsHelpLicenses": "Licenses (Source-Code)",
|
"settingsHelpLicenses": "Licenses (Source-Code)",
|
||||||
"@settingsHelpLicenses": {},
|
|
||||||
"settingsHelpCredits": "Licenses (Images)",
|
"settingsHelpCredits": "Licenses (Images)",
|
||||||
"@settingsHelpCredits": {},
|
|
||||||
"settingsHelpImprint": "Imprint & Privacy Policy",
|
"settingsHelpImprint": "Imprint & Privacy Policy",
|
||||||
"contactUsFaq": "Have you read our FAQ yet?",
|
"contactUsFaq": "Have you read our FAQ yet?",
|
||||||
"contactUsEmojis": "How do you feel? (optional)",
|
"contactUsEmojis": "How do you feel? (optional)",
|
||||||
|
|
@ -243,24 +142,16 @@
|
||||||
"contactUsShortcut": "Hide Feedback Icon",
|
"contactUsShortcut": "Hide Feedback Icon",
|
||||||
"settingsHelpTerms": "Terms of Service",
|
"settingsHelpTerms": "Terms of Service",
|
||||||
"settingsAppearanceTheme": "Theme",
|
"settingsAppearanceTheme": "Theme",
|
||||||
"@settingsAppearanceTheme": {},
|
|
||||||
"settingsAccountDeleteAccount": "Delete account",
|
"settingsAccountDeleteAccount": "Delete account",
|
||||||
"settingsAccountDeleteAccountWithBallance": "In the next step, you can select what you want to to with the remaining credit ({credit}).",
|
"settingsAccountDeleteAccountWithBallance": "In the next step, you can select what you want to to with the remaining credit ({credit}).",
|
||||||
"settingsAccountDeleteAccountNoBallance": "Once you delete your account, there is no going back.",
|
"settingsAccountDeleteAccountNoBallance": "Once you delete your account, there is no going back.",
|
||||||
"settingsAccountDeleteAccountNoInternet": "An Internet connection is required to delete your account.",
|
"settingsAccountDeleteAccountNoInternet": "An Internet connection is required to delete your account.",
|
||||||
"@settingsAccountDeleteAccount": {},
|
|
||||||
"settingsAccountDeleteModalTitle": "Are you sure?",
|
"settingsAccountDeleteModalTitle": "Are you sure?",
|
||||||
"@settingsAccountDeleteModalTitle": {},
|
|
||||||
"settingsAccountDeleteModalBody": "Your account will be deleted. There is no change to restore it.",
|
"settingsAccountDeleteModalBody": "Your account will be deleted. There is no change to restore it.",
|
||||||
"@settingsAccountDeleteModalBody": {},
|
|
||||||
"contactVerifyNumberTitle": "Verify safety number",
|
"contactVerifyNumberTitle": "Verify safety number",
|
||||||
"@contactVerifyNumberTitle": {},
|
|
||||||
"contactVerifyNumberTapToScan": "Tap to scan",
|
"contactVerifyNumberTapToScan": "Tap to scan",
|
||||||
"@contactVerifyNumberTapToScan": {},
|
|
||||||
"contactVerifyNumberMarkAsVerified": "Mark as verified",
|
"contactVerifyNumberMarkAsVerified": "Mark as verified",
|
||||||
"@contactVerifyNumberMarkAsVerified": {},
|
|
||||||
"contactVerifyNumberClearVerification": "Clear verification",
|
"contactVerifyNumberClearVerification": "Clear verification",
|
||||||
"@contactVerifyNumberClearVerification": {},
|
|
||||||
"contactVerifyNumberLongDesc": "To verify the end-to-end encryption with {username}, compare the numbers with their device. The person can also scan your code with their device.",
|
"contactVerifyNumberLongDesc": "To verify the end-to-end encryption with {username}, compare the numbers with their device. The person can also scan your code with their device.",
|
||||||
"@contactVerifyNumberLongDesc": {
|
"@contactVerifyNumberLongDesc": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -268,11 +159,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contactNickname": "Nickname",
|
"contactNickname": "Nickname",
|
||||||
"@contactNickname": {},
|
|
||||||
"contactNicknameNew": "New nickname",
|
"contactNicknameNew": "New nickname",
|
||||||
"@contactNicknameNew": {},
|
|
||||||
"deleteAllContactMessages": "Delete all text-messages",
|
"deleteAllContactMessages": "Delete all text-messages",
|
||||||
"@deleteAllContactMessages": {},
|
|
||||||
"deleteAllContactMessagesBody": "This will remove all messages, except stored media files, in your chat with {username}. This will NOT delete the messages stored at {username}s device!",
|
"deleteAllContactMessagesBody": "This will remove all messages, except stored media files, in your chat with {username}. This will NOT delete the messages stored at {username}s device!",
|
||||||
"@deleteAllContactMessagesBody": {
|
"@deleteAllContactMessagesBody": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -280,7 +168,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contactBlock": "Block",
|
"contactBlock": "Block",
|
||||||
"@contactBlock": {},
|
|
||||||
"contactBlockTitle": "Block {username}",
|
"contactBlockTitle": "Block {username}",
|
||||||
"@contactBlockTitle": {
|
"@contactBlockTitle": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -288,7 +175,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contactBlockBody": "A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.",
|
"contactBlockBody": "A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.",
|
||||||
"@contactBlockBody": {},
|
|
||||||
"contactRemove": "Remove user",
|
"contactRemove": "Remove user",
|
||||||
"contactRemoveTitle": "Remove {username}",
|
"contactRemoveTitle": "Remove {username}",
|
||||||
"contactRemoveBody": "Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT'S PHONE.",
|
"contactRemoveBody": "Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT'S PHONE.",
|
||||||
|
|
@ -313,23 +199,14 @@
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"switchFrontAndBackCamera": "Switch between front and back camera.",
|
"switchFrontAndBackCamera": "Switch between front and back camera.",
|
||||||
"@switchFrontAndBackCamera": {},
|
|
||||||
"addTextItem": "Text",
|
"addTextItem": "Text",
|
||||||
"@addTextItem": {},
|
|
||||||
"protectAsARealTwonly": "Send as real twonly!",
|
"protectAsARealTwonly": "Send as real twonly!",
|
||||||
"@protectAsARealTwonly": {},
|
|
||||||
"addDrawing": "Drawing",
|
"addDrawing": "Drawing",
|
||||||
"@addDrawing": {},
|
|
||||||
"addEmoji": "Emoji",
|
"addEmoji": "Emoji",
|
||||||
"@addEmoji": {},
|
|
||||||
"toggleFlashLight": "Toggle the flash light",
|
"toggleFlashLight": "Toggle the flash light",
|
||||||
"@toggleFlashLight": {},
|
|
||||||
"toggleHighQuality": "Toggle better resolution",
|
"toggleHighQuality": "Toggle better resolution",
|
||||||
"@toggleHighQuality": {},
|
|
||||||
"userFound": "User found",
|
"userFound": "User found",
|
||||||
"@userFound": {},
|
|
||||||
"userFoundBody": "Do you want to create a follow request?",
|
"userFoundBody": "Do you want to create a follow request?",
|
||||||
"@userFoundBody": {},
|
|
||||||
"searchUsernameNotFoundLong": "\"{username}\" is not a twonly user. Please check the username and try again.",
|
"searchUsernameNotFoundLong": "\"{username}\" is not a twonly user. Please check the username and try again.",
|
||||||
"@searchUsernameNotFoundLong": {
|
"@searchUsernameNotFoundLong": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -337,31 +214,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errorUnknown": "An unexpected error has occurred. Please try again later.",
|
"errorUnknown": "An unexpected error has occurred. Please try again later.",
|
||||||
"@errorUnknown": {},
|
|
||||||
"errorBadRequest": "The request could not be understood by the server due to malformed syntax. Please check your input and try again.",
|
"errorBadRequest": "The request could not be understood by the server due to malformed syntax. Please check your input and try again.",
|
||||||
"@errorBadRequest": {},
|
|
||||||
"errorTooManyRequests": "You have made too many requests in a short period. Please wait a moment before trying again.",
|
"errorTooManyRequests": "You have made too many requests in a short period. Please wait a moment before trying again.",
|
||||||
"@errorTooManyRequests": {},
|
|
||||||
"errorInternalError": "The server is currently not available. Please try again later.",
|
"errorInternalError": "The server is currently not available. Please try again later.",
|
||||||
"@errorInternalError": {},
|
|
||||||
"errorInvalidInvitationCode": "The invitation code you provided is invalid. Please check the code and try again.",
|
"errorInvalidInvitationCode": "The invitation code you provided is invalid. Please check the code and try again.",
|
||||||
"@errorInvalidInvitationCode": {},
|
|
||||||
"errorUsernameAlreadyTaken": "The username is already taken.",
|
"errorUsernameAlreadyTaken": "The username is already taken.",
|
||||||
"@errorUsernameAlreadyTaken": {},
|
|
||||||
"errorSignatureNotValid": "The provided signature is not valid. Please check your credentials and try again.",
|
"errorSignatureNotValid": "The provided signature is not valid. Please check your credentials and try again.",
|
||||||
"@errorSignatureNotValid": {},
|
|
||||||
"errorUsernameNotFound": "The username you entered does not exist. Please check the spelling or create a new account.",
|
"errorUsernameNotFound": "The username you entered does not exist. Please check the spelling or create a new account.",
|
||||||
"@errorUsernameNotFound": {},
|
|
||||||
"errorUsernameNotValid": "The username you provided does not meet the required criteria. Please choose a valid username.",
|
"errorUsernameNotValid": "The username you provided does not meet the required criteria. Please choose a valid username.",
|
||||||
"@errorUsernameNotValid": {},
|
|
||||||
"errorInvalidPublicKey": "The public key you provided is invalid. Please check the key and try again.",
|
"errorInvalidPublicKey": "The public key you provided is invalid. Please check the key and try again.",
|
||||||
"@errorInvalidPublicKey": {},
|
|
||||||
"errorSessionAlreadyAuthenticated": "You are already logged in. Please log out if you want to log in with a different account.",
|
"errorSessionAlreadyAuthenticated": "You are already logged in. Please log out if you want to log in with a different account.",
|
||||||
"@errorSessionAlreadyAuthenticated": {},
|
|
||||||
"errorSessionNotAuthenticated": "Your session is not authenticated. Please log in to continue.",
|
"errorSessionNotAuthenticated": "Your session is not authenticated. Please log in to continue.",
|
||||||
"@errorSessionNotAuthenticated": {},
|
|
||||||
"errorOnlyOneSessionAllowed": "Only one active session is allowed per user. Please log out from other devices to continue.",
|
"errorOnlyOneSessionAllowed": "Only one active session is allowed per user. Please log out from other devices to continue.",
|
||||||
"@errorOnlyOneSessionAllowed": {},
|
|
||||||
"errorNotEnoughCredit": "You do not have enough twonly-credit.",
|
"errorNotEnoughCredit": "You do not have enough twonly-credit.",
|
||||||
"errorVoucherInvalid": "The voucher code you entered is not valid.",
|
"errorVoucherInvalid": "The voucher code you entered is not valid.",
|
||||||
"errorPlanLimitReached": "You have reached your plans limit. Please upgrade your plan.",
|
"errorPlanLimitReached": "You have reached your plans limit. Please upgrade your plan.",
|
||||||
|
|
@ -609,5 +473,7 @@
|
||||||
"exportMemories": "Export memories (Beta)",
|
"exportMemories": "Export memories (Beta)",
|
||||||
"importMemories": "Import memories (Beta)",
|
"importMemories": "Import memories (Beta)",
|
||||||
"voiceMessageSlideToCancel": "Slide to cancel",
|
"voiceMessageSlideToCancel": "Slide to cancel",
|
||||||
"voiceMessageCancel": "Cancel"
|
"voiceMessageCancel": "Cancel",
|
||||||
|
"shareYourProfile": "Share your profile",
|
||||||
|
"scanOtherProfile": "Scan other profile"
|
||||||
}
|
}
|
||||||
|
|
@ -2761,6 +2761,18 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Cancel'**
|
/// **'Cancel'**
|
||||||
String get voiceMessageCancel;
|
String get voiceMessageCancel;
|
||||||
|
|
||||||
|
/// No description provided for @shareYourProfile.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Share your profile'**
|
||||||
|
String get shareYourProfile;
|
||||||
|
|
||||||
|
/// No description provided for @scanOtherProfile.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Scan other profile'**
|
||||||
|
String get scanOtherProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -1522,4 +1522,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get voiceMessageCancel => 'Abbrechen';
|
String get voiceMessageCancel => 'Abbrechen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareYourProfile => 'Teile dein Profil';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get scanOtherProfile => 'Scanne ein anderes Profil';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1512,4 +1512,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get voiceMessageCancel => 'Cancel';
|
String get voiceMessageCancel => 'Cancel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareYourProfile => 'Share your profile';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get scanOtherProfile => 'Scan other profile';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,3 +105,22 @@ Future<Fingerprint?> generateSessionFingerPrint(int target) async {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> getPublicKeyFromContact(int contactId) async {
|
||||||
|
final signalStore = await getSignalStore();
|
||||||
|
if (signalStore == null) return null;
|
||||||
|
try {
|
||||||
|
final targetIdentity = await signalStore.getIdentity(
|
||||||
|
SignalProtocolAddress(
|
||||||
|
contactId.toString(),
|
||||||
|
defaultDeviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (targetIdentity != null) {
|
||||||
|
return targetIdentity.publicKey.serialize();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
||||||
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||||
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
|
||||||
|
|
||||||
Future<Uint8List> getProfileQrCodeData() async {
|
Future<Uint8List> getProfileQrCodeData() async {
|
||||||
final signalIdentity = (await getSignalIdentity())!;
|
final signalIdentity = (await getSignalIdentity())!;
|
||||||
|
|
@ -34,6 +40,12 @@ Future<Uint8List> getProfileQrCodeData() async {
|
||||||
return qrEnvelope.writeToBuffer();
|
return qrEnvelope.writeToBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> getUserPublicKey() async {
|
||||||
|
final signalIdentity = (await getSignalIdentity())!;
|
||||||
|
final signalStore = await getSignalStoreFromIdentity(signalIdentity);
|
||||||
|
return (await signalStore.getIdentityKeyPair()).getPublicKey().serialize();
|
||||||
|
}
|
||||||
|
|
||||||
PublicProfile? parseQrCodeData(Uint8List rawBytes) {
|
PublicProfile? parseQrCodeData(Uint8List rawBytes) {
|
||||||
try {
|
try {
|
||||||
final envelop = QREnvelope.fromBuffer(rawBytes);
|
final envelop = QREnvelope.fromBuffer(rawBytes);
|
||||||
|
|
@ -41,7 +53,48 @@ PublicProfile? parseQrCodeData(Uint8List rawBytes) {
|
||||||
return PublicProfile.fromBuffer(envelop.data);
|
return PublicProfile.fromBuffer(envelop.data);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.warn(e);
|
// Log.warn(e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> addNewContactFromPublicProfile(PublicProfile profile) async {
|
||||||
|
final userdata = Response_UserData(
|
||||||
|
userId: profile.userId,
|
||||||
|
publicIdentityKey: profile.publicIdentityKey,
|
||||||
|
signedPrekey: profile.signedPrekey,
|
||||||
|
signedPrekeyId: profile.signedPrekeyId,
|
||||||
|
signedPrekeySignature: profile.signedPrekeySignature,
|
||||||
|
);
|
||||||
|
|
||||||
|
final added = await twonlyDB.contactsDao.insertOnConflictUpdate(
|
||||||
|
ContactsCompanion(
|
||||||
|
username: Value(profile.username),
|
||||||
|
userId: Value(profile.userId.toInt()),
|
||||||
|
requested: const Value(false),
|
||||||
|
blocked: const Value(false),
|
||||||
|
deletedByUser: const Value(false),
|
||||||
|
verified: const Value(
|
||||||
|
true,
|
||||||
|
), // This contact was added from a QR-Code scan, so the public key was not loaded from the server
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (added > 0) {
|
||||||
|
if (await createNewSignalSession(userdata)) {
|
||||||
|
// 1. Setup notifications keys with the other user
|
||||||
|
await setupNotificationWithUsers(
|
||||||
|
forceContact: userdata.userId.toInt(),
|
||||||
|
);
|
||||||
|
// 2. Then send user request
|
||||||
|
await sendCipherText(
|
||||||
|
userdata.userId.toInt(),
|
||||||
|
EncryptedContent(
|
||||||
|
contactRequest: EncryptedContent_ContactRequest(
|
||||||
|
type: EncryptedContent_ContactRequest_Type.REQUEST,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,16 @@ import 'package:flutter_android_volume_keydown/flutter_android_volume_keydown.da
|
||||||
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
|
import 'package:flutter_volume_controller/flutter_volume_controller.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/utils/qr.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/permissions_view.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/permissions_view.dart';
|
||||||
|
|
@ -24,8 +27,10 @@ import 'package:twonly/src/views/camera/camera_preview_components/video_recordin
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/zoom_selector.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/zoom_selector.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||||
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||||
import 'package:twonly/src/views/home.view.dart';
|
import 'package:twonly/src/views/home.view.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
int maxVideoRecordingTime = 60;
|
int maxVideoRecordingTime = 60;
|
||||||
|
|
||||||
|
|
@ -92,6 +97,7 @@ class CameraPreviewControllerView extends StatelessWidget {
|
||||||
const CameraPreviewControllerView({
|
const CameraPreviewControllerView({
|
||||||
required this.mainController,
|
required this.mainController,
|
||||||
required this.isVisible,
|
required this.isVisible,
|
||||||
|
this.hideControllers = false,
|
||||||
super.key,
|
super.key,
|
||||||
this.sendToGroup,
|
this.sendToGroup,
|
||||||
});
|
});
|
||||||
|
|
@ -99,6 +105,7 @@ class CameraPreviewControllerView extends StatelessWidget {
|
||||||
final MainCameraController mainController;
|
final MainCameraController mainController;
|
||||||
final Group? sendToGroup;
|
final Group? sendToGroup;
|
||||||
final bool isVisible;
|
final bool isVisible;
|
||||||
|
final bool hideControllers;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -111,6 +118,7 @@ class CameraPreviewControllerView extends StatelessWidget {
|
||||||
sendToGroup: sendToGroup,
|
sendToGroup: sendToGroup,
|
||||||
mainCameraController: mainController,
|
mainCameraController: mainController,
|
||||||
isVisible: isVisible,
|
isVisible: isVisible,
|
||||||
|
hideControllers: hideControllers,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return PermissionHandlerView(
|
return PermissionHandlerView(
|
||||||
|
|
@ -131,6 +139,7 @@ class CameraPreviewView extends StatefulWidget {
|
||||||
const CameraPreviewView({
|
const CameraPreviewView({
|
||||||
required this.mainCameraController,
|
required this.mainCameraController,
|
||||||
required this.isVisible,
|
required this.isVisible,
|
||||||
|
required this.hideControllers,
|
||||||
super.key,
|
super.key,
|
||||||
this.sendToGroup,
|
this.sendToGroup,
|
||||||
});
|
});
|
||||||
|
|
@ -138,6 +147,7 @@ class CameraPreviewView extends StatefulWidget {
|
||||||
final MainCameraController mainCameraController;
|
final MainCameraController mainCameraController;
|
||||||
final Group? sendToGroup;
|
final Group? sendToGroup;
|
||||||
final bool isVisible;
|
final bool isVisible;
|
||||||
|
final bool hideControllers;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CameraPreviewView> createState() => _CameraPreviewViewState();
|
State<CameraPreviewView> createState() => _CameraPreviewViewState();
|
||||||
|
|
@ -641,7 +651,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
widget.sendToGroup != null &&
|
widget.sendToGroup != null &&
|
||||||
!_isVideoRecording)
|
!_isVideoRecording)
|
||||||
SendToWidget(sendTo: widget.sendToGroup!.groupName),
|
SendToWidget(sendTo: widget.sendToGroup!.groupName),
|
||||||
if (!_sharePreviewIsShown && !_isVideoRecording)
|
if (!_sharePreviewIsShown &&
|
||||||
|
!_isVideoRecording &&
|
||||||
|
!widget.hideControllers)
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 5,
|
right: 5,
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|
@ -696,7 +708,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!_sharePreviewIsShown)
|
if (!_sharePreviewIsShown && !widget.hideControllers)
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 30,
|
bottom: 30,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|
@ -775,7 +787,8 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
videoRecordingStarted: _videoRecordingStarted,
|
videoRecordingStarted: _videoRecordingStarted,
|
||||||
maxVideoRecordingTime: maxVideoRecordingTime,
|
maxVideoRecordingTime: maxVideoRecordingTime,
|
||||||
),
|
),
|
||||||
if (!_sharePreviewIsShown && widget.sendToGroup != null)
|
if (!_sharePreviewIsShown && widget.sendToGroup != null ||
|
||||||
|
widget.hideControllers)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 5,
|
left: 5,
|
||||||
top: 10,
|
top: 10,
|
||||||
|
|
@ -796,6 +809,144 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 8,
|
||||||
|
top: 170,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200,
|
||||||
|
width: 150,
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
...widget.mainCameraController.scannedNewProfiles.values
|
||||||
|
.map(
|
||||||
|
(c) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
await addNewContactFromPublicProfile(c.profile);
|
||||||
|
widget.mainCameraController.scannedNewProfiles
|
||||||
|
.remove(c.profile.userId.toInt());
|
||||||
|
widget.mainCameraController.setState();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
color: context.color.surfaceContainer,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(c.profile.username),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
ColoredBox(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: FaIcon(
|
||||||
|
FontAwesomeIcons.userPlus,
|
||||||
|
color: isDarkMode(context)
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
|
size: 17,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
...widget.mainCameraController.contactsVerified.values.map(
|
||||||
|
(c) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
color: context.color.surfaceContainer,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
AvatarIcon(
|
||||||
|
contactId: c.contact.userId,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
getContactDisplayName(
|
||||||
|
c.contact,
|
||||||
|
maxLength: 13,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
ColoredBox(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: SizedBox(
|
||||||
|
width: 30,
|
||||||
|
child: Lottie.asset(
|
||||||
|
c.verificationOk
|
||||||
|
? 'assets/animations/success.json'
|
||||||
|
: 'assets/animations/failed.json',
|
||||||
|
repeat: false,
|
||||||
|
onLoaded: (p0) {
|
||||||
|
Future.delayed(const Duration(seconds: 4),
|
||||||
|
() {
|
||||||
|
widget.mainCameraController.setState();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (widget.mainCameraController.scannedUrl != null)
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
launchUrlString(
|
||||||
|
widget.mainCameraController.scannedUrl!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
color: context.color.surfaceContainer,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
substringBy(
|
||||||
|
widget.mainCameraController.scannedUrl!,
|
||||||
|
25,
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 8),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
ColoredBox(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: FaIcon(
|
||||||
|
FontAwesomeIcons.shareFromSquare,
|
||||||
|
color: isDarkMode(context)
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
|
size: 17,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,55 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
|
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
|
||||||
import 'package:screenshot/screenshot.dart';
|
import 'package:screenshot/screenshot.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
||||||
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/qr.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/barcode_detector_painter.dart';
|
import 'package:twonly/src/views/camera/painters/barcode_detector_painter.dart';
|
||||||
|
|
||||||
|
class ScannedVerifiedContact {
|
||||||
|
ScannedVerifiedContact({
|
||||||
|
required this.contact,
|
||||||
|
required this.verificationOk,
|
||||||
|
});
|
||||||
|
Contact contact;
|
||||||
|
bool verificationOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScannedNewProfile {
|
||||||
|
ScannedNewProfile({
|
||||||
|
required this.profile,
|
||||||
|
});
|
||||||
|
PublicProfile profile;
|
||||||
|
}
|
||||||
|
|
||||||
class MainCameraController {
|
class MainCameraController {
|
||||||
late void Function() setState;
|
late void Function() setState;
|
||||||
CameraController? cameraController;
|
CameraController? cameraController;
|
||||||
ScreenshotController screenshotController = ScreenshotController();
|
ScreenshotController screenshotController = ScreenshotController();
|
||||||
SelectedCameraDetails selectedCameraDetails = SelectedCameraDetails();
|
SelectedCameraDetails selectedCameraDetails = SelectedCameraDetails();
|
||||||
bool initCameraStarted = true;
|
bool initCameraStarted = true;
|
||||||
|
Map<int, ScannedVerifiedContact> contactsVerified = {};
|
||||||
|
Map<int, ScannedNewProfile> scannedNewProfiles = {};
|
||||||
|
String? scannedUrl;
|
||||||
|
|
||||||
Future<void> closeCamera() async {
|
Future<void> closeCamera() async {
|
||||||
await cameraController?.stopImageStream();
|
contactsVerified = {};
|
||||||
|
scannedNewProfiles = {};
|
||||||
|
scannedUrl = null;
|
||||||
|
try {
|
||||||
|
await cameraController?.stopImageStream();
|
||||||
|
} catch (e) {
|
||||||
|
Log.warn(e);
|
||||||
|
}
|
||||||
await cameraController?.dispose();
|
await cameraController?.dispose();
|
||||||
cameraController = null;
|
cameraController = null;
|
||||||
initCameraStarted = false;
|
initCameraStarted = false;
|
||||||
|
|
@ -47,7 +81,11 @@ class MainCameraController {
|
||||||
if (cameraController!.value.isRecordingVideo) {
|
if (cameraController!.value.isRecordingVideo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await cameraController!.stopImageStream();
|
try {
|
||||||
|
await cameraController!.stopImageStream();
|
||||||
|
} catch (e) {
|
||||||
|
Log.warn(e);
|
||||||
|
}
|
||||||
await cameraController!.dispose();
|
await cameraController!.dispose();
|
||||||
cameraController = null;
|
cameraController = null;
|
||||||
await selectCamera((selectedCameraDetails.cameraId + 1) % 2, false);
|
await selectCamera((selectedCameraDetails.cameraId + 1) % 2, false);
|
||||||
|
|
@ -72,16 +110,11 @@ class MainCameraController {
|
||||||
|
|
||||||
InputImage? _inputImageFromCameraImage(CameraImage image) {
|
InputImage? _inputImageFromCameraImage(CameraImage image) {
|
||||||
if (cameraController == null) return null;
|
if (cameraController == null) return null;
|
||||||
|
|
||||||
// get image rotation
|
|
||||||
// it is used in android to convert the InputImage from Dart to Java: https://github.com/flutter-ml/google_ml_kit_flutter/blob/master/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/InputImageConverter.java
|
|
||||||
// `rotation` is not used in iOS to convert the InputImage from Dart to Obj-C: https://github.com/flutter-ml/google_ml_kit_flutter/blob/master/packages/google_mlkit_commons/ios/Classes/MLKVisionImage%2BFlutterPlugin.m
|
|
||||||
// in both platforms `rotation` and `camera.lensDirection` can be used to compensate `x` and `y` coordinates on a canvas: https://github.com/flutter-ml/google_ml_kit_flutter/blob/master/packages/example/lib/vision_detector_views/painters/coordinates_translator.dart
|
|
||||||
final camera = cameraController!.description;
|
final camera = cameraController!.description;
|
||||||
final sensorOrientation = camera.sensorOrientation;
|
final sensorOrientation = camera.sensorOrientation;
|
||||||
// print(
|
|
||||||
// 'lensDirection: ${camera.lensDirection}, sensorOrientation: $sensorOrientation, ${_controller?.value.deviceOrientation} ${_controller?.value.lockedCaptureOrientation} ${_controller?.value.isCaptureOrientationLocked}');
|
|
||||||
InputImageRotation? rotation;
|
InputImageRotation? rotation;
|
||||||
|
|
||||||
if (Platform.isIOS) {
|
if (Platform.isIOS) {
|
||||||
rotation = InputImageRotationValue.fromRawValue(sensorOrientation);
|
rotation = InputImageRotationValue.fromRawValue(sensorOrientation);
|
||||||
} else if (Platform.isAndroid) {
|
} else if (Platform.isAndroid) {
|
||||||
|
|
@ -148,6 +181,50 @@ class MainCameraController {
|
||||||
cameraController!.description.lensDirection,
|
cameraController!.description.lensDirection,
|
||||||
);
|
);
|
||||||
customPaint = CustomPaint(painter: painter);
|
customPaint = CustomPaint(painter: painter);
|
||||||
|
|
||||||
|
for (final barcode in barcodes) {
|
||||||
|
if (barcode.displayValue != null) {
|
||||||
|
if (barcode.displayValue!.startsWith('http://') ||
|
||||||
|
barcode.displayValue!.startsWith('https://')) {
|
||||||
|
scannedUrl = barcode.displayValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (barcode.rawBytes == null) continue;
|
||||||
|
|
||||||
|
final profile = parseQrCodeData(barcode.rawBytes!);
|
||||||
|
|
||||||
|
if (profile == null) continue;
|
||||||
|
|
||||||
|
final contact =
|
||||||
|
await twonlyDB.contactsDao.getContactById(profile.userId.toInt());
|
||||||
|
|
||||||
|
if (contact != null) {
|
||||||
|
if (contactsVerified[contact.userId] == null) {
|
||||||
|
final storedPublicKey =
|
||||||
|
await getPublicKeyFromContact(contact.userId);
|
||||||
|
if (storedPublicKey != null) {
|
||||||
|
final verificationOk =
|
||||||
|
profile.publicIdentityKey.equals(storedPublicKey.toList());
|
||||||
|
contactsVerified[contact.userId] = ScannedVerifiedContact(
|
||||||
|
contact: contact,
|
||||||
|
verificationOk: verificationOk,
|
||||||
|
);
|
||||||
|
if (verificationOk) {
|
||||||
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
contact.userId,
|
||||||
|
const ContactsCompanion(verified: Value(true)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (scannedNewProfiles[profile.userId.toInt()] == null) {
|
||||||
|
scannedNewProfiles[profile.userId.toInt()] = ScannedNewProfile(
|
||||||
|
profile: profile,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_isBusy = false;
|
_isBusy = false;
|
||||||
setState();
|
setState();
|
||||||
|
|
|
||||||
51
lib/src/views/camera/camera_qr_scanner.view.dart
Normal file
51
lib/src/views/camera/camera_qr_scanner.view.dart
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
||||||
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
|
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
|
|
||||||
|
class QrCodeScanner extends StatefulWidget {
|
||||||
|
const QrCodeScanner({super.key});
|
||||||
|
@override
|
||||||
|
State<QrCodeScanner> createState() => QrCodeScannerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class QrCodeScannerState extends State<QrCodeScanner> {
|
||||||
|
final MainCameraController _mainCameraController = MainCameraController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_mainCameraController.setState = () {
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
};
|
||||||
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_mainCameraController.closeCamera();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: GestureDetector(
|
||||||
|
onDoubleTap: _mainCameraController.toggleSelectedCamera,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
MainCameraPreview(
|
||||||
|
mainCameraController: _mainCameraController,
|
||||||
|
),
|
||||||
|
CameraPreviewControllerView(
|
||||||
|
mainController: _mainCameraController,
|
||||||
|
hideControllers: true,
|
||||||
|
isVisible: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'dart:ui' as ui;
|
|
||||||
|
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
|
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
|
||||||
import 'package:twonly/src/utils/qr.dart';
|
|
||||||
|
|
||||||
import 'coordinates_translator.dart';
|
import 'coordinates_translator.dart';
|
||||||
|
|
||||||
class BarcodeDetectorPainter extends CustomPainter {
|
class BarcodeDetectorPainter extends CustomPainter {
|
||||||
|
|
@ -29,64 +24,7 @@ class BarcodeDetectorPainter extends CustomPainter {
|
||||||
..strokeWidth = 3.0
|
..strokeWidth = 3.0
|
||||||
..color = Colors.lightGreenAccent;
|
..color = Colors.lightGreenAccent;
|
||||||
|
|
||||||
final background = Paint()..color = const Color(0x99000000);
|
|
||||||
|
|
||||||
for (final barcode in barcodes) {
|
for (final barcode in barcodes) {
|
||||||
final bytes = barcode.rawBytes;
|
|
||||||
if (bytes == null) continue;
|
|
||||||
|
|
||||||
final profile = parseQrCodeData(bytes);
|
|
||||||
|
|
||||||
if (profile == null) continue;
|
|
||||||
|
|
||||||
final builder = ParagraphBuilder(
|
|
||||||
ParagraphStyle(
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
fontSize: 16,
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
..pushStyle(
|
|
||||||
ui.TextStyle(color: Colors.lightGreenAccent, background: background),
|
|
||||||
)
|
|
||||||
..addText(profile.username)
|
|
||||||
..pop();
|
|
||||||
|
|
||||||
final left = translateX(
|
|
||||||
barcode.boundingBox.left,
|
|
||||||
size,
|
|
||||||
imageSize,
|
|
||||||
rotation,
|
|
||||||
cameraLensDirection,
|
|
||||||
);
|
|
||||||
final top = translateY(
|
|
||||||
barcode.boundingBox.top,
|
|
||||||
size,
|
|
||||||
imageSize,
|
|
||||||
rotation,
|
|
||||||
cameraLensDirection,
|
|
||||||
);
|
|
||||||
final right = translateX(
|
|
||||||
barcode.boundingBox.right,
|
|
||||||
size,
|
|
||||||
imageSize,
|
|
||||||
rotation,
|
|
||||||
cameraLensDirection,
|
|
||||||
);
|
|
||||||
// final bottom = translateY(
|
|
||||||
// barcode.boundingBox.bottom,
|
|
||||||
// size,
|
|
||||||
// imageSize,
|
|
||||||
// rotation,
|
|
||||||
// cameraLensDirection,
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // Draw a bounding rectangle around the barcode
|
|
||||||
// canvas.drawRect(
|
|
||||||
// Rect.fromLTRB(left, top, right, bottom),
|
|
||||||
// paint,
|
|
||||||
// );
|
|
||||||
|
|
||||||
final cornerPoints = <Offset>[];
|
final cornerPoints = <Offset>[];
|
||||||
for (final point in barcode.cornerPoints) {
|
for (final point in barcode.cornerPoints) {
|
||||||
final x = translateX(
|
final x = translateX(
|
||||||
|
|
@ -107,25 +45,8 @@ class BarcodeDetectorPainter extends CustomPainter {
|
||||||
cornerPoints.add(Offset(x, y));
|
cornerPoints.add(Offset(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the first point to close the polygon
|
|
||||||
cornerPoints.add(cornerPoints.first);
|
cornerPoints.add(cornerPoints.first);
|
||||||
canvas
|
canvas.drawPoints(PointMode.polygon, cornerPoints, paint);
|
||||||
..drawPoints(PointMode.polygon, cornerPoints, paint)
|
|
||||||
..drawParagraph(
|
|
||||||
builder.build()
|
|
||||||
..layout(
|
|
||||||
ParagraphConstraints(
|
|
||||||
width: (right - left).abs(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Offset(
|
|
||||||
Platform.isAndroid &&
|
|
||||||
cameraLensDirection == CameraLensDirection.front
|
|
||||||
? right
|
|
||||||
: left,
|
|
||||||
top,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -306,9 +306,9 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.qrcode,
|
FontAwesomeIcons.qrcode,
|
||||||
color: Colors.black,
|
color: isDarkMode(context) ? Colors.black : Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
@ -324,9 +324,9 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.penToSquare,
|
FontAwesomeIcons.penToSquare,
|
||||||
color: Colors.black,
|
color: isDarkMode(context) ? Colors.black : Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/views/contact/contact_verify.view.dart';
|
import 'package:twonly/src/views/public_profile.view.dart';
|
||||||
|
|
||||||
class VerifiedShield extends StatefulWidget {
|
class VerifiedShield extends StatefulWidget {
|
||||||
const VerifiedShield({
|
const VerifiedShield({
|
||||||
|
|
@ -63,7 +63,7 @@ class _VerifiedShieldState extends State<VerifiedShield> {
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ContactVerifyView(contact!);
|
return const PublicProfileView();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ import 'package:twonly/src/views/components/flame.dart';
|
||||||
import 'package:twonly/src/views/components/max_flame_list_title.dart';
|
import 'package:twonly/src/views/components/max_flame_list_title.dart';
|
||||||
import 'package:twonly/src/views/components/select_chat_deletion_time.comp.dart';
|
import 'package:twonly/src/views/components/select_chat_deletion_time.comp.dart';
|
||||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||||
import 'package:twonly/src/views/contact/contact_verify.view.dart';
|
|
||||||
import 'package:twonly/src/views/groups/group.view.dart';
|
import 'package:twonly/src/views/groups/group.view.dart';
|
||||||
|
import 'package:twonly/src/views/public_profile.view.dart';
|
||||||
|
|
||||||
class ContactView extends StatefulWidget {
|
class ContactView extends StatefulWidget {
|
||||||
const ContactView(this.userId, {super.key});
|
const ContactView(this.userId, {super.key});
|
||||||
|
|
@ -159,7 +159,7 @@ class _ContactViewState extends State<ContactView> {
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ContactVerifyView(contact);
|
return const PublicProfileView();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,262 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:drift/drift.dart' hide Column;
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
|
||||||
import 'package:lottie/lottie.dart';
|
|
||||||
import 'package:twonly/globals.dart';
|
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
import 'package:twonly/src/views/components/fingerprint_text.dart';
|
|
||||||
import 'package:twonly/src/views/contact/contact_verify_qr_scan.view.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
class ContactVerifyView extends StatefulWidget {
|
|
||||||
const ContactVerifyView(this.contact, {super.key});
|
|
||||||
final Contact contact;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ContactVerifyView> createState() => _ContactVerifyViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore: constant_identifier_names
|
|
||||||
enum ScanResult { None, Success, Failed }
|
|
||||||
|
|
||||||
class _ContactVerifyViewState extends State<ContactVerifyView> {
|
|
||||||
Fingerprint? _fingerprint;
|
|
||||||
late Contact _contact;
|
|
||||||
late StreamSubscription<Contact?> _contactSub;
|
|
||||||
ScanResult _scanResult = ScanResult.None;
|
|
||||||
Uint8List? _qrCodeImageBytes;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_contact = widget.contact;
|
|
||||||
unawaited(loadAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
unawaited(_contactSub.cancel());
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> loadAsync() async {
|
|
||||||
_fingerprint = await generateSessionFingerPrint(widget.contact.userId);
|
|
||||||
|
|
||||||
if (_fingerprint != null) {
|
|
||||||
// final result = zx.encodeBarcode(
|
|
||||||
// contents: base64Encode(
|
|
||||||
// _fingerprint!.scannableFingerprint.fingerprints,
|
|
||||||
// ),
|
|
||||||
// params: EncodeParams(
|
|
||||||
// width: 150,
|
|
||||||
// height: 150,
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// if (result.isValid && result.data != null) {
|
|
||||||
// final img = imglib.Image.fromBytes(
|
|
||||||
// width: 150,
|
|
||||||
// height: 150,
|
|
||||||
// bytes: result.data!.buffer,
|
|
||||||
// numChannels: 1,
|
|
||||||
// );
|
|
||||||
// _qrCodeImageBytes = imglib.encodePng(img);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// final contact = twonlyDB.contactsDao
|
|
||||||
// .getContactByUserId(widget.contact.userId)
|
|
||||||
// .watchSingleOrNull();
|
|
||||||
// _contactSub = contact.listen((contact) {
|
|
||||||
// if (contact == null) return;
|
|
||||||
// setState(() {
|
|
||||||
// _contact = contact;
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> openQrScanner() async {
|
|
||||||
if (_fingerprint == null) return;
|
|
||||||
final isValid = await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ContactVerifyQrScanView(
|
|
||||||
widget.contact,
|
|
||||||
fingerprint: _fingerprint!,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
) as bool?;
|
|
||||||
if (isValid == null) {
|
|
||||||
return; // user just returned...
|
|
||||||
}
|
|
||||||
if (isValid) {
|
|
||||||
_scanResult = ScanResult.Success;
|
|
||||||
await updateUserVerifyState(true);
|
|
||||||
} else {
|
|
||||||
_scanResult = ScanResult.Failed;
|
|
||||||
await updateUserVerifyState(false);
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateUserVerifyState(bool verified) async {
|
|
||||||
final update = ContactsCompanion(verified: Value(verified));
|
|
||||||
await twonlyDB.contactsDao.updateContact(_contact.userId, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget get qrWidget => (_qrCodeImageBytes == null)
|
|
||||||
? const SizedBox(
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
)
|
|
||||||
: Image.memory(_qrCodeImageBytes!);
|
|
||||||
Widget get resultAnimation => SizedBox(
|
|
||||||
width: 150,
|
|
||||||
child: Lottie.asset(
|
|
||||||
(_scanResult == ScanResult.Success)
|
|
||||||
? 'assets/animations/success.json'
|
|
||||||
: 'assets/animations/failed.json',
|
|
||||||
repeat: false,
|
|
||||||
onLoaded: (p0) {
|
|
||||||
Future.delayed(const Duration(seconds: 3), () {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
_scanResult = ScanResult.None;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(context.lang.contactVerifyNumberTitle),
|
|
||||||
),
|
|
||||||
body: (_fingerprint == null)
|
|
||||||
? const Center(child: CircularProgressIndicator())
|
|
||||||
: ListView(
|
|
||||||
children: [
|
|
||||||
Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(25),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: openQrScanner,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
if (_scanResult == ScanResult.None)
|
|
||||||
qrWidget
|
|
||||||
else
|
|
||||||
resultAnimation,
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
SizedBox(
|
|
||||||
width: 200,
|
|
||||||
child: Text(
|
|
||||||
(_scanResult == ScanResult.None)
|
|
||||||
? context
|
|
||||||
.lang.contactVerifyNumberTapToScan
|
|
||||||
: '',
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 15,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
FingerprintText(
|
|
||||||
_fingerprint!.displayableFingerprint
|
|
||||||
.getDisplayText(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
|
||||||
child: Text(
|
|
||||||
context.lang.contactVerifyNumberLongDesc(
|
|
||||||
getContactDisplayName(_contact),
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 30, vertical: 10),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
await launchUrl(
|
|
||||||
Uri.parse(
|
|
||||||
'https://twonly.eu/en/faq/security/verify-security-number.html',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'Read more.',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
bottomNavigationBar: SafeArea(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 60),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (_contact.verified)
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: () => updateUserVerifyState(false),
|
|
||||||
label:
|
|
||||||
Text(context.lang.contactVerifyNumberClearVerification),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
FilledButton.icon(
|
|
||||||
icon: const FaIcon(FontAwesomeIcons.shieldHeart),
|
|
||||||
onPressed: () => updateUserVerifyState(true),
|
|
||||||
label: Text(
|
|
||||||
context.lang.contactVerifyNumberMarkAsVerified,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
|
||||||
|
|
||||||
class ContactVerifyQrScanView extends StatefulWidget {
|
|
||||||
const ContactVerifyQrScanView(
|
|
||||||
this.contact, {
|
|
||||||
required this.fingerprint,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
final Fingerprint fingerprint;
|
|
||||||
final Contact contact;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ContactVerifyQrScanView> createState() =>
|
|
||||||
_ContactVerifyQrScanViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ContactVerifyQrScanViewState extends State<ContactVerifyQrScanView> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return const Text('Not yet implemented.');
|
|
||||||
// return Scaffold(
|
|
||||||
// body: ReaderWidget(
|
|
||||||
// onScan: (result) async {
|
|
||||||
// var isValid = false;
|
|
||||||
// try {
|
|
||||||
// if (result.text != null) {
|
|
||||||
// final otherFingerPrint = base64Decode(result.text!);
|
|
||||||
// isValid = widget.fingerprint.scannableFingerprint.compareTo(
|
|
||||||
// otherFingerPrint,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// Log.error('$e');
|
|
||||||
// }
|
|
||||||
// return Navigator.pop(context, isValid);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:app_links/app_links.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
||||||
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
|
|
@ -12,6 +17,7 @@ import 'package:twonly/src/views/camera/camera_preview_components/main_camera_co
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
||||||
import 'package:twonly/src/views/memories/memories.view.dart';
|
import 'package:twonly/src/views/memories/memories.view.dart';
|
||||||
|
import 'package:twonly/src/views/public_profile.view.dart';
|
||||||
|
|
||||||
void Function(int) globalUpdateOfHomeViewPageIndex = (a) {};
|
void Function(int) globalUpdateOfHomeViewPageIndex = (a) {};
|
||||||
|
|
||||||
|
|
@ -49,6 +55,7 @@ class HomeViewState extends State<HomeView> {
|
||||||
final MainCameraController _mainCameraController = MainCameraController();
|
final MainCameraController _mainCameraController = MainCameraController();
|
||||||
|
|
||||||
final PageController homeViewPageController = PageController(initialPage: 1);
|
final PageController homeViewPageController = PageController(initialPage: 1);
|
||||||
|
late StreamSubscription<Uri> _deepLinkSub;
|
||||||
|
|
||||||
double buttonDiameter = 100;
|
double buttonDiameter = 100;
|
||||||
double offsetRatio = 0;
|
double offsetRatio = 0;
|
||||||
|
|
@ -105,6 +112,56 @@ class HomeViewState extends State<HomeView> {
|
||||||
});
|
});
|
||||||
unawaited(_mainCameraController.selectCamera(0, true));
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
unawaited(initAsync());
|
unawaited(initAsync());
|
||||||
|
|
||||||
|
// Subscribe to all events (initial link and further)
|
||||||
|
_deepLinkSub = AppLinks().uriLinkStream.listen((uri) async {
|
||||||
|
if (!uri.scheme.startsWith('http')) return;
|
||||||
|
if (uri.host != 'me.twonly.eu') return;
|
||||||
|
if (uri.hasEmptyPath) return;
|
||||||
|
|
||||||
|
final publicKey = uri.hasFragment ? uri.fragment : null;
|
||||||
|
final userPaths = uri.path.split('/');
|
||||||
|
if (userPaths.length != 2) return;
|
||||||
|
final username = userPaths[1];
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
if (username == gUser.username) {
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return const PublicProfileView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info(
|
||||||
|
'Opened via deep link!: username = $username public_key = ${uri.fragment}',
|
||||||
|
);
|
||||||
|
final contacts =
|
||||||
|
await twonlyDB.contactsDao.getContactsByUsername(username);
|
||||||
|
if (contacts.isEmpty) {
|
||||||
|
// load user from server...
|
||||||
|
} else if (publicKey != null) {
|
||||||
|
try {
|
||||||
|
final contact = contacts.first;
|
||||||
|
final storedPublicKey = await getPublicKeyFromContact(contact.userId);
|
||||||
|
final receivedPublicKey = base64Url.decode(publicKey);
|
||||||
|
if (storedPublicKey == null || receivedPublicKey.isEmpty) return;
|
||||||
|
|
||||||
|
if (storedPublicKey.equals(receivedPublicKey)) {
|
||||||
|
Log.info('Could verify the user');
|
||||||
|
} else {
|
||||||
|
Log.error('Show error message');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Log.warn(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -112,6 +169,7 @@ class HomeViewState extends State<HomeView> {
|
||||||
unawaited(selectNotificationStream.close());
|
unawaited(selectNotificationStream.close());
|
||||||
disableCameraTimer?.cancel();
|
disableCameraTimer?.cancel();
|
||||||
_mainCameraController.closeCamera();
|
_mainCameraController.closeCamera();
|
||||||
|
_deepLinkSub.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/utils/avatars.dart';
|
import 'package:twonly/src/utils/avatars.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/qr.dart';
|
import 'package:twonly/src/utils/qr.dart';
|
||||||
|
import 'package:twonly/src/views/camera/camera_qr_scanner.view.dart';
|
||||||
|
import 'package:twonly/src/views/components/better_list_title.dart';
|
||||||
|
|
||||||
class PublicProfileView extends StatefulWidget {
|
class PublicProfileView extends StatefulWidget {
|
||||||
const PublicProfileView({super.key});
|
const PublicProfileView({super.key});
|
||||||
|
|
@ -17,6 +22,7 @@ class PublicProfileView extends StatefulWidget {
|
||||||
class _PublicProfileViewState extends State<PublicProfileView> {
|
class _PublicProfileViewState extends State<PublicProfileView> {
|
||||||
Uint8List? _qrCode;
|
Uint8List? _qrCode;
|
||||||
Uint8List? _userAvatar;
|
Uint8List? _userAvatar;
|
||||||
|
Uint8List? _publicKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -27,6 +33,7 @@ class _PublicProfileViewState extends State<PublicProfileView> {
|
||||||
Future<void> initAsync() async {
|
Future<void> initAsync() async {
|
||||||
_qrCode = await getProfileQrCodeData();
|
_qrCode = await getProfileQrCodeData();
|
||||||
_userAvatar = await getUserAvatar();
|
_userAvatar = await getUserAvatar();
|
||||||
|
_publicKey = await getUserPublicKey();
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
@ -43,58 +50,78 @@ class _PublicProfileViewState extends State<PublicProfileView> {
|
||||||
),
|
),
|
||||||
if (_qrCode != null && _userAvatar != null)
|
if (_qrCode != null && _userAvatar != null)
|
||||||
Container(
|
Container(
|
||||||
child: Container(
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: context.color.primary,
|
||||||
color: context.color.primary,
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderRadius: BorderRadius.circular(12),
|
boxShadow: const [
|
||||||
border: Border.all(
|
BoxShadow(
|
||||||
color: Colors.black,
|
color: Colors.black26,
|
||||||
width: 2,
|
blurRadius: 6,
|
||||||
|
offset: Offset(0, 2),
|
||||||
),
|
),
|
||||||
boxShadow: const [
|
],
|
||||||
BoxShadow(
|
),
|
||||||
color: Colors.black26,
|
child: QrImageView.withQr(
|
||||||
blurRadius: 6,
|
qr: QrCode.fromUint8List(
|
||||||
offset: Offset(0, 2),
|
data: _qrCode!,
|
||||||
),
|
errorCorrectLevel: QrErrorCorrectLevel.M,
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: QrImageView.withQr(
|
eyeStyle: QrEyeStyle(
|
||||||
qr: QrCode.fromUint8List(
|
color: isDarkMode(context) ? Colors.black : Colors.white,
|
||||||
data: _qrCode!,
|
borderRadius: 2,
|
||||||
errorCorrectLevel: QrErrorCorrectLevel.M,
|
|
||||||
),
|
|
||||||
eyeStyle: const QrEyeStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
borderRadius: 2,
|
|
||||||
),
|
|
||||||
dataModuleStyle: const QrDataModuleStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
borderRadius: 2,
|
|
||||||
),
|
|
||||||
gapless: false,
|
|
||||||
embeddedImage: MemoryImage(_userAvatar!),
|
|
||||||
embeddedImageStyle: QrEmbeddedImageStyle(
|
|
||||||
size: const Size(60, 66),
|
|
||||||
embeddedImageShape: EmbeddedImageShape.square,
|
|
||||||
shapeColor: context.color.primary,
|
|
||||||
safeArea: true,
|
|
||||||
),
|
|
||||||
size: 250,
|
|
||||||
),
|
),
|
||||||
|
dataModuleStyle: QrDataModuleStyle(
|
||||||
|
color: isDarkMode(context) ? Colors.black : Colors.white,
|
||||||
|
borderRadius: 2,
|
||||||
|
),
|
||||||
|
gapless: false,
|
||||||
|
embeddedImage: MemoryImage(_userAvatar!),
|
||||||
|
embeddedImageStyle: QrEmbeddedImageStyle(
|
||||||
|
size: const Size(60, 66),
|
||||||
|
embeddedImageShape: EmbeddedImageShape.square,
|
||||||
|
shapeColor: context.color.primary,
|
||||||
|
safeArea: true,
|
||||||
|
),
|
||||||
|
size: 250,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
const SizedBox(height: 20),
|
||||||
gUser.displayName,
|
|
||||||
style: const TextStyle(fontSize: 24),
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
gUser.username,
|
gUser.username,
|
||||||
style: const TextStyle(fontSize: 18),
|
style: const TextStyle(fontSize: 24),
|
||||||
)
|
),
|
||||||
// QR Code,,,
|
const SizedBox(height: 20),
|
||||||
// Display Name
|
const Divider(),
|
||||||
// username...
|
const SizedBox(height: 20),
|
||||||
|
BetterListTile(
|
||||||
|
leading: const FaIcon(FontAwesomeIcons.qrcode),
|
||||||
|
text: context.lang.scanOtherProfile,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const QrCodeScanner(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BetterListTile(
|
||||||
|
leading: const FaIcon(
|
||||||
|
FontAwesomeIcons.shareFromSquare,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
text: context.lang.shareYourProfile,
|
||||||
|
subtitle: (_publicKey == null)
|
||||||
|
? null
|
||||||
|
: Text('https://me.twonly.eu/${gUser.username}'),
|
||||||
|
onTap: () {
|
||||||
|
final params = ShareParams(
|
||||||
|
text:
|
||||||
|
'https://me.twonly.eu/${gUser.username}#${base64Url.encode(_publicKey!)}',
|
||||||
|
);
|
||||||
|
SharePlus.instance.share(params);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
44
pubspec.lock
44
pubspec.lock
|
|
@ -32,6 +32,38 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.1"
|
version: "8.4.1"
|
||||||
|
app_links:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: app_links
|
||||||
|
sha256: "3462d9defc61565fde4944858b59bec5be2b9d5b05f20aed190adb3ad08a7abc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
|
app_links_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_linux
|
||||||
|
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
app_links_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_platform_interface
|
||||||
|
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
|
app_links_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_web
|
||||||
|
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -839,6 +871,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.2"
|
||||||
|
gtk:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gtk
|
||||||
|
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
hand_signature:
|
hand_signature:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1985,5 +2025,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.9.0 <4.0.0"
|
dart: ">=3.10.0 <4.0.0"
|
||||||
flutter: ">=3.35.0"
|
flutter: ">=3.38.1"
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ dependencies:
|
||||||
mutex: ^3.1.0
|
mutex: ^3.1.0
|
||||||
introduction_screen: ^4.0.0
|
introduction_screen: ^4.0.0
|
||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
|
app_links: ^7.0.0
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
dots_indicator:
|
dots_indicator:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue