Compare commits

..

33 Commits

Author SHA1 Message Date
Levitating Pineapple c2c689ca91 fix animator crash 2024-10-07 21:24:07 +03:00
Arturs Krumins 56922c6323 ios: remove showSettings and withNavigation (#4980)
* remove showSettings

* pass dismiss action trough navigation links

* move auth to all sheets
2024-10-07 18:16:01 +01:00
Levitating Pineapple 91823eec95 fix keyboard related glitches 2024-10-04 13:58:32 +03:00
Levitating Pineapple 8547fab700 improve handling of repeated sheet presentation state changes 2024-10-04 10:30:11 +03:00
Levitating Pineapple 5c078f91fc Merge branch 'master' into lp/custom-user-picker-sheet 2024-10-03 21:21:09 +03:00
Arturs Krumins ec444e7ce1 load sheet navigation titles before presenting (#4963)
* load sheet navigation titles before presenting

* list background during loading
2024-10-03 21:18:13 +03:00
Levitating Pineapple e69d94896d async camera authorization in 'Use from desktop' sheet 2024-10-01 19:20:48 +03:00
Levitating Pineapple a134b3cb2f fix iOS15 sheet animation when presenting sheet multiple times 2024-10-01 17:01:36 +03:00
Levitating Pineapple ab24369333 async qr code generation 2024-10-01 15:27:47 +03:00
Levitating Pineapple 592b8d49d0 Merge branch 'master' into lp/custom-user-picker-sheet 2024-09-30 22:47:40 +03:00
Arturs Krumins af7ebe1587 ios: user picker sheet concurent animation (#4955)
* ios: user picker sheet concurent animation

* bind showSettings; cleanup
2024-09-30 22:45:39 +03:00
Levitating Pineapple 2a8df6ace1 remove touchdown animation 2024-09-30 16:04:14 +03:00
Levitating Pineapple bbe614aef7 cubic animation curve; dynamic backdrop opacity 2024-09-30 13:55:51 +03:00
Levitating Pineapple 63ffcbc4e7 instantanious longPress; tweak animations 2024-09-27 15:33:21 +03:00
Levitating Pineapple 01edff3ca3 refactor separators 2024-09-27 11:58:05 +03:00
Levitating Pineapple cd9aa120bc Merge branch 'master' into lp/custom-user-picker-sheet 2024-09-27 11:09:24 +03:00
Levitating Pineapple 86dce0921e fix layout loop; add upper animation speed constraint 2024-09-26 20:43:59 +03:00
Levitating Pineapple e21d49db18 fix dark mode tap target; retain highlight; highlight in user scroller 2024-09-26 17:48:29 +03:00
Evgeny Poberezkin 73673ab073 Merge branch 'master' into lp/custom-user-picker-sheet 2024-09-26 13:50:40 +01:00
Levitating Pineapple ff065d7720 Merge branch 'master' into lp/custom-user-picker-sheet 2024-09-26 15:29:38 +03:00
Levitating Pineapple 07165039a8 touchable list row; prevent tap gesture passtrough 2024-09-26 14:58:15 +03:00
Levitating Pineapple 240aa6a2b1 delay user picker closing 2024-09-26 10:32:18 +03:00
Levitating Pineapple fcde05e9a4 reduce dif 2024-09-26 00:03:26 +03:00
Levitating Pineapple cabf8aba16 reset user scroll position on dismiss; cleanup 2024-09-25 23:42:19 +03:00
Levitating Pineapple de594aac88 user picker bottom padding 2024-09-25 21:07:55 +03:00
Levitating Pineapple 91f3335503 Merge branch 'master' into lp/custom-user-picker-sheet 2024-09-25 19:15:15 +03:00
Levitating Pineapple 2663e6330a resolve warning 2024-09-25 19:00:17 +03:00
Levitating Pineapple f28a5b6e92 implement UIViewPropertyAnimator 2024-09-25 18:58:10 +03:00
Evgeny Poberezkin d59fdb1e09 Merge branch 'master' into lp/custom-user-picker-sheet 2024-09-24 11:55:26 +01:00
Levitating Pineapple b4ec4df687 cleanup; dismiss 2024-09-23 23:55:05 +03:00
Levitating Pineapple f154a0bcb4 distance based animation duration 2024-09-23 23:02:53 +03:00
Levitating Pineapple ce5d113ff6 tweak appearance 2024-09-23 21:43:40 +03:00
Levitating Pineapple a35b20f54c ios: hide user picker sheet instantly, when opening another sheet 2024-09-23 18:43:05 +03:00
119 changed files with 1600 additions and 4347 deletions
+3 -12
View File
@@ -297,18 +297,9 @@ struct ContentView: View {
if let contactId = contacts?.first?.personHandle?.value,
let chat = chatModel.getChat(contactId),
case let .direct(contact) = chat.chatInfo {
let activeCall = chatModel.activeCall
// This line works when a user clicks on a video button in CallKit UI while in call.
// The app tries to make another call to the same contact and overwite activeCall instance making its state broken
if let activeCall, contactId == activeCall.contact.id, mediaType == .video, !activeCall.hasVideo {
Task {
await chatModel.callCommand.processCommand(.media(source: .camera, enable: true))
}
} else if activeCall == nil {
logger.debug("callToRecentContact: schedule call")
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
CallController.shared.startCall(contact, mediaType)
}
logger.debug("callToRecentContact: schedule call")
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
CallController.shared.startCall(contact, mediaType)
}
}
}
+1 -1
View File
@@ -526,7 +526,7 @@ final class ChatModel: ObservableObject {
}
func updateCurrentUserUiThemes(uiThemes: ThemeModeOverrides?) {
guard var current = currentUser, current.uiThemes != uiThemes else { return }
guard var current = currentUser else { return }
current.uiThemes = uiThemes
let i = users.firstIndex(where: { $0.user.userId == current.userId })
if let i {
+1
View File
@@ -2084,6 +2084,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
await withCall(contact) { call in
await MainActor.run {
call.callState = .offerReceived
call.peerMedia = callType.media
call.sharedKey = sharedKey
}
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
+1 -1
View File
@@ -122,7 +122,7 @@ extension ThemeWallpaper {
let preset: String? = if case let WallpaperType.preset(filename, _) = type { filename } else { nil }
let scale: Float? = if case let WallpaperType.preset(_, scale) = type { scale } else { if case let WallpaperType.image(_, scale, _) = type { scale } else { 1.0 } }
let scaleType: WallpaperScaleType? = if case let WallpaperType.image(_, _, scaleType) = type { scaleType } else { nil }
let image: String? = if case WallpaperType.image = type, let image = type.uiImage { resizeImageToStrSizeSync(image, maxDataSize: 5_000_000) } else { nil }
let image: String? = if case WallpaperType.image = type, let image = type.uiImage { resizeImageToStrSize(image, maxDataSize: 5_000_000) } else { nil }
return ThemeWallpaper (
preset: preset,
scale: scale,
+106 -158
View File
@@ -17,8 +17,8 @@ struct ActiveCallView: View {
@ObservedObject var call: Call
@Environment(\.scenePhase) var scenePhase
@State private var client: WebRTCClient? = nil
@State private var activeCall: WebRTCClient.Call? = nil
@State private var localRendererAspectRatio: CGFloat? = nil
@State var remoteContentMode: UIView.ContentMode = .scaleAspectFill
@Binding var canConnectCall: Bool
@State var prevColorScheme: ColorScheme = .dark
@State var pipShown = false
@@ -27,39 +27,24 @@ struct ActiveCallView: View {
var body: some View {
ZStack(alignment: .topLeading) {
ZStack(alignment: .bottom) {
if let client = client, call.hasVideo {
if let client = client, [call.peerMedia, call.localMedia].contains(.video), activeCall != nil {
GeometryReader { g in
let width = g.size.width * 0.3
ZStack(alignment: .topTrailing) {
CallViewRemote(client: client, activeCall: $activeCall, activeCallViewIsCollapsed: $m.activeCallViewIsCollapsed, pipShown: $pipShown)
CallViewLocal(client: client, activeCall: $activeCall, localRendererAspectRatio: $localRendererAspectRatio, pipShown: $pipShown)
.cornerRadius(10)
.frame(width: width, height: width / (localRendererAspectRatio ?? 1))
.padding([.top, .trailing], 17)
ZStack(alignment: .center) {
// For some reason, when the view in GeometryReader and ZStack is visible, it steals clicks on a back button, so showing something on top like this with background color helps (.clear color doesn't work)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.primary.opacity(0.000001))
CallViewRemote(client: client, call: call, activeCallViewIsCollapsed: $m.activeCallViewIsCollapsed, contentMode: $remoteContentMode, pipShown: $pipShown)
.onTapGesture {
remoteContentMode = remoteContentMode == .scaleAspectFill ? .scaleAspectFit : .scaleAspectFill
}
Group {
let localVideoTrack = client.activeCall?.localVideoTrack ?? client.notConnectedCall?.localCameraAndTrack?.1
if localVideoTrack != nil {
CallViewLocal(client: client, localRendererAspectRatio: $localRendererAspectRatio, pipShown: $pipShown)
.onDisappear {
localRendererAspectRatio = nil
}
} else {
Rectangle().fill(.black)
}
}
.cornerRadius(10)
.frame(width: width, height: localRendererAspectRatio == nil ? (g.size.width < g.size.height ? width * 1.33 : width / 1.33) : width / (localRendererAspectRatio ?? 1))
.padding([.top, .trailing], 17)
}
}
}
if let call = m.activeCall, let client = client, (!pipShown || !call.hasVideo) {
if let call = m.activeCall, let client = client, (!pipShown || !call.supportsVideo) {
ActiveCallOverlay(call: call, client: client)
}
}
@@ -69,9 +54,6 @@ struct ActiveCallView: View {
.onAppear {
logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase)), canConnectCall \(canConnectCall)")
AppDelegate.keepScreenOn(true)
Task {
await askRequiredPermissions()
}
createWebRTCClient()
dismissAllSheets()
hideKeyboard()
@@ -102,7 +84,7 @@ struct ActiveCallView: View {
private func createWebRTCClient() {
if client == nil && canConnectCall {
client = WebRTCClient({ msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio)
client = WebRTCClient($activeCall, { msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio)
Task {
await m.callCommand.setClient(client)
}
@@ -117,7 +99,7 @@ struct ActiveCallView: View {
logger.debug("ActiveCallView: response \(msg.resp.respType)")
switch msg.resp {
case let .capabilities(capabilities):
let callType = CallType(media: call.initialCallType, capabilities: capabilities)
let callType = CallType(media: call.localMedia, capabilities: capabilities)
Task {
do {
try await apiSendCallInvitation(call.contact, callType)
@@ -128,7 +110,7 @@ struct ActiveCallView: View {
call.callState = .invitationSent
call.localCapabilities = capabilities
}
if call.hasVideo && !AVAudioSession.sharedInstance().hasExternalAudioDevice() {
if call.supportsVideo && !AVAudioSession.sharedInstance().hasExternalAudioDevice() {
try? AVAudioSession.sharedInstance().setCategory(.playback, options: [.allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
}
CallSoundsPlayer.shared.startConnectingCallSound()
@@ -138,7 +120,7 @@ struct ActiveCallView: View {
Task {
do {
try await apiSendCallOffer(call.contact, offer, iceCandidates,
media: call.initialCallType, capabilities: capabilities)
media: call.localMedia, capabilities: capabilities)
} catch {
logger.error("apiSendCallOffer \(responseError(error))")
}
@@ -182,9 +164,6 @@ struct ActiveCallView: View {
}
if state.connectionState == "closed" {
closeCallView(client)
if let callUUID = m.activeCall?.callUUID {
CallController.shared.endCall(callUUID: callUUID)
}
m.activeCall = nil
m.activeCallViewIsCollapsed = false
}
@@ -203,14 +182,6 @@ struct ActiveCallView: View {
CallSoundsPlayer.shared.vibrate(long: false)
wasConnected = true
}
case let .peerMedia(source, enabled):
switch source {
case .mic: call.peerMediaSources.mic = enabled
case .camera: call.peerMediaSources.camera = enabled
case .screenAudio: call.peerMediaSources.screenAudio = enabled
case .screenVideo: call.peerMediaSources.screenVideo = enabled
case .unknown: ()
}
case .ended:
closeCallView(client)
call.callState = .ended
@@ -255,26 +226,6 @@ struct ActiveCallView: View {
}
}
private func askRequiredPermissions() async {
let mic = await WebRTCClient.isAuthorized(for: .audio)
await MainActor.run {
call.localMediaSources.mic = mic
}
let cameraAuthorized = AVCaptureDevice.authorizationStatus(for: .video) == .authorized
var camera = call.initialCallType == .audio || cameraAuthorized
if call.initialCallType == .video && !cameraAuthorized {
camera = await WebRTCClient.isAuthorized(for: .video)
await MainActor.run {
if camera, let client {
client.setCameraEnabled(true)
}
}
}
if !mic || !camera {
WebRTCClient.showUnauthorizedAlert(for: !mic ? .audio : .video)
}
}
private func closeCallView(_ client: WebRTCClient) {
if m.activeCall != nil {
m.showCallView = false
@@ -290,16 +241,44 @@ struct ActiveCallOverlay: View {
var body: some View {
VStack {
switch call.hasVideo {
case true:
switch call.localMedia {
case .video:
videoCallInfoView(call)
.foregroundColor(.white)
.opacity(0.8)
.padding(.horizontal)
// Fixed vertical padding required for preserving position of buttons row when changing audio-to-video and back in landscape orientation.
// Otherwise, bigger padding is added by SwiftUI when switching call types
.padding(.vertical, 10)
case false:
.padding()
Spacer()
HStack {
toggleAudioButton()
Spacer()
if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) {
toggleSpeakerButton()
.frame(width: 40, height: 40)
} else if call.hasMedia {
AudioDevicePicker()
.scaleEffect(2)
.frame(maxWidth: 40, maxHeight: 40)
} else {
Color.clear.frame(width: 40, height: 40)
}
Spacer()
endCallButton()
Spacer()
if call.videoEnabled {
flipCameraButton()
} else {
Color.clear.frame(width: 40, height: 40)
}
Spacer()
toggleVideoButton()
}
.padding(.horizontal, 20)
.padding(.bottom, 16)
.frame(maxWidth: .infinity, alignment: .center)
case .audio:
ZStack(alignment: .topLeading) {
Button {
chatModel.activeCallViewIsCollapsed = true
@@ -314,32 +293,35 @@ struct ActiveCallOverlay: View {
}
.foregroundColor(.white)
.opacity(0.8)
.padding(.horizontal)
.padding(.vertical, 10)
.padding()
.frame(maxHeight: .infinity)
}
}
Spacer()
Spacer()
HStack {
toggleMicButton()
Spacer()
audioDeviceButton()
Spacer()
endCallButton()
Spacer()
if call.localMediaSources.camera {
flipCameraButton()
} else {
Color.clear.frame(width: 60, height: 60)
ZStack(alignment: .bottom) {
toggleAudioButton()
.frame(maxWidth: .infinity, alignment: .leading)
endCallButton()
// Check if the only input is microphone. And in this case show toggle button,
// If there are more inputs, it probably means something like bluetooth headphones are available
// and in this case show iOS button for choosing different output.
// There is no way to get available outputs, only inputs
if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) {
toggleSpeakerButton()
.frame(maxWidth: .infinity, alignment: .trailing)
} else if call.hasMedia {
AudioDevicePicker()
.scaleEffect(2)
.frame(maxWidth: 50, maxHeight: 40)
.frame(maxWidth: .infinity, alignment: .trailing)
} else {
Color.clear.frame(width: 50, height: 40)
}
}
Spacer()
toggleCameraButton()
.padding(.bottom, 60)
.padding(.horizontal, 48)
}
.padding(.horizontal, 20)
.padding(.bottom, 16)
.frame(maxWidth: 440, alignment: .center)
}
.frame(maxWidth: .infinity)
.onAppear {
@@ -401,56 +383,36 @@ struct ActiveCallOverlay: View {
private func endCallButton() -> some View {
let cc = CallController.shared
return callButton("phone.down.fill", padding: 10) {
return callButton("phone.down.fill", width: 60, height: 60) {
if let uuid = call.callUUID {
cc.endCall(callUUID: uuid)
} else {
cc.endCall(call: call) {}
}
}
.background(.red)
.clipShape(.circle)
.foregroundColor(.red)
}
private func toggleMicButton() -> some View {
controlButton(call, call.localMediaSources.mic ? "mic.fill" : "mic.slash", padding: 14) {
private func toggleAudioButton() -> some View {
controlButton(call, call.audioEnabled ? "mic.fill" : "mic.slash") {
Task {
if await WebRTCClient.isAuthorized(for: .audio) {
client.setAudioEnabled(!call.localMediaSources.mic)
} else { WebRTCClient.showUnauthorizedAlert(for: .audio) }
}
}
}
func audioDeviceButton() -> some View {
// Check if the only input is microphone. And in this case show toggle button,
// If there are more inputs, it probably means something like bluetooth headphones are available
// and in this case show iOS button for choosing different output.
// There is no way to get available outputs, only inputs
Group {
if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) {
toggleSpeakerButton()
} else {
audioDevicePickerButton()
}
}
.onChange(of: call.localMediaSources.hasVideo) { hasVideo in
let current = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType
let speakerEnabled = current == .builtInSpeaker
let receiverEnabled = current == .builtInReceiver
// react automatically only when receiver were selected, otherwise keep an external device selected
if !speakerEnabled && hasVideo && receiverEnabled {
client.setSpeakerEnabledAndConfigureSession(!speakerEnabled, skipExternalDevice: true)
call.speakerEnabled = !speakerEnabled
client.setAudioEnabled(!call.audioEnabled)
DispatchQueue.main.async {
call.audioEnabled = !call.audioEnabled
}
}
}
}
private func toggleSpeakerButton() -> some View {
controlButton(call, !call.peerMediaSources.mic ? "speaker.slash" : call.speakerEnabled ? "speaker.wave.2.fill" : "speaker.wave.1.fill", padding: !call.peerMediaSources.mic ? 16 : call.speakerEnabled ? 15 : 17) {
let speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker
client.setSpeakerEnabledAndConfigureSession(!speakerEnabled)
call.speakerEnabled = !speakerEnabled
controlButton(call, call.speakerEnabled ? "speaker.wave.2.fill" : "speaker.wave.1.fill") {
Task {
let speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker
client.setSpeakerEnabledAndConfigureSession(!speakerEnabled)
DispatchQueue.main.async {
call.speakerEnabled = !speakerEnabled
}
}
}
.onAppear {
deviceManager.call = call
@@ -458,67 +420,53 @@ struct ActiveCallOverlay: View {
}
}
private func toggleCameraButton() -> some View {
controlButton(call, call.localMediaSources.camera ? "video.fill" : "video.slash", padding: call.localMediaSources.camera ? 16 : 14) {
private func toggleVideoButton() -> some View {
controlButton(call, call.videoEnabled ? "video.fill" : "video.slash") {
Task {
if await WebRTCClient.isAuthorized(for: .video) {
client.setCameraEnabled(!call.localMediaSources.camera)
} else { WebRTCClient.showUnauthorizedAlert(for: .video) }
client.setVideoEnabled(!call.videoEnabled)
DispatchQueue.main.async {
call.videoEnabled = !call.videoEnabled
}
}
}
.disabled(call.initialCallType == .audio && client.activeCall?.peerHasOldVersion == true)
}
@ViewBuilder private func flipCameraButton() -> some View {
controlButton(call, "arrow.triangle.2.circlepath", padding: 12) {
controlButton(call, "arrow.triangle.2.circlepath") {
Task {
if await WebRTCClient.isAuthorized(for: .video) {
client.flipCamera()
}
client.flipCamera()
}
}
}
@ViewBuilder private func controlButton(_ call: Call, _ imageName: String, padding: CGFloat, _ perform: @escaping () -> Void) -> some View {
callButton(imageName, padding: padding, perform)
.background(call.peerMediaSources.hasVideo ? Color.black.opacity(0.2) : Color.white.opacity(0.2))
.clipShape(.circle)
@ViewBuilder private func controlButton(_ call: Call, _ imageName: String, _ perform: @escaping () -> Void) -> some View {
if call.hasMedia {
callButton(imageName, width: 50, height: 38, perform)
.foregroundColor(.white)
.opacity(0.85)
} else {
Color.clear.frame(width: 50, height: 38)
}
}
@ViewBuilder private func audioDevicePickerButton() -> some View {
AudioDevicePicker()
.opacity(0.8)
.scaleEffect(2)
.padding(10)
.frame(width: 60, height: 60)
.background(call.peerMediaSources.hasVideo ? Color.black.opacity(0.2) : Color.white.opacity(0.2))
.clipShape(.circle)
}
private func callButton(_ imageName: String, padding: CGFloat, _ perform: @escaping () -> Void) -> some View {
private func callButton(_ imageName: String, width: CGFloat, height: CGFloat, _ perform: @escaping () -> Void) -> some View {
Button {
perform()
} label: {
Image(systemName: imageName)
.resizable()
.scaledToFit()
.padding(padding)
.frame(width: 60, height: 60)
.frame(maxWidth: width, maxHeight: height)
}
.foregroundColor(whiteColorWithAlpha)
}
private var whiteColorWithAlpha: Color {
get { Color(red: 204 / 255, green: 204 / 255, blue: 204 / 255) }
}
}
struct ActiveCallOverlay_Previews: PreviewProvider {
static var previews: some View {
Group{
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, initialCallType: .video), client: WebRTCClient({ _ in }, Binding.constant(nil)))
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .video), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
.background(.black)
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, initialCallType: .audio), client: WebRTCClient({ _ in }, Binding.constant(nil)))
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .audio), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
.background(.black)
}
}
@@ -104,7 +104,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
}
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
if callManager.enableMedia(source: .mic, enable: !action.isMuted, callUUID: action.callUUID.uuidString.lowercased()) {
if callManager.enableMedia(media: .audio, enable: !action.isMuted, callUUID: action.callUUID.uuidString.lowercased()) {
action.fulfill()
} else {
action.fail()
@@ -121,8 +121,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
RTCAudioSession.sharedInstance().audioSessionDidActivate(audioSession)
RTCAudioSession.sharedInstance().isAudioEnabled = true
do {
let hasVideo = ChatModel.shared.activeCall?.hasVideo == true
if hasVideo {
let supportsVideo = ChatModel.shared.activeCall?.supportsVideo == true
if supportsVideo {
try audioSession.setCategory(.playAndRecord, mode: .videoChat, options: [.defaultToSpeaker, .mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
} else {
try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
@@ -133,7 +133,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
try? await Task.sleep(nanoseconds: UInt64(i) * 300_000000)
if let preferred = audioSession.preferredInputDevice() {
await MainActor.run { try? audioSession.setPreferredInput(preferred) }
} else if hasVideo {
} else if supportsVideo {
await MainActor.run { try? audioSession.overrideOutputAudioPort(.speaker) }
}
}
+5 -5
View File
@@ -12,7 +12,7 @@ import SimpleXChat
class CallManager {
func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> String {
let uuid = UUID().uuidString.lowercased()
let call = Call(direction: .outgoing, contact: contact, callUUID: uuid, callState: .waitCapabilities, initialCallType: media)
let call = Call(direction: .outgoing, contact: contact, callUUID: uuid, callState: .waitCapabilities, localMedia: media)
call.speakerEnabled = media == .video
ChatModel.shared.activeCall = call
return uuid
@@ -22,7 +22,7 @@ class CallManager {
let m = ChatModel.shared
if let call = m.activeCall, call.callUUID == callUUID {
m.showCallView = true
Task { await m.callCommand.processCommand(.capabilities(media: call.initialCallType)) }
Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) }
return true
}
return false
@@ -44,7 +44,7 @@ class CallManager {
contact: invitation.contact,
callUUID: invitation.callUUID,
callState: .invitationAccepted,
initialCallType: invitation.callType.media,
localMedia: invitation.callType.media,
sharedKey: invitation.sharedKey
)
call.speakerEnabled = invitation.callType.media == .video
@@ -68,10 +68,10 @@ class CallManager {
}
}
func enableMedia(source: CallMediaSource, enable: Bool, callUUID: String) -> Bool {
func enableMedia(media: CallMediaType, enable: Bool, callUUID: String) -> Bool {
if let call = ChatModel.shared.activeCall, call.callUUID == callUUID {
let m = ChatModel.shared
Task { await m.callCommand.processCommand(.media(source: source, enable: enable)) }
Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) }
return true
}
return false
@@ -10,43 +10,40 @@ import AVKit
struct CallViewRemote: UIViewRepresentable {
var client: WebRTCClient
@ObservedObject var call: Call
var activeCall: Binding<WebRTCClient.Call?>
@State var enablePip: (Bool) -> Void = {_ in }
@Binding var activeCallViewIsCollapsed: Bool
@Binding var contentMode: UIView.ContentMode
@Binding var pipShown: Bool
init(client: WebRTCClient, activeCall: Binding<WebRTCClient.Call?>, activeCallViewIsCollapsed: Binding<Bool>, pipShown: Binding<Bool>) {
self.client = client
self.activeCall = activeCall
self._activeCallViewIsCollapsed = activeCallViewIsCollapsed
self._pipShown = pipShown
}
func makeUIView(context: Context) -> UIView {
let view = UIView()
let remoteCameraRenderer = RTCMTLVideoView(frame: view.frame)
remoteCameraRenderer.videoContentMode = contentMode
remoteCameraRenderer.tag = 0
let remoteScreenRenderer = RTCMTLVideoView(frame: view.frame)
remoteScreenRenderer.videoContentMode = contentMode
remoteScreenRenderer.tag = 1
remoteScreenRenderer.alpha = call.peerMediaSources.screenVideo ? 1 : 0
if let call = activeCall.wrappedValue {
let remoteRenderer = RTCMTLVideoView(frame: view.frame)
remoteRenderer.videoContentMode = .scaleAspectFill
client.addRemoteRenderer(call, remoteRenderer)
addSubviewAndResize(remoteRenderer, into: view)
context.coordinator.cameraRenderer = remoteCameraRenderer
context.coordinator.screenRenderer = remoteScreenRenderer
client.addRemoteCameraRenderer(remoteCameraRenderer)
client.addRemoteScreenRenderer(remoteScreenRenderer)
addSubviewAndResize(remoteCameraRenderer, remoteScreenRenderer, into: view)
if AVPictureInPictureController.isPictureInPictureSupported() {
makeViewWithRTCRenderer(remoteCameraRenderer, remoteScreenRenderer, view, context)
if AVPictureInPictureController.isPictureInPictureSupported() {
makeViewWithRTCRenderer(call, remoteRenderer, view, context)
}
}
return view
}
func makeViewWithRTCRenderer(_ remoteCameraRenderer: RTCMTLVideoView, _ remoteScreenRenderer: RTCMTLVideoView, _ view: UIView, _ context: Context) {
let pipRemoteCameraRenderer = RTCMTLVideoView(frame: view.frame)
pipRemoteCameraRenderer.videoContentMode = .scaleAspectFill
let pipRemoteScreenRenderer = RTCMTLVideoView(frame: view.frame)
pipRemoteScreenRenderer.videoContentMode = .scaleAspectFill
func makeViewWithRTCRenderer(_ call: WebRTCClient.Call, _ remoteRenderer: RTCMTLVideoView, _ view: UIView, _ context: Context) {
let pipRemoteRenderer = RTCMTLVideoView(frame: view.frame)
pipRemoteRenderer.videoContentMode = .scaleAspectFill
let pipVideoCallViewController = AVPictureInPictureVideoCallViewController()
pipVideoCallViewController.preferredContentSize = CGSize(width: 1080, height: 1920)
addSubviewAndResize(pipRemoteRenderer, into: pipVideoCallViewController.view)
let pipContentSource = AVPictureInPictureController.ContentSource(
activeVideoCallSourceView: view,
contentViewController: pipVideoCallViewController
@@ -58,9 +55,7 @@ struct CallViewRemote: UIViewRepresentable {
context.coordinator.pipController = pipController
context.coordinator.willShowHide = { show in
if show {
client.addRemoteCameraRenderer(pipRemoteCameraRenderer)
client.addRemoteScreenRenderer(pipRemoteScreenRenderer)
context.coordinator.relayout()
client.addRemoteRenderer(call, pipRemoteRenderer)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
activeCallViewIsCollapsed = true
}
@@ -72,29 +67,13 @@ struct CallViewRemote: UIViewRepresentable {
}
context.coordinator.didShowHide = { show in
if show {
remoteCameraRenderer.isHidden = true
remoteScreenRenderer.isHidden = true
remoteRenderer.isHidden = true
} else {
client.removeRemoteCameraRenderer(pipRemoteCameraRenderer)
client.removeRemoteScreenRenderer(pipRemoteScreenRenderer)
remoteCameraRenderer.isHidden = false
remoteScreenRenderer.isHidden = false
client.removeRemoteRenderer(call, pipRemoteRenderer)
remoteRenderer.isHidden = false
}
pipShown = show
}
context.coordinator.relayout = {
let camera = call.peerMediaSources.camera
let screenVideo = call.peerMediaSources.screenVideo
pipRemoteCameraRenderer.alpha = camera ? 1 : 0
pipRemoteScreenRenderer.alpha = screenVideo ? 1 : 0
if screenVideo {
addSubviewAndResize(pipRemoteScreenRenderer, pipRemoteCameraRenderer, pip: true, into: pipVideoCallViewController.view)
} else {
addSubviewAndResize(pipRemoteCameraRenderer, pipRemoteScreenRenderer, pip: true, into: pipVideoCallViewController.view)
}
(pipVideoCallViewController.view.subviews[0] as! RTCMTLVideoView).videoContentMode = contentMode
(pipVideoCallViewController.view.subviews[1] as! RTCMTLVideoView).videoContentMode = .scaleAspectFill
}
DispatchQueue.main.async {
enablePip = { enable in
if enable != pipShown /* pipController.isPictureInPictureActive */ {
@@ -109,50 +88,24 @@ struct CallViewRemote: UIViewRepresentable {
}
func makeCoordinator() -> Coordinator {
Coordinator(client)
Coordinator()
}
func updateUIView(_ view: UIView, context: Context) {
logger.debug("CallView.updateUIView remote")
let camera = view.subviews.first(where: { $0.tag == 0 })!
let screen = view.subviews.first(where: { $0.tag == 1 })!
let screenVideo = call.peerMediaSources.screenVideo
if screenVideo && screen.alpha == 0 {
screen.alpha = 1
addSubviewAndResize(screen, camera, into: view)
} else if !screenVideo && screen.alpha == 1 {
screen.alpha = 0
addSubviewAndResize(camera, screen, into: view)
}
(view.subviews[0] as! RTCMTLVideoView).videoContentMode = contentMode
(view.subviews[1] as! RTCMTLVideoView).videoContentMode = .scaleAspectFill
camera.alpha = call.peerMediaSources.camera ? 1 : 0
screen.alpha = call.peerMediaSources.screenVideo ? 1 : 0
DispatchQueue.main.async {
if activeCallViewIsCollapsed != pipShown {
enablePip(activeCallViewIsCollapsed)
} else if pipShown {
context.coordinator.relayout()
}
}
}
// MARK: - Coordinator
class Coordinator: NSObject, AVPictureInPictureControllerDelegate {
var cameraRenderer: RTCMTLVideoView?
var screenRenderer: RTCMTLVideoView?
var client: WebRTCClient
var pipController: AVPictureInPictureController? = nil
var willShowHide: (Bool) -> Void = { _ in }
var didShowHide: (Bool) -> Void = { _ in }
var relayout: () -> Void = {}
required init(_ client: WebRTCClient) {
self.client = client
}
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
willShowHide(true)
}
@@ -174,20 +127,11 @@ struct CallViewRemote: UIViewRepresentable {
}
deinit {
// TODO: deinit is not called when changing call type from audio to video and back,
// which causes many renderers can be created and added to stream (if enabling/disabling
// video while not yet connected in outgoing call)
pipController?.stopPictureInPicture()
pipController?.canStartPictureInPictureAutomaticallyFromInline = false
pipController?.contentSource = nil
pipController?.delegate = nil
pipController = nil
if let cameraRenderer {
client.removeRemoteCameraRenderer(cameraRenderer)
}
if let screenRenderer {
client.removeRemoteScreenRenderer(screenRenderer)
}
}
}
@@ -204,109 +148,51 @@ struct CallViewRemote: UIViewRepresentable {
struct CallViewLocal: UIViewRepresentable {
var client: WebRTCClient
var activeCall: Binding<WebRTCClient.Call?>
var localRendererAspectRatio: Binding<CGFloat?>
@State var pipStateChanged: (Bool) -> Void = {_ in }
@Binding var pipShown: Bool
init(client: WebRTCClient, localRendererAspectRatio: Binding<CGFloat?>, pipShown: Binding<Bool>) {
init(client: WebRTCClient, activeCall: Binding<WebRTCClient.Call?>, localRendererAspectRatio: Binding<CGFloat?>, pipShown: Binding<Bool>) {
self.client = client
self.activeCall = activeCall
self.localRendererAspectRatio = localRendererAspectRatio
self._pipShown = pipShown
}
func makeUIView(context: Context) -> UIView {
let view = UIView()
let localRenderer = RTCEAGLVideoView(frame: .zero)
context.coordinator.renderer = localRenderer
client.addLocalRenderer(localRenderer)
addSubviewAndResize(localRenderer, nil, into: view)
DispatchQueue.main.async {
pipStateChanged = { shown in
localRenderer.isHidden = shown
if let call = activeCall.wrappedValue {
let localRenderer = RTCEAGLVideoView(frame: .zero)
client.addLocalRenderer(call, localRenderer)
client.startCaptureLocalVideo(call)
addSubviewAndResize(localRenderer, into: view)
DispatchQueue.main.async {
pipStateChanged = { shown in
localRenderer.isHidden = shown
}
}
}
return view
}
func makeCoordinator() -> Coordinator {
Coordinator(client)
}
func updateUIView(_ view: UIView, context: Context) {
logger.debug("CallView.updateUIView local")
pipStateChanged(pipShown)
}
// MARK: - Coordinator
class Coordinator: NSObject, AVPictureInPictureControllerDelegate {
var renderer: RTCEAGLVideoView?
var client: WebRTCClient
required init(_ client: WebRTCClient) {
self.client = client
}
deinit {
if let renderer {
client.removeLocalRenderer(renderer)
}
}
}
}
private func addSubviewAndResize(_ fullscreen: UIView, _ end: UIView?, pip: Bool = false, into containerView: UIView) {
if containerView.subviews.firstIndex(of: fullscreen) == 0 && ((end == nil && containerView.subviews.count == 1) || (end != nil && containerView.subviews.firstIndex(of: end!) == 1)) {
// Nothing to do, elements on their places
return
}
containerView.removeConstraints(containerView.constraints)
containerView.subviews.forEach { sub in sub.removeFromSuperview()}
containerView.addSubview(fullscreen)
fullscreen.translatesAutoresizingMaskIntoConstraints = false
fullscreen.layer.cornerRadius = 0
fullscreen.layer.masksToBounds = false
if let end {
containerView.addSubview(end)
end.translatesAutoresizingMaskIntoConstraints = false
end.layer.cornerRadius = pip ? 8 : 10
end.layer.masksToBounds = true
}
let constraintFullscreenV = NSLayoutConstraint.constraints(
withVisualFormat: "V:|[fullscreen]|",
private func addSubviewAndResize(_ view: UIView, into containerView: UIView) {
containerView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|",
options: [],
metrics: nil,
views: ["fullscreen": fullscreen]
)
let constraintFullscreenH = NSLayoutConstraint.constraints(
withVisualFormat: "H:|[fullscreen]|",
views: ["view": view]))
containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|",
options: [],
metrics: nil,
views: ["fullscreen": fullscreen]
)
containerView.addConstraints(constraintFullscreenV)
containerView.addConstraints(constraintFullscreenH)
if let end {
let constraintEndWidth = NSLayoutConstraint(
item: end, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: pip ? 0.5 : 0.3, constant: 0
)
let constraintEndHeight = NSLayoutConstraint(
item: end, attribute: .height, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: pip ? 0.5 * 1.33 : 0.3 * 1.33, constant: 0
)
let constraintEndX = NSLayoutConstraint(
item: end, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .trailing, multiplier: pip ? 0.5 : 0.7, constant: pip ? -8 : -17
)
let constraintEndY = NSLayoutConstraint(
item: end, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1, constant: pip ? -8 : -92
)
containerView.addConstraint(constraintEndWidth)
containerView.addConstraint(constraintEndHeight)
containerView.addConstraint(constraintEndX)
containerView.addConstraint(constraintEndY)
}
views: ["view": view]))
containerView.layoutIfNeeded()
}
+17 -39
View File
@@ -19,13 +19,14 @@ class Call: ObservableObject, Equatable {
var direction: CallDirection
var contact: Contact
var callUUID: String?
var initialCallType: CallMediaType
@Published var localMediaSources: CallMediaSources
var localMedia: CallMediaType
@Published var callState: CallState
@Published var localCapabilities: CallCapabilities?
@Published var peerMediaSources: CallMediaSources = CallMediaSources()
@Published var peerMedia: CallMediaType?
@Published var sharedKey: String?
@Published var audioEnabled = true
@Published var speakerEnabled = false
@Published var videoEnabled: Bool
@Published var connectionInfo: ConnectionInfo?
@Published var connectedAt: Date? = nil
@@ -34,33 +35,32 @@ class Call: ObservableObject, Equatable {
contact: Contact,
callUUID: String?,
callState: CallState,
initialCallType: CallMediaType,
localMedia: CallMediaType,
sharedKey: String? = nil
) {
self.direction = direction
self.contact = contact
self.callUUID = callUUID
self.callState = callState
self.initialCallType = initialCallType
self.localMedia = localMedia
self.sharedKey = sharedKey
self.localMediaSources = CallMediaSources(
mic: AVCaptureDevice.authorizationStatus(for: .audio) == .authorized,
camera: initialCallType == .video && AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
self.videoEnabled = localMedia == .video
}
var encrypted: Bool { get { localEncrypted && sharedKey != nil } }
private var localEncrypted: Bool { get { localCapabilities?.encryption ?? false } }
var localEncrypted: Bool { get { localCapabilities?.encryption ?? false } }
var encryptionStatus: LocalizedStringKey {
get {
switch callState {
case .waitCapabilities: return ""
case .invitationSent: return localEncrypted ? "e2e encrypted" : "no e2e encryption"
case .invitationAccepted: return sharedKey == nil ? "contact has no e2e encryption" : "contact has e2e encryption"
default: return !localEncrypted ? "no e2e encryption" : sharedKey == nil ? "contact has no e2e encryption" : "e2e encrypted"
default: return !localEncrypted ? "no e2e encryption" : sharedKey == nil ? "contact has no e2e encryption" : "e2e encrypted"
}
}
}
var hasVideo: Bool { get { localMediaSources.hasVideo || peerMediaSources.hasVideo } }
var hasMedia: Bool { get { callState == .offerSent || callState == .negotiated || callState == .connected } }
var supportsVideo: Bool { get { peerMedia == .video || localMedia == .video } }
}
enum CallDirection {
@@ -105,28 +105,18 @@ struct WVAPIMessage: Equatable, Decodable, Encodable {
var command: WCallCommand?
}
struct CallMediaSources: Equatable, Codable {
var mic: Bool = false
var camera: Bool = false
var screenAudio: Bool = false
var screenVideo: Bool = false
var hasVideo: Bool { get { camera || screenVideo } }
}
enum WCallCommand: Equatable, Encodable, Decodable {
case capabilities(media: CallMediaType)
case start(media: CallMediaType, aesKey: String? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil)
case offer(offer: String, iceCandidates: String, media: CallMediaType, aesKey: String? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil)
case answer(answer: String, iceCandidates: String)
case ice(iceCandidates: String)
case media(source: CallMediaSource, enable: Bool)
case media(media: CallMediaType, enable: Bool)
case end
enum CodingKeys: String, CodingKey {
case type
case media
case source
case aesKey
case offer
case answer
@@ -177,9 +167,9 @@ enum WCallCommand: Equatable, Encodable, Decodable {
case let .ice(iceCandidates):
try container.encode("ice", forKey: .type)
try container.encode(iceCandidates, forKey: .iceCandidates)
case let .media(source, enable):
case let .media(media, enable):
try container.encode("media", forKey: .type)
try container.encode(source, forKey: .media)
try container.encode(media, forKey: .media)
try container.encode(enable, forKey: .enable)
case .end:
try container.encode("end", forKey: .type)
@@ -215,9 +205,9 @@ enum WCallCommand: Equatable, Encodable, Decodable {
let iceCandidates = try container.decode(String.self, forKey: CodingKeys.iceCandidates)
self = .ice(iceCandidates: iceCandidates)
case "media":
let source = try container.decode(CallMediaSource.self, forKey: CodingKeys.source)
let media = try container.decode(CallMediaType.self, forKey: CodingKeys.media)
let enable = try container.decode(Bool.self, forKey: CodingKeys.enable)
self = .media(source: source, enable: enable)
self = .media(media: media, enable: enable)
case "end":
self = .end
default:
@@ -234,7 +224,6 @@ enum WCallResponse: Equatable, Decodable {
case ice(iceCandidates: String)
case connection(state: ConnectionState)
case connected(connectionInfo: ConnectionInfo)
case peerMedia(source: CallMediaSource, enabled: Bool)
case ended
case ok
case error(message: String)
@@ -249,8 +238,6 @@ enum WCallResponse: Equatable, Decodable {
case state
case connectionInfo
case message
case source
case enabled
}
var respType: String {
@@ -262,7 +249,6 @@ enum WCallResponse: Equatable, Decodable {
case .ice: return "ice"
case .connection: return "connection"
case .connected: return "connected"
case .peerMedia: return "peerMedia"
case .ended: return "ended"
case .ok: return "ok"
case .error: return "error"
@@ -297,10 +283,6 @@ enum WCallResponse: Equatable, Decodable {
case "connected":
let connectionInfo = try container.decode(ConnectionInfo.self, forKey: CodingKeys.connectionInfo)
self = .connected(connectionInfo: connectionInfo)
case "peerMedia":
let source = try container.decode(CallMediaSource.self, forKey: CodingKeys.source)
let enabled = try container.decode(Bool.self, forKey: CodingKeys.enabled)
self = .peerMedia(source: source, enabled: enabled)
case "ended":
self = .ended
case "ok":
@@ -342,10 +324,6 @@ extension WCallResponse: Encodable {
case let .connected(connectionInfo):
try container.encode("connected", forKey: .type)
try container.encode(connectionInfo, forKey: .connectionInfo)
case let .peerMedia(source, enabled):
try container.encode("peerMedia", forKey: .type)
try container.encode(source, forKey: .source)
try container.encode(enabled, forKey: .enabled)
case .ended:
try container.encode("ended", forKey: .type)
case .ok:
@@ -398,7 +376,7 @@ actor WebRTCCommandProcessor {
func shouldRunCommand(_ client: WebRTCClient, _ c: WCallCommand) -> Bool {
switch c {
case .capabilities, .start, .offer, .end: true
default: client.activeCall != nil
default: client.activeCall.wrappedValue != nil
}
}
}
+103 -415
View File
@@ -23,24 +23,15 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
struct Call {
var connection: RTCPeerConnection
var iceCandidates: IceCandidates
var localMedia: CallMediaType
var localCamera: RTCVideoCapturer?
var localAudioTrack: RTCAudioTrack?
var localVideoTrack: RTCVideoTrack?
var remoteAudioTrack: RTCAudioTrack?
var remoteVideoTrack: RTCVideoTrack?
var remoteScreenAudioTrack: RTCAudioTrack?
var remoteScreenVideoTrack: RTCVideoTrack?
var device: AVCaptureDevice.Position
var localVideoSource: RTCVideoSource?
var localStream: RTCVideoTrack?
var remoteStream: RTCVideoTrack?
var device: AVCaptureDevice.Position = .front
var aesKey: String?
var frameEncryptor: RTCFrameEncryptor?
var frameDecryptor: RTCFrameDecryptor?
var peerHasOldVersion: Bool
}
struct NotConnectedCall {
var audioTrack: RTCAudioTrack?
var localCameraAndTrack: (RTCVideoCapturer, RTCVideoTrack)?
var device: AVCaptureDevice.Position = .front
}
actor IceCandidates {
@@ -60,20 +51,17 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
private let rtcAudioSession = RTCAudioSession.sharedInstance()
private let audioQueue = DispatchQueue(label: "chat.simplex.app.audio")
private var sendCallResponse: (WVAPIMessage) async -> Void
var activeCall: Call?
var notConnectedCall: NotConnectedCall?
var activeCall: Binding<Call?>
private var localRendererAspectRatio: Binding<CGFloat?>
var cameraRenderers: [RTCVideoRenderer] = []
var screenRenderers: [RTCVideoRenderer] = []
@available(*, unavailable)
override init() {
fatalError("Unimplemented")
}
required init(_ sendCallResponse: @escaping (WVAPIMessage) async -> Void, _ localRendererAspectRatio: Binding<CGFloat?>) {
required init(_ activeCall: Binding<Call?>, _ sendCallResponse: @escaping (WVAPIMessage) async -> Void, _ localRendererAspectRatio: Binding<CGFloat?>) {
self.sendCallResponse = sendCallResponse
self.activeCall = activeCall
self.localRendererAspectRatio = localRendererAspectRatio
rtcAudioSession.useManualAudio = CallController.useCallKit()
rtcAudioSession.isAudioEnabled = !CallController.useCallKit()
@@ -90,45 +78,39 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call {
let connection = createPeerConnection(iceServers ?? getWebRTCIceServers() ?? defaultIceServers, relay)
connection.delegate = self
let device = notConnectedCall?.device ?? .front
createAudioSender(connection)
var localStream: RTCVideoTrack? = nil
var remoteStream: RTCVideoTrack? = nil
var localCamera: RTCVideoCapturer? = nil
var localAudioTrack: RTCAudioTrack? = nil
var localVideoTrack: RTCVideoTrack? = nil
if let localCameraAndTrack = notConnectedCall?.localCameraAndTrack {
(localCamera, localVideoTrack) = localCameraAndTrack
} else if notConnectedCall == nil && mediaType == .video {
(localCamera, localVideoTrack) = createVideoTrackAndStartCapture(device)
var localVideoSource: RTCVideoSource? = nil
if mediaType == .video {
(localStream, remoteStream, localCamera, localVideoSource) = createVideoSender(connection)
}
if let audioTrack = notConnectedCall?.audioTrack {
localAudioTrack = audioTrack
} else if notConnectedCall == nil {
localAudioTrack = createAudioTrack()
}
notConnectedCall?.localCameraAndTrack = nil
notConnectedCall?.audioTrack = nil
var frameEncryptor: RTCFrameEncryptor? = nil
var frameDecryptor: RTCFrameDecryptor? = nil
if aesKey != nil {
let encryptor = RTCFrameEncryptor.init(sizeChange: Int32(WebRTCClient.ivTagBytes))
encryptor.delegate = self
frameEncryptor = encryptor
connection.senders.forEach { $0.setRtcFrameEncryptor(encryptor) }
let decryptor = RTCFrameDecryptor.init(sizeChange: -Int32(WebRTCClient.ivTagBytes))
decryptor.delegate = self
frameDecryptor = decryptor
// Has no video receiver in outgoing call if applied here, see [peerConnection(_ connection: RTCPeerConnection, didChange newState]
// connection.receivers.forEach { $0.setRtcFrameDecryptor(decryptor) }
}
return Call(
connection: connection,
iceCandidates: IceCandidates(),
localMedia: mediaType,
localCamera: localCamera,
localAudioTrack: localAudioTrack,
localVideoTrack: localVideoTrack,
device: device,
localVideoSource: localVideoSource,
localStream: localStream,
remoteStream: remoteStream,
aesKey: aesKey,
frameEncryptor: frameEncryptor,
frameDecryptor: frameDecryptor,
peerHasOldVersion: false
frameDecryptor: frameDecryptor
)
}
@@ -169,24 +151,18 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
func sendCallCommand(command: WCallCommand) async {
var resp: WCallResponse? = nil
let pc = activeCall?.connection
let pc = activeCall.wrappedValue?.connection
switch command {
case let .capabilities(media): // outgoing
let localCameraAndTrack: (RTCVideoCapturer, RTCVideoTrack)? = media == .video
? createVideoTrackAndStartCapture(.front)
: nil
notConnectedCall = NotConnectedCall(audioTrack: createAudioTrack(), localCameraAndTrack: localCameraAndTrack, device: .front)
case .capabilities:
resp = .capabilities(capabilities: CallCapabilities(encryption: WebRTCClient.enableEncryption))
case let .start(media: media, aesKey, iceServers, relay): // incoming
case let .start(media: media, aesKey, iceServers, relay):
logger.debug("starting incoming call - create webrtc session")
if activeCall != nil { endCall() }
if activeCall.wrappedValue != nil { endCall() }
let encryption = WebRTCClient.enableEncryption
let call = initializeCall(iceServers?.toWebRTCIceServers(), media, encryption ? aesKey : nil, relay)
activeCall = call
setupLocalTracks(true, call)
activeCall.wrappedValue = call
let (offer, error) = await call.connection.offer()
if let offer = offer {
setupEncryptionForLocalTracks(call)
resp = .offer(
offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: offer.type.toSdpType(), sdp: offer.sdp))),
iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates())),
@@ -196,24 +172,18 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
} else {
resp = .error(message: "offer error: \(error?.localizedDescription ?? "unknown error")")
}
case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay): // outgoing
if activeCall != nil {
case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay):
if activeCall.wrappedValue != nil {
resp = .error(message: "accept: call already started")
} else if !WebRTCClient.enableEncryption && aesKey != nil {
resp = .error(message: "accept: encryption is not supported")
} else if let offer: CustomRTCSessionDescription = decodeJSON(decompressFromBase64(input: offer)),
let remoteIceCandidates: [RTCIceCandidate] = decodeJSON(decompressFromBase64(input: iceCandidates)) {
let call = initializeCall(iceServers?.toWebRTCIceServers(), media, WebRTCClient.enableEncryption ? aesKey : nil, relay)
activeCall = call
activeCall.wrappedValue = call
let pc = call.connection
if let type = offer.type, let sdp = offer.sdp {
if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil {
setupLocalTracks(false, call)
setupEncryptionForLocalTracks(call)
pc.transceivers.forEach { transceiver in
transceiver.setDirection(.sendRecv, error: nil)
}
await adaptToOldVersion(pc.transceivers.count <= 2)
let (answer, error) = await pc.answer()
if let answer = answer {
self.addIceCandidates(pc, remoteIceCandidates)
@@ -230,7 +200,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
}
}
}
case let .answer(answer, iceCandidates): // incoming
case let .answer(answer, iceCandidates):
if pc == nil {
resp = .error(message: "answer: call not started")
} else if pc?.localDescription == nil {
@@ -242,9 +212,6 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
let type = answer.type, let sdp = answer.sdp,
let pc = pc {
if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil {
var currentDirection: RTCRtpTransceiverDirection = .sendOnly
pc.transceivers[2].currentDirection(&currentDirection)
await adaptToOldVersion(currentDirection == .sendOnly)
addIceCandidates(pc, remoteIceCandidates)
resp = .ok
} else {
@@ -259,11 +226,13 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
} else {
resp = .error(message: "ice: call not started")
}
case let .media(source, enable):
if activeCall == nil {
case let .media(media, enable):
if activeCall.wrappedValue == nil {
resp = .error(message: "media: call not started")
} else if activeCall.wrappedValue?.localMedia == .audio && media == .video {
resp = .error(message: "media: no video")
} else {
await enableMedia(source, enable)
enableMedia(media, enable)
resp = .ok
}
case .end:
@@ -278,7 +247,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
func getInitialIceCandidates() async -> [RTCIceCandidate] {
await untilIceComplete(timeoutMs: 750, stepMs: 150) {}
let candidates = await activeCall?.iceCandidates.getAndClear() ?? []
let candidates = await activeCall.wrappedValue?.iceCandidates.getAndClear() ?? []
logger.debug("WebRTCClient: sending initial ice candidates: \(candidates.count)")
return candidates
}
@@ -286,7 +255,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
func waitForMoreIceCandidates() {
Task {
await untilIceComplete(timeoutMs: 12000, stepMs: 1500) {
let candidates = await self.activeCall?.iceCandidates.getAndClear() ?? []
let candidates = await self.activeCall.wrappedValue?.iceCandidates.getAndClear() ?? []
if candidates.count > 0 {
logger.debug("WebRTCClient: sending more ice candidates: \(candidates.count)")
await self.sendIceCandidates(candidates)
@@ -303,203 +272,25 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
)
}
func setupMuteUnmuteListener(_ transceiver: RTCRtpTransceiver, _ track: RTCMediaStreamTrack) {
// logger.log("Setting up mute/unmute listener in the call without encryption for mid = \(transceiver.mid)")
Task {
// for some reason even for disabled tracks one packet arrives (seeing this on screenVideo track)
var lastPacketsReceived = 1
// muted initially
var mutedSeconds = 4
while let call = self.activeCall, transceiver.receiver.track?.readyState == .live {
let stats: RTCStatisticsReport = await call.connection.statistics(for: transceiver.receiver)
let stat = stats.statistics.values.first(where: { stat in stat.type == "inbound-rtp"})
if let stat {
//logger.debug("Stat \(stat.debugDescription)")
let packets = stat.values["packetsReceived"] as! Int
if packets <= lastPacketsReceived {
mutedSeconds += 1
if mutedSeconds == 3 {
await MainActor.run {
self.onMediaMuteUnmute(transceiver.mid, true)
}
}
} else {
if mutedSeconds >= 3 {
await MainActor.run {
self.onMediaMuteUnmute(transceiver.mid, false)
}
}
lastPacketsReceived = packets
mutedSeconds = 0
}
}
try? await Task.sleep(nanoseconds: 1000_000000)
}
}
func enableMedia(_ media: CallMediaType, _ enable: Bool) {
logger.debug("WebRTCClient: enabling media \(media.rawValue) \(enable)")
media == .video ? setVideoEnabled(enable) : setAudioEnabled(enable)
}
@MainActor
func onMediaMuteUnmute(_ transceiverMid: String?, _ mute: Bool) {
guard let activeCall = ChatModel.shared.activeCall else { return }
let source = mediaSourceFromTransceiverMid(transceiverMid)
logger.log("Mute/unmute \(source.rawValue) track = \(mute) with mid = \(transceiverMid ?? "nil")")
if source == .mic && activeCall.peerMediaSources.mic == mute {
activeCall.peerMediaSources.mic = !mute
} else if (source == .camera && activeCall.peerMediaSources.camera == mute) {
activeCall.peerMediaSources.camera = !mute
} else if (source == .screenAudio && activeCall.peerMediaSources.screenAudio == mute) {
activeCall.peerMediaSources.screenAudio = !mute
} else if (source == .screenVideo && activeCall.peerMediaSources.screenVideo == mute) {
activeCall.peerMediaSources.screenVideo = !mute
}
}
@MainActor
func enableMedia(_ source: CallMediaSource, _ enable: Bool) {
logger.debug("WebRTCClient: enabling media \(source.rawValue) \(enable)")
source == .camera ? setCameraEnabled(enable) : setAudioEnabled(enable)
}
@MainActor
func adaptToOldVersion(_ peerHasOldVersion: Bool) {
activeCall?.peerHasOldVersion = peerHasOldVersion
if peerHasOldVersion {
logger.debug("The peer has an old version. Remote audio track is nil = \(self.activeCall?.remoteAudioTrack == nil), video = \(self.activeCall?.remoteVideoTrack == nil)")
onMediaMuteUnmute("0", false)
if activeCall?.remoteVideoTrack != nil {
onMediaMuteUnmute("1", false)
}
if ChatModel.shared.activeCall?.localMediaSources.camera == true && ChatModel.shared.activeCall?.peerMediaSources.camera == false {
logger.debug("Stopping video track for the old version")
activeCall?.connection.senders[1].track = nil
ChatModel.shared.activeCall?.localMediaSources.camera = false
(activeCall?.localCamera as? RTCCameraVideoCapturer)?.stopCapture()
activeCall?.localCamera = nil
activeCall?.localVideoTrack = nil
}
}
}
func addLocalRenderer(_ renderer: RTCEAGLVideoView) {
if let activeCall {
if let track = activeCall.localVideoTrack {
track.add(renderer)
}
} else if let notConnectedCall {
if let track = notConnectedCall.localCameraAndTrack?.1 {
track.add(renderer)
}
}
func addLocalRenderer(_ activeCall: Call, _ renderer: RTCEAGLVideoView) {
activeCall.localStream?.add(renderer)
// To get width and height of a frame, see videoView(videoView:, didChangeVideoSize)
renderer.delegate = self
}
func removeLocalRenderer(_ renderer: RTCEAGLVideoView) {
if let activeCall {
if let track = activeCall.localVideoTrack {
track.remove(renderer)
}
} else if let notConnectedCall {
if let track = notConnectedCall.localCameraAndTrack?.1 {
track.remove(renderer)
}
}
renderer.delegate = nil
}
func videoView(_ videoView: RTCVideoRenderer, didChangeVideoSize size: CGSize) {
guard size.height > 0 else { return }
localRendererAspectRatio.wrappedValue = size.width / size.height
}
func setupLocalTracks(_ incomingCall: Bool, _ call: Call) {
let pc = call.connection
let transceivers = call.connection.transceivers
let audioTrack = call.localAudioTrack
let videoTrack = call.localVideoTrack
if incomingCall {
let micCameraInit = RTCRtpTransceiverInit()
// streamIds required for old versions which adds tracks from stream, not from track property
micCameraInit.streamIds = ["micCamera"]
let screenAudioVideoInit = RTCRtpTransceiverInit()
screenAudioVideoInit.streamIds = ["screenAudioVideo"]
// incoming call, no transceivers yet. But they should be added in order: mic, camera, screen audio, screen video
// mid = 0, mic
if let audioTrack {
pc.addTransceiver(with: audioTrack, init: micCameraInit)
} else {
pc.addTransceiver(of: .audio, init: micCameraInit)
}
// mid = 1, camera
if let videoTrack {
pc.addTransceiver(with: videoTrack, init: micCameraInit)
} else {
pc.addTransceiver(of: .video, init: micCameraInit)
}
// mid = 2, screenAudio
pc.addTransceiver(of: .audio, init: screenAudioVideoInit)
// mid = 3, screenVideo
pc.addTransceiver(of: .video, init: screenAudioVideoInit)
} else {
// new version
if transceivers.count > 2 {
// Outgoing call. All transceivers are ready. Don't addTrack() because it will create new transceivers, replace existing (nil) tracks
transceivers
.first(where: { elem in mediaSourceFromTransceiverMid(elem.mid) == .mic })?
.sender.track = audioTrack
transceivers
.first(where: { elem in mediaSourceFromTransceiverMid(elem.mid) == .camera })?
.sender.track = videoTrack
} else {
// old version, only two transceivers
if let audioTrack {
pc.add(audioTrack, streamIds: ["micCamera"])
} else {
// it's important to have any track in order to be able to turn it on again (currently it's off)
let sender = pc.add(createAudioTrack(), streamIds: ["micCamera"])
sender?.track = nil
}
if let videoTrack {
pc.add(videoTrack, streamIds: ["micCamera"])
} else {
// it's important to have any track in order to be able to turn it on again (currently it's off)
let localVideoSource = WebRTCClient.factory.videoSource()
let localVideoTrack = WebRTCClient.factory.videoTrack(with: localVideoSource, trackId: "video0")
let sender = pc.add(localVideoTrack, streamIds: ["micCamera"])
sender?.track = nil
}
}
}
}
func mediaSourceFromTransceiverMid(_ mid: String?) -> CallMediaSource {
switch mid {
case "0":
return .mic
case "1":
return .camera
case "2":
return .screenAudio
case "3":
return .screenVideo
default:
return .unknown
}
}
// Should be called after local description set
func setupEncryptionForLocalTracks(_ call: Call) {
if let encryptor = call.frameEncryptor {
call.connection.senders.forEach { $0.setRtcFrameEncryptor(encryptor) }
}
}
func frameDecryptor(_ decryptor: RTCFrameDecryptor, mediaType: RTCRtpMediaType, withFrame encrypted: Data) -> Data? {
guard encrypted.count > 0 else { return nil }
if var key: [CChar] = activeCall?.aesKey?.cString(using: .utf8),
if var key: [CChar] = activeCall.wrappedValue?.aesKey?.cString(using: .utf8),
let pointer: UnsafeMutableRawPointer = malloc(encrypted.count) {
memcpy(pointer, (encrypted as NSData).bytes, encrypted.count)
let isKeyFrame = encrypted[0] & 1 == 0
@@ -513,7 +304,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
func frameEncryptor(_ encryptor: RTCFrameEncryptor, mediaType: RTCRtpMediaType, withFrame unencrypted: Data) -> Data? {
guard unencrypted.count > 0 else { return nil }
if var key: [CChar] = activeCall?.aesKey?.cString(using: .utf8),
if var key: [CChar] = activeCall.wrappedValue?.aesKey?.cString(using: .utf8),
let pointer: UnsafeMutableRawPointer = malloc(unencrypted.count + WebRTCClient.ivTagBytes) {
memcpy(pointer, (unencrypted as NSData).bytes, unencrypted.count)
let isKeyFrame = unencrypted[0] & 1 == 0
@@ -536,42 +327,18 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
}
}
func addRemoteCameraRenderer(_ renderer: RTCVideoRenderer) {
if activeCall?.remoteVideoTrack != nil {
activeCall?.remoteVideoTrack?.add(renderer)
} else {
cameraRenderers.append(renderer)
}
func addRemoteRenderer(_ activeCall: Call, _ renderer: RTCVideoRenderer) {
activeCall.remoteStream?.add(renderer)
}
func removeRemoteCameraRenderer(_ renderer: RTCVideoRenderer) {
if activeCall?.remoteVideoTrack != nil {
activeCall?.remoteVideoTrack?.remove(renderer)
} else {
cameraRenderers.removeAll(where: { $0.isEqual(renderer) })
}
func removeRemoteRenderer(_ activeCall: Call, _ renderer: RTCVideoRenderer) {
activeCall.remoteStream?.remove(renderer)
}
func addRemoteScreenRenderer(_ renderer: RTCVideoRenderer) {
if activeCall?.remoteScreenVideoTrack != nil {
activeCall?.remoteScreenVideoTrack?.add(renderer)
} else {
screenRenderers.append(renderer)
}
}
func removeRemoteScreenRenderer(_ renderer: RTCVideoRenderer) {
if activeCall?.remoteScreenVideoTrack != nil {
activeCall?.remoteScreenVideoTrack?.remove(renderer)
} else {
screenRenderers.removeAll(where: { $0.isEqual(renderer) })
}
}
func startCaptureLocalVideo(_ device: AVCaptureDevice.Position?, _ capturer: RTCVideoCapturer?) {
func startCaptureLocalVideo(_ activeCall: Call) {
#if targetEnvironment(simulator)
guard
let capturer = (activeCall?.localCamera ?? notConnectedCall?.localCameraAndTrack?.0) as? RTCFileVideoCapturer
let capturer = activeCall.localCamera as? RTCFileVideoCapturer
else {
logger.error("Unable to work with a file capturer")
return
@@ -581,10 +348,10 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
capturer.startCapturing(fromFileNamed: "sounds/video.mp4")
#else
guard
let capturer = capturer as? RTCCameraVideoCapturer,
let camera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == device })
let capturer = activeCall.localCamera as? RTCCameraVideoCapturer,
let camera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == activeCall.device })
else {
logger.error("Unable to find a camera or local track")
logger.error("Unable to find a camera")
return
}
@@ -610,6 +377,19 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
#endif
}
private func createAudioSender(_ connection: RTCPeerConnection) {
let streamId = "stream"
let audioTrack = createAudioTrack()
connection.add(audioTrack, streamIds: [streamId])
}
private func createVideoSender(_ connection: RTCPeerConnection) -> (RTCVideoTrack?, RTCVideoTrack?, RTCVideoCapturer?, RTCVideoSource?) {
let streamId = "stream"
let (localVideoTrack, localCamera, localVideoSource) = createVideoTrack()
connection.add(localVideoTrack, streamIds: [streamId])
return (localVideoTrack, connection.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack, localCamera, localVideoSource)
}
private func createAudioTrack() -> RTCAudioTrack {
let audioConstrains = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
let audioSource = WebRTCClient.factory.audioSource(with: audioConstrains)
@@ -617,7 +397,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
return audioTrack
}
private func createVideoTrackAndStartCapture(_ device: AVCaptureDevice.Position) -> (RTCVideoCapturer, RTCVideoTrack) {
private func createVideoTrack() -> (RTCVideoTrack, RTCVideoCapturer, RTCVideoSource) {
let localVideoSource = WebRTCClient.factory.videoSource()
#if targetEnvironment(simulator)
@@ -627,8 +407,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
#endif
let localVideoTrack = WebRTCClient.factory.videoTrack(with: localVideoSource, trackId: "video0")
startCaptureLocalVideo(device, localCamera)
return (localCamera, localVideoTrack)
return (localVideoTrack, localCamera, localVideoSource)
}
func endCall() {
@@ -641,16 +420,15 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
}
private func _endCall() {
(notConnectedCall?.localCameraAndTrack?.0 as? RTCCameraVideoCapturer)?.stopCapture()
guard let call = activeCall else { return }
guard let call = activeCall.wrappedValue else { return }
logger.debug("WebRTCClient: ending the call")
activeCall.wrappedValue = nil
(call.localCamera as? RTCCameraVideoCapturer)?.stopCapture()
call.connection.close()
call.connection.delegate = nil
call.frameEncryptor?.delegate = nil
call.frameDecryptor?.delegate = nil
(call.localCamera as? RTCCameraVideoCapturer)?.stopCapture()
audioSessionToDefaults()
activeCall = nil
}
func untilIceComplete(timeoutMs: UInt64, stepMs: UInt64, action: @escaping () async -> Void) async {
@@ -659,7 +437,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
_ = try? await Task.sleep(nanoseconds: stepMs * 1000000)
t += stepMs
await action()
} while t < timeoutMs && activeCall?.connection.iceGatheringState != .complete
} while t < timeoutMs && activeCall.wrappedValue?.connection.iceGatheringState != .complete
}
}
@@ -720,40 +498,11 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
logger.debug("Connection should negotiate")
}
func peerConnection(_ peerConnection: RTCPeerConnection, didStartReceivingOn transceiver: RTCRtpTransceiver) {
if let track = transceiver.receiver.track {
DispatchQueue.main.async {
// Doesn't work for outgoing video call (audio in video call works ok still, same as incoming call)
// if let decryptor = self.activeCall?.frameDecryptor {
// transceiver.receiver.setRtcFrameDecryptor(decryptor)
// }
let source = self.mediaSourceFromTransceiverMid(transceiver.mid)
switch source {
case .mic: self.activeCall?.remoteAudioTrack = track as? RTCAudioTrack
case .camera:
self.activeCall?.remoteVideoTrack = track as? RTCVideoTrack
self.cameraRenderers.forEach({ renderer in
self.activeCall?.remoteVideoTrack?.add(renderer)
})
self.cameraRenderers.removeAll()
case .screenAudio: self.activeCall?.remoteScreenAudioTrack = track as? RTCAudioTrack
case .screenVideo:
self.activeCall?.remoteScreenVideoTrack = track as? RTCVideoTrack
self.screenRenderers.forEach({ renderer in
self.activeCall?.remoteScreenVideoTrack?.add(renderer)
})
self.screenRenderers.removeAll()
case .unknown: ()
}
}
self.setupMuteUnmuteListener(transceiver, track)
}
}
func peerConnection(_ connection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
debugPrint("Connection new connection state: \(newState.toString() ?? "" + newState.rawValue.description) \(connection.receivers)")
guard let connectionStateString = newState.toString(),
guard let call = activeCall.wrappedValue,
let connectionStateString = newState.toString(),
let iceConnectionStateString = connection.iceConnectionState.toString(),
let iceGatheringStateString = connection.iceGatheringState.toString(),
let signalingStateString = connection.signalingState.toString()
@@ -774,14 +523,18 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
switch newState {
case .checking:
if let frameDecryptor = activeCall?.frameDecryptor {
if let frameDecryptor = activeCall.wrappedValue?.frameDecryptor {
connection.receivers.forEach { $0.setRtcFrameDecryptor(frameDecryptor) }
}
let enableSpeaker: Bool = ChatModel.shared.activeCall?.localMediaSources.hasVideo == true
let enableSpeaker: Bool
switch call.localMedia {
case .video: enableSpeaker = true
default: enableSpeaker = false
}
setSpeakerEnabledAndConfigureSession(enableSpeaker)
case .connected: sendConnectedEvent(connection)
case .disconnected, .failed: endCall()
default: ()
default: do {}
}
}
}
@@ -793,7 +546,7 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
func peerConnection(_ connection: RTCPeerConnection, didGenerate candidate: WebRTC.RTCIceCandidate) {
// logger.debug("Connection generated candidate \(candidate.debugDescription)")
Task {
await self.activeCall?.iceCandidates.append(candidate.toCandidate(nil, nil))
await self.activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil))
}
}
@@ -848,42 +601,11 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
}
extension WebRTCClient {
static func isAuthorized(for type: AVMediaType) async -> Bool {
let status = AVCaptureDevice.authorizationStatus(for: type)
var isAuthorized = status == .authorized
if status == .notDetermined {
isAuthorized = await AVCaptureDevice.requestAccess(for: type)
}
return isAuthorized
func setAudioEnabled(_ enabled: Bool) {
setTrackEnabled(RTCAudioTrack.self, enabled)
}
static func showUnauthorizedAlert(for type: AVMediaType) {
if type == .audio {
AlertManager.shared.showAlert(Alert(
title: Text("No permission to record speech"),
message: Text("To record speech please grant permission to use Microphone."),
primaryButton: .default(Text("Open Settings")) {
DispatchQueue.main.async {
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
}
},
secondaryButton: .cancel()
))
} else if type == .video {
AlertManager.shared.showAlert(Alert(
title: Text("No permission to record video"),
message: Text("To record video please grant permission to use Camera."),
primaryButton: .default(Text("Open Settings")) {
DispatchQueue.main.async {
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
}
},
secondaryButton: .cancel()
))
}
}
func setSpeakerEnabledAndConfigureSession( _ enabled: Bool, skipExternalDevice: Bool = false) {
func setSpeakerEnabledAndConfigureSession( _ enabled: Bool) {
logger.debug("WebRTCClient: configuring session with speaker enabled \(enabled)")
audioQueue.async { [weak self] in
guard let self = self else { return }
@@ -896,7 +618,7 @@ extension WebRTCClient {
if enabled {
try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue, with: [.defaultToSpeaker, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
try self.rtcAudioSession.setMode(AVAudioSession.Mode.videoChat.rawValue)
if hasExternalAudioDevice && !skipExternalDevice, let preferred = self.rtcAudioSession.session.preferredInputDevice() {
if hasExternalAudioDevice, let preferred = self.rtcAudioSession.session.preferredInputDevice() {
try self.rtcAudioSession.setPreferredInput(preferred)
} else {
try self.rtcAudioSession.overrideOutputAudioPort(.speaker)
@@ -906,7 +628,7 @@ extension WebRTCClient {
try self.rtcAudioSession.setMode(AVAudioSession.Mode.voiceChat.rawValue)
try self.rtcAudioSession.overrideOutputAudioPort(.none)
}
if hasExternalAudioDevice && !skipExternalDevice {
if hasExternalAudioDevice {
logger.debug("WebRTCClient: configuring session with external device available, skip configuring speaker")
}
try self.rtcAudioSession.setActive(true)
@@ -937,59 +659,25 @@ extension WebRTCClient {
}
}
@MainActor
func setAudioEnabled(_ enabled: Bool) {
if activeCall != nil {
activeCall?.localAudioTrack = enabled ? createAudioTrack() : nil
activeCall?.connection.transceivers.first(where: { t in mediaSourceFromTransceiverMid(t.mid) == .mic })?.sender.track = activeCall?.localAudioTrack
} else if notConnectedCall != nil {
notConnectedCall?.audioTrack = enabled ? createAudioTrack() : nil
}
ChatModel.shared.activeCall?.localMediaSources.mic = enabled
}
@MainActor
func setCameraEnabled(_ enabled: Bool) {
if let call = activeCall {
if enabled {
if call.localVideoTrack == nil {
let device = activeCall?.device ?? notConnectedCall?.device ?? .front
let (camera, track) = createVideoTrackAndStartCapture(device)
activeCall?.localCamera = camera
activeCall?.localVideoTrack = track
}
} else {
(call.localCamera as? RTCCameraVideoCapturer)?.stopCapture()
activeCall?.localCamera = nil
activeCall?.localVideoTrack = nil
}
call.connection.transceivers
.first(where: { t in mediaSourceFromTransceiverMid(t.mid) == .camera })?
.sender.track = activeCall?.localVideoTrack
ChatModel.shared.activeCall?.localMediaSources.camera = activeCall?.localVideoTrack != nil
} else if let call = notConnectedCall {
if enabled {
let device = activeCall?.device ?? notConnectedCall?.device ?? .front
notConnectedCall?.localCameraAndTrack = createVideoTrackAndStartCapture(device)
} else {
(call.localCameraAndTrack?.0 as? RTCCameraVideoCapturer)?.stopCapture()
notConnectedCall?.localCameraAndTrack = nil
}
ChatModel.shared.activeCall?.localMediaSources.camera = notConnectedCall?.localCameraAndTrack != nil
}
func setVideoEnabled(_ enabled: Bool) {
setTrackEnabled(RTCVideoTrack.self, enabled)
}
func flipCamera() {
let device = activeCall?.device ?? notConnectedCall?.device
if activeCall != nil {
activeCall?.device = device == .front ? .back : .front
} else {
notConnectedCall?.device = device == .front ? .back : .front
switch activeCall.wrappedValue?.device {
case .front: activeCall.wrappedValue?.device = .back
case .back: activeCall.wrappedValue?.device = .front
default: ()
}
startCaptureLocalVideo(
activeCall?.device ?? notConnectedCall?.device,
(activeCall?.localCamera ?? notConnectedCall?.localCameraAndTrack?.0) as? RTCCameraVideoCapturer
)
if let call = activeCall.wrappedValue {
startCaptureLocalVideo(call)
}
}
private func setTrackEnabled<T: RTCMediaStreamTrack>(_ type: T.Type, _ enabled: Bool) {
activeCall.wrappedValue?.connection.transceivers
.compactMap { $0.sender.track as? T }
.forEach { $0.isEnabled = enabled }
}
}
@@ -459,7 +459,7 @@ struct ComposeView: View {
Task {
var media: [(String, UploadContent)] = []
for content in selected {
if let img = await resizeImageToStrSize(content.uiImage, maxDataSize: 14000) {
if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) {
media.append((img, content))
await MainActor.run {
composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: media))
@@ -551,7 +551,7 @@ struct ComposeView: View {
}
private func addMediaContent(_ content: UploadContent) async {
if let img = await resizeImageToStrSize(content.uiImage, maxDataSize: 14000) {
if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) {
var newMedia: [(String, UploadContent?)] = []
if case var .mediaPreviews(media) = composeState.preview {
media.append((img, content))
@@ -110,13 +110,10 @@ struct GroupProfileView: View {
}
}
.onChange(of: chosenImage) { image in
Task {
let resized: String? = if let image {
await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
} else {
nil
}
await MainActor.run { groupProfile.image = resized }
if let image = image {
groupProfile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
} else {
groupProfile.image = nil
}
}
.onAppear {
@@ -137,13 +137,10 @@ struct AddGroupView: View {
createInvalidNameAlert(mkValidName(profile.displayName), $profile.displayName)
}
.onChange(of: chosenImage) { image in
Task {
let resized: String? = if let image {
await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
} else {
nil
}
await MainActor.run { profile.image = resized }
if let image = image {
profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
} else {
profile.image = nil
}
}
.modifier(ThemedBackground(grouped: true))
@@ -196,14 +196,11 @@ struct AdvancedNetworkSettings: View {
if developerTools {
Section {
Picker("Transport isolation", selection: $netCfg.sessionMode) {
let modes = TransportSessionMode.values.contains(netCfg.sessionMode)
? TransportSessionMode.values
: TransportSessionMode.values + [netCfg.sessionMode]
ForEach(modes, id: \.self) { Text($0.text) }
ForEach(TransportSessionMode.values, id: \.self) { Text($0.text) }
}
.frame(height: 36)
} footer: {
sessionModeInfo(netCfg.sessionMode)
Text(sessionModeInfo(netCfg.sessionMode))
.foregroundColor(theme.colors.secondary)
}
}
@@ -356,13 +353,10 @@ struct AdvancedNetworkSettings: View {
}
}
private func sessionModeInfo(_ mode: TransportSessionMode) -> Text {
let userMode = Text("A separate TCP connection will be used **for each chat profile you have in the app**.")
return switch mode {
case .user: userMode
case .session: userMode + Text("\n") + Text("New SOCKS credentials will be used every time you start the app.")
case .server: userMode + Text("\n") + Text("New SOCKS credentials will be used for each server.")
case .entity: Text("A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail.")
private func sessionModeInfo(_ mode: TransportSessionMode) -> LocalizedStringKey {
switch mode {
case .user: return "A separate TCP connection will be used **for each chat profile you have in the app**."
case .entity: return "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail."
}
}
@@ -58,8 +58,6 @@ extension AppSettings {
profileImageCornerRadiusGroupDefault.set(val)
def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
}
if let val = uiChatItemRoundness { def.setValue(val, forKey: DEFAULT_CHAT_ITEM_ROUNDNESS)}
if let val = uiChatItemTail { def.setValue(val, forKey: DEFAULT_CHAT_ITEM_TAIL)}
if let val = uiColorScheme { currentThemeDefault.set(val) }
if let val = uiDarkColorScheme { systemDarkThemeDefault.set(val) }
if let val = uiCurrentThemeIds { currentThemeIdsDefault.set(val) }
@@ -93,8 +91,6 @@ extension AppSettings {
c.iosCallKitEnabled = callKitEnabledGroupDefault.get()
c.iosCallKitCallsInRecents = def.bool(forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS)
c.uiProfileImageCornerRadius = def.double(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
c.uiChatItemRoundness = def.double(forKey: DEFAULT_CHAT_ITEM_ROUNDNESS)
c.uiChatItemTail = def.bool(forKey: DEFAULT_CHAT_ITEM_TAIL)
c.uiColorScheme = currentThemeDefault.get()
c.uiDarkColorScheme = systemDarkThemeDefault.get()
c.uiCurrentThemeIds = currentThemeIdsDefault.get()
@@ -94,13 +94,10 @@ struct UserProfile: View {
}
}
.onChange(of: chosenImage) { image in
Task {
let resized: String? = if let image {
await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
} else {
nil
}
await MainActor.run { profile.image = resized }
if let image {
profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
} else {
profile.image = nil
}
}
// Modals
@@ -737,7 +737,7 @@
</trans-unit>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -925,10 +925,6 @@
<target>Кода за достъп до приложение се заменя с код за самоунищожение.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Версия на приложението</target>
@@ -1346,11 +1342,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Потребителски профил</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<note>No comment provided by engineer.</note>
@@ -2943,7 +2934,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Грешка при смяна на профил!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -2988,7 +2979,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Грешка: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4366,14 +4358,6 @@ This is your link for group %@!</source>
<target>Нов kод за достъп</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Нов чат</target>
@@ -4490,14 +4474,6 @@ This is your link for group %@!</source>
<target>Няма мрежова връзка</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Няма разрешение за запис на гласово съобщение</target>
@@ -5901,10 +5877,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Sent via proxy</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<note>No comment provided by engineer.</note>
@@ -6705,14 +6677,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Ще бъдете подканени да извършите идентификация, преди тази функция да бъде активирана.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>За да запишете гласово съобщение, моля, дайте разрешение за използване на микрофон.</target>
@@ -7037,6 +7001,11 @@ To connect, please ask your contact to create another connection link and check
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Потребителски профил</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<note>No comment provided by engineer.</note>
@@ -7298,6 +7267,11 @@ To connect, please ask your contact to create another connection link and check
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Вие</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>**Не трябва** да използвате една и съща база данни на две устройства.</target>
@@ -715,7 +715,7 @@
</trans-unit>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -898,10 +898,6 @@
<target>Přístupový kód aplikace je nahrazen sebedestrukčním přístupovým heslem.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Verze aplikace</target>
@@ -1302,11 +1298,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Profil uživatele</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<note>No comment provided by engineer.</note>
@@ -2845,7 +2836,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Chyba při přepínání profilu!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -2888,7 +2879,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Chyba: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4210,14 +4202,6 @@ This is your link for group %@!</source>
<target>Nové heslo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<note>No comment provided by engineer.</note>
@@ -4332,14 +4316,6 @@ This is your link for group %@!</source>
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Nemáte oprávnění nahrávat hlasové zprávy</target>
@@ -5703,10 +5679,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Sent via proxy</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<note>No comment provided by engineer.</note>
@@ -6484,14 +6456,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Před zapnutím této funkce budete vyzváni k dokončení ověření.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Chcete-li nahrávat hlasové zprávy, udělte povolení k použití mikrofonu.</target>
@@ -6802,6 +6766,11 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Profil uživatele</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<note>No comment provided by engineer.</note>
@@ -7046,6 +7015,11 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Vy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<note>No comment provided by engineer.</note>
@@ -164,22 +164,18 @@
</trans-unit>
<trans-unit id="%d file(s) are still being downloaded." xml:space="preserve">
<source>%d file(s) are still being downloaded.</source>
<target>%d Datei(en) wird/werden immer noch heruntergeladen.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) failed to download." xml:space="preserve">
<source>%d file(s) failed to download.</source>
<target>Bei %d Datei(en) ist das Herunterladen fehlgeschlagen.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were deleted." xml:space="preserve">
<source>%d file(s) were deleted.</source>
<target>%d Datei(en) wurde(n) gelöscht.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were not downloaded." xml:space="preserve">
<source>%d file(s) were not downloaded.</source>
<target>%d Datei(en) wurde(n) nicht heruntergeladen.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d hours" xml:space="preserve">
@@ -189,7 +185,6 @@
</trans-unit>
<trans-unit id="%d messages not forwarded" xml:space="preserve">
<source>%d messages not forwarded</source>
<target>%d Nachrichten wurden nicht weitergeleitet</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="%d min" xml:space="preserve">
@@ -753,7 +748,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Alle Profile</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -945,10 +940,6 @@
<target>App-Zugangscode wurde durch den Selbstzerstörungs-Zugangscode ersetzt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>App Version</target>
@@ -1385,11 +1376,6 @@
<target>Die Chat-Präferenzen wurden geändert.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Benutzerprofil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Chat-Design</target>
@@ -1791,7 +1777,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Corner" xml:space="preserve">
<source>Corner</source>
<target>Abrundung Ecken</target>
<target>Ecke</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Correct name to %@?" xml:space="preserve">
@@ -2465,7 +2451,6 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Do not use credentials with proxy." xml:space="preserve">
<source>Do not use credentials with proxy.</source>
<target>Verwenden Sie keine Anmeldeinformationen mit einem Proxy.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Don't create address" xml:space="preserve">
@@ -2511,7 +2496,6 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Download files" xml:space="preserve">
<source>Download files</source>
<target>Dateien herunterladen</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Downloaded" xml:space="preserve">
@@ -3042,7 +3026,7 @@ Das ist Ihr eigener Einmal-Link!</target>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Fehler beim Umschalten des Profils!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3087,7 +3071,8 @@ Das ist Ihr eigener Einmal-Link!</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Fehler: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -3177,8 +3162,6 @@ Das ist Ihr eigener Einmal-Link!</target>
<trans-unit id="File errors:&#10;%@" xml:space="preserve">
<source>File errors:
%@</source>
<target>Datei-Fehler:
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="File not found - most likely file was deleted or cancelled." xml:space="preserve">
@@ -3318,7 +3301,6 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Forward %d message(s)?" xml:space="preserve">
<source>Forward %d message(s)?</source>
<target>%d Nachricht(en) weiterleiten?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
@@ -3328,12 +3310,10 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Forward messages" xml:space="preserve">
<source>Forward messages</source>
<target>Nachrichten weiterleiten</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Forward messages without files?" xml:space="preserve">
<source>Forward messages without files?</source>
<target>Nachrichten ohne Dateien weiterleiten?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
@@ -3348,7 +3328,6 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Forwarding %lld messages" xml:space="preserve">
<source>Forwarding %lld messages</source>
<target>%lld Nachricht(en) wird/werden weitergeleitet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server %@ failed to connect to destination server %@. Please try later." xml:space="preserve">
@@ -3647,7 +3626,6 @@ Fehler: %2$@</target>
</trans-unit>
<trans-unit id="IP address" xml:space="preserve">
<source>IP address</source>
<target>IP-Adresse</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="If you can't meet in person, show QR code in a video call, or share the link." xml:space="preserve">
@@ -4352,7 +4330,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Messages were deleted after you selected them." xml:space="preserve">
<source>Messages were deleted after you selected them.</source>
<target>Die Nachrichten wurden gelöscht, nachdem Sie sie ausgewählt hatten.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
@@ -4510,14 +4487,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Neuer Zugangscode</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Neuer Chat</target>
@@ -4638,14 +4607,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Keine Netzwerkverbindung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Keine Berechtigung für das Aufnehmen von Sprachnachrichten</target>
@@ -4668,7 +4629,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Nothing to forward!" xml:space="preserve">
<source>Nothing to forward!</source>
<target>Es gibt nichts zum Weiterleiten!</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve">
@@ -4897,8 +4857,6 @@ Dies erfordert die Aktivierung eines VPNs.</target>
<trans-unit id="Other file errors:&#10;%@" xml:space="preserve">
<source>Other file errors:
%@</source>
<target>Andere(r) Datei-Fehler:
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
@@ -4938,7 +4896,6 @@ Dies erfordert die Aktivierung eines VPNs.</target>
</trans-unit>
<trans-unit id="Password" xml:space="preserve">
<source>Password</source>
<target>Passwort</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Password to show" xml:space="preserve">
@@ -5092,7 +5049,6 @@ Fehler: %@</target>
</trans-unit>
<trans-unit id="Port" xml:space="preserve">
<source>Port</source>
<target>Port</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Possibly, certificate fingerprint in server address is incorrect" xml:space="preserve">
@@ -5284,7 +5240,6 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Proxy requires password" xml:space="preserve">
<source>Proxy requires password</source>
<target>Der Proxy benötigt ein Passwort</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Push notifications" xml:space="preserve">
@@ -5695,7 +5650,6 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="SOCKS proxy" xml:space="preserve">
<source>SOCKS proxy</source>
<target>SOCKS-Proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
@@ -5811,7 +5765,6 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Saving %lld messages" xml:space="preserve">
<source>Saving %lld messages</source>
<target>Es wird/werden %lld Nachricht(en) gesichert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scale" xml:space="preserve">
@@ -6114,10 +6067,6 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
<target>Über einen Proxy gesendet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Server-Adresse</target>
@@ -6630,7 +6579,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Tail" xml:space="preserve">
<source>Tail</source>
<target>Sprechblasen-Format</target>
<target>Sprechblase</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Take picture" xml:space="preserve">
@@ -6957,14 +6906,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funktion aktiviert wird.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Bitte erlauben Sie die Nutzung des Mikrofons, um Sprachnachrichten aufnehmen zu können.</target>
@@ -7234,7 +7175,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="Use SOCKS proxy" xml:space="preserve">
<source>Use SOCKS proxy</source>
<target>SOCKS-Proxy nutzen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
@@ -7302,6 +7242,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>Die App mit einer Hand bedienen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Benutzerprofil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Benutzer-Auswahl</target>
@@ -7309,7 +7254,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="Username" xml:space="preserve">
<source>Username</source>
<target>Benutzername</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
@@ -7572,6 +7516,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>XFTP-Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>Sie dürfen die selbe Datenbank **nicht** auf zwei Geräten nutzen.</target>
@@ -7953,7 +7902,6 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="Your credentials may be sent unencrypted." xml:space="preserve">
<source>Your credentials may be sent unencrypted.</source>
<target>Ihre Anmeldeinformationen können unverschlüsselt versendet werden.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your current chat database will be DELETED and REPLACED with the imported one." xml:space="preserve">
@@ -8433,7 +8381,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="expired" xml:space="preserve">
<source>expired</source>
<target>Abgelaufen</target>
<target>abgelaufen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
@@ -8665,7 +8613,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="other" xml:space="preserve">
<source>other</source>
<target>Andere</target>
<target>andere</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="other errors" xml:space="preserve">
@@ -753,7 +753,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>All profiles</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -945,11 +945,6 @@
<target>App passcode is replaced with self-destruct passcode.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<target>App session</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>App version</target>
@@ -1386,11 +1381,6 @@
<target>Chat preferences were changed.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Chat profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Chat theme</target>
@@ -3043,7 +3033,7 @@ This is your own one-time link!</target>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Error switching profile!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3088,7 +3078,8 @@ This is your own one-time link!</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Error: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4511,16 +4502,6 @@ This is your link for group %@!</target>
<target>New Passcode</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<target>New SOCKS credentials will be used every time you start the app.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<target>New SOCKS credentials will be used for each server.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>New chat</target>
@@ -4641,16 +4622,6 @@ This is your link for group %@!</target>
<target>No network connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<target>No permission to record speech</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<target>No permission to record video</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>No permission to record voice message</target>
@@ -6119,11 +6090,6 @@ Enable in *Network &amp; servers* settings.</target>
<target>Sent via proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<target>Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Server address</target>
@@ -6963,16 +6929,6 @@ You will be prompted to complete authentication before this feature is enabled.<
You will be prompted to complete authentication before this feature is enabled.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<target>To record speech please grant permission to use Microphone.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<target>To record video please grant permission to use Camera.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>To record voice message please grant permission to use Microphone.</target>
@@ -7310,6 +7266,11 @@ To connect, please ask your contact to create another connection link and check
<target>Use the app with one hand.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>User profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>User selection</target>
@@ -7580,6 +7541,11 @@ To connect, please ask your contact to create another connection link and check
<target>XFTP server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>You</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>You **must not** use the same database on two devices.</target>
@@ -139,7 +139,6 @@
</trans-unit>
<trans-unit id="%@, %@" xml:space="preserve">
<source>%1$@, %2$@</source>
<target>%1$@, %2$@</target>
<note>format for date separator in chat</note>
</trans-unit>
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
@@ -164,22 +163,18 @@
</trans-unit>
<trans-unit id="%d file(s) are still being downloaded." xml:space="preserve">
<source>%d file(s) are still being downloaded.</source>
<target>%d archivo(s) se está(n) descargando todavía.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) failed to download." xml:space="preserve">
<source>%d file(s) failed to download.</source>
<target>La descarga ha fallado para %d archivo(s).</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were deleted." xml:space="preserve">
<source>%d file(s) were deleted.</source>
<target>%d archivo(s) ha(n) sido eliminado(s).</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were not downloaded." xml:space="preserve">
<source>%d file(s) were not downloaded.</source>
<target>%d archivo(s) no se ha(n) descargado.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d hours" xml:space="preserve">
@@ -189,7 +184,6 @@
</trans-unit>
<trans-unit id="%d messages not forwarded" xml:space="preserve">
<source>%d messages not forwarded</source>
<target>%d mensajes no enviados</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="%d min" xml:space="preserve">
@@ -537,7 +531,7 @@
</trans-unit>
<trans-unit id="A new random profile will be shared." xml:space="preserve">
<source>A new random profile will be shared.</source>
<target>Compartirás un perfil nuevo aleatorio.</target>
<target>Se compartirá un perfil nuevo aleatorio.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="A separate TCP connection will be used **for each chat profile you have in the app**." xml:space="preserve">
@@ -753,7 +747,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Todos los perfiles</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -945,10 +939,6 @@
<target>El código de acceso será reemplazado por código de autodestrucción.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Versión de la aplicación</target>
@@ -1056,7 +1046,6 @@
</trans-unit>
<trans-unit id="Auto-accept settings" xml:space="preserve">
<source>Auto-accept settings</source>
<target>Auto aceptar configuración</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Back" xml:space="preserve">
@@ -1382,14 +1371,8 @@
</trans-unit>
<trans-unit id="Chat preferences were changed." xml:space="preserve">
<source>Chat preferences were changed.</source>
<target>Las preferencias del chat han sido modificadas.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Perfil de usuario</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Tema de chat</target>
@@ -1791,7 +1774,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Corner" xml:space="preserve">
<source>Corner</source>
<target>Esquina</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Correct name to %@?" xml:space="preserve">
@@ -2000,7 +1982,7 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Database passphrase is different from saved in the keychain." xml:space="preserve">
<source>Database passphrase is different from saved in the keychain.</source>
<target>La contraseña es diferente a la almacenada en Keychain.</target>
<target>La contraseña es distinta a la almacenada en Keychain.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Database passphrase is required to open chat." xml:space="preserve">
@@ -2425,7 +2407,7 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve">
<source>Disconnect desktop?</source>
<target>¿Desconectar del ordenador?</target>
<target>¿Desconectar ordenador?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve">
@@ -2465,7 +2447,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Do not use credentials with proxy." xml:space="preserve">
<source>Do not use credentials with proxy.</source>
<target>No uses credenciales con proxy.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Don't create address" xml:space="preserve">
@@ -2480,7 +2461,7 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>No volver a mostrar</target>
<target>No mostrar de nuevo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Downgrade and open chat" xml:space="preserve">
@@ -2511,7 +2492,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Download files" xml:space="preserve">
<source>Download files</source>
<target>Descargar archivos</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Downloaded" xml:space="preserve">
@@ -2791,7 +2771,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Error changing connection profile" xml:space="preserve">
<source>Error changing connection profile</source>
<target>Error al cambiar el perfil de conexión</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error changing role" xml:space="preserve">
@@ -2806,7 +2785,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Error changing to incognito!" xml:space="preserve">
<source>Error changing to incognito!</source>
<target>¡Error al cambiar a incógnito!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
@@ -2931,7 +2909,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Error migrating settings" xml:space="preserve">
<source>Error migrating settings</source>
<target>Error al migrar la configuración</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error opening chat" xml:space="preserve">
@@ -3036,13 +3013,12 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Error switching profile" xml:space="preserve">
<source>Error switching profile</source>
<target>Error al cambiar perfil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>¡Error al cambiar perfil!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3087,7 +3063,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Error: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -3177,8 +3154,6 @@ This is your own one-time link!</source>
<trans-unit id="File errors:&#10;%@" xml:space="preserve">
<source>File errors:
%@</source>
<target>Error(es) de archivo
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="File not found - most likely file was deleted or cancelled." xml:space="preserve">
@@ -3318,7 +3293,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Forward %d message(s)?" xml:space="preserve">
<source>Forward %d message(s)?</source>
<target>¿Reenviar %d mensaje(s)?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
@@ -3328,12 +3302,10 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Forward messages" xml:space="preserve">
<source>Forward messages</source>
<target>Reenviar mensajes</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Forward messages without files?" xml:space="preserve">
<source>Forward messages without files?</source>
<target>¿Reenviar mensajes sin los archivos?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
@@ -3348,7 +3320,6 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Forwarding %lld messages" xml:space="preserve">
<source>Forwarding %lld messages</source>
<target>Reenviando %lld mensajes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server %@ failed to connect to destination server %@. Please try later." xml:space="preserve">
@@ -3402,12 +3373,12 @@ Error: %2$@</target>
</trans-unit>
<trans-unit id="Fully decentralized visible only to members." xml:space="preserve">
<source>Fully decentralized visible only to members.</source>
<target>Totalmente descentralizado. Visible sólo para los miembros.</target>
<target>Completamente descentralizado y sólo visible para los miembros.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Fully re-implemented - work in background!" xml:space="preserve">
<source>Fully re-implemented - work in background!</source>
<target>Totalmente revisado. ¡Funciona en segundo plano!</target>
<target>Completamente reimplementado: ¡funciona en segundo plano!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Further reduced battery usage" xml:space="preserve">
@@ -3647,7 +3618,6 @@ Error: %2$@</target>
</trans-unit>
<trans-unit id="IP address" xml:space="preserve">
<source>IP address</source>
<target>Dirección IP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="If you can't meet in person, show QR code in a video call, or share the link." xml:space="preserve">
@@ -4297,7 +4267,6 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Message shape" xml:space="preserve">
<source>Message shape</source>
<target>Forma del mensaje</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -4352,7 +4321,6 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Messages were deleted after you selected them." xml:space="preserve">
<source>Messages were deleted after you selected them.</source>
<target>Los mensajes han sido borrados después de seleccionarlos.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
@@ -4510,14 +4478,6 @@ This is your link for group %@!</source>
<target>Código Nuevo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Nuevo chat</target>
@@ -4638,14 +4598,6 @@ This is your link for group %@!</source>
<target>Sin conexión de red</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Sin permiso para grabar mensajes de voz</target>
@@ -4668,7 +4620,6 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Nothing to forward!" xml:space="preserve">
<source>Nothing to forward!</source>
<target>¡Nada para reenviar!</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve">
@@ -4897,8 +4848,6 @@ Requiere activación de la VPN.</target>
<trans-unit id="Other file errors:&#10;%@" xml:space="preserve">
<source>Other file errors:
%@</source>
<target>Otro(s) error(es) de archivo.
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
@@ -4938,7 +4887,6 @@ Requiere activación de la VPN.</target>
</trans-unit>
<trans-unit id="Password" xml:space="preserve">
<source>Password</source>
<target>Contraseña</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Password to show" xml:space="preserve">
@@ -5092,7 +5040,6 @@ Error: %@</target>
</trans-unit>
<trans-unit id="Port" xml:space="preserve">
<source>Port</source>
<target>Puerto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Possibly, certificate fingerprint in server address is incorrect" xml:space="preserve">
@@ -5284,7 +5231,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Proxy requires password" xml:space="preserve">
<source>Proxy requires password</source>
<target>El proxy requiere contraseña</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Push notifications" xml:space="preserve">
@@ -5510,7 +5456,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Remove archive?" xml:space="preserve">
<source>Remove archive?</source>
<target>¿Eliminar archivo?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove image" xml:space="preserve">
@@ -5695,7 +5640,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="SOCKS proxy" xml:space="preserve">
<source>SOCKS proxy</source>
<target>Proxy SOCKS</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
@@ -5786,7 +5730,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Save your profile?" xml:space="preserve">
<source>Save your profile?</source>
<target>¿Guardar tu perfil?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Saved" xml:space="preserve">
@@ -5811,7 +5754,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Saving %lld messages" xml:space="preserve">
<source>Saving %lld messages</source>
<target>Guardando %lld mensajes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scale" xml:space="preserve">
@@ -5896,7 +5838,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Select chat profile" xml:space="preserve">
<source>Select chat profile</source>
<target>Selecciona perfil de chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Selected %lld" xml:space="preserve">
@@ -5946,7 +5887,7 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<target>Envía un mensaje para conectar</target>
<target>Envia un mensaje para conectar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send disappearing message" xml:space="preserve">
@@ -6114,10 +6055,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<target>Mediante proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Dirección del servidor</target>
@@ -6240,7 +6177,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Settings were changed." xml:space="preserve">
<source>Settings were changed.</source>
<target>La configuración ha sido modificada.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Shape profile images" xml:space="preserve">
@@ -6280,7 +6216,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Share profile" xml:space="preserve">
<source>Share profile</source>
<target>Comparte perfil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
@@ -6450,7 +6385,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Some app settings were not migrated." xml:space="preserve">
<source>Some app settings were not migrated.</source>
<target>Algunas configuraciones de la app no han sido migradas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
@@ -6630,7 +6564,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Tail" xml:space="preserve">
<source>Tail</source>
<target>Cola</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Take picture" xml:space="preserve">
@@ -6827,7 +6760,6 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida.</target>
</trans-unit>
<trans-unit id="The uploaded database archive will be permanently removed from the servers." xml:space="preserve">
<source>The uploaded database archive will be permanently removed from the servers.</source>
<target>El archivo de bases de datos subido será eliminado permanentemente de los servidores.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Themes" xml:space="preserve">
@@ -6957,14 +6889,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Se te pedirá que completes la autenticación antes de activar esta función.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Para grabar el mensaje de voz concede permiso para usar el micrófono.</target>
@@ -7234,7 +7158,6 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión
</trans-unit>
<trans-unit id="Use SOCKS proxy" xml:space="preserve">
<source>Use SOCKS proxy</source>
<target>Usar proxy SOCKS</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
@@ -7302,6 +7225,11 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión
<target>Usa la aplicación con una sola mano.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Perfil de usuario</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Selección de usuarios</target>
@@ -7309,7 +7237,6 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión
</trans-unit>
<trans-unit id="Username" xml:space="preserve">
<source>Username</source>
<target>Nombre de usuario</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
@@ -7572,6 +7499,11 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión
<target>Servidor XFTP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Tú</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>**No debes** usar la misma base de datos en dos dispositivos.</target>
@@ -7923,7 +7855,6 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="Your chat preferences" xml:space="preserve">
<source>Your chat preferences</source>
<target>Tus preferencias de chat</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Your chat profiles" xml:space="preserve">
@@ -7933,7 +7864,6 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
<target>Tu conexión ha sido trasladada a %@ pero ha ocurrido un error inesperado al redirigirte al perfil.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
@@ -7953,7 +7883,6 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="Your credentials may be sent unencrypted." xml:space="preserve">
<source>Your credentials may be sent unencrypted.</source>
<target>Tus credenciales podrían ser enviadas sin cifrar.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your current chat database will be DELETED and REPLACED with the imported one." xml:space="preserve">
@@ -7993,7 +7922,6 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="Your profile was changed. If you save it, the updated profile will be sent to all your contacts." xml:space="preserve">
<source>Your profile was changed. If you save it, the updated profile will be sent to all your contacts.</source>
<target>Tu perfil ha sido modificado. Si lo guardas la actualización será enviada a todos tus contactos.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Your profile, contacts and delivered messages are stored on your device." xml:space="preserve">
@@ -8013,7 +7941,7 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="Your server address" xml:space="preserve">
<source>Your server address</source>
<target>Dirección del servidor</target>
<target>Dirección de tu servidor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your settings" xml:space="preserve">
@@ -9137,7 +9065,7 @@ last received msg: %2$@</source>
</trans-unit>
<trans-unit id="Database passphrase is different from saved in the keychain." xml:space="preserve">
<source>Database passphrase is different from saved in the keychain.</source>
<target>La contraseña de la base de datos es diferente a la almacenada en keychain.</target>
<target>La contraseña de la base de datos es distinta a la almacenada en keychain.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Database passphrase is required to open chat." xml:space="preserve">
@@ -710,7 +710,7 @@
</trans-unit>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -892,10 +892,6 @@
<target>Sovelluksen pääsykoodi korvataan itsetuhoutuvalla pääsykoodilla.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Sovellusversio</target>
@@ -1295,11 +1291,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Käyttäjäprofiili</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<note>No comment provided by engineer.</note>
@@ -2835,7 +2826,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Virhe profiilin vaihdossa!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -2878,7 +2869,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Virhe: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4200,14 +4192,6 @@ This is your link for group %@!</source>
<target>Uusi pääsykoodi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<note>No comment provided by engineer.</note>
@@ -4321,14 +4305,6 @@ This is your link for group %@!</source>
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Ei lupaa ääniviestin tallentamiseen</target>
@@ -5690,10 +5666,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Sent via proxy</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<note>No comment provided by engineer.</note>
@@ -6470,14 +6442,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus otetaan käyttöön.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Jos haluat nauhoittaa ääniviestin, anna lupa käyttää mikrofonia.</target>
@@ -6787,6 +6751,11 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Käyttäjäprofiili</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<note>No comment provided by engineer.</note>
@@ -7031,6 +7000,11 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Sinä</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<note>No comment provided by engineer.</note>
@@ -747,7 +747,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Tous les profiles</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -939,10 +939,6 @@
<target>Le code d'accès de l'application est remplacé par un code d'autodestruction.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Version de l'app</target>
@@ -1377,11 +1373,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Profil d'utilisateur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Thème de chat</target>
@@ -3027,7 +3018,7 @@ Il s'agit de votre propre lien unique !</target>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Erreur lors du changement de profil !</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3072,7 +3063,8 @@ Il s'agit de votre propre lien unique !</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Erreur: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4486,14 +4478,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Nouveau code d'accès</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Nouveau chat</target>
@@ -4614,14 +4598,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Pas de connexion au réseau</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Pas l'autorisation d'enregistrer un message vocal</target>
@@ -6079,10 +6055,6 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<target>Envoyé via le proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Adresse du serveur</target>
@@ -6917,14 +6889,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Vous serez invité à confirmer l'authentification avant que cette fonction ne soit activée.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Pour enregistrer un message vocal, veuillez accorder la permission d'utiliser le microphone.</target>
@@ -7261,6 +7225,11 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
<target>Utiliser l'application d'une main.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Profil d'utilisateur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Sélection de l'utilisateur</target>
@@ -7530,6 +7499,11 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
<target>Serveur XFTP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Vous</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>Vous **ne devez pas** utiliser la même base de données sur deux appareils.</target>
File diff suppressed because it is too large Load Diff
@@ -164,22 +164,18 @@
</trans-unit>
<trans-unit id="%d file(s) are still being downloaded." xml:space="preserve">
<source>%d file(s) are still being downloaded.</source>
<target>%d file è/sono ancora in scaricamento.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) failed to download." xml:space="preserve">
<source>%d file(s) failed to download.</source>
<target>%d file ha/hanno fallito lo scaricamento.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were deleted." xml:space="preserve">
<source>%d file(s) were deleted.</source>
<target>%d file è/sono stato/i eliminato/i.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were not downloaded." xml:space="preserve">
<source>%d file(s) were not downloaded.</source>
<target>%d file non è/sono stato/i scaricato/i.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d hours" xml:space="preserve">
@@ -189,7 +185,6 @@
</trans-unit>
<trans-unit id="%d messages not forwarded" xml:space="preserve">
<source>%d messages not forwarded</source>
<target>%d messaggi non inoltrati</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="%d min" xml:space="preserve">
@@ -753,7 +748,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Tutti gli profili</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -945,10 +940,6 @@
<target>Il codice di accesso dell'app viene sostituito da un codice di autodistruzione.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Versione dell'app</target>
@@ -1385,11 +1376,6 @@
<target>Le preferenze della chat sono state cambiate.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Profilo utente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Tema della chat</target>
@@ -2465,7 +2451,6 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Do not use credentials with proxy." xml:space="preserve">
<source>Do not use credentials with proxy.</source>
<target>Non usare credenziali con proxy.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Don't create address" xml:space="preserve">
@@ -2511,7 +2496,6 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Download files" xml:space="preserve">
<source>Download files</source>
<target>Scarica i file</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Downloaded" xml:space="preserve">
@@ -3042,7 +3026,7 @@ Questo è il tuo link una tantum!</target>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Errore nel cambio di profilo!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3087,7 +3071,8 @@ Questo è il tuo link una tantum!</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Errore: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -3177,8 +3162,6 @@ Questo è il tuo link una tantum!</target>
<trans-unit id="File errors:&#10;%@" xml:space="preserve">
<source>File errors:
%@</source>
<target>Errori di file:
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="File not found - most likely file was deleted or cancelled." xml:space="preserve">
@@ -3318,7 +3301,6 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Forward %d message(s)?" xml:space="preserve">
<source>Forward %d message(s)?</source>
<target>Inoltrare %d messaggio/i?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
@@ -3328,12 +3310,10 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Forward messages" xml:space="preserve">
<source>Forward messages</source>
<target>Inoltra i messaggi</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Forward messages without files?" xml:space="preserve">
<source>Forward messages without files?</source>
<target>Inoltrare i messaggi senza file?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
@@ -3348,7 +3328,6 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Forwarding %lld messages" xml:space="preserve">
<source>Forwarding %lld messages</source>
<target>Inoltro di %lld messaggi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server %@ failed to connect to destination server %@. Please try later." xml:space="preserve">
@@ -3647,7 +3626,6 @@ Errore: %2$@</target>
</trans-unit>
<trans-unit id="IP address" xml:space="preserve">
<source>IP address</source>
<target>Indirizzo IP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="If you can't meet in person, show QR code in a video call, or share the link." xml:space="preserve">
@@ -4352,7 +4330,6 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Messages were deleted after you selected them." xml:space="preserve">
<source>Messages were deleted after you selected them.</source>
<target>I messaggi sono stati eliminati dopo che li hai selezionati.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
@@ -4510,14 +4487,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Nuovo codice di accesso</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Nuova chat</target>
@@ -4638,14 +4607,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Nessuna connessione di rete</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Nessuna autorizzazione per registrare messaggi vocali</target>
@@ -4668,7 +4629,6 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Nothing to forward!" xml:space="preserve">
<source>Nothing to forward!</source>
<target>Niente da inoltrare!</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve">
@@ -4897,8 +4857,6 @@ Richiede l'attivazione della VPN.</target>
<trans-unit id="Other file errors:&#10;%@" xml:space="preserve">
<source>Other file errors:
%@</source>
<target>Altri errori di file:
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
@@ -4938,7 +4896,6 @@ Richiede l'attivazione della VPN.</target>
</trans-unit>
<trans-unit id="Password" xml:space="preserve">
<source>Password</source>
<target>Password</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Password to show" xml:space="preserve">
@@ -5092,7 +5049,6 @@ Errore: %@</target>
</trans-unit>
<trans-unit id="Port" xml:space="preserve">
<source>Port</source>
<target>Porta</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Possibly, certificate fingerprint in server address is incorrect" xml:space="preserve">
@@ -5284,7 +5240,6 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Proxy requires password" xml:space="preserve">
<source>Proxy requires password</source>
<target>Il proxy richiede una password</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Push notifications" xml:space="preserve">
@@ -5695,7 +5650,6 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="SOCKS proxy" xml:space="preserve">
<source>SOCKS proxy</source>
<target>Proxy SOCKS</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
@@ -5811,7 +5765,6 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Saving %lld messages" xml:space="preserve">
<source>Saving %lld messages</source>
<target>Salvataggio di %lld messaggi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scale" xml:space="preserve">
@@ -6114,10 +6067,6 @@ Attivalo nelle impostazioni *Rete e server*.</target>
<target>Inviato via proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Indirizzo server</target>
@@ -6957,14 +6906,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzionalità.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Per registrare un messaggio vocale, concedi l'autorizzazione all'uso del microfono.</target>
@@ -7234,7 +7175,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="Use SOCKS proxy" xml:space="preserve">
<source>Use SOCKS proxy</source>
<target>Usa proxy SOCKS</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
@@ -7302,6 +7242,11 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
<target>Usa l'app con una mano sola.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Profilo utente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Selezione utente</target>
@@ -7309,7 +7254,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="Username" xml:space="preserve">
<source>Username</source>
<target>Nome utente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
@@ -7572,6 +7516,11 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
<target>Server XFTP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Tu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>**Non devi** usare lo stesso database su due dispositivi.</target>
@@ -7953,7 +7902,6 @@ Ripetere la richiesta di connessione?</target>
</trans-unit>
<trans-unit id="Your credentials may be sent unencrypted." xml:space="preserve">
<source>Your credentials may be sent unencrypted.</source>
<target>Le credenziali potrebbero essere inviate in chiaro.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your current chat database will be DELETED and REPLACED with the imported one." xml:space="preserve">
@@ -8173,7 +8121,7 @@ Ripetere la richiesta di connessione?</target>
</trans-unit>
<trans-unit id="changed your role to %@" xml:space="preserve">
<source>changed your role to %@</source>
<target>ha cambiato il tuo ruolo in %@</target>
<target>cambiato il tuo ruolo in %@</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="changing address for %@…" xml:space="preserve">
@@ -727,7 +727,7 @@
</trans-unit>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -915,10 +915,6 @@
<target>アプリのパスコードは自己破壊パスコードに置き換えられます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>アプリのバージョン</target>
@@ -1319,11 +1315,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>ユーザープロフィール</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<note>No comment provided by engineer.</note>
@@ -2860,7 +2851,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>プロフィール切り替えにエラー発生!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -2903,7 +2894,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>エラー : %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4224,14 +4216,6 @@ This is your link for group %@!</source>
<target>新しいパスコード</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<note>No comment provided by engineer.</note>
@@ -4346,14 +4330,6 @@ This is your link for group %@!</source>
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>音声メッセージを録音する権限がありません</target>
@@ -5708,10 +5684,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Sent via proxy</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<note>No comment provided by engineer.</note>
@@ -6488,14 +6460,6 @@ You will be prompted to complete authentication before this feature is enabled.<
オンにするには、認証ステップが行われます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>音声メッセージを録音する場合は、マイクの使用を許可してください。</target>
@@ -6805,6 +6769,11 @@ To connect, please ask your contact to create another connection link and check
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>ユーザープロフィール</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<note>No comment provided by engineer.</note>
@@ -7049,6 +7018,11 @@ To connect, please ask your contact to create another connection link and check
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>あなた</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<note>No comment provided by engineer.</note>
@@ -164,22 +164,18 @@
</trans-unit>
<trans-unit id="%d file(s) are still being downloaded." xml:space="preserve">
<source>%d file(s) are still being downloaded.</source>
<target>%d bestand(en) worden nog gedownload.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) failed to download." xml:space="preserve">
<source>%d file(s) failed to download.</source>
<target>%d bestand(en) konden niet worden gedownload.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were deleted." xml:space="preserve">
<source>%d file(s) were deleted.</source>
<target>%d bestand(en) zijn verwijderd.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d file(s) were not downloaded." xml:space="preserve">
<source>%d file(s) were not downloaded.</source>
<target>%d bestand(en) zijn niet gedownload.</target>
<note>forward confirmation reason</note>
</trans-unit>
<trans-unit id="%d hours" xml:space="preserve">
@@ -189,7 +185,6 @@
</trans-unit>
<trans-unit id="%d messages not forwarded" xml:space="preserve">
<source>%d messages not forwarded</source>
<target>%d berichten niet doorgestuurd</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="%d min" xml:space="preserve">
@@ -753,7 +748,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Alle profielen</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -945,10 +940,6 @@
<target>De app-toegangscode wordt vervangen door een zelfvernietigings wachtwoord.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>App versie</target>
@@ -1385,11 +1376,6 @@
<target>Chatvoorkeuren zijn gewijzigd.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Gebruikers profiel</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Chat thema</target>
@@ -1487,7 +1473,7 @@
</trans-unit>
<trans-unit id="Completed" xml:space="preserve">
<source>Completed</source>
<target>Voltooid</target>
<target>voltooid</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure ICE servers" xml:space="preserve">
@@ -2465,7 +2451,6 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit>
<trans-unit id="Do not use credentials with proxy." xml:space="preserve">
<source>Do not use credentials with proxy.</source>
<target>Gebruik geen inloggegevens met proxy.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Don't create address" xml:space="preserve">
@@ -2511,7 +2496,6 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit>
<trans-unit id="Download files" xml:space="preserve">
<source>Download files</source>
<target>Bestanden downloaden</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Downloaded" xml:space="preserve">
@@ -3042,7 +3026,7 @@ Dit is uw eigen eenmalige link!</target>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Fout bij wisselen van profiel!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3087,7 +3071,8 @@ Dit is uw eigen eenmalige link!</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Fout: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -3177,8 +3162,6 @@ Dit is uw eigen eenmalige link!</target>
<trans-unit id="File errors:&#10;%@" xml:space="preserve">
<source>File errors:
%@</source>
<target>Bestandsfouten:
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="File not found - most likely file was deleted or cancelled." xml:space="preserve">
@@ -3318,7 +3301,6 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit>
<trans-unit id="Forward %d message(s)?" xml:space="preserve">
<source>Forward %d message(s)?</source>
<target>%d bericht(en) doorsturen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
@@ -3328,12 +3310,10 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit>
<trans-unit id="Forward messages" xml:space="preserve">
<source>Forward messages</source>
<target>Berichten doorsturen</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Forward messages without files?" xml:space="preserve">
<source>Forward messages without files?</source>
<target>Berichten doorsturen zonder bestanden?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
@@ -3348,7 +3328,6 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit>
<trans-unit id="Forwarding %lld messages" xml:space="preserve">
<source>Forwarding %lld messages</source>
<target>%lld berichten doorsturen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server %@ failed to connect to destination server %@. Please try later." xml:space="preserve">
@@ -3647,7 +3626,6 @@ Fout: %2$@</target>
</trans-unit>
<trans-unit id="IP address" xml:space="preserve">
<source>IP address</source>
<target>IP-adres</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="If you can't meet in person, show QR code in a video call, or share the link." xml:space="preserve">
@@ -4352,7 +4330,6 @@ Dit is jouw link voor groep %@!</target>
</trans-unit>
<trans-unit id="Messages were deleted after you selected them." xml:space="preserve">
<source>Messages were deleted after you selected them.</source>
<target>Berichten zijn verwijderd nadat u ze had geselecteerd.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
@@ -4510,14 +4487,6 @@ Dit is jouw link voor groep %@!</target>
<target>Nieuwe toegangscode</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Nieuw gesprek</target>
@@ -4638,14 +4607,6 @@ Dit is jouw link voor groep %@!</target>
<target>Geen netwerkverbinding</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Geen toestemming om spraakbericht op te nemen</target>
@@ -4668,7 +4629,6 @@ Dit is jouw link voor groep %@!</target>
</trans-unit>
<trans-unit id="Nothing to forward!" xml:space="preserve">
<source>Nothing to forward!</source>
<target>Niets om door te sturen!</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve">
@@ -4897,8 +4857,6 @@ Vereist het inschakelen van VPN.</target>
<trans-unit id="Other file errors:&#10;%@" xml:space="preserve">
<source>Other file errors:
%@</source>
<target>Andere bestandsfouten:
%@</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
@@ -4938,7 +4896,6 @@ Vereist het inschakelen van VPN.</target>
</trans-unit>
<trans-unit id="Password" xml:space="preserve">
<source>Password</source>
<target>Wachtwoord</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Password to show" xml:space="preserve">
@@ -5092,7 +5049,6 @@ Fout: %@</target>
</trans-unit>
<trans-unit id="Port" xml:space="preserve">
<source>Port</source>
<target>Poort</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Possibly, certificate fingerprint in server address is incorrect" xml:space="preserve">
@@ -5284,7 +5240,6 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
</trans-unit>
<trans-unit id="Proxy requires password" xml:space="preserve">
<source>Proxy requires password</source>
<target>Proxy vereist wachtwoord</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Push notifications" xml:space="preserve">
@@ -5695,7 +5650,6 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
</trans-unit>
<trans-unit id="SOCKS proxy" xml:space="preserve">
<source>SOCKS proxy</source>
<target>SOCKS proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
@@ -5811,7 +5765,6 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
</trans-unit>
<trans-unit id="Saving %lld messages" xml:space="preserve">
<source>Saving %lld messages</source>
<target>%lld berichten opslaan</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scale" xml:space="preserve">
@@ -6114,10 +6067,6 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<target>Verzonden via proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Server adres</target>
@@ -6575,17 +6524,17 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
</trans-unit>
<trans-unit id="Subscribed" xml:space="preserve">
<source>Subscribed</source>
<target>Subscribed</target>
<target>Ingeschreven</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription errors" xml:space="preserve">
<source>Subscription errors</source>
<target>Subscription fouten</target>
<target>Inschrijving fouten</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<target>Subscriptions genegeerd</target>
<target>Inschrijvingen genegeerd</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Support SimpleX Chat" xml:space="preserve">
@@ -6956,14 +6905,6 @@ You will be prompted to complete authentication before this feature is enabled.<
U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingeschakeld.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Geef toestemming om de microfoon te gebruiken om een spraakbericht op te nemen.</target>
@@ -7233,7 +7174,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
</trans-unit>
<trans-unit id="Use SOCKS proxy" xml:space="preserve">
<source>Use SOCKS proxy</source>
<target>Gebruik SOCKS proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
@@ -7301,6 +7241,11 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
<target>Gebruik de app met één hand.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Gebruikers profiel</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Gebruikersselectie</target>
@@ -7308,7 +7253,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
</trans-unit>
<trans-unit id="Username" xml:space="preserve">
<source>Username</source>
<target>Gebruikersnaam</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
@@ -7571,6 +7515,11 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
<target>XFTP server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Jij</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>U **mag** niet dezelfde database op twee apparaten gebruiken.</target>
@@ -7952,7 +7901,6 @@ Verbindingsverzoek herhalen?</target>
</trans-unit>
<trans-unit id="Your credentials may be sent unencrypted." xml:space="preserve">
<source>Your credentials may be sent unencrypted.</source>
<target>Uw inloggegevens worden mogelijk niet-versleuteld verzonden.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your current chat database will be DELETED and REPLACED with the imported one." xml:space="preserve">
@@ -8933,7 +8881,7 @@ laatst ontvangen bericht: %2$@</target>
</trans-unit>
<trans-unit id="you are observer" xml:space="preserve">
<source>you are observer</source>
<target>je bent waarnemer</target>
<target>jij bent waarnemer</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you blocked %@" xml:space="preserve">
@@ -8963,7 +8911,7 @@ laatst ontvangen bericht: %2$@</target>
</trans-unit>
<trans-unit id="you left" xml:space="preserve">
<source>you left</source>
<target>je bent vertrokken</target>
<target>jij bent vertrokken</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you removed %@" xml:space="preserve">
@@ -747,7 +747,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Wszystkie profile</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -939,10 +939,6 @@
<target>Pin aplikacji został zastąpiony pinem samozniszczenia.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Wersja aplikacji</target>
@@ -1377,11 +1373,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Profil użytkownika</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Motyw czatu</target>
@@ -3027,7 +3018,7 @@ To jest twój jednorazowy link!</target>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Błąd przełączania profilu!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3072,7 +3063,8 @@ To jest twój jednorazowy link!</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Błąd: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4486,14 +4478,6 @@ To jest twój link do grupy %@!</target>
<target>Nowy Pin</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Nowy czat</target>
@@ -4614,14 +4598,6 @@ To jest twój link do grupy %@!</target>
<target>Brak połączenia z siecią</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Brak uprawnień do nagrywania wiadomości głosowej</target>
@@ -6079,10 +6055,6 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<target>Wysłano przez proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Adres serwera</target>
@@ -6917,14 +6889,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Aby nagrać wiadomość głosową należy udzielić zgody na użycie Mikrofonu.</target>
@@ -7261,6 +7225,11 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
<target>Korzystaj z aplikacji jedną ręką.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Profil użytkownika</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Wybór użytkownika</target>
@@ -7530,6 +7499,11 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
<target>Serwer XFTP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Ty</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>**Nie możesz** używać tej samej bazy na dwóch urządzeniach.</target>
@@ -5502,74 +5502,6 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi
<source>Archived contacts</source>
<target state="translated">Contatos arquivados</target>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve" approved="no">
<source>Cellular</source>
<target state="translated">Rede móvel</target>
</trans-unit>
<trans-unit id="%d file(s) failed to download." xml:space="preserve" approved="no">
<source>%d file(s) failed to download.</source>
<target state="translated">%d arquivo(s) falharam ao ser baixados.</target>
</trans-unit>
<trans-unit id="%d file(s) were deleted." xml:space="preserve" approved="no">
<source>%d file(s) were deleted.</source>
<target state="translated">%d arquivo(s) foram apagados.</target>
</trans-unit>
<trans-unit id="%d messages not forwarded" xml:space="preserve" approved="no">
<source>%d messages not forwarded</source>
<target state="translated">%d mensagens não encaminhadas</target>
</trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve" approved="no">
<source>Bad desktop address</source>
<target state="translated">Endereço de desktop incorreto</target>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve" approved="no">
<source>Blur for better privacy.</source>
<target state="translated">Borrar para melhor privacidade.</target>
</trans-unit>
<trans-unit id="%d file(s) are still being downloaded." xml:space="preserve" approved="no">
<source>%d file(s) are still being downloaded.</source>
<target state="translated">%d arquivo(s) ainda estão sendo baixados.</target>
</trans-unit>
<trans-unit id="%d file(s) were not downloaded." xml:space="preserve" approved="no">
<source>%d file(s) were not downloaded.</source>
<target state="translated">%d arquivo(s) não foram baixados.</target>
</trans-unit>
<trans-unit id="Chat colors" xml:space="preserve" approved="no">
<source>Chat colors</source>
<target state="translated">Cores do chat</target>
</trans-unit>
<trans-unit id="%lld group events" xml:space="preserve" approved="no">
<source>%lld group events</source>
<target state="translated">%lld eventos do grupo</target>
</trans-unit>
<trans-unit id="%lld messages moderated by %@" xml:space="preserve" approved="no">
<source>%lld messages moderated by %@</source>
<target state="translated">%lld mensagens moderadas por %@</target>
</trans-unit>
<trans-unit id="%@, %@" xml:space="preserve" approved="no">
<source>%1$@, %2$@</source>
<target state="translated">%1$@, %2$@</target>
</trans-unit>
<trans-unit id="%lld new interface languages" xml:space="preserve" approved="no">
<source>%lld new interface languages</source>
<target state="translated">%lld novos idiomas de interface</target>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve" approved="no">
<source>Better networking</source>
<target state="translated">Melhores redes</target>
</trans-unit>
<trans-unit id="Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" xml:space="preserve" approved="no">
<source>Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</source>
<target state="translated">Búlgaro, Finlandês, Tailandês e Ucraniano - obrigado aos usuários e [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</target>
</trans-unit>
<trans-unit id="Better groups" xml:space="preserve" approved="no">
<source>Better groups</source>
<target state="translated">Melhores grupos</target>
</trans-unit>
<trans-unit id="Capacity exceeded - recipient did not receive previously sent messages." xml:space="preserve" approved="no">
<source>Capacity exceeded - recipient did not receive previously sent messages.</source>
<target state="translated">Capacidade excedida - o destinatário não recebeu as mensagens enviadas anteriormente.</target>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="pt-BR" datatype="plaintext">
@@ -747,7 +747,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Все профили</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -939,10 +939,6 @@
<target>Код доступа в приложение будет заменен кодом самоуничтожения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Версия приложения</target>
@@ -1377,11 +1373,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Профиль чата</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Тема чата</target>
@@ -3027,7 +3018,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Ошибка выбора профиля!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3072,7 +3063,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Ошибка: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4486,14 +4478,6 @@ This is your link for group %@!</source>
<target>Новый Код</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Новый чат</target>
@@ -4614,14 +4598,6 @@ This is your link for group %@!</source>
<target>Нет интернет-соединения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Нет разрешения для записи голосового сообщения</target>
@@ -6079,10 +6055,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Отправлено через прокси</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Адрес сервера</target>
@@ -6917,14 +6889,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Вам будет нужно пройти аутентификацию для включения блокировки.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Для записи голосового сообщения, пожалуйста разрешите доступ к микрофону.</target>
@@ -7261,6 +7225,11 @@ To connect, please ask your contact to create another connection link and check
<target>Используйте приложение одной рукой.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Профиль чата</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Выбор пользователя</target>
@@ -7530,6 +7499,11 @@ To connect, please ask your contact to create another connection link and check
<target>XFTP сервер</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Вы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>Вы **не должны** использовать одну и ту же базу данных на двух устройствах.</target>
@@ -702,7 +702,7 @@
</trans-unit>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -884,10 +884,6 @@
<target>รหัสผ่านแอปจะถูกแทนที่ด้วยรหัสผ่านที่ทำลายตัวเอง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>เวอร์ชันแอป</target>
@@ -1287,11 +1283,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>โปรไฟล์ผู้ใช้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<note>No comment provided by engineer.</note>
@@ -2820,7 +2811,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>เกิดข้อผิดพลาดในการเปลี่ยนโปรไฟล์!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -2863,7 +2854,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>ข้อผิดพลาด: % @</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4182,14 +4174,6 @@ This is your link for group %@!</source>
<target>รหัสผ่านใหม่</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<note>No comment provided by engineer.</note>
@@ -4302,14 +4286,6 @@ This is your link for group %@!</source>
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>ไม่อนุญาตให้บันทึกข้อความเสียง</target>
@@ -5665,10 +5641,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Sent via proxy</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<note>No comment provided by engineer.</note>
@@ -6442,14 +6414,6 @@ You will be prompted to complete authentication before this feature is enabled.<
คุณจะได้รับแจ้งให้ยืนยันตัวตนให้เสร็จสมบูรณ์ก่อนที่จะเปิดใช้งานคุณลักษณะนี้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>ในการบันทึกข้อความเสียง โปรดให้สิทธิ์ในการใช้ไมโครโฟน</target>
@@ -6757,6 +6721,11 @@ To connect, please ask your contact to create another connection link and check
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>โปรไฟล์ผู้ใช้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<note>No comment provided by engineer.</note>
@@ -7001,6 +6970,11 @@ To connect, please ask your contact to create another connection link and check
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>คุณ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<note>No comment provided by engineer.</note>
@@ -737,7 +737,7 @@
</trans-unit>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -927,10 +927,6 @@
<target>Uygulama parolası kendi kendini imha eden parolayla değiştirildi.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Uygulama sürümü</target>
@@ -1349,11 +1345,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Kullanıcı profili</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<note>No comment provided by engineer.</note>
@@ -2951,7 +2942,7 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Profil değiştirilirken hata oluştu!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -2996,7 +2987,8 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Hata: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4382,14 +4374,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Yeni şifre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Yeni sohbet</target>
@@ -4506,14 +4490,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Ağ bağlantısı yok</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Sesli mesaj kaydetmek için izin yok</target>
@@ -5926,10 +5902,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Sent via proxy</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<note>No comment provided by engineer.</note>
@@ -6736,14 +6708,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenecektir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Sesli mesaj kaydetmek için lütfen Mikrofon kullanım izni verin.</target>
@@ -7071,6 +7035,11 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Kullanıcı profili</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<note>No comment provided by engineer.</note>
@@ -7335,6 +7304,11 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Sen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>Aynı veritabanını iki cihazda **kullanmamalısınız**.</target>
@@ -747,7 +747,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Всі профілі</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -939,10 +939,6 @@
<target>Пароль програми замінено на пароль самознищення.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>Версія програми</target>
@@ -1377,11 +1373,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>Профіль користувача</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Тема чату</target>
@@ -3027,7 +3018,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>Помилка перемикання профілю!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3072,7 +3063,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Помилка: %@</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4486,14 +4478,6 @@ This is your link for group %@!</source>
<target>Новий пароль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>Новий чат</target>
@@ -4614,14 +4598,6 @@ This is your link for group %@!</source>
<target>Немає підключення до мережі</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Немає дозволу на запис голосового повідомлення</target>
@@ -6079,10 +6055,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Відправлено через проксі</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Адреса сервера</target>
@@ -6917,14 +6889,6 @@ You will be prompted to complete authentication before this feature is enabled.<
Перед увімкненням цієї функції вам буде запропоновано пройти автентифікацію.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>Щоб записати голосове повідомлення, будь ласка, надайте дозвіл на використання мікрофону.</target>
@@ -7261,6 +7225,11 @@ To connect, please ask your contact to create another connection link and check
<target>Використовуйте додаток однією рукою.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Профіль користувача</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Вибір користувача</target>
@@ -7530,6 +7499,11 @@ To connect, please ask your contact to create another connection link and check
<target>XFTP-сервер</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Ти</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>Ви **не повинні використовувати** одну і ту ж базу даних на двох пристроях.</target>
@@ -747,7 +747,7 @@
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>所有配置文件</target>
<note>profile dropdown</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
<source>All your contacts will remain connected.</source>
@@ -939,10 +939,6 @@
<target>应用程序密码被替换为自毁密码。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App session" xml:space="preserve">
<source>App session</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<source>App version</source>
<target>应用程序版本</target>
@@ -1377,11 +1373,6 @@
<source>Chat preferences were changed.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve">
<source>Chat profile</source>
<target>用户资料</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>聊天主题</target>
@@ -3027,7 +3018,7 @@ This is your own one-time link!</source>
<trans-unit id="Error switching profile!" xml:space="preserve">
<source>Error switching profile!</source>
<target>切换资料错误!</target>
<note>alertTitle</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error synchronizing connection" xml:space="preserve">
<source>Error synchronizing connection</source>
@@ -3072,7 +3063,8 @@ This is your own one-time link!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>错误: @</target>
<note>alert message</note>
<note>file error text
snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -4486,14 +4478,6 @@ This is your link for group %@!</source>
<target>新密码</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used every time you start the app." xml:space="preserve">
<source>New SOCKS credentials will be used every time you start the app.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New SOCKS credentials will be used for each server." xml:space="preserve">
<source>New SOCKS credentials will be used for each server.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat" xml:space="preserve">
<source>New chat</source>
<target>新聊天</target>
@@ -4614,14 +4598,6 @@ This is your link for group %@!</source>
<target>无网络连接</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record speech" xml:space="preserve">
<source>No permission to record speech</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record video" xml:space="preserve">
<source>No permission to record video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>没有录制语音消息的权限</target>
@@ -6079,10 +6055,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>通过代理发送</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>服务器地址</target>
@@ -6917,14 +6889,6 @@ You will be prompted to complete authentication before this feature is enabled.<
在启用此功能之前,系统将提示您完成身份验证。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record speech please grant permission to use Microphone." xml:space="preserve">
<source>To record speech please grant permission to use Microphone.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record video please grant permission to use Camera." xml:space="preserve">
<source>To record video please grant permission to use Camera.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To record voice message please grant permission to use Microphone." xml:space="preserve">
<source>To record voice message please grant permission to use Microphone.</source>
<target>请授权使用麦克风以录制语音消息。</target>
@@ -7261,6 +7225,11 @@ To connect, please ask your contact to create another connection link and check
<target>用一只手使用应用程序。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>用户资料</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>用户选择</target>
@@ -7530,6 +7499,11 @@ To connect, please ask your contact to create another connection link and check
<target>XFTP 服务器</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>您</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You **must not** use the same database on two devices." xml:space="preserve">
<source>You **must not** use the same database on two devices.</source>
<target>您 **不得** 在两台设备上使用相同的数据库。</target>
+14 -46
View File
@@ -26,7 +26,7 @@ enum NSENotification {
case nse(UNMutableNotificationContent)
case callkit(RcvCallInvitation)
case empty
case msgInfo(NtfMsgAckInfo)
case msgInfo(NtfMsgInfo)
var isCallInvitation: Bool {
switch self {
@@ -119,9 +119,7 @@ class NotificationService: UNNotificationServiceExtension {
var threadId: UUID? = NSEThreads.shared.newThread()
var notificationInfo: NtfMessages?
var receiveEntityId: String?
var receiveConnId: String?
var expectedMessage: String?
var allowedGetNextAttempts: Int = 3
// return true if the message is taken - it prevents sending it to another NotificationService instance for processing
var shouldProcessNtf = false
var appSubscriber: AppSubscriber?
@@ -193,21 +191,17 @@ class NotificationService: UNNotificationServiceExtension {
let dbStatus = startChat()
if case .ok = dbStatus,
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.receivedMsg_ == nil ? 0 : 1))")
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessage_ == nil ? 0 : 1))")
if let connEntity = ntfInfo.connEntity_ {
setBestAttemptNtf(
ntfInfo.ntfsEnabled
? .nse(createConnectionEventNtf(ntfInfo.user, connEntity))
: .empty
)
if let id = connEntity.id, ntfInfo.expectedMsg_ != nil {
if let id = connEntity.id, ntfInfo.msgTs != nil {
notificationInfo = ntfInfo
receiveEntityId = id
receiveConnId = connEntity.conn.agentConnId
let expectedMsgId = ntfInfo.expectedMsg_?.msgId
let receivedMsgId = ntfInfo.receivedMsg_?.msgId
logger.debug("NotificationService: receiveNtfMessages: expectedMsgId = \(expectedMsgId ?? "nil", privacy: .private), receivedMsgId = \(receivedMsgId ?? "nil", privacy: .private)")
expectedMessage = expectedMsgId
expectedMessage = ntfInfo.ntfMessage_.flatMap { $0.msgId }
shouldProcessNtf = true
return
}
@@ -225,34 +219,22 @@ class NotificationService: UNNotificationServiceExtension {
}
func processReceivedNtf(_ ntf: NSENotification) -> Bool {
guard let ntfInfo = notificationInfo, let expectedMsgTs = ntfInfo.expectedMsg_?.msgTs else { return false }
guard let ntfInfo = notificationInfo, let msgTs = ntfInfo.msgTs else { return false }
if !ntfInfo.user.showNotifications {
self.setBestAttemptNtf(.empty)
}
if case let .msgInfo(info) = ntf {
if info.msgId == expectedMessage {
expectedMessage = nil
logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): expected")
logger.debug("NotificationService processNtf: msgInfo")
self.deliverBestAttemptNtf()
return true
} else if let msgTs = info.msgTs_, msgTs > expectedMsgTs {
logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unexpected msgInfo, let other instance to process it, stopping this one")
} else if info.msgTs > msgTs {
logger.debug("NotificationService processNtf: unexpected msgInfo, let other instance to process it, stopping this one")
self.deliverBestAttemptNtf()
return false
} else if allowedGetNextAttempts > 0, let receiveConnId = receiveConnId {
logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unexpected msgInfo, get next message")
allowedGetNextAttempts -= 1
if let receivedMsg = apiGetConnNtfMessage(connId: receiveConnId) {
logger.debug("NotificationService processNtf, on apiGetConnNtfMessage: msgInfo msgId = \(info.msgId, privacy: .private), receivedMsg msgId = \(receivedMsg.msgId, privacy: .private)")
return true
} else {
logger.debug("NotificationService processNtf, on apiGetConnNtfMessage: msgInfo msgId = \(info.msgId, privacy: .private): no next message, deliver best attempt")
self.deliverBestAttemptNtf()
return false
}
} else {
logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unknown message, let other instance to process it")
self.deliverBestAttemptNtf()
logger.debug("NotificationService processNtf: unknown message, let other instance to process it")
return false
}
} else if ntfInfo.user.showNotifications {
@@ -710,9 +692,9 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
return nil
}
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
if case let .ntfMessages(user, connEntity_, expectedMsg_, receivedMsg_) = r, let user = user {
logger.debug("apiGetNtfMessage response ntfMessages: \(receivedMsg_ == nil ? 0 : 1)")
return NtfMessages(user: user, connEntity_: connEntity_, expectedMsg_: expectedMsg_, receivedMsg_: receivedMsg_)
if case let .ntfMessages(user, connEntity_, msgTs, ntfMessage_) = r, let user = user {
logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessage_ == nil ? 0 : 1)")
return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessage_: ntfMessage_)
} else if case let .chatCmdError(_, error) = r {
logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))")
} else {
@@ -721,20 +703,6 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
return nil
}
func apiGetConnNtfMessage(connId: String) -> NtfMsgInfo? {
guard apiGetActiveUser() != nil else {
logger.debug("no active user")
return nil
}
let r = sendSimpleXCmd(.apiGetConnNtfMessage(connId: connId))
if case let .connNtfMessage(receivedMsg_) = r {
logger.debug("apiGetConnNtfMessage response receivedMsg_: \(receivedMsg_ == nil ? 0 : 1)")
return receivedMsg_
}
logger.debug("apiGetConnNtfMessage error: \(responseError(r))")
return nil
}
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? {
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline))
@@ -772,8 +740,8 @@ func setNetworkConfig(_ cfg: NetCfg) throws {
struct NtfMessages {
var user: User
var connEntity_: ConnectionEntity?
var expectedMsg_: NtfMsgInfo?
var receivedMsg_: NtfMsgInfo?
var msgTs: Date?
var ntfMessage_: NtfMsgInfo?
var ntfsEnabled: Bool {
user.showNotifications && (connEntity_?.ntfsEnabled ?? false)
+3 -3
View File
@@ -417,7 +417,7 @@ fileprivate func getSharedContent(_ ip: NSItemProvider) async -> Result<SharedCo
let data = try? Data(contentsOf: url),
let image = UIImage(data: data),
let cryptoFile = saveFile(data, generateNewFileName("IMG", "gif"), encrypted: privacyEncryptLocalFilesGroupDefault.get()),
let preview = await resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) {
let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) {
.success(.image(preview: preview, cryptoFile: cryptoFile))
} else { .failure(ErrorAlert("Error preparing message")) }
@@ -425,7 +425,7 @@ fileprivate func getSharedContent(_ ip: NSItemProvider) async -> Result<SharedCo
} else {
if let image = await staticImage(),
let cryptoFile = saveImage(image),
let preview = await resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) {
let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) {
.success(.image(preview: preview, cryptoFile: cryptoFile))
} else { .failure(ErrorAlert("Error preparing message")) }
}
@@ -435,7 +435,7 @@ fileprivate func getSharedContent(_ ip: NSItemProvider) async -> Result<SharedCo
if let url = try? await inPlaceUrl(type: type),
let trancodedUrl = await transcodeVideo(from: url),
let (image, duration) = AVAsset(url: trancodedUrl).generatePreview(),
let preview = await resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE),
let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE),
let cryptoFile = moveTempFileFromURL(trancodedUrl) {
try? FileManager.default.removeItem(at: trancodedUrl)
return .success(.movie(preview: preview, duration: duration, cryptoFile: cryptoFile))
@@ -29,7 +29,7 @@
"Database error" = "Error en base de datos";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "La contraseña de la base de datos es diferente a la almacenada en keychain.";
"Database passphrase is different from saved in the keychain." = "La contraseña de la base de datos es distinta a la almacenada en keychain.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "Se requiere la contraseña de la base de datos para abrir la aplicación.";
@@ -26,13 +26,13 @@
"Database encrypted!" = "Adatbázis titkosítva!";
/* No comment provided by engineer. */
"Database error" = "Adatbázishiba";
"Database error" = "Adatbázis hiba";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata eltér a kulcstartóban lévőtől.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "Adatbázis-jelmondat szükséges a csevegés megnyitásához.";
"Database passphrase is required to open chat." = "Adatbázis jelmondat szükséges a csevegés megnyitásához.";
/* No comment provided by engineer. */
"Database upgrade required" = "Adatbázis fejlesztése szükséges";
@@ -50,7 +50,7 @@
"File error" = "Fájlhiba";
/* No comment provided by engineer. */
"Incompatible database version" = "Nem kompatibilis adatbázis-verzió";
"Incompatible database version" = "Nem kompatibilis adatbázis verzió";
/* No comment provided by engineer. */
"Invalid migration confirmation" = "Érvénytelen átköltöztetési visszaigazolás";
@@ -95,7 +95,7 @@
"Slow network?" = "Lassú internetkapcsolat?";
/* No comment provided by engineer. */
"Unknown database error: %@" = "Ismeretlen adatbázishiba: %@";
"Unknown database error: %@" = "Ismeretlen adatbázis hiba: %@";
/* No comment provided by engineer. */
"Unsupported format" = "Nem támogatott formátum";
@@ -104,7 +104,7 @@
"Wait" = "Várjon";
/* No comment provided by engineer. */
"Wrong database passphrase" = "Hibás adatbázis-jelmondat";
"Wrong database passphrase" = "Hibás adatbázis jelmondat";
/* No comment provided by engineer. */
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "A megosztást az Adatvédelem és biztonság / SimpleX-zár menüben engedélyezheti.";
+30 -30
View File
@@ -144,11 +144,6 @@
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
640417CD2B29B8C200CCB412 /* NewChatMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640417CB2B29B8C200CCB412 /* NewChatMenuButton.swift */; };
640417CE2B29B8C200CCB412 /* NewChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640417CC2B29B8C200CCB412 /* NewChatView.swift */; };
640548A32CB56735005DE1E4 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6405489E2CB56735005DE1E4 /* libgmpxx.a */; };
640548A42CB56735005DE1E4 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6405489F2CB56735005DE1E4 /* libgmp.a */; };
640548A52CB56735005DE1E4 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640548A02CB56735005DE1E4 /* libffi.a */; };
640548A62CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640548A12CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv-ghc9.6.3.a */; };
640548A72CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 640548A22CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv.a */; };
6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */; };
6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */; };
6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */; };
@@ -223,6 +218,11 @@
D741547A29AF90B00022400A /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547929AF90B00022400A /* PushKit.framework */; };
D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; };
D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; };
E51B923B2CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51B92362CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU.a */; };
E51B923C2CAB2B8800C212F2 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51B92372CAB2B8800C212F2 /* libgmp.a */; };
E51B923D2CAB2B8800C212F2 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51B92382CAB2B8800C212F2 /* libffi.a */; };
E51B923E2CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51B92392CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU-ghc9.6.3.a */; };
E51B923F2CAB2B8800C212F2 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51B923A2CAB2B8800C212F2 /* libgmpxx.a */; };
E51CC1E62C62085600DB91FE /* OneHandUICard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51CC1E52C62085600DB91FE /* OneHandUICard.swift */; };
E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; };
E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; };
@@ -486,11 +486,6 @@
5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; };
640417CB2B29B8C200CCB412 /* NewChatMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewChatMenuButton.swift; sourceTree = "<group>"; };
640417CC2B29B8C200CCB412 /* NewChatView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewChatView.swift; sourceTree = "<group>"; };
6405489E2CB56735005DE1E4 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
6405489F2CB56735005DE1E4 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
640548A02CB56735005DE1E4 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
640548A12CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv-ghc9.6.3.a"; sourceTree = "<group>"; };
640548A22CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv.a"; sourceTree = "<group>"; };
6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIInvalidJSONView.swift; sourceTree = "<group>"; };
6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextInvitingContactMemberView.swift; sourceTree = "<group>"; };
6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMemberCreatedContactView.swift; sourceTree = "<group>"; };
@@ -564,6 +559,11 @@
D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
E51B92362CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU.a"; sourceTree = "<group>"; };
E51B92372CAB2B8800C212F2 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
E51B92382CAB2B8800C212F2 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
E51B92392CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU-ghc9.6.3.a"; sourceTree = "<group>"; };
E51B923A2CAB2B8800C212F2 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
E51CC1E52C62085600DB91FE /* OneHandUICard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneHandUICard.swift; sourceTree = "<group>"; };
E5DCF9702C590272007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
E5DCF9722C590274007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -655,14 +655,14 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
640548A32CB56735005DE1E4 /* libgmpxx.a in Frameworks */,
640548A72CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv.a in Frameworks */,
640548A52CB56735005DE1E4 /* libffi.a in Frameworks */,
640548A42CB56735005DE1E4 /* libgmp.a in Frameworks */,
E51B923B2CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU.a in Frameworks */,
E51B923D2CAB2B8800C212F2 /* libffi.a in Frameworks */,
E51B923C2CAB2B8800C212F2 /* libgmp.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
640548A62CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv-ghc9.6.3.a in Frameworks */,
E51B923F2CAB2B8800C212F2 /* libgmpxx.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
E51B923E2CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU-ghc9.6.3.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -739,11 +739,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
640548A02CB56735005DE1E4 /* libffi.a */,
6405489F2CB56735005DE1E4 /* libgmp.a */,
6405489E2CB56735005DE1E4 /* libgmpxx.a */,
640548A12CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv-ghc9.6.3.a */,
640548A22CB56735005DE1E4 /* libHSsimplex-chat-6.1.0.7-EtclnBf7vnkLnhnDA1lixv.a */,
E51B92382CAB2B8800C212F2 /* libffi.a */,
E51B92372CAB2B8800C212F2 /* libgmp.a */,
E51B923A2CAB2B8800C212F2 /* libgmpxx.a */,
E51B92392CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU-ghc9.6.3.a */,
E51B92362CAB2B8800C212F2 /* libHSsimplex-chat-6.1.0.5-HWShuJBYoppEOt1hLB8GPU.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1899,7 +1899,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -1948,7 +1948,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -1989,7 +1989,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2009,7 +2009,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2034,7 +2034,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
@@ -2071,7 +2071,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
@@ -2108,7 +2108,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2159,7 +2159,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2210,7 +2210,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -2244,7 +2244,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 242;
CURRENT_PROJECT_VERSION = 241;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
+6 -22
View File
@@ -56,7 +56,6 @@ public enum ChatCommand {
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
case apiDeleteToken(token: DeviceToken)
case apiGetNtfMessage(nonce: String, encNtfInfo: String)
case apiGetConnNtfMessage(connId: String)
case apiNewGroup(userId: Int64, incognito: Bool, groupProfile: GroupProfile)
case apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole)
case apiJoinGroup(groupId: Int64)
@@ -215,7 +214,6 @@ public enum ChatCommand {
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
case let .apiDeleteToken(token): return "/_ntf delete \(token.cmdString)"
case let .apiGetNtfMessage(nonce, encNtfInfo): return "/_ntf message \(nonce) \(encNtfInfo)"
case let .apiGetConnNtfMessage(connId): return "/_ntf conn message \(connId)"
case let .apiNewGroup(userId, incognito, groupProfile): return "/_group \(userId) incognito=\(onOff(incognito)) \(encodeJSON(groupProfile))"
case let .apiAddMember(groupId, contactId, memberRole): return "/_add #\(groupId) \(contactId) \(memberRole)"
case let .apiJoinGroup(groupId): return "/_join #\(groupId)"
@@ -370,7 +368,6 @@ public enum ChatCommand {
case .apiVerifyToken: return "apiVerifyToken"
case .apiDeleteToken: return "apiDeleteToken"
case .apiGetNtfMessage: return "apiGetNtfMessage"
case .apiGetConnNtfMessage: return "apiGetConnNtfMessage"
case .apiNewGroup: return "apiNewGroup"
case .apiAddMember: return "apiAddMember"
case .apiJoinGroup: return "apiJoinGroup"
@@ -682,9 +679,8 @@ public enum ChatResponse: Decodable, Error {
case callInvitations(callInvitations: [RcvCallInvitation])
case ntfTokenStatus(status: NtfTknStatus)
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode, ntfServer: String)
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, expectedMsg_: NtfMsgInfo?, receivedMsg_: NtfMsgInfo?)
case connNtfMessage(receivedMsg_: NtfMsgInfo?)
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgAckInfo)
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessage_: NtfMsgInfo?)
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo)
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
case contactDisabled(user: UserRef, contact: Contact)
// remote desktop responses/events
@@ -852,7 +848,6 @@ public enum ChatResponse: Decodable, Error {
case .ntfTokenStatus: return "ntfTokenStatus"
case .ntfToken: return "ntfToken"
case .ntfMessages: return "ntfMessages"
case .connNtfMessage: return "connNtfMessage"
case .ntfMessage: return "ntfMessage"
case .contactConnectionDeleted: return "contactConnectionDeleted"
case .contactDisabled: return "contactDisabled"
@@ -1029,8 +1024,7 @@ public enum ChatResponse: Decodable, Error {
case let .callInvitations(invs): return String(describing: invs)
case let .ntfTokenStatus(status): return String(describing: status)
case let .ntfToken(token, status, ntfMode, ntfServer): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)\nntfServer: \(ntfServer)"
case let .ntfMessages(u, connEntity, expectedMsg_, receivedMsg_): return withUser(u, "connEntity: \(String(describing: connEntity))\nexpectedMsg_: \(String(describing: expectedMsg_))\nreceivedMsg_: \(String(describing: receivedMsg_))")
case let .connNtfMessage(receivedMsg_): return "receivedMsg_: \(String(describing: receivedMsg_))"
case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))")
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
case let .contactDisabled(u, contact): return withUser(u, String(describing: contact))
@@ -1359,7 +1353,7 @@ public struct NetCfg: Codable, Equatable {
public var sessionMode = TransportSessionMode.user
public var smpProxyMode: SMPProxyMode = .unknown
public var smpProxyFallback: SMPProxyFallback = .allowProtected
public var smpWebPort = false
var smpWebPort = false
public var tcpConnectTimeout: Int // microseconds
public var tcpTimeout: Int // microseconds
public var tcpTimeoutPerKb: Int // microseconds
@@ -1491,22 +1485,18 @@ public enum OnionHosts: String, Identifiable {
public enum TransportSessionMode: String, Codable, Identifiable {
case user
case session
case server
case entity
public var text: LocalizedStringKey {
switch self {
case .user: return "Chat profile"
case .session: return "App session"
case .server: return "Server"
case .user: return "User profile"
case .entity: return "Connection"
}
}
public var id: TransportSessionMode { self }
public static let values: [TransportSessionMode] = [.user, .session, .server, .entity]
public static let values: [TransportSessionMode] = [.user, .entity]
}
public struct KeepAliveOpts: Codable, Equatable {
@@ -2261,8 +2251,6 @@ public struct AppSettings: Codable, Equatable {
public var iosCallKitEnabled: Bool? = nil
public var iosCallKitCallsInRecents: Bool? = nil
public var uiProfileImageCornerRadius: Double? = nil
public var uiChatItemRoundness: Double? = nil
public var uiChatItemTail: Bool? = nil
public var uiColorScheme: String? = nil
public var uiDarkColorScheme: String? = nil
public var uiCurrentThemeIds: [String: String]? = nil
@@ -2295,8 +2283,6 @@ public struct AppSettings: Codable, Equatable {
if iosCallKitEnabled != def.iosCallKitEnabled { empty.iosCallKitEnabled = iosCallKitEnabled }
if iosCallKitCallsInRecents != def.iosCallKitCallsInRecents { empty.iosCallKitCallsInRecents = iosCallKitCallsInRecents }
if uiProfileImageCornerRadius != def.uiProfileImageCornerRadius { empty.uiProfileImageCornerRadius = uiProfileImageCornerRadius }
if uiChatItemRoundness != def.uiChatItemRoundness { empty.uiChatItemRoundness = uiChatItemRoundness }
if uiChatItemTail != def.uiChatItemTail { empty.uiChatItemTail = uiChatItemTail }
if uiColorScheme != def.uiColorScheme { empty.uiColorScheme = uiColorScheme }
if uiDarkColorScheme != def.uiDarkColorScheme { empty.uiDarkColorScheme = uiDarkColorScheme }
if uiCurrentThemeIds != def.uiCurrentThemeIds { empty.uiCurrentThemeIds = uiCurrentThemeIds }
@@ -2330,8 +2316,6 @@ public struct AppSettings: Codable, Equatable {
iosCallKitEnabled: true,
iosCallKitCallsInRecents: false,
uiProfileImageCornerRadius: 22.5,
uiChatItemRoundness: 0.75,
uiChatItemTail: true,
uiColorScheme: DefaultTheme.SYSTEM_THEME_NAME,
uiDarkColorScheme: DefaultTheme.SIMPLEX.themeName,
uiCurrentThemeIds: nil as [String: String]?,
+2 -3
View File
@@ -67,7 +67,7 @@ public func registerGroupDefaults() {
GROUP_DEFAULT_NTF_ENABLE_LOCAL: false,
GROUP_DEFAULT_NTF_ENABLE_PERIODIC: false,
GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS: OnionHosts.no.rawValue,
GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.session.rawValue,
GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.user.rawValue,
GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE: SMPProxyMode.unknown.rawValue,
GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK: SMPProxyFallback.allowProtected.rawValue,
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout,
@@ -232,7 +232,7 @@ public let networkUseOnionHostsGroupDefault = EnumDefault<OnionHosts>(
public let networkSessionModeGroupDefault = EnumDefault<TransportSessionMode>(
defaults: groupDefaults,
forKey: GROUP_DEFAULT_NETWORK_SESSION_MODE,
withDefault: .session
withDefault: .user
)
public let networkSMPProxyModeGroupDefault = EnumDefault<SMPProxyMode>(
@@ -357,7 +357,6 @@ public func getNetCfg() -> NetCfg {
sessionMode: sessionMode,
smpProxyMode: smpProxyMode,
smpProxyFallback: smpProxyFallback,
smpWebPort: false,
tcpConnectTimeout: tcpConnectTimeout,
tcpTimeout: tcpTimeout,
tcpTimeoutPerKb: tcpTimeoutPerKb,
-8
View File
@@ -80,14 +80,6 @@ public enum CallMediaType: String, Codable, Equatable {
case audio = "audio"
}
public enum CallMediaSource: String, Codable, Equatable {
case mic = "mic"
case camera = "camera"
case screenAudio = "screenAudio"
case screenVideo = "screenVideo"
case unknown = "unknown"
}
public enum VideoCamera: String, Codable, Equatable {
case user = "user"
case environment = "environment"
+11 -26
View File
@@ -2240,42 +2240,32 @@ public struct MemberSubError: Decodable, Hashable {
}
public enum ConnectionEntity: Decodable, Hashable {
case rcvDirectMsgConnection(entityConnection: Connection, contact: Contact?)
case rcvGroupMsgConnection(entityConnection: Connection, groupInfo: GroupInfo, groupMember: GroupMember)
case sndFileConnection(entityConnection: Connection, sndFileTransfer: SndFileTransfer)
case rcvFileConnection(entityConnection: Connection, rcvFileTransfer: RcvFileTransfer)
case userContactConnection(entityConnection: Connection, userContact: UserContact)
case rcvDirectMsgConnection(contact: Contact?)
case rcvGroupMsgConnection(groupInfo: GroupInfo, groupMember: GroupMember)
case sndFileConnection(sndFileTransfer: SndFileTransfer)
case rcvFileConnection(rcvFileTransfer: RcvFileTransfer)
case userContactConnection(userContact: UserContact)
public var id: String? {
switch self {
case let .rcvDirectMsgConnection(_, contact):
case let .rcvDirectMsgConnection(contact):
return contact?.id
case let .rcvGroupMsgConnection(_, _, groupMember):
case let .rcvGroupMsgConnection(_, groupMember):
return groupMember.id
case let .userContactConnection(_, userContact):
case let .userContactConnection(userContact):
return userContact.id
default:
return nil
}
}
public var conn: Connection {
switch self {
case let .rcvDirectMsgConnection(entityConnection, _): entityConnection
case let .rcvGroupMsgConnection(entityConnection, _, _): entityConnection
case let .sndFileConnection(entityConnection, _): entityConnection
case let .rcvFileConnection(entityConnection, _): entityConnection
case let .userContactConnection(entityConnection, _): entityConnection
}
}
public var ntfsEnabled: Bool {
switch self {
case let .rcvDirectMsgConnection(_, contact): return contact?.chatSettings.enableNtfs == .all
case let .rcvGroupMsgConnection(_, groupInfo, _): return groupInfo.chatSettings.enableNtfs == .all
case let .rcvDirectMsgConnection(contact): return contact?.chatSettings.enableNtfs == .all
case let .rcvGroupMsgConnection(groupInfo, _): return groupInfo.chatSettings.enableNtfs == .all
case .sndFileConnection: return false
case .rcvFileConnection: return false
case let .userContactConnection(_, userContact): return userContact.groupId == nil
case let .userContactConnection(userContact): return userContact.groupId == nil
}
}
}
@@ -2285,11 +2275,6 @@ public struct NtfMsgInfo: Decodable, Hashable {
public var msgTs: Date
}
public struct NtfMsgAckInfo: Decodable, Hashable {
public var msgId: String
public var msgTs_: Date?
}
public struct ChatItemDeletion: Decodable, Hashable {
public var deletedChatItem: AChatItem
public var toChatItem: AChatItem? = nil
+2 -10
View File
@@ -100,7 +100,7 @@ public func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha
return data
}
public func resizeImageToStrSizeSync(_ image: UIImage, maxDataSize: Int64) -> String? {
public func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String? {
var img = image
let hasAlpha = imageHasAlpha(image)
var str = compressImageStr(img, hasAlpha: hasAlpha)
@@ -116,15 +116,7 @@ public func resizeImageToStrSizeSync(_ image: UIImage, maxDataSize: Int64) -> St
return str
}
public func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) async -> String? {
resizeImageToStrSizeSync(image, maxDataSize: maxDataSize)
}
public func compressImageStr(_ image: UIImage, _ compressionQuality: CGFloat = 0.85, hasAlpha: Bool) -> String? {
// // Heavy workload to verify if UI gets blocked by the call
// for i in 0..<100 {
// print(image.jpegData(compressionQuality: Double(i) / 100)?.count ?? 0, terminator: ", ")
// }
let ext = hasAlpha ? "png" : "jpg"
if let data = hasAlpha ? image.pngData() : image.jpegData(compressionQuality: compressionQuality) {
return "data:image/\(ext);base64,\(data.base64EncodedString())"
@@ -434,7 +426,7 @@ public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
logger.error("Couldn't load image preview from link metadata with error: \(error.localizedDescription)")
} else {
if let image = object as? UIImage,
let resized = resizeImageToStrSizeSync(image, maxDataSize: 14000),
let resized = resizeImageToStrSize(image, maxDataSize: 14000),
let title = metadata.title,
let uri = metadata.originalURL {
linkPreview = LinkPreview(uri: uri, title: title, image: resized)
+2 -2
View File
@@ -94,7 +94,7 @@ public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntit
var body: String? = nil
var targetContentIdentifier: String? = nil
switch connEntity {
case let .rcvDirectMsgConnection(_, contact):
case let .rcvDirectMsgConnection(contact):
if let contact = contact {
title = hideContent ? contactHidden : "\(contact.chatViewName):"
targetContentIdentifier = contact.id
@@ -102,7 +102,7 @@ public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntit
title = NSLocalizedString("New contact:", comment: "notification")
}
body = NSLocalizedString("message received", comment: "notification")
case let .rcvGroupMsgConnection(_, groupInfo, groupMember):
case let .rcvGroupMsgConnection(groupInfo, groupMember):
title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: hideContent)
body = NSLocalizedString("message received", comment: "notification")
targetContentIdentifier = groupInfo.id
+9 -5
View File
@@ -791,9 +791,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Чат настройки";
/* No comment provided by engineer. */
"Chat profile" = "Потребителски профил";
/* No comment provided by engineer. */
"Chats" = "Чатове";
@@ -1735,7 +1732,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Грешка при спиране на чата";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Грешка при смяна на профил!";
/* No comment provided by engineer. */
@@ -1762,7 +1759,8 @@
/* No comment provided by engineer. */
"Error: " = "Грешка: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Грешка: %@";
/* No comment provided by engineer. */
@@ -3992,6 +3990,9 @@
/* No comment provided by engineer. */
"Use the app while in the call." = "Използвайте приложението по време на разговора.";
/* No comment provided by engineer. */
"User profile" = "Потребителски профил";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Използват се сървърите на SimpleX Chat.";
@@ -4166,6 +4167,9 @@
/* No comment provided by engineer. */
"you" = "вие";
/* No comment provided by engineer. */
"You" = "Вие";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "**Не трябва** да използвате една и съща база данни на две устройства.";
+9 -5
View File
@@ -644,9 +644,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Předvolby chatu";
/* No comment provided by engineer. */
"Chat profile" = "Profil uživatele";
/* No comment provided by engineer. */
"Chats" = "Chaty";
@@ -1425,7 +1422,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Chyba při zastavení chatu";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Chyba při přepínání profilu!";
/* No comment provided by engineer. */
@@ -1446,7 +1443,8 @@
/* No comment provided by engineer. */
"Error: " = "Chyba: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Chyba: %@";
/* No comment provided by engineer. */
@@ -3232,6 +3230,9 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Používat servery SimpleX Chat?";
/* No comment provided by engineer. */
"User profile" = "Profil uživatele";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Používat servery SimpleX Chat.";
@@ -3349,6 +3350,9 @@
/* pref value */
"yes" = "ano";
/* No comment provided by engineer. */
"You" = "Vy";
/* No comment provided by engineer. */
"You accepted connection" = "Přijali jste spojení";
+13 -81
View File
@@ -175,24 +175,9 @@
/* time interval */
"%d days" = "%d Tage";
/* forward confirmation reason */
"%d file(s) are still being downloaded." = "%d Datei(en) wird/werden immer noch heruntergeladen.";
/* forward confirmation reason */
"%d file(s) failed to download." = "Bei %d Datei(en) ist das Herunterladen fehlgeschlagen.";
/* forward confirmation reason */
"%d file(s) were deleted." = "%d Datei(en) wurde(n) gelöscht.";
/* forward confirmation reason */
"%d file(s) were not downloaded." = "%d Datei(en) wurde(n) nicht heruntergeladen.";
/* time interval */
"%d hours" = "%d Stunden";
/* alert title */
"%d messages not forwarded" = "%d Nachrichten wurden nicht weitergeleitet";
/* time interval */
"%d min" = "%d min";
@@ -472,7 +457,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Von %@ werden alle neuen Nachrichten ausgeblendet!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Alle Profile";
/* No comment provided by engineer. */
@@ -914,9 +899,6 @@
/* alert message */
"Chat preferences were changed." = "Die Chat-Präferenzen wurden geändert.";
/* No comment provided by engineer. */
"Chat profile" = "Benutzerprofil";
/* No comment provided by engineer. */
"Chat theme" = "Chat-Design";
@@ -1206,7 +1188,7 @@
"Core version: v%@" = "Core Version: v%@";
/* No comment provided by engineer. */
"Corner" = "Abrundung Ecken";
"Corner" = "Ecke";
/* No comment provided by engineer. */
"Correct name to %@?" = "Richtiger Name für %@?";
@@ -1641,9 +1623,6 @@
/* No comment provided by engineer. */
"Do NOT send messages directly, even if your or destination server does not support private routing." = "Nachrichten werden nicht direkt versendet, selbst wenn Ihr oder der Zielserver kein privates Routing unterstützt.";
/* No comment provided by engineer. */
"Do not use credentials with proxy." = "Verwenden Sie keine Anmeldeinformationen mit einem Proxy.";
/* No comment provided by engineer. */
"Do NOT use private routing." = "Sie nutzen KEIN privates Routing.";
@@ -1675,9 +1654,6 @@
/* server test step */
"Download file" = "Datei herunterladen";
/* alert action */
"Download files" = "Dateien herunterladen";
/* No comment provided by engineer. */
"Downloaded" = "Heruntergeladen";
@@ -2044,7 +2020,7 @@
/* No comment provided by engineer. */
"Error switching profile" = "Fehler beim Wechseln des Profils";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Fehler beim Umschalten des Profils!";
/* No comment provided by engineer. */
@@ -2071,7 +2047,8 @@
/* No comment provided by engineer. */
"Error: " = "Fehler: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Fehler: %@";
/* No comment provided by engineer. */
@@ -2096,7 +2073,7 @@
"Expand" = "Erweitern";
/* No comment provided by engineer. */
"expired" = "Abgelaufen";
"expired" = "abgelaufen";
/* No comment provided by engineer. */
"Export database" = "Datenbank exportieren";
@@ -2131,9 +2108,6 @@
/* No comment provided by engineer. */
"File error" = "Datei-Fehler";
/* alert message */
"File errors:\n%@" = "Datei-Fehler:\n%@";
/* file error text */
"File not found - most likely file was deleted or cancelled." = "Datei nicht gefunden - höchstwahrscheinlich wurde die Datei gelöscht oder der Transfer abgebrochen.";
@@ -2215,18 +2189,9 @@
/* chat item action */
"Forward" = "Weiterleiten";
/* alert title */
"Forward %d message(s)?" = "%d Nachricht(en) weiterleiten?";
/* No comment provided by engineer. */
"Forward and save messages" = "Nachrichten weiterleiten und speichern";
/* alert action */
"Forward messages" = "Nachrichten weiterleiten";
/* alert message */
"Forward messages without files?" = "Nachrichten ohne Dateien weiterleiten?";
/* No comment provided by engineer. */
"forwarded" = "weitergeleitet";
@@ -2236,9 +2201,6 @@
/* No comment provided by engineer. */
"Forwarded from" = "Weitergeleitet aus";
/* No comment provided by engineer. */
"Forwarding %lld messages" = "%lld Nachricht(en) wird/werden weitergeleitet";
/* No comment provided by engineer. */
"Forwarding server %@ failed to connect to destination server %@. Please try later." = "Weiterleitungsserver %@ konnte sich nicht mit dem Zielserver %@ verbinden. Bitte versuchen Sie es später erneut.";
@@ -2626,9 +2588,6 @@
/* No comment provided by engineer. */
"iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Für die sichere Speicherung des Passworts nach dem Neustart der App und dem Wechsel des Passworts wird der iOS Schlüsselbund verwendet - dies erlaubt den Empfang von Push-Benachrichtigungen.";
/* No comment provided by engineer. */
"IP address" = "IP-Adresse";
/* No comment provided by engineer. */
"Irreversible message deletion" = "Unwiederbringliches löschen einer Nachricht";
@@ -2914,9 +2873,6 @@
/* No comment provided by engineer. */
"Messages sent" = "Gesendete Nachrichten";
/* alert message */
"Messages were deleted after you selected them." = "Die Nachrichten wurden gelöscht, nachdem Sie sie ausgewählt hatten.";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Nachrichten, Dateien und Anrufe sind durch **Ende-zu-Ende-Verschlüsselung** mit Perfect Forward Secrecy, Ablehnung und Einbruchs-Wiederherstellung geschützt.";
@@ -3127,9 +3083,6 @@
/* No comment provided by engineer. */
"Nothing selected" = "Nichts ausgewählt";
/* alert title */
"Nothing to forward!" = "Es gibt nichts zum Weiterleiten!";
/* No comment provided by engineer. */
"Notifications" = "Benachrichtigungen";
@@ -3271,7 +3224,7 @@
"Or show this code" = "Oder diesen QR-Code anzeigen";
/* No comment provided by engineer. */
"other" = "Andere";
"other" = "andere";
/* No comment provided by engineer. */
"Other" = "Andere";
@@ -3282,9 +3235,6 @@
/* No comment provided by engineer. */
"other errors" = "Andere Fehler";
/* alert message */
"Other file errors:\n%@" = "Andere(r) Datei-Fehler:\n%@";
/* member role */
"owner" = "Eigentümer";
@@ -3306,9 +3256,6 @@
/* No comment provided by engineer. */
"Passcode set!" = "Zugangscode eingestellt!";
/* No comment provided by engineer. */
"Password" = "Passwort";
/* No comment provided by engineer. */
"Password to show" = "Passwort anzeigen";
@@ -3405,9 +3352,6 @@
/* No comment provided by engineer. */
"Polish interface" = "Polnische Bedienoberfläche";
/* No comment provided by engineer. */
"Port" = "Port";
/* server test error */
"Possibly, certificate fingerprint in server address is incorrect" = "Der Fingerabdruck des Zertifikats in der Serveradresse ist wahrscheinlich ungültig";
@@ -3519,9 +3463,6 @@
/* No comment provided by engineer. */
"Proxied servers" = "Proxy-Server";
/* No comment provided by engineer. */
"Proxy requires password" = "Der Proxy benötigt ein Passwort";
/* No comment provided by engineer. */
"Push notifications" = "Push-Benachrichtigungen";
@@ -3863,9 +3804,6 @@
/* No comment provided by engineer. */
"Saved WebRTC ICE servers will be removed" = "Gespeicherte WebRTC ICE-Server werden entfernt";
/* No comment provided by engineer. */
"Saving %lld messages" = "Es wird/werden %lld Nachricht(en) gesichert";
/* No comment provided by engineer. */
"Scale" = "Skalieren";
@@ -4271,9 +4209,6 @@
/* No comment provided by engineer. */
"SMP server" = "SMP-Server";
/* No comment provided by engineer. */
"SOCKS proxy" = "SOCKS-Proxy";
/* blur media */
"Soft" = "Weich";
@@ -4380,7 +4315,7 @@
"System authentication" = "System-Authentifizierung";
/* No comment provided by engineer. */
"Tail" = "Sprechblasen-Format";
"Tail" = "Sprechblase";
/* No comment provided by engineer. */
"Take picture" = "Machen Sie ein Foto";
@@ -4808,9 +4743,6 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Verwenden Sie SimpleX-Chat-Server?";
/* No comment provided by engineer. */
"Use SOCKS proxy" = "SOCKS-Proxy nutzen";
/* No comment provided by engineer. */
"Use the app while in the call." = "Die App kann während eines Anrufs genutzt werden.";
@@ -4818,10 +4750,10 @@
"Use the app with one hand." = "Die App mit einer Hand bedienen.";
/* No comment provided by engineer. */
"User selection" = "Benutzer-Auswahl";
"User profile" = "Benutzerprofil";
/* No comment provided by engineer. */
"Username" = "Benutzername";
"User selection" = "Benutzer-Auswahl";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Verwendung von SimpleX-Chat-Servern.";
@@ -5024,6 +4956,9 @@
/* No comment provided by engineer. */
"you" = "Profil";
/* No comment provided by engineer. */
"You" = "Profil";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "Sie dürfen die selbe Datenbank **nicht** auf zwei Geräten nutzen.";
@@ -5276,9 +5211,6 @@
/* No comment provided by engineer. */
"Your contacts will remain connected." = "Ihre Kontakte bleiben weiterhin verbunden.";
/* No comment provided by engineer. */
"Your credentials may be sent unencrypted." = "Ihre Anmeldeinformationen können unverschlüsselt versendet werden.";
/* No comment provided by engineer. */
"Your current chat database will be DELETED and REPLACED with the imported one." = "Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die Importierte ERSETZT.";
+16 -144
View File
@@ -160,9 +160,6 @@
/* notification title */
"%@ wants to connect!" = "¡ %@ quiere contactar!";
/* format for date separator in chat */
"%@, %@" = "%1$@, %2$@";
/* No comment provided by engineer. */
"%@, %@ and %lld members" = "%@, %@ y %lld miembro(s) más";
@@ -175,24 +172,9 @@
/* time interval */
"%d days" = "%d días";
/* forward confirmation reason */
"%d file(s) are still being downloaded." = "%d archivo(s) se está(n) descargando todavía.";
/* forward confirmation reason */
"%d file(s) failed to download." = "La descarga ha fallado para %d archivo(s).";
/* forward confirmation reason */
"%d file(s) were deleted." = "%d archivo(s) ha(n) sido eliminado(s).";
/* forward confirmation reason */
"%d file(s) were not downloaded." = "%d archivo(s) no se ha(n) descargado.";
/* time interval */
"%d hours" = "%d horas";
/* alert title */
"%d messages not forwarded" = "%d mensajes no enviados";
/* time interval */
"%d min" = "%d minutos";
@@ -323,7 +305,7 @@
"A new contact" = "Contacto nuevo";
/* No comment provided by engineer. */
"A new random profile will be shared." = "Compartirás un perfil nuevo aleatorio.";
"A new random profile will be shared." = "Se compartirá un perfil nuevo aleatorio.";
/* No comment provided by engineer. */
"A separate TCP connection will be used **for each chat profile you have in the app**." = "Se usará una conexión TCP independiente **por cada perfil que tengas en la aplicación**.";
@@ -472,7 +454,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "¡Los mensajes nuevos de %@ estarán ocultos!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Todos los perfiles";
/* No comment provided by engineer. */
@@ -667,9 +649,6 @@
/* No comment provided by engineer. */
"Auto-accept images" = "Aceptar imágenes automáticamente";
/* alert title */
"Auto-accept settings" = "Auto aceptar configuración";
/* No comment provided by engineer. */
"Back" = "Volver";
@@ -911,12 +890,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Preferencias de Chat";
/* alert message */
"Chat preferences were changed." = "Las preferencias del chat han sido modificadas.";
/* No comment provided by engineer. */
"Chat profile" = "Perfil de usuario";
/* No comment provided by engineer. */
"Chat theme" = "Tema de chat";
@@ -1205,9 +1178,6 @@
/* No comment provided by engineer. */
"Core version: v%@" = "Versión Core: v%@";
/* No comment provided by engineer. */
"Corner" = "Esquina";
/* No comment provided by engineer. */
"Correct name to %@?" = "¿Corregir el nombre a %@?";
@@ -1335,7 +1305,7 @@
"Database passphrase & export" = "Base de datos y contraseña";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "La contraseña es diferente a la almacenada en Keychain.";
"Database passphrase is different from saved in the keychain." = "La contraseña es distinta a la almacenada en Keychain.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "Para abrir la aplicación se requiere la contraseña de la base de datos.";
@@ -1624,7 +1594,7 @@
"Disconnect" = "Desconectar";
/* No comment provided by engineer. */
"Disconnect desktop?" = "¿Desconectar del ordenador?";
"Disconnect desktop?" = "¿Desconectar ordenador?";
/* No comment provided by engineer. */
"Discover and join groups" = "Descubre y únete a grupos";
@@ -1641,9 +1611,6 @@
/* No comment provided by engineer. */
"Do NOT send messages directly, even if your or destination server does not support private routing." = "NO enviar mensajes directamente incluso si tu servidor o el de destino no soportan enrutamiento privado.";
/* No comment provided by engineer. */
"Do not use credentials with proxy." = "No uses credenciales con proxy.";
/* No comment provided by engineer. */
"Do NOT use private routing." = "NO usar enrutamiento privado.";
@@ -1657,7 +1624,7 @@
"Don't enable" = "No activar";
/* No comment provided by engineer. */
"Don't show again" = "No volver a mostrar";
"Don't show again" = "No mostrar de nuevo";
/* No comment provided by engineer. */
"Downgrade and open chat" = "Degradar y abrir Chat";
@@ -1675,9 +1642,6 @@
/* server test step */
"Download file" = "Descargar archivo";
/* alert action */
"Download files" = "Descargar archivos";
/* No comment provided by engineer. */
"Downloaded" = "Descargado";
@@ -1894,18 +1858,12 @@
/* No comment provided by engineer. */
"Error changing address" = "Error al cambiar servidor";
/* No comment provided by engineer. */
"Error changing connection profile" = "Error al cambiar el perfil de conexión";
/* No comment provided by engineer. */
"Error changing role" = "Error al cambiar rol";
/* No comment provided by engineer. */
"Error changing setting" = "Error cambiando configuración";
/* No comment provided by engineer. */
"Error changing to incognito!" = "¡Error al cambiar a incógnito!";
/* No comment provided by engineer. */
"Error connecting to forwarding server %@. Please try later." = "Error al conectar con el servidor de reenvío %@. Por favor, inténtalo más tarde.";
@@ -1978,9 +1936,6 @@
/* No comment provided by engineer. */
"Error loading %@ servers" = "Error al cargar servidores %@";
/* No comment provided by engineer. */
"Error migrating settings" = "Error al migrar la configuración";
/* No comment provided by engineer. */
"Error opening chat" = "Error al abrir chat";
@@ -2042,9 +1997,6 @@
"Error stopping chat" = "Error al parar SimpleX";
/* No comment provided by engineer. */
"Error switching profile" = "Error al cambiar perfil";
/* alertTitle */
"Error switching profile!" = "¡Error al cambiar perfil!";
/* No comment provided by engineer. */
@@ -2071,7 +2023,8 @@
/* No comment provided by engineer. */
"Error: " = "Error: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Error: %@";
/* No comment provided by engineer. */
@@ -2131,9 +2084,6 @@
/* No comment provided by engineer. */
"File error" = "Error de archivo";
/* alert message */
"File errors:\n%@" = "Error(es) de archivo\n%@";
/* file error text */
"File not found - most likely file was deleted or cancelled." = "Archivo no encontrado, probablemente haya sido borrado o cancelado.";
@@ -2215,18 +2165,9 @@
/* chat item action */
"Forward" = "Reenviar";
/* alert title */
"Forward %d message(s)?" = "¿Reenviar %d mensaje(s)?";
/* No comment provided by engineer. */
"Forward and save messages" = "Reenviar y guardar mensajes";
/* alert action */
"Forward messages" = "Reenviar mensajes";
/* alert message */
"Forward messages without files?" = "¿Reenviar mensajes sin los archivos?";
/* No comment provided by engineer. */
"forwarded" = "reenviado";
@@ -2236,9 +2177,6 @@
/* No comment provided by engineer. */
"Forwarded from" = "Reenviado por";
/* No comment provided by engineer. */
"Forwarding %lld messages" = "Reenviando %lld mensajes";
/* No comment provided by engineer. */
"Forwarding server %@ failed to connect to destination server %@. Please try later." = "El servidor de reenvío %@ no ha podido conectarse al servidor de destino %@. Por favor, intentalo más tarde.";
@@ -2267,10 +2205,10 @@
"Full name (optional)" = "Nombre completo (opcional)";
/* No comment provided by engineer. */
"Fully decentralized visible only to members." = "Totalmente descentralizado. Visible sólo para los miembros.";
"Fully decentralized visible only to members." = "Completamente descentralizado y sólo visible para los miembros.";
/* No comment provided by engineer. */
"Fully re-implemented - work in background!" = "Totalmente revisado. ¡Funciona en segundo plano!";
"Fully re-implemented - work in background!" = "Completamente reimplementado: ¡funciona en segundo plano!";
/* No comment provided by engineer. */
"Further reduced battery usage" = "Reducción consumo de batería";
@@ -2626,9 +2564,6 @@
/* No comment provided by engineer. */
"iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "iOS Keychain se usará para almacenar la contraseña de forma segura después de reiniciar la aplicación o cambiar la contraseña. Esto permitirá recibir notificaciones automáticas.";
/* No comment provided by engineer. */
"IP address" = "Dirección IP";
/* No comment provided by engineer. */
"Irreversible message deletion" = "Eliminación irreversible del mensaje";
@@ -2881,9 +2816,6 @@
/* No comment provided by engineer. */
"Message servers" = "Servidores de mensajes";
/* No comment provided by engineer. */
"Message shape" = "Forma del mensaje";
/* No comment provided by engineer. */
"Message source remains private." = "El autor del mensaje se mantiene privado.";
@@ -2914,9 +2846,6 @@
/* No comment provided by engineer. */
"Messages sent" = "Mensajes enviados";
/* alert message */
"Messages were deleted after you selected them." = "Los mensajes han sido borrados después de seleccionarlos.";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Los mensajes, archivos y llamadas están protegidos mediante **cifrado de extremo a extremo** con secreto perfecto hacía adelante, repudio y recuperación tras ataque.";
@@ -3127,9 +3056,6 @@
/* No comment provided by engineer. */
"Nothing selected" = "Nada seleccionado";
/* alert title */
"Nothing to forward!" = "¡Nada para reenviar!";
/* No comment provided by engineer. */
"Notifications" = "Notificaciones";
@@ -3282,9 +3208,6 @@
/* No comment provided by engineer. */
"other errors" = "otros errores";
/* alert message */
"Other file errors:\n%@" = "Otro(s) error(es) de archivo.\n%@";
/* member role */
"owner" = "propietario";
@@ -3306,9 +3229,6 @@
/* No comment provided by engineer. */
"Passcode set!" = "¡Código de acceso guardado!";
/* No comment provided by engineer. */
"Password" = "Contraseña";
/* No comment provided by engineer. */
"Password to show" = "Contraseña para hacerlo visible";
@@ -3405,9 +3325,6 @@
/* No comment provided by engineer. */
"Polish interface" = "Interfaz en polaco";
/* No comment provided by engineer. */
"Port" = "Puerto";
/* server test error */
"Possibly, certificate fingerprint in server address is incorrect" = "Posiblemente la huella digital del certificado en la dirección del servidor es incorrecta";
@@ -3519,9 +3436,6 @@
/* No comment provided by engineer. */
"Proxied servers" = "Servidores con proxy";
/* No comment provided by engineer. */
"Proxy requires password" = "El proxy requiere contraseña";
/* No comment provided by engineer. */
"Push notifications" = "Notificaciones automáticas";
@@ -3667,9 +3581,6 @@
/* No comment provided by engineer. */
"Remove" = "Eliminar";
/* No comment provided by engineer. */
"Remove archive?" = "¿Eliminar archivo?";
/* No comment provided by engineer. */
"Remove image" = "Eliminar imagen";
@@ -3842,9 +3753,6 @@
/* No comment provided by engineer. */
"Save welcome message?" = "¿Guardar mensaje de bienvenida?";
/* alert title */
"Save your profile?" = "¿Guardar tu perfil?";
/* No comment provided by engineer. */
"saved" = "guardado";
@@ -3863,9 +3771,6 @@
/* No comment provided by engineer. */
"Saved WebRTC ICE servers will be removed" = "Los servidores WebRTC ICE guardados serán eliminados";
/* No comment provided by engineer. */
"Saving %lld messages" = "Guardando %lld mensajes";
/* No comment provided by engineer. */
"Scale" = "Escala";
@@ -3929,9 +3834,6 @@
/* chat item action */
"Select" = "Seleccionar";
/* No comment provided by engineer. */
"Select chat profile" = "Selecciona perfil de chat";
/* No comment provided by engineer. */
"Selected %lld" = "Seleccionados %lld";
@@ -3963,7 +3865,7 @@
"send direct message" = "Enviar mensaje directo";
/* No comment provided by engineer. */
"Send direct message to connect" = "Envía un mensaje para conectar";
"Send direct message to connect" = "Envia un mensaje para conectar";
/* No comment provided by engineer. */
"Send disappearing message" = "Enviar mensaje temporal";
@@ -4145,9 +4047,6 @@
/* No comment provided by engineer. */
"Settings" = "Configuración";
/* alert message */
"Settings were changed." = "La configuración ha sido modificada.";
/* No comment provided by engineer. */
"Shape profile images" = "Dar forma a las imágenes de perfil";
@@ -4169,9 +4068,6 @@
/* No comment provided by engineer. */
"Share link" = "Compartir enlace";
/* No comment provided by engineer. */
"Share profile" = "Comparte perfil";
/* No comment provided by engineer. */
"Share this 1-time invite link" = "Comparte este enlace de un solo uso";
@@ -4271,15 +4167,9 @@
/* No comment provided by engineer. */
"SMP server" = "Servidor SMP";
/* No comment provided by engineer. */
"SOCKS proxy" = "Proxy SOCKS";
/* blur media */
"Soft" = "Suave";
/* No comment provided by engineer. */
"Some app settings were not migrated." = "Algunas configuraciones de la app no han sido migradas.";
/* No comment provided by engineer. */
"Some file(s) were not exported:" = "Algunos archivos no han sido exportados:";
@@ -4379,9 +4269,6 @@
/* No comment provided by engineer. */
"System authentication" = "Autenticación del sistema";
/* No comment provided by engineer. */
"Tail" = "Cola";
/* No comment provided by engineer. */
"Take picture" = "Tomar foto";
@@ -4511,9 +4398,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "El texto pegado no es un enlace SimpleX.";
/* No comment provided by engineer. */
"The uploaded database archive will be permanently removed from the servers." = "El archivo de bases de datos subido será eliminado permanentemente de los servidores.";
/* No comment provided by engineer. */
"Themes" = "Temas";
@@ -4808,9 +4692,6 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "¿Usar servidores SimpleX Chat?";
/* No comment provided by engineer. */
"Use SOCKS proxy" = "Usar proxy SOCKS";
/* No comment provided by engineer. */
"Use the app while in the call." = "Usar la aplicación durante la llamada.";
@@ -4818,10 +4699,10 @@
"Use the app with one hand." = "Usa la aplicación con una sola mano.";
/* No comment provided by engineer. */
"User selection" = "Selección de usuarios";
"User profile" = "Perfil de usuario";
/* No comment provided by engineer. */
"Username" = "Nombre de usuario";
"User selection" = "Selección de usuarios";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Usar servidores SimpleX Chat.";
@@ -5024,6 +4905,9 @@
/* No comment provided by engineer. */
"you" = "tu";
/* No comment provided by engineer. */
"You" = "Tú";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "**No debes** usar la misma base de datos en dos dispositivos.";
@@ -5258,15 +5142,9 @@
/* No comment provided by engineer. */
"Your chat database is not encrypted - set passphrase to encrypt it." = "La base de datos no está cifrada - establece una contraseña para cifrarla.";
/* alert title */
"Your chat preferences" = "Tus preferencias de chat";
/* No comment provided by engineer. */
"Your chat profiles" = "Mis perfiles";
/* No comment provided by engineer. */
"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Tu conexión ha sido trasladada a %@ pero ha ocurrido un error inesperado al redirigirte al perfil.";
/* No comment provided by engineer. */
"Your contact sent a file that is larger than currently supported maximum size (%@)." = "El contacto ha enviado un archivo mayor al máximo admitido (%@).";
@@ -5276,9 +5154,6 @@
/* No comment provided by engineer. */
"Your contacts will remain connected." = "Tus contactos permanecerán conectados.";
/* No comment provided by engineer. */
"Your credentials may be sent unencrypted." = "Tus credenciales podrían ser enviadas sin cifrar.";
/* No comment provided by engineer. */
"Your current chat database will be DELETED and REPLACED with the imported one." = "La base de datos actual será ELIMINADA y SUSTITUIDA por la importada.";
@@ -5303,9 +5178,6 @@
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. Los servidores SimpleX no pueden ver tu perfil.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Tu perfil ha sido modificado. Si lo guardas la actualización será enviada a todos tus contactos.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Tu perfil, contactos y mensajes se almacenan en tu dispositivo.";
@@ -5316,7 +5188,7 @@
"Your server" = "Tu servidor";
/* No comment provided by engineer. */
"Your server address" = "Dirección del servidor";
"Your server address" = "Dirección de tu servidor";
/* No comment provided by engineer. */
"Your settings" = "Configuración";
+9 -5
View File
@@ -629,9 +629,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Chat-asetukset";
/* No comment provided by engineer. */
"Chat profile" = "Käyttäjäprofiili";
/* No comment provided by engineer. */
"Chats" = "Keskustelut";
@@ -1398,7 +1395,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Virhe keskustelun lopettamisessa";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Virhe profiilin vaihdossa!";
/* No comment provided by engineer. */
@@ -1419,7 +1416,8 @@
/* No comment provided by engineer. */
"Error: " = "Virhe: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Virhe: %@";
/* No comment provided by engineer. */
@@ -3190,6 +3188,9 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Käytä SimpleX Chat palvelimia?";
/* No comment provided by engineer. */
"User profile" = "Käyttäjäprofiili";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Käyttää SimpleX Chat -palvelimia.";
@@ -3307,6 +3308,9 @@
/* pref value */
"yes" = "kyllä";
/* No comment provided by engineer. */
"You" = "Sinä";
/* No comment provided by engineer. */
"You accepted connection" = "Hyväksyit yhteyden";
+10 -6
View File
@@ -454,7 +454,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Tous les nouveaux messages de %@ seront cachés !";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Tous les profiles";
/* No comment provided by engineer. */
@@ -890,9 +890,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Préférences de chat";
/* No comment provided by engineer. */
"Chat profile" = "Profil d'utilisateur";
/* No comment provided by engineer. */
"Chat theme" = "Thème de chat";
@@ -1999,7 +1996,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Erreur lors de l'arrêt du chat";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Erreur lors du changement de profil !";
/* No comment provided by engineer. */
@@ -2026,7 +2023,8 @@
/* No comment provided by engineer. */
"Error: " = "Erreur : ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Erreur: %@";
/* No comment provided by engineer. */
@@ -4700,6 +4698,9 @@
/* No comment provided by engineer. */
"Use the app with one hand." = "Utiliser l'application d'une main.";
/* No comment provided by engineer. */
"User profile" = "Profil d'utilisateur";
/* No comment provided by engineer. */
"User selection" = "Sélection de l'utilisateur";
@@ -4904,6 +4905,9 @@
/* No comment provided by engineer. */
"you" = "vous";
/* No comment provided by engineer. */
"You" = "Vous";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "Vous **ne devez pas** utiliser la même base de données sur deux appareils.";
File diff suppressed because it is too large Load Diff
+10 -78
View File
@@ -175,24 +175,9 @@
/* time interval */
"%d days" = "%d giorni";
/* forward confirmation reason */
"%d file(s) are still being downloaded." = "%d file è/sono ancora in scaricamento.";
/* forward confirmation reason */
"%d file(s) failed to download." = "%d file ha/hanno fallito lo scaricamento.";
/* forward confirmation reason */
"%d file(s) were deleted." = "%d file è/sono stato/i eliminato/i.";
/* forward confirmation reason */
"%d file(s) were not downloaded." = "%d file non è/sono stato/i scaricato/i.";
/* time interval */
"%d hours" = "%d ore";
/* alert title */
"%d messages not forwarded" = "%d messaggi non inoltrati";
/* time interval */
"%d min" = "%d min";
@@ -472,7 +457,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Tutti i nuovi messaggi da %@ verrranno nascosti!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Tutti gli profili";
/* No comment provided by engineer. */
@@ -864,7 +849,7 @@
"changed role of %@ to %@" = "ha cambiato il ruolo di %1$@ in %2$@";
/* rcv group event chat item */
"changed your role to %@" = "ha cambiato il tuo ruolo in %@";
"changed your role to %@" = "cambiato il tuo ruolo in %@";
/* chat item text */
"changing address for %@…" = "cambio indirizzo per %@…";
@@ -914,9 +899,6 @@
/* alert message */
"Chat preferences were changed." = "Le preferenze della chat sono state cambiate.";
/* No comment provided by engineer. */
"Chat profile" = "Profilo utente";
/* No comment provided by engineer. */
"Chat theme" = "Tema della chat";
@@ -1641,9 +1623,6 @@
/* No comment provided by engineer. */
"Do NOT send messages directly, even if your or destination server does not support private routing." = "NON inviare messaggi direttamente, anche se il tuo server o quello di destinazione non supporta l'instradamento privato.";
/* No comment provided by engineer. */
"Do not use credentials with proxy." = "Non usare credenziali con proxy.";
/* No comment provided by engineer. */
"Do NOT use private routing." = "NON usare l'instradamento privato.";
@@ -1675,9 +1654,6 @@
/* server test step */
"Download file" = "Scarica file";
/* alert action */
"Download files" = "Scarica i file";
/* No comment provided by engineer. */
"Downloaded" = "Scaricato";
@@ -2044,7 +2020,7 @@
/* No comment provided by engineer. */
"Error switching profile" = "Errore nel cambio di profilo";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Errore nel cambio di profilo!";
/* No comment provided by engineer. */
@@ -2071,7 +2047,8 @@
/* No comment provided by engineer. */
"Error: " = "Errore: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Errore: %@";
/* No comment provided by engineer. */
@@ -2131,9 +2108,6 @@
/* No comment provided by engineer. */
"File error" = "Errore del file";
/* alert message */
"File errors:\n%@" = "Errori di file:\n%@";
/* file error text */
"File not found - most likely file was deleted or cancelled." = "File non trovato - probabilmente è stato eliminato o annullato.";
@@ -2215,18 +2189,9 @@
/* chat item action */
"Forward" = "Inoltra";
/* alert title */
"Forward %d message(s)?" = "Inoltrare %d messaggio/i?";
/* No comment provided by engineer. */
"Forward and save messages" = "Inoltra e salva i messaggi";
/* alert action */
"Forward messages" = "Inoltra i messaggi";
/* alert message */
"Forward messages without files?" = "Inoltrare i messaggi senza file?";
/* No comment provided by engineer. */
"forwarded" = "inoltrato";
@@ -2236,9 +2201,6 @@
/* No comment provided by engineer. */
"Forwarded from" = "Inoltrato da";
/* No comment provided by engineer. */
"Forwarding %lld messages" = "Inoltro di %lld messaggi";
/* No comment provided by engineer. */
"Forwarding server %@ failed to connect to destination server %@. Please try later." = "Il server di inoltro %@ non è riuscito a connettersi al server di destinazione %@. Riprova più tardi.";
@@ -2626,9 +2588,6 @@
/* No comment provided by engineer. */
"iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Il portachiavi di iOS verrà usato per archiviare in modo sicuro la password dopo il riavvio dell'app o la modifica della password; consentirà di ricevere notifiche push.";
/* No comment provided by engineer. */
"IP address" = "Indirizzo IP";
/* No comment provided by engineer. */
"Irreversible message deletion" = "Eliminazione irreversibile del messaggio";
@@ -2914,9 +2873,6 @@
/* No comment provided by engineer. */
"Messages sent" = "Messaggi inviati";
/* alert message */
"Messages were deleted after you selected them." = "I messaggi sono stati eliminati dopo che li hai selezionati.";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "I messaggi, i file e le chiamate sono protetti da **crittografia end-to-end** con perfect forward secrecy, ripudio e recupero da intrusione.";
@@ -3127,9 +3083,6 @@
/* No comment provided by engineer. */
"Nothing selected" = "Nessuna selezione";
/* alert title */
"Nothing to forward!" = "Niente da inoltrare!";
/* No comment provided by engineer. */
"Notifications" = "Notifiche";
@@ -3282,9 +3235,6 @@
/* No comment provided by engineer. */
"other errors" = "altri errori";
/* alert message */
"Other file errors:\n%@" = "Altri errori di file:\n%@";
/* member role */
"owner" = "proprietario";
@@ -3306,9 +3256,6 @@
/* No comment provided by engineer. */
"Passcode set!" = "Codice di accesso impostato!";
/* No comment provided by engineer. */
"Password" = "Password";
/* No comment provided by engineer. */
"Password to show" = "Password per mostrare";
@@ -3405,9 +3352,6 @@
/* No comment provided by engineer. */
"Polish interface" = "Interfaccia polacca";
/* No comment provided by engineer. */
"Port" = "Porta";
/* server test error */
"Possibly, certificate fingerprint in server address is incorrect" = "Probabilmente l'impronta del certificato nell'indirizzo del server è sbagliata";
@@ -3519,9 +3463,6 @@
/* No comment provided by engineer. */
"Proxied servers" = "Server via proxy";
/* No comment provided by engineer. */
"Proxy requires password" = "Il proxy richiede una password";
/* No comment provided by engineer. */
"Push notifications" = "Notifiche push";
@@ -3863,9 +3804,6 @@
/* No comment provided by engineer. */
"Saved WebRTC ICE servers will be removed" = "I server WebRTC ICE salvati verranno rimossi";
/* No comment provided by engineer. */
"Saving %lld messages" = "Salvataggio di %lld messaggi";
/* No comment provided by engineer. */
"Scale" = "Scala";
@@ -4271,9 +4209,6 @@
/* No comment provided by engineer. */
"SMP server" = "Server SMP";
/* No comment provided by engineer. */
"SOCKS proxy" = "Proxy SOCKS";
/* blur media */
"Soft" = "Leggera";
@@ -4808,9 +4743,6 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Usare i server di SimpleX Chat?";
/* No comment provided by engineer. */
"Use SOCKS proxy" = "Usa proxy SOCKS";
/* No comment provided by engineer. */
"Use the app while in the call." = "Usa l'app mentre sei in chiamata.";
@@ -4818,10 +4750,10 @@
"Use the app with one hand." = "Usa l'app con una mano sola.";
/* No comment provided by engineer. */
"User selection" = "Selezione utente";
"User profile" = "Profilo utente";
/* No comment provided by engineer. */
"Username" = "Nome utente";
"User selection" = "Selezione utente";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Utilizzo dei server SimpleX Chat.";
@@ -5024,6 +4956,9 @@
/* No comment provided by engineer. */
"you" = "tu";
/* No comment provided by engineer. */
"You" = "Tu";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "**Non devi** usare lo stesso database su due dispositivi.";
@@ -5276,9 +5211,6 @@
/* No comment provided by engineer. */
"Your contacts will remain connected." = "I tuoi contatti resteranno connessi.";
/* No comment provided by engineer. */
"Your credentials may be sent unencrypted." = "Le credenziali potrebbero essere inviate in chiaro.";
/* No comment provided by engineer. */
"Your current chat database will be DELETED and REPLACED with the imported one." = "Il tuo attuale database della chat verrà ELIMINATO e SOSTITUITO con quello importato.";
+9 -5
View File
@@ -701,9 +701,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "チャット設定";
/* No comment provided by engineer. */
"Chat profile" = "ユーザープロフィール";
/* No comment provided by engineer. */
"Chats" = "チャット";
@@ -1473,7 +1470,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "チャット停止にエラー発生";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "プロフィール切り替えにエラー発生!";
/* No comment provided by engineer. */
@@ -1494,7 +1491,8 @@
/* No comment provided by engineer. */
"Error: " = "エラー : ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "エラー : %@";
/* No comment provided by engineer. */
@@ -3244,6 +3242,9 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "SimpleX チャット サーバーを使用しますか?";
/* No comment provided by engineer. */
"User profile" = "ユーザープロフィール";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "SimpleX チャット サーバーを使用する。";
@@ -3361,6 +3362,9 @@
/* pref value */
"yes" = "はい";
/* No comment provided by engineer. */
"You" = "あなた";
/* No comment provided by engineer. */
"You accepted connection" = "接続を承認しました";
+15 -83
View File
@@ -175,24 +175,9 @@
/* time interval */
"%d days" = "%d dagen";
/* forward confirmation reason */
"%d file(s) are still being downloaded." = "%d bestand(en) worden nog gedownload.";
/* forward confirmation reason */
"%d file(s) failed to download." = "%d bestand(en) konden niet worden gedownload.";
/* forward confirmation reason */
"%d file(s) were deleted." = "%d bestand(en) zijn verwijderd.";
/* forward confirmation reason */
"%d file(s) were not downloaded." = "%d bestand(en) zijn niet gedownload.";
/* time interval */
"%d hours" = "%d uren";
/* alert title */
"%d messages not forwarded" = "%d berichten niet doorgestuurd";
/* time interval */
"%d min" = "%d min";
@@ -472,7 +457,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Alle nieuwe berichten van %@ worden verborgen!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Alle profielen";
/* No comment provided by engineer. */
@@ -914,9 +899,6 @@
/* alert message */
"Chat preferences were changed." = "Chatvoorkeuren zijn gewijzigd.";
/* No comment provided by engineer. */
"Chat profile" = "Gebruikers profiel";
/* No comment provided by engineer. */
"Chat theme" = "Chat thema";
@@ -981,7 +963,7 @@
"complete" = "compleet";
/* No comment provided by engineer. */
"Completed" = "Voltooid";
"Completed" = "voltooid";
/* No comment provided by engineer. */
"Configure ICE servers" = "ICE servers configureren";
@@ -1641,9 +1623,6 @@
/* No comment provided by engineer. */
"Do NOT send messages directly, even if your or destination server does not support private routing." = "Stuur GEEN berichten rechtstreeks, zelfs als uw of de bestemmingsserver geen privéroutering ondersteunt.";
/* No comment provided by engineer. */
"Do not use credentials with proxy." = "Gebruik geen inloggegevens met proxy.";
/* No comment provided by engineer. */
"Do NOT use private routing." = "Gebruik GEEN privéroutering.";
@@ -1675,9 +1654,6 @@
/* server test step */
"Download file" = "Download bestand";
/* alert action */
"Download files" = "Bestanden downloaden";
/* No comment provided by engineer. */
"Downloaded" = "Gedownload";
@@ -2044,7 +2020,7 @@
/* No comment provided by engineer. */
"Error switching profile" = "Fout bij wisselen van profiel";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Fout bij wisselen van profiel!";
/* No comment provided by engineer. */
@@ -2071,7 +2047,8 @@
/* No comment provided by engineer. */
"Error: " = "Fout: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Fout: %@";
/* No comment provided by engineer. */
@@ -2131,9 +2108,6 @@
/* No comment provided by engineer. */
"File error" = "Bestandsfout";
/* alert message */
"File errors:\n%@" = "Bestandsfouten:\n%@";
/* file error text */
"File not found - most likely file was deleted or cancelled." = "Bestand niet gevonden - hoogstwaarschijnlijk is het bestand verwijderd of geannuleerd.";
@@ -2215,18 +2189,9 @@
/* chat item action */
"Forward" = "Doorsturen";
/* alert title */
"Forward %d message(s)?" = "%d bericht(en) doorsturen?";
/* No comment provided by engineer. */
"Forward and save messages" = "Berichten doorsturen en opslaan";
/* alert action */
"Forward messages" = "Berichten doorsturen";
/* alert message */
"Forward messages without files?" = "Berichten doorsturen zonder bestanden?";
/* No comment provided by engineer. */
"forwarded" = "doorgestuurd";
@@ -2236,9 +2201,6 @@
/* No comment provided by engineer. */
"Forwarded from" = "Doorgestuurd vanuit";
/* No comment provided by engineer. */
"Forwarding %lld messages" = "%lld berichten doorsturen";
/* No comment provided by engineer. */
"Forwarding server %@ failed to connect to destination server %@. Please try later." = "De doorstuurserver %@ kon geen verbinding maken met de bestemmingsserver %@. Probeer het later opnieuw.";
@@ -2626,9 +2588,6 @@
/* No comment provided by engineer. */
"iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "iOS-keychain wordt gebruikt om het wachtwoord veilig op te slaan nadat u de app opnieuw hebt opgestart of het wachtwoord hebt gewijzigd, hiermee kunt u push meldingen ontvangen.";
/* No comment provided by engineer. */
"IP address" = "IP-adres";
/* No comment provided by engineer. */
"Irreversible message deletion" = "Onomkeerbare berichtverwijdering";
@@ -2914,9 +2873,6 @@
/* No comment provided by engineer. */
"Messages sent" = "Berichten verzonden";
/* alert message */
"Messages were deleted after you selected them." = "Berichten zijn verwijderd nadat u ze had geselecteerd.";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Berichten, bestanden en oproepen worden beschermd door **end-to-end codering** met perfecte voorwaartse geheimhouding, afwijzing en inbraakherstel.";
@@ -3127,9 +3083,6 @@
/* No comment provided by engineer. */
"Nothing selected" = "Niets geselecteerd";
/* alert title */
"Nothing to forward!" = "Niets om door te sturen!";
/* No comment provided by engineer. */
"Notifications" = "Meldingen";
@@ -3282,9 +3235,6 @@
/* No comment provided by engineer. */
"other errors" = "overige fouten";
/* alert message */
"Other file errors:\n%@" = "Andere bestandsfouten:\n%@";
/* member role */
"owner" = "Eigenaar";
@@ -3306,9 +3256,6 @@
/* No comment provided by engineer. */
"Passcode set!" = "Toegangscode ingesteld!";
/* No comment provided by engineer. */
"Password" = "Wachtwoord";
/* No comment provided by engineer. */
"Password to show" = "Wachtwoord om weer te geven";
@@ -3405,9 +3352,6 @@
/* No comment provided by engineer. */
"Polish interface" = "Poolse interface";
/* No comment provided by engineer. */
"Port" = "Poort";
/* server test error */
"Possibly, certificate fingerprint in server address is incorrect" = "Mogelijk is de certificaat vingerafdruk in het server adres onjuist";
@@ -3519,9 +3463,6 @@
/* No comment provided by engineer. */
"Proxied servers" = "Proxied servers";
/* No comment provided by engineer. */
"Proxy requires password" = "Proxy vereist wachtwoord";
/* No comment provided by engineer. */
"Push notifications" = "Push meldingen";
@@ -3863,9 +3804,6 @@
/* No comment provided by engineer. */
"Saved WebRTC ICE servers will be removed" = "Opgeslagen WebRTC ICE servers worden verwijderd";
/* No comment provided by engineer. */
"Saving %lld messages" = "%lld berichten opslaan";
/* No comment provided by engineer. */
"Scale" = "Schaal";
@@ -4271,9 +4209,6 @@
/* No comment provided by engineer. */
"SMP server" = "SMP server";
/* No comment provided by engineer. */
"SOCKS proxy" = "SOCKS proxy";
/* blur media */
"Soft" = "Soft";
@@ -4362,13 +4297,13 @@
"Submit" = "Indienen";
/* No comment provided by engineer. */
"Subscribed" = "Subscribed";
"Subscribed" = "Ingeschreven";
/* No comment provided by engineer. */
"Subscription errors" = "Subscription fouten";
"Subscription errors" = "Inschrijving fouten";
/* No comment provided by engineer. */
"Subscriptions ignored" = "Subscriptions genegeerd";
"Subscriptions ignored" = "Inschrijvingen genegeerd";
/* No comment provided by engineer. */
"Support SimpleX Chat" = "Ondersteuning van SimpleX Chat";
@@ -4805,9 +4740,6 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "SimpleX Chat servers gebruiken?";
/* No comment provided by engineer. */
"Use SOCKS proxy" = "Gebruik SOCKS proxy";
/* No comment provided by engineer. */
"Use the app while in the call." = "Gebruik de app tijdens het gesprek.";
@@ -4815,10 +4747,10 @@
"Use the app with one hand." = "Gebruik de app met één hand.";
/* No comment provided by engineer. */
"User selection" = "Gebruikersselectie";
"User profile" = "Gebruikers profiel";
/* No comment provided by engineer. */
"Username" = "Gebruikersnaam";
"User selection" = "Gebruikersselectie";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "SimpleX Chat servers gebruiken.";
@@ -5021,6 +4953,9 @@
/* No comment provided by engineer. */
"you" = "jij";
/* No comment provided by engineer. */
"You" = "Jij";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "U **mag** niet dezelfde database op twee apparaten gebruiken.";
@@ -5070,7 +5005,7 @@
"You are not connected to these servers. Private routing is used to deliver messages to them." = "U bent niet verbonden met deze servers. Privéroutering wordt gebruikt om berichten bij hen af te leveren.";
/* No comment provided by engineer. */
"you are observer" = "je bent waarnemer";
"you are observer" = "jij bent waarnemer";
/* snd group event chat item */
"you blocked %@" = "je hebt %@ geblokkeerd";
@@ -5172,7 +5107,7 @@
"You joined this group. Connecting to inviting group member." = "Je bent lid geworden van deze groep. Verbinding maken met uitnodigend groepslid.";
/* snd group event chat item */
"you left" = "je bent vertrokken";
"you left" = "jij bent vertrokken";
/* No comment provided by engineer. */
"You may migrate the exported database." = "U kunt de geëxporteerde database migreren.";
@@ -5273,9 +5208,6 @@
/* No comment provided by engineer. */
"Your contacts will remain connected." = "Uw contacten blijven verbonden.";
/* No comment provided by engineer. */
"Your credentials may be sent unencrypted." = "Uw inloggegevens worden mogelijk niet-versleuteld verzonden.";
/* No comment provided by engineer. */
"Your current chat database will be DELETED and REPLACED with the imported one." = "Uw huidige chat database wordt VERWIJDERD en VERVANGEN door de geïmporteerde.";
+10 -6
View File
@@ -454,7 +454,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Wszystkie nowe wiadomości z %@ zostaną ukryte!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Wszystkie profile";
/* No comment provided by engineer. */
@@ -890,9 +890,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Preferencje czatu";
/* No comment provided by engineer. */
"Chat profile" = "Profil użytkownika";
/* No comment provided by engineer. */
"Chat theme" = "Motyw czatu";
@@ -1999,7 +1996,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Błąd zatrzymania czatu";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Błąd przełączania profilu!";
/* No comment provided by engineer. */
@@ -2026,7 +2023,8 @@
/* No comment provided by engineer. */
"Error: " = "Błąd: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Błąd: %@";
/* No comment provided by engineer. */
@@ -4700,6 +4698,9 @@
/* No comment provided by engineer. */
"Use the app with one hand." = "Korzystaj z aplikacji jedną ręką.";
/* No comment provided by engineer. */
"User profile" = "Profil użytkownika";
/* No comment provided by engineer. */
"User selection" = "Wybór użytkownika";
@@ -4904,6 +4905,9 @@
/* No comment provided by engineer. */
"you" = "Ty";
/* No comment provided by engineer. */
"You" = "Ty";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "**Nie możesz** używać tej samej bazy na dwóch urządzeniach.";
+10 -6
View File
@@ -454,7 +454,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Все новые сообщения от %@ будут скрыты!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Все профили";
/* No comment provided by engineer. */
@@ -890,9 +890,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Предпочтения";
/* No comment provided by engineer. */
"Chat profile" = "Профиль чата";
/* No comment provided by engineer. */
"Chat theme" = "Тема чата";
@@ -1999,7 +1996,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Ошибка при остановке чата";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Ошибка выбора профиля!";
/* No comment provided by engineer. */
@@ -2026,7 +2023,8 @@
/* No comment provided by engineer. */
"Error: " = "Ошибка: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Ошибка: %@";
/* No comment provided by engineer. */
@@ -4700,6 +4698,9 @@
/* No comment provided by engineer. */
"Use the app with one hand." = "Используйте приложение одной рукой.";
/* No comment provided by engineer. */
"User profile" = "Профиль чата";
/* No comment provided by engineer. */
"User selection" = "Выбор пользователя";
@@ -4904,6 +4905,9 @@
/* No comment provided by engineer. */
"you" = "Вы";
/* No comment provided by engineer. */
"You" = "Вы";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "Вы **не должны** использовать одну и ту же базу данных на двух устройствах.";
+9 -5
View File
@@ -605,9 +605,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "ค่ากําหนดในการแชท";
/* No comment provided by engineer. */
"Chat profile" = "โปรไฟล์ผู้ใช้";
/* No comment provided by engineer. */
"Chats" = "แชท";
@@ -1350,7 +1347,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "เกิดข้อผิดพลาดในการหยุดแชท";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "เกิดข้อผิดพลาดในการเปลี่ยนโปรไฟล์!";
/* No comment provided by engineer. */
@@ -1371,7 +1368,8 @@
/* No comment provided by engineer. */
"Error: " = "ผิดพลาด: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "ข้อผิดพลาด: % @";
/* No comment provided by engineer. */
@@ -3097,6 +3095,9 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "ใช้เซิร์ฟเวอร์ SimpleX Chat ไหม?";
/* No comment provided by engineer. */
"User profile" = "โปรไฟล์ผู้ใช้";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "กำลังใช้เซิร์ฟเวอร์ SimpleX Chat อยู่";
@@ -3214,6 +3215,9 @@
/* pref value */
"yes" = "ใช่";
/* No comment provided by engineer. */
"You" = "คุณ";
/* No comment provided by engineer. */
"You accepted connection" = "คุณยอมรับการเชื่อมต่อ";
+9 -5
View File
@@ -800,9 +800,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Sohbet tercihleri";
/* No comment provided by engineer. */
"Chat profile" = "Kullanıcı profili";
/* No comment provided by engineer. */
"Chats" = "Sohbetler";
@@ -1759,7 +1756,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Sohbet durdurulurken hata oluştu";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Profil değiştirilirken hata oluştu!";
/* No comment provided by engineer. */
@@ -1786,7 +1783,8 @@
/* No comment provided by engineer. */
"Error: " = "Hata: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Hata: %@";
/* No comment provided by engineer. */
@@ -4094,6 +4092,9 @@
/* No comment provided by engineer. */
"Use the app while in the call." = "Görüşme sırasında uygulamayı kullanın.";
/* No comment provided by engineer. */
"User profile" = "Kullanıcı profili";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "SimpleX Chat sunucuları kullanılıyor.";
@@ -4280,6 +4281,9 @@
/* No comment provided by engineer. */
"you" = "sen";
/* No comment provided by engineer. */
"You" = "Sen";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "Aynı veritabanını iki cihazda **kullanmamalısınız**.";
+10 -6
View File
@@ -454,7 +454,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Всі нові повідомлення від %@ будуть приховані!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "Всі профілі";
/* No comment provided by engineer. */
@@ -890,9 +890,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "Налаштування чату";
/* No comment provided by engineer. */
"Chat profile" = "Профіль користувача";
/* No comment provided by engineer. */
"Chat theme" = "Тема чату";
@@ -1999,7 +1996,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "Помилка зупинки чату";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "Помилка перемикання профілю!";
/* No comment provided by engineer. */
@@ -2026,7 +2023,8 @@
/* No comment provided by engineer. */
"Error: " = "Помилка: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "Помилка: %@";
/* No comment provided by engineer. */
@@ -4700,6 +4698,9 @@
/* No comment provided by engineer. */
"Use the app with one hand." = "Використовуйте додаток однією рукою.";
/* No comment provided by engineer. */
"User profile" = "Профіль користувача";
/* No comment provided by engineer. */
"User selection" = "Вибір користувача";
@@ -4904,6 +4905,9 @@
/* No comment provided by engineer. */
"you" = "ти";
/* No comment provided by engineer. */
"You" = "Ти";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "Ви **не повинні використовувати** одну і ту ж базу даних на двох пристроях.";
+10 -6
View File
@@ -454,7 +454,7 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "来自 %@ 的所有新消息都将被隐藏!";
/* profile dropdown */
/* No comment provided by engineer. */
"All profiles" = "所有配置文件";
/* No comment provided by engineer. */
@@ -890,9 +890,6 @@
/* No comment provided by engineer. */
"Chat preferences" = "聊天偏好设置";
/* No comment provided by engineer. */
"Chat profile" = "用户资料";
/* No comment provided by engineer. */
"Chat theme" = "聊天主题";
@@ -1999,7 +1996,7 @@
/* No comment provided by engineer. */
"Error stopping chat" = "停止聊天错误";
/* alertTitle */
/* No comment provided by engineer. */
"Error switching profile!" = "切换资料错误!";
/* No comment provided by engineer. */
@@ -2026,7 +2023,8 @@
/* No comment provided by engineer. */
"Error: " = "错误: ";
/* alert message */
/* file error text
snd error text */
"Error: %@" = "错误: @";
/* No comment provided by engineer. */
@@ -4700,6 +4698,9 @@
/* No comment provided by engineer. */
"Use the app with one hand." = "用一只手使用应用程序。";
/* No comment provided by engineer. */
"User profile" = "用户资料";
/* No comment provided by engineer. */
"User selection" = "用户选择";
@@ -4904,6 +4905,9 @@
/* No comment provided by engineer. */
"you" = "您";
/* No comment provided by engineer. */
"You" = "您";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "您 **不得** 在两台设备上使用相同的数据库。";
@@ -226,9 +226,8 @@ actual fun ActiveCallView() {
ActiveCallOverlay(call, chatModel, callAudioDeviceManager)
}
}
KeyChangeEffect(call?.localMediaSources?.hasVideo) {
if (call != null && call.hasVideo && callAudioDeviceManager.currentDevice.value?.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
// enabling speaker on user action (peer action ignored) and not disabling it again
KeyChangeEffect(call?.hasVideo) {
if (call != null) {
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.hasVideo, true)
}
}
@@ -115,9 +115,6 @@ fun AppearanceScope.AppearanceLayout(
SectionDividerSpaced()
ThemesSection(systemDarkTheme)
SectionDividerSpaced()
MessageShapeSection()
SectionDividerSpaced()
ProfileImageSection()
@@ -71,9 +71,6 @@ object ChatModel {
val groupMembers = mutableStateListOf<GroupMember>()
val groupMembersIndexes = mutableStateMapOf<Long, Int>()
// false: default placement, true: floating window.
// Used for deciding to add terminal items on main thread or not. Floating means appPrefs.terminalAlwaysVisible
var terminalsVisible = setOf<Boolean>()
val terminalItems = mutableStateOf<List<TerminalItem>>(listOf())
val userAddress = mutableStateOf<UserContactLinkRec?>(null)
val chatItemTTL = mutableStateOf<ChatItemTTL>(ChatItemTTL.None)
@@ -775,16 +772,6 @@ object ChatModel {
fun addTerminalItem(item: TerminalItem) {
val maxItems = if (appPreferences.developerTools.get()) 500 else 200
if (terminalsVisible.isNotEmpty()) {
withApi {
addTerminalItem(item, maxItems)
}
} else {
addTerminalItem(item, maxItems)
}
}
private fun addTerminalItem(item: TerminalItem, maxItems: Int) {
if (terminalItems.value.size >= maxItems) {
terminalItems.value = terminalItems.value.subList(1, terminalItems.value.size)
}
@@ -219,8 +219,6 @@ class AppPreferences {
}, settingsThemes)
val themeOverrides = mkThemeOverridesPreference()
val profileImageCornerRadius = mkFloatPreference(SHARED_PREFS_PROFILE_IMAGE_CORNER_RADIUS, 22.5f)
val chatItemRoundness = mkFloatPreference(SHARED_PREFS_CHAT_ITEM_ROUNDNESS, 0.75f)
val chatItemTail = mkBoolPreference(SHARED_PREFS_CHAT_ITEM_TAIL, true)
val fontScale = mkFloatPreference(SHARED_PREFS_FONT_SCALE, 1f)
val densityScale = mkFloatPreference(SHARED_PREFS_DENSITY_SCALE, 1f)
@@ -424,8 +422,6 @@ class AppPreferences {
private const val SHARED_PREFS_THEMES_OLD = "Themes"
private const val SHARED_PREFS_THEME_OVERRIDES = "ThemeOverrides"
private const val SHARED_PREFS_PROFILE_IMAGE_CORNER_RADIUS = "ProfileImageCornerRadius"
private const val SHARED_PREFS_CHAT_ITEM_ROUNDNESS = "ChatItemRoundness"
private const val SHARED_PREFS_CHAT_ITEM_TAIL = "ChatItemTail"
private const val SHARED_PREFS_FONT_SCALE = "FontScale"
private const val SHARED_PREFS_DENSITY_SCALE = "DensityScale"
private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion"
@@ -3654,7 +3650,7 @@ data class NetCfg(
val socksMode: SocksMode = SocksMode.Always,
val hostMode: HostMode = HostMode.OnionViaSocks,
val requiredHostMode: Boolean = false,
val sessionMode: TransportSessionMode = TransportSessionMode.default,
val sessionMode: TransportSessionMode = TransportSessionMode.User,
val smpProxyMode: SMPProxyMode = SMPProxyMode.Unknown,
val smpProxyFallback: SMPProxyFallback = SMPProxyFallback.AllowProtected,
val smpWebPort: Boolean = false,
@@ -3781,13 +3777,10 @@ enum class SMPProxyFallback {
@Serializable
enum class TransportSessionMode {
@SerialName("user") User,
@SerialName("session") Session,
@SerialName("server") Server,
@SerialName("entity") Entity;
companion object {
val default = Session
val safeValues = arrayOf(User, Session, Server)
val default = User
}
}
@@ -6341,8 +6334,6 @@ data class AppSettings(
var iosCallKitEnabled: Boolean? = null,
var iosCallKitCallsInRecents: Boolean? = null,
var uiProfileImageCornerRadius: Float? = null,
var uiChatItemRoundness: Float? = null,
var uiChatItemTail: Boolean? = null,
var uiColorScheme: String? = null,
var uiDarkColorScheme: String? = null,
var uiCurrentThemeIds: Map<String, String>? = null,
@@ -6375,8 +6366,6 @@ data class AppSettings(
if (iosCallKitEnabled != def.iosCallKitEnabled) { empty.iosCallKitEnabled = iosCallKitEnabled }
if (iosCallKitCallsInRecents != def.iosCallKitCallsInRecents) { empty.iosCallKitCallsInRecents = iosCallKitCallsInRecents }
if (uiProfileImageCornerRadius != def.uiProfileImageCornerRadius) { empty.uiProfileImageCornerRadius = uiProfileImageCornerRadius }
if (uiChatItemRoundness != def.uiChatItemRoundness) { empty.uiChatItemRoundness = uiChatItemRoundness }
if (uiChatItemTail != def.uiChatItemTail) { empty.uiChatItemTail = uiChatItemTail }
if (uiColorScheme != def.uiColorScheme) { empty.uiColorScheme = uiColorScheme }
if (uiDarkColorScheme != def.uiDarkColorScheme) { empty.uiDarkColorScheme = uiDarkColorScheme }
if (uiCurrentThemeIds != def.uiCurrentThemeIds) { empty.uiCurrentThemeIds = uiCurrentThemeIds }
@@ -6420,8 +6409,6 @@ data class AppSettings(
iosCallKitEnabled?.let { def.iosCallKitEnabled.set(it) }
iosCallKitCallsInRecents?.let { def.iosCallKitCallsInRecents.set(it) }
uiProfileImageCornerRadius?.let { def.profileImageCornerRadius.set(it) }
uiChatItemRoundness?.let { def.chatItemRoundness.set(it) }
uiChatItemTail?.let { def.chatItemTail.set(it) }
uiColorScheme?.let { def.currentTheme.set(it) }
uiDarkColorScheme?.let { def.systemDarkTheme.set(it) }
uiCurrentThemeIds?.let { def.currentThemeIds.set(it) }
@@ -6455,8 +6442,6 @@ data class AppSettings(
iosCallKitEnabled = true,
iosCallKitCallsInRecents = false,
uiProfileImageCornerRadius = 22.5f,
uiChatItemRoundness = 0.75f,
uiChatItemTail = true,
uiColorScheme = DefaultTheme.SYSTEM_THEME_NAME,
uiDarkColorScheme = DefaultTheme.SIMPLEX.themeName,
uiCurrentThemeIds = null,
@@ -6491,8 +6476,6 @@ data class AppSettings(
iosCallKitEnabled = def.iosCallKitEnabled.get(),
iosCallKitCallsInRecents = def.iosCallKitCallsInRecents.get(),
uiProfileImageCornerRadius = def.profileImageCornerRadius.get(),
uiChatItemRoundness = def.chatItemRoundness.get(),
uiChatItemTail = def.chatItemTail.get(),
uiColorScheme = def.currentTheme.get() ?: DefaultTheme.SYSTEM_THEME_NAME,
uiDarkColorScheme = def.systemDarkTheme.get() ?: DefaultTheme.SIMPLEX.themeName,
uiCurrentThemeIds = def.currentThemeIds.get(),
@@ -20,12 +20,11 @@ import chat.simplex.common.views.chat.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.stringResource
@Composable
fun TerminalView(floating: Boolean = false, close: () -> Unit) {
fun TerminalView(chatModel: ChatModel, close: () -> Unit) {
val composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = false)) }
val close = {
close()
@@ -38,7 +37,6 @@ fun TerminalView(floating: Boolean = false, close: () -> Unit) {
})
TerminalLayout(
composeState,
floating,
sendCommand = { sendCommand(chatModel, composeState) },
close
)
@@ -67,7 +65,6 @@ private fun sendCommand(chatModel: ChatModel, composeState: MutableState<Compose
@Composable
fun TerminalLayout(
composeState: MutableState<ComposeState>,
floating: Boolean,
sendCommand: () -> Unit,
close: () -> Unit
) {
@@ -121,40 +118,19 @@ fun TerminalLayout(
color = MaterialTheme.colors.background,
contentColor = LocalContentColor.current
) {
TerminalLog(floating)
TerminalLog()
}
}
}
}
@Composable
fun TerminalLog(floating: Boolean) {
fun TerminalLog() {
val reversedTerminalItems by remember {
derivedStateOf { chatModel.terminalItems.value.asReversed() }
}
val clipboard = LocalClipboardManager.current
val listState = LocalAppBarHandler.current?.listState ?: rememberLazyListState()
LaunchedEffect(Unit) {
var autoScrollToBottom = true
launch {
snapshotFlow { listState.layoutInfo.totalItemsCount }
.filter { autoScrollToBottom }
.collect {
try {
listState.scrollToItem(0)
} catch (e: Exception) {
Log.e(TAG, e.stackTraceToString())
}
}
}
launch {
snapshotFlow { listState.firstVisibleItemIndex }
.collect {
autoScrollToBottom = listState.firstVisibleItemIndex == 0
}
}
}
LazyColumnWithScrollBar(reverseLayout = true, state = listState) {
LazyColumnWithScrollBar(reverseLayout = true) {
items(reversedTerminalItems, key = { item -> item.id to item.createdAtNanos }) { item ->
val rhId = item.remoteHostId
val rhIdStr = if (rhId == null) "" else "$rhId "
@@ -166,12 +142,7 @@ fun TerminalLog(floating: Boolean) {
modifier = Modifier
.fillMaxWidth()
.clickable {
val modalPlace = if (floating) {
ModalManager.floatingTerminal
} else {
ModalManager.start
}
modalPlace.showModal(endButtons = { ShareButton { clipboard.shareText(item.details) } }) {
ModalManager.start.showModal(endButtons = { ShareButton { clipboard.shareText(item.details) } }) {
SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) {
val details = item.details
.let {
@@ -185,16 +156,6 @@ fun TerminalLog(floating: Boolean) {
)
}
}
DisposableEffect(Unit) {
val terminals = chatModel.terminalsVisible.toMutableSet()
terminals += floating
chatModel.terminalsVisible = terminals
onDispose {
val terminals = chatModel.terminalsVisible.toMutableSet()
terminals -= floating
chatModel.terminalsVisible = terminals
}
}
}
@Preview/*(
@@ -208,7 +169,6 @@ fun PreviewTerminalLayout() {
TerminalLayout(
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = false)) },
sendCommand = {},
floating = false,
close = {}
)
}
@@ -83,7 +83,6 @@ enum class CallState {
@Serializable
sealed class WCallCommand {
@Serializable @SerialName("capabilities") data class Capabilities(val media: CallMediaType): WCallCommand()
@Serializable @SerialName("permission") data class Permission(val title: String, val chrome: String, val safari: String): WCallCommand()
@Serializable @SerialName("start") data class Start(val media: CallMediaType, val aesKey: String? = null, val iceServers: List<RTCIceServer>? = null, val relay: Boolean? = null): WCallCommand()
@Serializable @SerialName("offer") data class Offer(val offer: String, val iceCandidates: String, val media: CallMediaType, val aesKey: String? = null, val iceServers: List<RTCIceServer>? = null, val relay: Boolean? = null): WCallCommand()
@Serializable @SerialName("answer") data class Answer (val answer: String, val iceCandidates: String): WCallCommand()
@@ -24,10 +24,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.views.chat.item.ItemAction
import chat.simplex.common.views.chat.item.MarkdownText
import chat.simplex.common.views.helpers.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.group.MemberProfileImage
import chat.simplex.common.views.chat.item.*
import chat.simplex.common.views.chatlist.*
import chat.simplex.res.MR
import dev.icerock.moko.resources.ImageResource
@@ -74,7 +75,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
Column {
Box(
Modifier.clipChatItem().background(itemColor).padding(bottom = 3.dp)
Modifier.clip(RoundedCornerShape(18.dp)).background(itemColor).padding(bottom = 3.dp)
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = {})
.onRightClick { showMenu.value = true }
) {
@@ -121,7 +122,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
Column {
Box(
Modifier.clipChatItem().background(quoteColor).padding(bottom = 3.dp)
Modifier.clip(RoundedCornerShape(18.dp)).background(quoteColor).padding(bottom = 3.dp)
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = {})
.onRightClick { showMenu.value = true }
) {
@@ -1036,7 +1036,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
tryOrShowError("${cItem.id}ChatItem", error = {
CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart)
}) {
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools, showViaProxy = showViaProxy, showTimestamp = itemSeparation.timestamp)
}
}
@@ -1081,15 +1081,6 @@ fun BoxWithConstraintsScope.ChatItemsList(
}
}
@Composable
fun adjustTailPaddingOffset(originalPadding: Dp, start: Boolean): Dp {
val chatItemTail = remember { appPreferences.chatItemTail.state }
val style = shapeStyle(cItem, chatItemTail.value, itemSeparation.largeGap, true)
val tailRendered = style is ShapeStyle.Bubble && style.tailVisible
return originalPadding + (if (tailRendered) 0.dp else if (start) msgTailWidthDp * 2 else msgTailWidthDp)
}
Box {
val voiceWithTransparentBack = cItem.content.msgContent is MsgContent.MCVoice && cItem.content.text.isEmpty() && cItem.quotedItem == null && cItem.meta.itemForwarded == null
val selectionVisible = selectedChatItems.value != null && cItem.canBeDeletedForSelf
@@ -1108,7 +1099,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
Column(
Modifier
.padding(top = 8.dp)
.padding(start = 8.dp, end = if (voiceWithTransparentBack) 12.dp else adjustTailPaddingOffset(66.dp, start = false))
.padding(start = 8.dp, end = if (voiceWithTransparentBack) 12.dp else 66.dp)
.fillMaxWidth()
.then(swipeableModifier),
verticalArrangement = Arrangement.spacedBy(4.dp),
@@ -1120,7 +1111,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
Text(
memberNames(member, prevMember, memCount),
Modifier
.padding(start = (MEMBER_IMAGE_SIZE * fontSizeSqrtMultiplier) + DEFAULT_PADDING_HALF)
.padding(start = MEMBER_IMAGE_SIZE + DEFAULT_PADDING_HALF)
.weight(1f, false),
fontSize = 13.5.sp,
color = MaterialTheme.colors.secondary,
@@ -1128,13 +1119,9 @@ fun BoxWithConstraintsScope.ChatItemsList(
maxLines = 1
)
if (memCount == 1 && member.memberRole > GroupMemberRole.Member) {
val chatItemTail = remember { appPreferences.chatItemTail.state }
val style = shapeStyle(cItem, chatItemTail.value, itemSeparation.largeGap, true)
val tailRendered = style is ShapeStyle.Bubble && style.tailVisible
Text(
member.memberRole.text,
Modifier.padding(start = DEFAULT_PADDING_HALF * 1.5f, end = DEFAULT_PADDING_HALF + if (tailRendered) msgTailWidthDp else 0.dp),
Modifier.padding(start = DEFAULT_PADDING_HALF * 1.5f, end = DEFAULT_PADDING_HALF),
fontSize = 13.5.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colors.secondary,
@@ -1150,11 +1137,12 @@ fun BoxWithConstraintsScope.ChatItemsList(
androidx.compose.animation.AnimatedVisibility(selectionVisible, enter = fadeIn(), exit = fadeOut()) {
SelectedChatItem(Modifier, cItem.id, selectedChatItems)
}
Row(Modifier.graphicsLayer { translationX = selectionOffset.toPx() }) {
Row(Modifier.graphicsLayer { translationX = selectionOffset.toPx() },
horizontalArrangement = Arrangement.spacedBy(4.dp)) {
Box(Modifier.clickable { showMemberInfo(chatInfo.groupInfo, member) }) {
MemberImage(member)
}
Box(modifier = Modifier.padding(top = 2.dp, start = 4.dp).chatItemOffset(cItem, itemSeparation.largeGap, revealed = revealed.value)) {
Box(modifier = Modifier.padding(top = 2.dp)) {
ChatItemViewShortHand(cItem, itemSeparation, range, false)
}
}
@@ -1176,8 +1164,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
}
Row(
Modifier
.padding(start = 8.dp + (MEMBER_IMAGE_SIZE * fontSizeSqrtMultiplier) + 4.dp, end = if (voiceWithTransparentBack) 12.dp else adjustTailPaddingOffset(66.dp, start = false))
.chatItemOffset(cItem, itemSeparation.largeGap, revealed = revealed.value)
.padding(start = 8.dp + MEMBER_IMAGE_SIZE + 4.dp, end = if (voiceWithTransparentBack) 12.dp else 66.dp)
.then(swipeableOrSelectionModifier)
) {
ChatItemViewShortHand(cItem, itemSeparation, range)
@@ -1191,8 +1178,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
}
Box(
Modifier
.padding(start = if (voiceWithTransparentBack) 12.dp else adjustTailPaddingOffset(104.dp, start = true), end = 12.dp)
.chatItemOffset(cItem, itemSeparation.largeGap, revealed = revealed.value)
.padding(start = if (voiceWithTransparentBack) 12.dp else 104.dp, end = 12.dp)
.then(if (selectionVisible) Modifier else swipeableModifier)
) {
ChatItemViewShortHand(cItem, itemSeparation, range)
@@ -1204,14 +1190,11 @@ fun BoxWithConstraintsScope.ChatItemsList(
AnimatedVisibility (selectionVisible, enter = fadeIn(), exit = fadeOut()) {
SelectedChatItem(Modifier.padding(start = 8.dp), cItem.id, selectedChatItems)
}
Box(
Modifier.padding(
start = if (sent && !voiceWithTransparentBack) adjustTailPaddingOffset(76.dp, start = true) else 12.dp,
end = if (sent || voiceWithTransparentBack) 12.dp else adjustTailPaddingOffset(76.dp, start = false),
)
.chatItemOffset(cItem, itemSeparation.largeGap, revealed = revealed.value)
.then(if (!selectionVisible || !sent) swipeableOrSelectionModifier else Modifier)
start = if (sent && !voiceWithTransparentBack) 76.dp else 12.dp,
end = if (sent || voiceWithTransparentBack) 12.dp else 76.dp,
).then(if (!selectionVisible || !sent) swipeableOrSelectionModifier else Modifier)
) {
ChatItemViewShortHand(cItem, itemSeparation, range)
}
@@ -3,14 +3,13 @@ package chat.simplex.common.views.chat.item
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.*
@@ -27,16 +26,9 @@ import chat.simplex.common.views.chat.*
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
import kotlinx.datetime.Clock
import kotlin.math.*
// TODO refactor so that FramedItemView can show all CIContent items if they're deleted (see Swift code)
private val msgRectMaxRadius = 18.dp
private val msgBubbleMaxRadius = msgRectMaxRadius * 1.2f
val msgTailWidthDp = 9.dp
private val msgTailMinHeightDp = msgTailWidthDp * 1.254f // ~56deg
private val msgTailMaxHeightDp = msgTailWidthDp * 1.732f // 60deg
val chatEventStyle = SpanStyle(fontSize = 12.sp, fontWeight = FontWeight.Light, color = CurrentColors.value.colors.secondary)
fun chatEventText(ci: ChatItem): AnnotatedString =
@@ -82,7 +74,6 @@ fun ChatItemView(
developerTools: Boolean,
showViaProxy: Boolean,
showTimestamp: Boolean,
itemSeparation: ItemSeparation,
preview: Boolean = false,
) {
val uriHandler = LocalUriHandler.current
@@ -109,7 +100,7 @@ fun ChatItemView(
@Composable
fun ChatItemReactions() {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.chatItemOffset(cItem, itemSeparation.largeGap, inverted = true, revealed = true)) {
Row(verticalAlignment = Alignment.CenterVertically) {
cItem.reactions.forEach { r ->
var modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp).clip(RoundedCornerShape(8.dp))
if (cInfo.featureEnabled(ChatFeature.Reactions) && (cItem.allowAddReaction || r.userReacted)) {
@@ -136,13 +127,13 @@ fun ChatItemView(
Column(horizontalAlignment = if (cItem.chatDir.sent) Alignment.End else Alignment.Start) {
Column(
Modifier
.clipChatItem(cItem, itemSeparation.largeGap, revealed.value)
.clip(RoundedCornerShape(18.dp))
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = onClick)
.onRightClick { showMenu.value = true },
) {
@Composable
fun framedItemView() {
FramedItemView(cInfo, cItem, uriHandler, imageProvider, linkMode = linkMode, showViaProxy = showViaProxy, showMenu, showTimestamp = showTimestamp, tailVisible = itemSeparation.largeGap, receiveFile, onLinkLongClick, scrollToItem)
FramedItemView(cInfo, cItem, uriHandler, imageProvider, linkMode = linkMode, showViaProxy = showViaProxy, showMenu, showTimestamp = showTimestamp, receiveFile, onLinkLongClick, scrollToItem)
}
fun deleteMessageQuestionText(): String {
@@ -804,154 +795,6 @@ fun ItemAction(text: String, color: Color = Color.Unspecified, onClick: () -> Un
}
}
@Composable
fun Modifier.chatItemOffset(cItem: ChatItem, tailVisible: Boolean, inverted: Boolean = false, revealed: Boolean): Modifier {
val chatItemTail = remember { appPreferences.chatItemTail.state }
val style = shapeStyle(cItem, chatItemTail.value, tailVisible, revealed)
val offset = if (style is ShapeStyle.Bubble) {
if (style.tailVisible) {
if (cItem.chatDir.sent) msgTailWidthDp else -msgTailWidthDp
} else {
0.dp
}
} else 0.dp
return this.offset(x = if (inverted) (-1f * offset) else offset)
}
@Composable
fun Modifier.clipChatItem(chatItem: ChatItem? = null, tailVisible: Boolean = false, revealed: Boolean = false): Modifier {
val chatItemRoundness = remember { appPreferences.chatItemRoundness.state }
val chatItemTail = remember { appPreferences.chatItemTail.state }
val style = shapeStyle(chatItem, chatItemTail.value, tailVisible, revealed)
val cornerRoundness = chatItemRoundness.value.coerceIn(0f, 1f)
val shape = when (style) {
is ShapeStyle.Bubble -> chatItemShape(cornerRoundness, LocalDensity.current, style.tailVisible, chatItem?.chatDir?.sent == true)
is ShapeStyle.RoundRect -> RoundedCornerShape(style.radius * cornerRoundness)
}
return this.clip(shape)
}
private fun chatItemShape(roundness: Float, density: Density, tailVisible: Boolean, sent: Boolean = false): GenericShape = GenericShape { size, _ ->
val (msgTailWidth, msgBubbleMaxRadius) = with(density) { Pair(msgTailWidthDp.toPx(), msgBubbleMaxRadius.toPx()) }
val width = if (sent && tailVisible) size.width - msgTailWidth else size.width
val height = size.height
val rxMax = min(msgBubbleMaxRadius, width / 2)
val ryMax = min(msgBubbleMaxRadius, height / 2)
val rx = roundness * rxMax
val ry = roundness * ryMax
val tailHeight = with(density) {
min(
msgTailMinHeightDp.toPx() + roundness * (msgTailMaxHeightDp.toPx() - msgTailMinHeightDp.toPx()),
height / 2
)
}
moveTo(rx, 0f)
lineTo(width - rx, 0f) // Top Line
if (roundness > 0) {
quadraticBezierTo(width, 0f, width, ry) // Top-right corner
}
if (height > 2 * ry) {
lineTo(width, height - ry) // Right side
}
if (roundness > 0) {
quadraticBezierTo(width, height, width - rx, height) // Bottom-right corner
}
if (tailVisible) {
lineTo(0f, height) // Bottom line
if (roundness > 0) {
val d = tailHeight - msgTailWidth * msgTailWidth / tailHeight
val controlPoint = Offset(msgTailWidth, height - tailHeight + d * sqrt(roundness))
quadraticBezierTo(controlPoint.x, controlPoint.y, msgTailWidth, height - tailHeight)
} else {
lineTo(msgTailWidth, height - tailHeight)
}
if (height > ry + tailHeight) {
lineTo(msgTailWidth, ry)
}
} else {
lineTo(rx, height) // Bottom line
if (roundness > 0) {
quadraticBezierTo(0f, height, 0f, height - ry) // Bottom-left corner
}
if (height > 2 * ry) {
lineTo(0f, ry) // Left side
}
}
if (roundness > 0) {
val bubbleInitialX = if (tailVisible) msgTailWidth else 0f
quadraticBezierTo(bubbleInitialX, 0f, bubbleInitialX + rx, 0f) // Top-left corner
}
if (sent) {
val matrix = Matrix()
matrix.scale(-1f, 1f)
this.transform(matrix)
this.translate(Offset(size.width, 0f))
}
}
sealed class ShapeStyle {
data class Bubble(val tailVisible: Boolean, val startPadding: Boolean) : ShapeStyle()
data class RoundRect(val radius: Dp) : ShapeStyle()
}
fun shapeStyle(chatItem: ChatItem? = null, tailEnabled: Boolean, tailVisible: Boolean, revealed: Boolean): ShapeStyle {
if (chatItem == null) {
return ShapeStyle.RoundRect(msgRectMaxRadius)
}
when (chatItem.content) {
is CIContent.SndMsgContent,
is CIContent.RcvMsgContent,
is CIContent.RcvDecryptionError,
is CIContent.SndDeleted,
is CIContent.RcvDeleted,
is CIContent.RcvIntegrityError,
is CIContent.SndModerated,
is CIContent.RcvModerated,
is CIContent.RcvBlocked,
is CIContent.InvalidJSON -> {
if (chatItem.meta.itemDeleted != null && (!revealed || chatItem.isDeletedContent)) {
return ShapeStyle.RoundRect(msgRectMaxRadius)
}
val tail = when (val content = chatItem.content.msgContent) {
is MsgContent.MCImage,
is MsgContent.MCVideo,
is MsgContent.MCVoice -> {
if (content.text.isEmpty()) {
false
} else {
tailVisible
}
}
is MsgContent.MCText -> {
if (isShortEmoji(content.text)) {
false
} else {
tailVisible
}
}
else -> tailVisible
}
return if (tailEnabled) {
ShapeStyle.Bubble(tail, !chatItem.chatDir.sent)
} else {
ShapeStyle.RoundRect(msgRectMaxRadius)
}
}
is CIContent.RcvGroupInvitation,
is CIContent.SndGroupInvitation -> return ShapeStyle.RoundRect(msgRectMaxRadius)
else -> return ShapeStyle.RoundRect(8.dp)
}
}
fun cancelFileAlertDialog(fileId: Long, cancelFile: (Long) -> Unit, cancelAction: CancelAction) {
AlertManager.shared.showAlertDialog(
title = generalGetString(cancelAction.alert.titleId),
@@ -1088,7 +931,6 @@ fun PreviewChatItemView(
showViaProxy = false,
showTimestamp = true,
preview = true,
itemSeparation = ItemSeparation(timestamp = true, largeGap = true, null)
)
}
@@ -1128,8 +970,7 @@ fun PreviewChatItemViewDeletedContent() {
developerTools = false,
showViaProxy = false,
preview = true,
showTimestamp = true,
itemSeparation = ItemSeparation(timestamp = true, largeGap = true, null)
showTimestamp = true
)
}
}
@@ -2,10 +2,12 @@ package chat.simplex.common.views.chat.item
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
@@ -34,7 +36,6 @@ fun FramedItemView(
showViaProxy: Boolean,
showMenu: MutableState<Boolean>,
showTimestamp: Boolean,
tailVisible: Boolean = false,
receiveFile: (Long) -> Unit,
onLinkLongClick: (link: String) -> Unit = {},
scrollToItem: (Long) -> Unit = {},
@@ -189,7 +190,7 @@ fun FramedItemView(
val sentColor = MaterialTheme.appColors.sentMessage
val receivedColor = MaterialTheme.appColors.receivedMessage
Box(Modifier
.clipChatItem(ci, tailVisible, revealed = true)
.clip(RoundedCornerShape(18.dp))
.background(
when {
transparentBackground -> Color.Transparent
@@ -199,14 +200,7 @@ fun FramedItemView(
)) {
var metaColor = MaterialTheme.colors.secondary
Box(contentAlignment = Alignment.BottomEnd) {
val chatItemTail = remember { appPreferences.chatItemTail.state }
val style = shapeStyle(ci, chatItemTail.value, tailVisible, revealed = true)
val tailRendered = style is ShapeStyle.Bubble && style.tailVisible
Column(
Modifier
.width(IntrinsicSize.Max)
.padding(start = if (tailRendered) msgTailWidthDp else 0.dp, end = if (sent && tailRendered) msgTailWidthDp else 0.dp)
) {
Column(Modifier.width(IntrinsicSize.Max)) {
PriorityLayout(Modifier, CHAT_IMAGE_LAYOUT_ID) {
if (ci.meta.itemDeleted != null) {
when (ci.meta.itemDeleted) {
@@ -285,13 +279,7 @@ fun FramedItemView(
}
}
}
Box(
Modifier
.padding(
bottom = 6.dp,
end = 12.dp + if (tailRendered && sent) msgTailWidthDp else 0.dp,
)
) {
Box(Modifier.padding(bottom = 6.dp, end = 12.dp)) {
CIMetaView(ci, chatTTL, metaColor, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
}
}
@@ -2,8 +2,10 @@ package chat.simplex.common.views.helpers
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
@@ -207,14 +209,11 @@ class ModalManager(private val placement: ModalPlacement? = null) {
val end = if (appPlatform.isAndroid) shared else ModalManager(ModalPlacement.END)
val fullscreen = if (appPlatform.isAndroid) shared else ModalManager(ModalPlacement.FULLSCREEN)
val floatingTerminal = if (appPlatform.isAndroid) shared else ModalManager(ModalPlacement.START)
fun closeAllModalsEverywhere() {
start.closeModals()
center.closeModals()
end.closeModals()
fullscreen.closeModals()
floatingTerminal.closeModals()
}
@OptIn(ExperimentalAnimationApi::class)
@@ -218,7 +218,7 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U
SectionDividerSpaced(maxTopPadding = true)
}
if (currentRemoteHost == null) {
if (currentRemoteHost == null && developerTools) {
SectionView(stringResource(MR.strings.network_session_mode_transport_isolation).uppercase()) {
SessionModePicker(sessionMode, showModal, updateSessionMode)
}
@@ -35,7 +35,6 @@ import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.ThemeManager.colorFromReadableHex
import chat.simplex.common.ui.theme.ThemeManager.toReadableHex
import chat.simplex.common.views.chat.item.PreviewChatItemView
import chat.simplex.common.views.chat.item.msgTailWidthDp
import chat.simplex.res.MR
import com.godaddy.android.colorpicker.ClassicColorPicker
import com.godaddy.android.colorpicker.HsvColor
@@ -85,31 +84,6 @@ object AppearanceScope {
}
}
@Composable
fun MessageShapeSection() {
SectionView(stringResource(MR.strings.settings_section_title_message_shape).uppercase(), contentPadding = PaddingValues()) {
Row(modifier = Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING + 4.dp ) ,verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(MR.strings.settings_message_shape_corner), color = colors.onBackground)
Spacer(Modifier.width(10.dp))
Slider(
remember { appPreferences.chatItemRoundness.state }.value,
valueRange = 0f..1f,
steps = 20,
onValueChange = {
val diff = it % 0.05f
appPreferences.chatItemRoundness.set(it + (if (diff >= 0.025f) -diff + 0.05f else -diff))
saveThemeToDatabase(null)
},
colors = SliderDefaults.colors(
activeTickColor = Color.Transparent,
inactiveTickColor = Color.Transparent,
)
)
}
SettingsPreferenceItem(icon = null, stringResource(MR.strings.settings_message_shape_tail), appPreferences.chatItemTail)
}
}
@Composable
fun FontScaleSection() {
val localFontScale = remember { mutableStateOf(appPrefs.fontScale.get()) }
@@ -195,17 +169,13 @@ object AppearanceScope {
.padding(DEFAULT_PADDING_HALF)
) {
if (withMessages) {
val chatItemTail = remember { appPreferences.chatItemTail.state }
Column(verticalArrangement = Arrangement.spacedBy(4.dp), modifier = if (chatItemTail.value) Modifier else Modifier.padding(horizontal = msgTailWidthDp)) {
val alice = remember { ChatItem.getSampleData(1, CIDirection.DirectRcv(), Clock.System.now(), generalGetString(MR.strings.wallpaper_preview_hello_bob)) }
PreviewChatItemView(alice)
PreviewChatItemView(
ChatItem.getSampleData(2, CIDirection.DirectSnd(), Clock.System.now(), stringResource(MR.strings.wallpaper_preview_hello_alice),
quotedItem = CIQuote(alice.chatDir, alice.id, sentAt = alice.meta.itemTs, formattedText = alice.formattedText, content = MsgContent.MCText(alice.content.text))
)
)
}
val alice = remember { ChatItem.getSampleData(1, CIDirection.DirectRcv(), Clock.System.now(), generalGetString(MR.strings.wallpaper_preview_hello_bob)) }
PreviewChatItemView(alice)
PreviewChatItemView(
ChatItem.getSampleData(2, CIDirection.DirectSnd(), Clock.System.now(), stringResource(MR.strings.wallpaper_preview_hello_alice),
quotedItem = CIQuote(alice.chatDir, alice.id, sentAt = alice.meta.itemTs, formattedText = alice.formattedText, content = MsgContent.MCText(alice.content.text))
)
)
} else {
Box(Modifier.fillMaxSize())
}
@@ -35,7 +35,7 @@ fun DeveloperView(
val unchangedHints = mutableStateOf(unchangedHintPreferences())
SectionView {
InstallTerminalAppItem(uriHandler)
ChatConsoleItem { withAuth(generalGetString(MR.strings.auth_open_chat_console), generalGetString(MR.strings.auth_log_in_using_credential), showCustomModal { it, close -> TerminalView(false, close) }) }
ChatConsoleItem { withAuth(generalGetString(MR.strings.auth_open_chat_console), generalGetString(MR.strings.auth_log_in_using_credential), showCustomModal { it, close -> TerminalView(it, close) }) }
ResetHintsItem(unchangedHints)
SettingsPreferenceItem(painterResource(MR.images.ic_code), stringResource(MR.strings.show_developer_options), developerTools)
SectionTextFooter(
@@ -164,7 +164,9 @@ fun NetworkAndServersView() {
val showCustomModal = { it: @Composable (close: () -> Unit) -> Unit -> ModalManager.fullscreen.showCustomModal { close -> it(close) }}
UseSocksProxySwitch(networkUseSocksProxy, toggleSocksProxy)
SettingsActionItem(painterResource(MR.images.ic_settings_ethernet), stringResource(MR.strings.network_socks_proxy_settings), { showCustomModal { SocksProxySettings(networkUseSocksProxy.value, networkProxy, onionHosts, sessionMode.value, true, it) } })
SessionModePicker(sessionMode, showModal, updateSessionMode)
if (developerTools) {
SessionModePicker(sessionMode, showModal, updateSessionMode)
}
}
@Composable
@@ -456,17 +458,9 @@ fun SessionModePicker(
) {
val density = LocalDensity.current
val values = remember {
val safeModes = TransportSessionMode.safeValues
val modes: Array<TransportSessionMode> =
if (appPrefs.developerTools.get()) TransportSessionMode.values()
else if (safeModes.contains(sessionMode.value)) safeModes
else safeModes + sessionMode.value
modes.map {
val userModeDescr: AnnotatedString = escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_session_mode_user_description), density)
TransportSessionMode.values().map {
when (it) {
TransportSessionMode.User -> ValueTitleDesc(TransportSessionMode.User, generalGetString(MR.strings.network_session_mode_user), userModeDescr)
TransportSessionMode.Session -> ValueTitleDesc(TransportSessionMode.Session, generalGetString(MR.strings.network_session_mode_session), userModeDescr + AnnotatedString("\n") + escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_session_mode_session_description), density))
TransportSessionMode.Server -> ValueTitleDesc(TransportSessionMode.Server, generalGetString(MR.strings.network_session_mode_server), userModeDescr + AnnotatedString("\n") + escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_session_mode_server_description), density))
TransportSessionMode.User -> ValueTitleDesc(TransportSessionMode.User, generalGetString(MR.strings.network_session_mode_user), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_session_mode_user_description), density))
TransportSessionMode.Entity -> ValueTitleDesc(TransportSessionMode.Entity, generalGetString(MR.strings.network_session_mode_entity), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_session_mode_entity_description), density))
}
}
@@ -1265,8 +1265,8 @@
<string name="you_can_share_your_address">يمكنك مشاركة عنوانك كرابط أو رمز QR - يمكن لأي شخص الاتصال بك.</string>
<string name="you_can_create_it_later">يمكنك إنشاؤه لاحقًا</string>
<string name="invite_prohibited_description">أنت تحاول دعوة جهة اتصال قمت بمشاركة ملف تعريف متخفي معها إلى المجموعة التي تستخدم فيها ملفك الشخصي الرئيسي</string>
<string name="user_unmute">ألغِ الكتم</string>
<string name="unmute_chat">ألغِ الكتم</string>
<string name="user_unmute">إلغاء الكتم</string>
<string name="unmute_chat">إلغاء الكتم</string>
<string name="you_accepted_connection">لقد قبلت الاتصال</string>
<string name="unhide_profile">إلغاء إخفاء ملف تعريف</string>
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">يجب أن تكون جهة الاتصال متصلة بالإنترنت حتى يكتمل الاتصال.
@@ -2090,22 +2090,4 @@
<string name="switching_profile_error_title">خطأ في تبديل الملف الشخصي</string>
<string name="select_chat_profile">حدد ملف تعريف الدردشة</string>
<string name="switching_profile_error_message">لقد تم نقل اتصالك إلى %s ولكن حدث خطأ غير متوقع أثناء إعادة توجيهك إلى الملف الشخصي.</string>
<string name="forward_alert_title_messages_to_forward">تحويل %1$s رسالة؟</string>
<string name="forward_files_messages_deleted_after_selection_title">لم يحوّل %1$s من الرسائل</string>
<string name="compose_forward_messages_n">جارِ تحويل %1$s رسالة</string>
<string name="forward_multiple">حوّل الرسائل…</string>
<string name="forward_alert_forward_messages_without_files">تحويل الرسائل بدون ملفات؟</string>
<string name="compose_save_messages_n">جارِ حفظ %1$s رسالة</string>
<string name="network_proxy_incorrect_config_desc">تأكد من صحة تضبيط الوكيل.</string>
<string name="n_other_file_errors">%1$d خطأ في ملف آخر.</string>
<string name="forward_files_messages_deleted_after_selection_desc">حُذفت الرسائل بعد تحديدها.</string>
<string name="forward_alert_title_nothing_to_forward">لا يوجد شيء لتحويله!</string>
<string name="network_proxy_password">كلمة المرور</string>
<string name="network_proxy_auth">استيثاق الوكيل</string>
<string name="delete_messages_cannot_be_undone_warning">سيتم حذف الرسائل - لا يمكن التراجع عن هذا!</string>
<string name="icon_descr_sound_muted">الصوت مكتوم</string>
<string name="error_initializing_web_view_wrong_arch">حدث خطأ أثناء تهيئة WebView. تأكد من تثبيت WebView وأن بنيته المدعومة هي arm64.\nالخطأ: %s</string>
<string name="settings_section_title_message_shape">شكل الرسالة</string>
<string name="settings_message_shape_tail">ذيل</string>
<string name="settings_message_shape_corner">ركن</string>
</resources>
@@ -811,12 +811,8 @@
<string name="network_use_onion_hosts_required_desc">Onion hosts will be required for connection.\nPlease note: you will not be able to connect to the servers without .onion address.</string>
<string name="network_session_mode_transport_isolation">Transport isolation</string>
<string name="network_session_mode_user">Chat profile</string>
<string name="network_session_mode_session">App session</string>
<string name="network_session_mode_server">Server</string>
<string name="network_session_mode_entity">Connection</string>
<string name="network_session_mode_user_description"><![CDATA[A separate TCP connection (and SOCKS credential) will be used <b>for each chat profile you have in the app</b>.]]></string>
<string name="network_session_mode_session_description">New SOCKS credentials will be used every time you start the app.</string>
<string name="network_session_mode_server_description">New SOCKS credentials will be used for each server.</string>
<string name="network_session_mode_entity_description"><![CDATA[A separate TCP connection (and SOCKS credential) will be used <b>for each contact and group member</b>.\n<b>Please note</b>: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail.]]></string>
<string name="update_network_session_mode_question">Update transport isolation mode?</string>
<string name="disable_onion_hosts_when_not_supported"><![CDATA[Set <i>Use .onion hosts</i> to No if SOCKS proxy does not support them.]]></string>
@@ -1047,9 +1043,6 @@
<string name="call_already_ended">Call already ended!</string>
<string name="icon_descr_video_call">video call</string>
<string name="icon_descr_audio_call">audio call</string>
<string name="call_desktop_permission_denied_title">To make calls, allow to use your microphone. End the call and try to call again.</string>
<string name="call_desktop_permission_denied_chrome">Click info button near address field to allow using microphone.</string>
<string name="call_desktop_permission_denied_safari">Open Safari Settings / Websites / Microphone, then choose Allow for localhost.</string>
<!-- Call settings -->
<string name="settings_audio_video_calls">Audio &amp; video calls</string>
@@ -1203,9 +1196,6 @@
<string name="settings_section_title_icon">APP ICON</string>
<string name="settings_section_title_themes">THEMES</string>
<string name="settings_section_title_profile_images">Profile images</string>
<string name="settings_section_title_message_shape">Message shape</string>
<string name="settings_message_shape_corner">Corner</string>
<string name="settings_message_shape_tail">Tail</string>
<string name="settings_section_title_chat_theme">Chat theme</string>
<string name="settings_section_title_user_theme">Profile theme</string>
<string name="settings_section_title_chat_colors">Chat colors</string>
@@ -1990,7 +1990,7 @@
<string name="downloaded_files">Heruntergeladene Dateien</string>
<string name="download_errors">Fehler beim Herunterladen</string>
<string name="duplicates_label">Duplikate</string>
<string name="expired_label">Abgelaufen</string>
<string name="expired_label">abgelaufen</string>
<string name="open_server_settings_button">Server-Einstellungen öffnen</string>
<string name="other_errors">Andere Fehler</string>
<string name="proxied">Proxy</string>
@@ -2170,26 +2170,4 @@
<string name="network_proxy_auth_mode_isolate_by_auth_user">Verwenden Sie für jedes Profil unterschiedliche Proxy-Anmeldeinformationen.</string>
<string name="network_proxy_random_credentials">Verwenden Sie zufällige Anmeldeinformationen</string>
<string name="network_proxy_username">Benutzername</string>
<string name="n_file_errors">%1$d Datei-Fehler:
\n%2$s</string>
<string name="forward_files_in_progress_desc">%1$d Datei(en) wird/werden immer noch heruntergeladen.</string>
<string name="forward_files_failed_to_receive_desc">Bei %1$d Datei(en) ist das Herunterladen fehlgeschlagen.</string>
<string name="error_forwarding_messages">Fehler beim Weiterleiten der Nachrichten</string>
<string name="forward_files_messages_deleted_after_selection_desc">Die Nachrichten wurden gelöscht, nachdem Sie sie ausgewählt hatten.</string>
<string name="forward_alert_title_nothing_to_forward">Es gibt nichts zum Weiterleiten!</string>
<string name="n_other_file_errors">%1$d andere(r) Datei-Fehler.</string>
<string name="forward_alert_title_messages_to_forward">%1$s Nachricht(en) weiterleiten?</string>
<string name="forward_files_missing_desc">%1$d Datei(en) wurde(n) gelöscht.</string>
<string name="forward_files_not_accepted_desc">%1$d Datei(en) wurde(n) nicht heruntergeladen.</string>
<string name="forward_alert_forward_messages_without_files">Nachrichten ohne Dateien weiterleiten?</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s Nachrichten wurden nicht weitergeleitet</string>
<string name="forward_files_not_accepted_receive_files">Herunterladen</string>
<string name="compose_forward_messages_n">Es wird/werden %1$s Nachricht(en) weitergeleitet</string>
<string name="forward_multiple">Nachrichten werden weitergeleitet…</string>
<string name="compose_save_messages_n">Es wird/werden %1$s Nachricht(en) gesichert</string>
<string name="icon_descr_sound_muted">Ton stummgeschaltet</string>
<string name="error_initializing_web_view_wrong_arch">Fehler bei der Initialisierung von WebView. Stellen Sie sicher, dass Sie WebView installiert haben, und es die ARM64-Architektur unterstützt.\nFehler: %s</string>
<string name="settings_section_title_message_shape">Form der Nachricht</string>
<string name="settings_message_shape_tail">Sprechblase</string>
<string name="settings_message_shape_corner">Abrundung Ecken</string>
</resources>
@@ -83,7 +83,7 @@
<string name="database_passphrase_will_be_updated">La contraseña de cifrado de la base de datos será actualizada.</string>
<string name="info_row_database_id">ID base de datos</string>
<string name="direct_messages_are_prohibited_in_chat">Los mensajes directos entre miembros del grupo no están permitidos.</string>
<string name="passphrase_is_different">La contraseña de la base de datos es diferente a la almacenada en Keystore.</string>
<string name="passphrase_is_different">La contraseña de la base de datos es distinta a la almacenada en Keystore.</string>
<string name="database_will_be_encrypted_and_passphrase_stored">La base de datos será cifrada y la contraseña se guardará en Keystore.</string>
<string name="delete_contact_question">¿Eliminar contacto\?</string>
<string name="delete_message__question">¿Eliminar mensaje\?</string>
@@ -830,7 +830,7 @@
<string name="chat_help_tap_button">Pulsa el botón</string>
<string name="to_start_a_new_chat_help_header">Para iniciar un chat nuevo</string>
<string name="switch_receiving_address">Cambiar servidor de recepción</string>
<string name="group_is_decentralized">Totalmente descentralizado. Visible sólo para los miembros.</string>
<string name="group_is_decentralized">Completamente descentralizado y sólo visible para los miembros.</string>
<string name="to_connect_via_link_title">Para conectarte mediante enlace</string>
<string name="smp_servers_test_failed">¡Error en prueba del servidor!</string>
<string name="smp_servers_test_some_failed">Algunos servidores no superaron la prueba:</string>
@@ -949,7 +949,7 @@
<string name="your_chat_profiles">Mis perfiles</string>
<string name="your_simplex_contact_address">Mi dirección SimpleX</string>
<string name="smp_servers_your_server">Tu servidor</string>
<string name="smp_servers_your_server_address">Dirección del servidor</string>
<string name="smp_servers_your_server_address">Dirección de tu servidor</string>
<string name="your_current_profile">Tu perfil actual</string>
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. Los servidores SimpleX no pueden ver tu perfil.</string>
<string name="language_system">Sistema</string>
@@ -971,7 +971,7 @@
<string name="button_welcome_message">Mensaje de bienvenida</string>
<string name="group_welcome_title">Mensaje de bienvenida</string>
<string name="make_profile_private">¡Hacer perfil privado!</string>
<string name="dont_show_again">No volver a mostrar</string>
<string name="dont_show_again">No mostrar de nuevo</string>
<string name="muted_when_inactive">¡Silenciado cuando está inactivo!</string>
<string name="v4_6_group_moderation">Moderación de grupos</string>
<string name="v4_6_hidden_chat_profiles">Perfiles ocultos</string>
@@ -1347,7 +1347,7 @@
<string name="connect_via_link_incognito">Conectar en incógnito</string>
<string name="turn_off_battery_optimization_button">Permitir</string>
<string name="turn_off_system_restriction_button">Abrir configuración</string>
<string name="connect__a_new_random_profile_will_be_shared">Compartirás un perfil nuevo aleatorio.</string>
<string name="connect__a_new_random_profile_will_be_shared">Se compartirá un perfil nuevo aleatorio.</string>
<string name="paste_the_link_you_received_to_connect_with_your_contact">Pega el enlace recibido para conectar con tu contacto…</string>
<string name="connect__your_profile_will_be_shared">El perfil %1$s será compartido.</string>
<string name="disable_notifications_button">Desactivar notificaciones</string>
@@ -1383,7 +1383,7 @@
<string name="v5_3_new_desktop_app">Nueva aplicación para ordenador!</string>
<string name="v5_3_new_interface_languages">6 idiomas nuevos para el interfaz</string>
<string name="v5_3_encrypt_local_files_descr">Cifrado de los nuevos archivos locales (excepto vídeos).</string>
<string name="compose_send_direct_message_to_connect">Envía un mensaje para conectar</string>
<string name="compose_send_direct_message_to_connect">Enviar mensaje directo para conectar</string>
<string name="v5_3_discover_join_groups">Descubre y únete a grupos</string>
<string name="v5_3_simpler_incognito_mode">Modo incógnito simplificado</string>
<string name="v5_3_new_interface_languages_descr">Árabe, Búlgaro, Finlandés, Hebreo, Tailandés y Ucraniano - gracias a los usuarios y Weblate.</string>
@@ -1450,7 +1450,7 @@
<string name="bad_desktop_address">Dirección ordenador incorrecta</string>
<string name="devices">Dispositivo</string>
<string name="non_content_uri_alert_title">Ruta archivo no valida.</string>
<string name="disconnect_desktop_question">¿Desconectar del ordenador?</string>
<string name="disconnect_desktop_question">¿Desconectar ordenador?</string>
<string name="block_member_desc">¡Los mensajes nuevos de %s estarán ocultos!</string>
<string name="desktop_app_version_is_incompatible">La versión de aplicación del ordenador %s no es compatible con esta aplicación.</string>
<string name="blocked_item_description">bloqueado</string>
@@ -2070,45 +2070,4 @@
<string name="new_message">Nuevo mensaje</string>
<string name="error_parsing_uri_title">Enlace no válido</string>
<string name="error_parsing_uri_desc">Por favor, comprueba que el enlace SimpleX es correcto.</string>
<string name="forward_files_in_progress_desc">%1$d archivo(s) se está(n) descargando todavía.</string>
<string name="n_other_file_errors">%1$d otro(s) error(es) de archivo.</string>
<string name="settings_section_title_chat_database">BASE DE DATOS</string>
<string name="error_forwarding_messages">Error en reenvío de mensajes</string>
<string name="forward_alert_title_messages_to_forward">¿Reenviar %1$s mensaje(s)?</string>
<string name="forward_multiple">Reenviar mensajes…</string>
<string name="system_mode_toast">Modo de sistema</string>
<string name="forward_alert_forward_messages_without_files">¿Reenviar mensajes sin los archivos?</string>
<string name="network_proxy_incorrect_config_desc">Asegúrate de que la configuración del proxy es correcta.</string>
<string name="forward_files_missing_desc">%1$d archivo(s) ha(n) sido eliminado(s).</string>
<string name="n_file_errors">%1$d error(es) de archivo
\n%2$s</string>
<string name="forward_files_failed_to_receive_desc">La descarga ha fallado para %1$d archivo(s).</string>
<string name="forward_files_not_accepted_desc">%1$d archivo(s) no se ha(n) descargado.</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s mensajes no enviados</string>
<string name="forward_files_not_accepted_receive_files">Descargar</string>
<string name="compose_forward_messages_n">Reenviando %1$s mensajes</string>
<string name="forward_files_messages_deleted_after_selection_desc">Los mensajes han sido borrados después de seleccionarlos.</string>
<string name="forward_alert_title_nothing_to_forward">¡Nada para reenviar!</string>
<string name="compose_save_messages_n">Guardando %1$s mensajes</string>
<string name="network_proxy_auth_mode_no_auth">No uses credenciales con proxy.</string>
<string name="network_proxy_incorrect_config_title">Error guardando proxy</string>
<string name="network_proxy_password">Contraseña</string>
<string name="network_proxy_auth">Autenticación proxy</string>
<string name="network_proxy_auth_mode_isolate_by_auth_entity">Credenciales proxy diferentes para cada conexión.</string>
<string name="network_proxy_auth_mode_isolate_by_auth_user">Credenciales proxy diferentes para cada perfil.</string>
<string name="network_proxy_random_credentials">Credenciales aleatorias</string>
<string name="network_proxy_username">Nombre de usuario</string>
<string name="network_proxy_auth_mode_username_password">Tus credenciales podrían ser enviadas sin cifrar.</string>
<string name="migrate_from_device_remove_archive_question">¿Eliminar archivo?</string>
<string name="migrate_from_device_uploaded_archive_will_be_removed">El archivo de bases de datos subido será eliminado permanentemente de los servidores.</string>
<string name="delete_messages_cannot_be_undone_warning">Los mensajes serán eliminados. ¡No podrá deshacerse!</string>
<string name="switching_profile_error_title">Error al cambiar perfil</string>
<string name="select_chat_profile">Selecciona perfil de chat</string>
<string name="new_chat_share_profile">Comparte perfil</string>
<string name="switching_profile_error_message">Tu conexión ha sido trasladada a %s pero ha ocurrido un error inesperado al redirigirte al perfil.</string>
<string name="icon_descr_sound_muted">Sonido silenciado</string>
<string name="error_initializing_web_view_wrong_arch">Error al inicializar WebView. Asegúrese de que tiene WebView instalado y que su arquitectura soportada es arm64.\nError: %s</string>
<string name="settings_section_title_message_shape">Formato de mensaje</string>
<string name="settings_message_shape_corner">Esquina</string>
<string name="settings_message_shape_tail">Cola</string>
</resources>
File diff suppressed because it is too large Load Diff
@@ -413,7 +413,7 @@
<string name="icon_descr_group_inactive">Gruppo inattivo</string>
<string name="rcv_conn_event_switch_queue_phase_completed">indirizzo cambiato per te</string>
<string name="rcv_group_event_changed_member_role">ha cambiato il ruolo di %s in %s</string>
<string name="rcv_group_event_changed_your_role">ha cambiato il tuo ruolo in %s</string>
<string name="rcv_group_event_changed_your_role">cambiato il tuo ruolo in %s</string>
<string name="rcv_conn_event_switch_queue_phase_changing">cambio indirizzo…</string>
<string name="snd_conn_event_switch_queue_phase_changing_for_member">cambio indirizzo per %s…</string>
<string name="rcv_group_event_member_connected">si è connesso/a</string>
@@ -2105,10 +2105,4 @@
<string name="compose_forward_messages_n">Inoltro di %1$s messaggi</string>
<string name="forward_multiple">Inoltra messaggi…</string>
<string name="compose_save_messages_n">Salvataggio di %1$s messaggi</string>
<string name="icon_descr_sound_muted">Audio silenziato</string>
<string name="error_initializing_web_view_wrong_arch">Errore di inizializzazione di WebView. Assicurati di avere WebView installato e che la sua architettura supportata sia arm64.
\nErrore: %s</string>
<string name="settings_message_shape_corner">Angolo</string>
<string name="settings_section_title_message_shape">Forma del messaggio</string>
<string name="settings_message_shape_tail">Coda</string>
</resources>
@@ -1886,9 +1886,4 @@
<string name="cannot_share_message_alert_title">メッセージを送信することができません</string>
<string name="cant_call_contact_alert_title">連絡先と通話することができません</string>
<string name="servers_info_sessions_connecting">接続待ち</string>
<string name="privacy_media_blur_radius_off">無し</string>
<string name="privacy_media_blur_radius_soft">控え目</string>
<string name="network_options_save_and_reconnect">保存して再接続</string>
<string name="privacy_media_blur_radius_strong">強め</string>
<string name="privacy_media_blur_radius_medium">普通</string>
</resources>
@@ -791,7 +791,7 @@
<string name="group_invitation_tap_to_join_incognito">Tik hier om incognito lid te worden</string>
<string name="youve_accepted_group_invitation_connecting_to_inviting_group_member">Je bent lid geworden van deze groep. Verbinding maken met uitnodigend lid.</string>
<string name="you_sent_group_invitation">Je hebt een groep uitnodiging verzonden</string>
<string name="snd_group_event_user_left">je bent vertrokken</string>
<string name="snd_group_event_user_left">jij bent vertrokken</string>
<string name="snd_conn_event_switch_queue_phase_completed">je bent van adres veranderd</string>
<string name="select_contacts">Selecteer contacten</string>
<string name="skip_inviting_button">Sla het uitnodigen van leden over</string>
@@ -950,7 +950,7 @@
<string name="initial_member_role">Initiële rol</string>
<string name="group_member_role_observer">Waarnemer</string>
<string name="observer_cant_send_message_title">Je kunt geen berichten versturen!</string>
<string name="you_are_observer">je bent waarnemer</string>
<string name="you_are_observer">jij bent waarnemer</string>
<string name="language_system">Systeem</string>
<string name="v4_6_audio_video_calls">Audio en video oproepen</string>
<string name="confirm_password">Bevestig wachtwoord</string>
@@ -1888,7 +1888,7 @@
<string name="servers_info_private_data_disclaimer">Beginnend vanaf %s.
\nAlle gegevens zijn privé op uw apparaat.</string>
<string name="servers_info_connected_servers_section_header">Verbonden servers</string>
<string name="servers_info_subscriptions_connections_pending">In behandeling</string>
<string name="servers_info_subscriptions_connections_pending">in behandeling</string>
<string name="servers_info_previously_connected_servers_section_header">Eerder verbonden servers</string>
<string name="servers_info_proxied_servers_section_header">Proxied servers</string>
<string name="servers_info_subscriptions_total">Totaal</string>
@@ -1929,7 +1929,7 @@
<string name="downloaded_files">Gedownloade bestanden</string>
<string name="secured">Beveiligd</string>
<string name="size">Maat</string>
<string name="subscribed">Subscribed</string>
<string name="subscribed">Ingeschreven</string>
<string name="uploaded_files">Geüploade bestanden</string>
<string name="upload_errors">Upload fouten</string>
<string name="download_errors">Downloadfouten</string>
@@ -1938,7 +1938,7 @@
<string name="all_users">Alle profielen</string>
<string name="attempts_label">pogingen</string>
<string name="chunks_deleted">Stukken verwijderd</string>
<string name="completed">Voltooid</string>
<string name="completed">voltooid</string>
<string name="servers_info_sessions_connected">Verbonden</string>
<string name="servers_info_sessions_connecting">Verbinden</string>
<string name="servers_info_reconnect_server_error">Fout bij opnieuw verbinding maken met de server</string>
@@ -1977,8 +1977,8 @@
<string name="app_check_for_updates">Controleer op updates</string>
<string name="app_check_for_updates_notice_title">Controleer op updates</string>
<string name="app_check_for_updates_notice_disable">Uitschakelen</string>
<string name="subscription_errors">Subscription fouten</string>
<string name="subscription_results_ignored">Subscriptions genegeerd</string>
<string name="subscription_errors">Inschrijving fouten</string>
<string name="subscription_results_ignored">Inschrijvingen genegeerd</string>
<string name="app_check_for_updates_stable">Stabiel</string>
<string name="app_check_for_updates_update_available">Update beschikbaar: %s</string>
<string name="app_check_for_updates_canceled">Downloaden van update geannuleerd</string>
@@ -2076,35 +2076,4 @@
<string name="migrate_from_device_remove_archive_question">Archief verwijderen?</string>
<string name="delete_messages_cannot_be_undone_warning">Berichten worden verwijderd. Dit kan niet ongedaan worden gemaakt!</string>
<string name="migrate_from_device_uploaded_archive_will_be_removed">Het geüploade databasearchief wordt permanent van de servers verwijderd.</string>
<string name="n_file_errors">%1$d bestandsfout(en):
\n%2$s</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s berichten niet doorgestuurd</string>
<string name="forward_alert_title_messages_to_forward">%1$s bericht(en) doorsturen?</string>
<string name="network_proxy_auth">Proxy-authenticatie</string>
<string name="forward_files_messages_deleted_after_selection_desc">Berichten zijn verwijderd nadat u ze had geselecteerd.</string>
<string name="network_proxy_username">Gebruikersnaam</string>
<string name="n_other_file_errors">%1$d overige bestandsfout(en).</string>
<string name="error_forwarding_messages">Fout bij het doorsturen van berichten</string>
<string name="forward_alert_title_nothing_to_forward">Niets om door te sturen!</string>
<string name="forward_files_in_progress_desc">%1$d bestand(en) worden nog gedownload.</string>
<string name="forward_files_not_accepted_desc">%1$d bestand(en) zijn niet gedownload.</string>
<string name="forward_alert_forward_messages_without_files">Berichten doorsturen zonder bestanden?</string>
<string name="forward_files_failed_to_receive_desc">%1$d bestand(en) konden niet worden gedownload.</string>
<string name="forward_files_missing_desc">%1$d bestand(en) zijn verwijderd.</string>
<string name="forward_files_not_accepted_receive_files">Download</string>
<string name="compose_forward_messages_n">%1$s berichten doorsturen</string>
<string name="forward_multiple">Berichten doorsturen…</string>
<string name="compose_save_messages_n">%1$s berichten opslaan</string>
<string name="network_proxy_auth_mode_no_auth">Gebruik geen inloggegevens met proxy.</string>
<string name="network_proxy_auth_mode_isolate_by_auth_entity">Gebruik voor elke verbinding verschillende proxy-inloggegevens.</string>
<string name="network_proxy_auth_mode_isolate_by_auth_user">Gebruik voor elk profiel verschillende proxy-inloggegevens.</string>
<string name="network_proxy_random_credentials">Gebruik willekeurige inloggegevens</string>
<string name="network_proxy_auth_mode_username_password">Uw inloggegevens worden mogelijk niet-versleuteld verzonden.</string>
<string name="network_proxy_incorrect_config_title">Fout bij opslaan proxy</string>
<string name="network_proxy_incorrect_config_desc">Zorg ervoor dat de proxyconfiguratie correct is.</string>
<string name="network_proxy_password">Wachtwoord</string>
<string name="icon_descr_sound_muted">Geluid gedempt</string>
<string name="error_initializing_web_view_wrong_arch">Fout bij initialiseren van WebView. Zorg ervoor dat WebView geïnstalleerd is en de ondersteunde architectuur is arm64.\nFout: %s</string>
<string name="settings_message_shape_corner">Hoek</string>
<string name="settings_section_title_message_shape">Berichtvorm</string>
</resources>
@@ -2069,19 +2069,4 @@
<string name="v6_0_connect_faster_descr">Szybciej łącz się ze znajomymi.</string>
<string name="v6_0_connection_servers_status">Kontroluj swoją sieć</string>
<string name="v6_0_delete_many_messages_descr">Usuń do 20 wiadomości na raz.</string>
<string name="error_forwarding_messages">Błąd przekazywania wiadomości</string>
<string name="forward_files_not_accepted_desc">%1$d plik(ów/i) nie zostały pobrane.</string>
<string name="forward_alert_title_messages_to_forward">Czy przekazać %1$s wiadomoś(ć/ci)?</string>
<string name="forward_alert_forward_messages_without_files">Przekazać wiadomości bez plików?</string>
<string name="forward_files_missing_desc">%1$d plik(ów/i) zostały usunięte.</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s wiadomoś(ć/ci) nie przekazane</string>
<string name="compose_forward_messages_n">Przekazywanie %1$s wiadomoś(ć/ci)</string>
<string name="forward_multiple">Przekazywanie wiadomości…</string>
<string name="network_proxy_auth_mode_no_auth">Nie używaj danych logowania do proxy.</string>
<string name="network_proxy_incorrect_config_title">Błąd zapisywania ustawień proxy</string>
<string name="network_proxy_incorrect_config_desc">Sprawdź czy konfiguracja serwera proxy jest poprawna.</string>
<string name="forward_files_in_progress_desc">%1$d plik(ów/i) dalej są pobierane.</string>
<string name="forward_files_failed_to_receive_desc">%1$d plik(ów/i) nie udało się pobrać.</string>
<string name="switching_profile_error_title">Błąd zmiany profilu</string>
<string name="settings_section_title_chat_database">BAZA CZATU</string>
</resources>
@@ -2068,40 +2068,4 @@
<string name="app_check_for_updates_button_remind_later">Me lembre mais tarde</string>
<string name="app_check_for_updates_notice_desc">Para ser notificado sobre os novos lançamentos, habilite a checagem periódica de versões Estáveis e Beta.</string>
<string name="one_hand_ui">Barra de ferramentas de conversa acessível</string>
<string name="forward_files_failed_to_receive_desc">Falha no baixar de %1$d arquivo(s).</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s mensagens não encaminhadas.</string>
<string name="settings_section_title_chat_database">DADOS DO BATE-PAPO</string>
<string name="network_proxy_random_credentials">Utilize credenciais aleatórias</string>
<string name="migrate_from_device_uploaded_archive_will_be_removed">O arquivo de banco de dados enviado será removido permanentemente dos servidores.</string>
<string name="network_proxy_auth_mode_isolate_by_auth_entity">Use credenciais diferentes de proxy para cada conexão.</string>
<string name="switching_profile_error_message">Sua conexão foi movida para %s, mas um erro inesperado ocorreu ao redirecioná-lo para o seu perfil.</string>
<string name="n_file_errors">%1$d erro(s) de arquivo(s):
\n%2$s</string>
<string name="n_other_file_errors">%1$d outro erro de arquivo.</string>
<string name="error_forwarding_messages">Erro ao encaminhar mensagens.</string>
<string name="forward_alert_title_messages_to_forward">Encaminhar %1$s mensagens?</string>
<string name="forward_alert_forward_messages_without_files">Encaminhar mensagens sem arquivos?</string>
<string name="forward_files_messages_deleted_after_selection_desc">As mensagens foram excluidas após vocês selecioná-las.</string>
<string name="forward_alert_title_nothing_to_forward">Nada para encaminhar!</string>
<string name="forward_files_in_progress_desc">%1$d arquivo(s) ainda estão sendo baixados.</string>
<string name="forward_files_missing_desc">%1$d arquivos foram excluidos.</string>
<string name="forward_files_not_accepted_desc">%1$d arquivos não foram baixados.</string>
<string name="forward_files_not_accepted_receive_files">Baixar</string>
<string name="forward_multiple">Emcaminhar mensagens…</string>
<string name="compose_forward_messages_n">Encaminhando %1$s mensagens.</string>
<string name="compose_save_messages_n">Salvando %1$s mensagens</string>
<string name="network_proxy_auth">Autenticação de proxy</string>
<string name="network_proxy_auth_mode_no_auth">Não utilize credenciais com proxy.</string>
<string name="network_proxy_incorrect_config_desc">Certifique-se de que configuração do proxy está correta.</string>
<string name="network_proxy_auth_mode_isolate_by_auth_user">Use diferentes credenciais de proxy para cada perfil.</string>
<string name="network_proxy_auth_mode_username_password">Suas credenciais podem ser enviadas sem criptografia.</string>
<string name="migrate_from_device_remove_archive_question">Remover arquivo?</string>
<string name="delete_messages_cannot_be_undone_warning">As mensagens serão excluídas - isso não pode ser desfeito!</string>
<string name="switching_profile_error_title">Erro ao alternar perfil</string>
<string name="select_chat_profile">Selecionar perfil de bate-papo</string>
<string name="new_chat_share_profile">Compartilhar perfil</string>
<string name="system_mode_toast">Modo sistema</string>
<string name="network_proxy_incorrect_config_title">Erro ao salvar proxy</string>
<string name="network_proxy_password">Senha</string>
<string name="network_proxy_username">Nome de usuário</string>
</resources>
@@ -49,7 +49,7 @@
<string name="accept_feature">Aceitar</string>
<string name="allow_your_contacts_to_send_disappearing_messages">Permitir que seus contatos enviem mensagens que desaparecem.</string>
<string name="accept_feature_set_1_day">Definir 1 dia</string>
<string name="allow_irreversible_message_deletion_only_if">Permitir a exclusão irreversível de mensagens apenas se o seu contato permitir. (24 horas)</string>
<string name="allow_irreversible_message_deletion_only_if">Permitir a exclusão irreversível de mensagens apenas se o seu contato permitir.</string>
<string name="allow_your_contacts_irreversibly_delete">Permitir que seus contatos eliminem de forma irreversível mensagens enviadas.</string>
<string name="allow_voice_messages_only_if">Permitir mensagens de voz apenas se o contato permitir.</string>
<string name="allow_your_contacts_to_send_voice_messages">Permitir que seus contatos enviem mensagens de voz.</string>
@@ -130,7 +130,7 @@
<string name="network_enable_socks_info">Aceder aos servidores via proxy SOCKS no porto %d\? O proxy tem de iniciar antes de ativar esta opção.</string>
<string name="smp_servers_add_to_another_device">Adicionar a outro dispositivo</string>
<string name="group_member_role_admin">administrador</string>
<string name="allow_to_delete_messages">Permitir apagar irreversivelmente as mensagens enviadas. (24 horas)</string>
<string name="allow_to_delete_messages">Permitir apagar irreversivelmente as mensagens enviadas.</string>
<string name="delete_address__question">Eliminar endereço\?</string>
<string name="delete_after">Eliminar após</string>
<string name="delete_verb">Eliminar</string>
@@ -565,7 +565,7 @@
<string name="v4_3_voice_messages_desc">Máximo de 40 segundos, recebido instantaneamente.</string>
<string name="icon_descr_more_button">Mais</string>
<string name="network_and_servers">Rede e servidores</string>
<string name="network_settings_title">Configurações avançadas</string>
<string name="network_settings_title">Definições de rede</string>
<string name="settings_section_title_experimenta">EXPERIMENTAL</string>
<string name="you_can_start_chat_via_setting_or_by_restarting_the_app">Você pode iniciar a conversa através das Definições da aplicação / Base de Dados ou reiniciando a aplicação.</string>
<string name="update_network_settings_confirmation">Atualizar</string>
@@ -968,15 +968,4 @@
<string name="abort_switch_receiving_address_desc">Mudança de endereço será cancelada. Antigo endereço de recebimento será usado.</string>
<string name="allow_calls_question">Permitir ligações?</string>
<string name="servers_info_subscriptions_connections_subscribed">Conexões ativas</string>
<string name="migrate_from_device_all_data_will_be_uploaded">Todos os seus contatos, conversas e arquivos serão encriptados e enviados em chunks para relays XFTP configurados.</string>
<string name="n_other_file_errors">%1$d erro(s) de outro arquivo.</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s mensagens não encaminhadas</string>
<string name="network_smp_proxy_mode_always">Sempre</string>
<string name="network_smp_proxy_mode_always_description">Sempre use uma rota privata.</string>
<string name="allow_to_send_simplex_links">Permitir o envio de links SimpleX.</string>
<string name="moderated_items_description">%1$d mensagens moderadas por %2$s</string>
<string name="rcv_group_and_other_events">e %d outros</string>
<string name="all_users">Todos os perfis</string>
<string name="connect_plan_already_connecting">Já conectando!</string>
<string name="connect_plan_already_joining_the_group">Já entrando no grupo!</string>
</resources>
@@ -674,57 +674,4 @@
<string name="migrate_from_device_delete_database_from_device">Șterge baza de date de pe acest dispozitiv</string>
<string name="delete_chat_profile">Șterge profil de conversație</string>
<string name="delete_files_and_media_for_all_users">Șterge fișiere pentru toate profilurile de conversație</string>
<string name="opensource_protocol_and_code_anybody_can_run_servers">Oricine poate găzdui servere.</string>
<string name="deleted_chats">Contacte arhivate</string>
<string name="app_check_for_updates_beta">Beta</string>
<string name="calls_prohibited_alert_title">Apeluri interzise!</string>
<string name="cant_call_member_alert_title">Nu se poate apela membrul grupului</string>
<string name="chat_database_exported_title">Bază de date conversație exportată</string>
<string name="app_check_for_updates">Verifică pentru actualizări</string>
<string name="profile_update_event_contact_name_changed">Contactul %1$s a schimbat la %2$s</string>
<string name="cant_call_contact_deleted_alert_text">Contactul este șters.</string>
<string name="v6_0_connection_servers_status">Controlează-ți rețeaua</string>
<string name="database_encryption_will_be_updated">Fraza de acces pentru criptarea bazei de date va fi actualizată și stocată în Keystore.</string>
<string name="database_passphrase">Fraza de acces a bazei de date</string>
<string name="xftp_servers_configured">Servere XFTP configurate</string>
<string name="set_password_to_export_desc">Baza de date este criptată folosind o expresie de acces aleatorie. Schimbați-o înainte de a exporta.</string>
<string name="info_view_call_button">apel</string>
<string name="info_view_connect_button">conectare</string>
<string name="contact_deleted">Contact șters!</string>
<string name="conversation_deleted">Conversație ștearsă!</string>
<string name="app_check_for_updates_download_completed_title">Actualizarea aplicației este descărcată</string>
<string name="app_check_for_updates_notice_title">Verifică pentru actualizări</string>
<string name="create_address_button">Creează</string>
<string name="privacy_media_blur_radius">Estompează media</string>
<string name="settings_section_title_chat_database">BAZĂ DE DATE CHAT</string>
<string name="v6_0_connect_faster_descr">Conectează-te cu prietenii mai ușor.</string>
<string name="attempts_label">încercări</string>
<string name="completed">Completat</string>
<string name="developer_options">ID-urile bazei de date și opțiunea de izolare a transportului.</string>
<string name="info_row_database_id">ID bază de date</string>
<string name="share_text_database_id">ID bază de date: %d</string>
<string name="connections">Conexiuni</string>
<string name="created">Creat</string>
<string name="v6_0_privacy_blur">Estompează pentru intimitate mai bună.</string>
<string name="v6_0_connection_servers_status_descr">Stare conexiune și servere</string>
<string name="database_error">Eroare bază de date</string>
<string name="contacts_can_mark_messages_for_deletion">Contactele pot marca mesajele pentru ștergere; tu le vei putea vedea.</string>
<string name="database_downgrade">Downgrade al bazei de date</string>
<string name="database_migration_in_progress">Migrarea bazei de date este în proces.
\nPoate dura câteva minute.</string>
<string name="database_passphrase_and_export">Fraza de acces a bazei de date și export</string>
<string name="all_users">Toate profilurile</string>
<string name="current_user">Profil actual</string>
<string name="confirm_delete_contact_question">Confirmi ștergerea contactului?</string>
<string name="delete_contact_cannot_undo_warning">Contactul va fi șters - acest lucru nu poate fi anulat!</string>
<string name="smp_servers_configured">Servere SMP configurate</string>
<string name="servers_info_sessions_connected">Conectat</string>
<string name="correct_name_to">Corectează numele la %s?</string>
<string name="chat_database_exported_continue">Continuă</string>
<string name="encrypted_with_random_passphrase">Baza de date este criptată folosind o expresie de acces aleatorie, o poți schimba</string>
<string name="cant_send_message_to_member_alert_title">Nu se pot trimite mesaje membrului grupului</string>
<string name="servers_info_sessions_connecting">Se conectează</string>
<string name="servers_info_connected_servers_section_header">Servere conectate</string>
<string name="cant_call_contact_alert_title">Nu se poate apela contactul</string>
<string name="cant_call_contact_connecting_wait_alert_text">Se conectează la contact, te rog așteaptă sau verifică mai târziu!</string>
</resources>
@@ -592,7 +592,7 @@
<string name="hidden_profile_password">Gizli profil parolası</string>
<string name="how_to_use_markdown">Markdown nasıl kullanılır</string>
<string name="how_it_works">Nasıl çalışıyor</string>
<string name="immune_to_spam_and_abuse">Spamdan etkilenmez</string>
<string name="immune_to_spam_and_abuse">Kötüye kullanmaya ve istenmeyen mesajlara duyarlı</string>
<string name="icon_descr_hang_up">Çağırıyı bitir.</string>
<string name="icon_descr_flip_camera">Kameranın karşı yüzüne geç</string>
<string name="import_database_question">Konuşma veri tabanı içe aktarılsın mı?</string>
@@ -825,7 +825,7 @@
<string name="mark_read">Okundu olarak işaretle</string>
<string name="mark_unread">Okunmadı olarak işaretle</string>
<string name="chat_console">Sohbet konsolu</string>
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Pil ömrü için iyi</b>. Uygulama mesajları 10 dakikada bir kontrol eder. Aramaları veya acil mesajları kaçırabilirsiniz.]]></string>
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Pil için iyi</b>. Arka plan hizmeti mesajları 10 dakikada bir kontrol eder. Aramaları veya acil mesajları kaçırabilirsiniz.]]></string>
<string name="v4_4_verify_connection_security_desc">Güvenlik kodlarını kişilerinizle karşılaştırın.</string>
<string name="notifications_mode_service_desc">Arka plan hizmeti her zaman çalışır - mesajlar gelir gelmez bildirim gönderilir.</string>
<string name="onboarding_notifications_mode_off_desc"><![CDATA[<b>Pil için en iyisi</b>. Sadece uygulama çalışırken bildirim alırsınız (arka plan hizmeti YOK).]]></string>
@@ -1188,7 +1188,7 @@
<string name="la_lock_mode">SimpleX Kilit modu</string>
<string name="share_file">Dosya paylaş…</string>
<string name="periodic_notifications_desc">Uygulama yeni mesajları periyodik olarak alır - günde pilin yüzde birkaçını kullanır. Uygulama anlık bildirimleri kullanmaz - cihazınızdan gelen veriler sunuculara gönderilmez.</string>
<string name="first_platform_without_user_ids">Herhangi bir kullanıcı tanımlayıcısı yok.</string>
<string name="first_platform_without_user_ids">Herhangi bir kullanıcı tanımlayıcısı olmayan ilk platform - tasarım gereği gizli.</string>
<string name="icon_descr_speaker_off">Hoparlör kapalı</string>
<string name="sync_connection_force_desc">Şifreleme çalışıyor ve yeni bir şifreleme anlaşması gerekli değil. Yoksa bağlantı hataları ortaya çıkabilir!</string>
<string name="show_call_on_lock_screen">Göster</string>
@@ -1854,185 +1854,4 @@
<string name="message_queue_info_server_info">sunucu kuyruk bilgisi: %1$s
\n
\nson alınan msj: %2$s</string>
<string name="smp_proxy_error_connecting">Yönlendirme sunucusuna (%1$s) bağlantı sırasında hata oluştu. Lütfen daha sonra tekrar deneyin.</string>
<string name="file_error_relay">Dosya sunucusu hatası. %1$s</string>
<string name="scan_paste_link">Tara / Bağlantı yapıştır</string>
<string name="app_check_for_updates">Güncellemeleri kontrol et</string>
<string name="app_check_for_updates_disabled">Devre dışı</string>
<string name="app_check_for_updates_download_completed_title">Uygulama Güncellemesi indirildi</string>
<string name="app_check_for_updates_button_remind_later">Daha sonra hatırlat</string>
<string name="app_check_for_updates_notice_title">Güncellemeleri kontrol et</string>
<string name="servers_info_reconnect_server_message">Mesajı göndermeye zorlamak için sunucuya yeniden bağlan. Bu ekstra internet kullanır.</string>
<string name="servers_info_modal_error_title">Hata</string>
<string name="error_forwarding_messages">Mesajların yönlendirilmesi sırasında hata oluştu.</string>
<string name="file_error_no_file">Dosya bulunamadı - muhtemelen dosya silindi veya göderim iptal edildi.</string>
<string name="forward_alert_title_messages_to_forward">%1$s Mesaj yönlendirilsin mi ?</string>
<string name="selected_chat_items_nothing_selected">Hiçbir şey seçilmedi.</string>
<string name="forward_alert_title_nothing_to_forward">Yönlendirilecek bir şey yok!</string>
<string name="selected_chat_items_selected_n">%d seçildi.</string>
<string name="forward_alert_forward_messages_without_files">Mesajlar dosyalar olmadan iletilsin mi ?</string>
<string name="forward_files_messages_deleted_after_selection_desc">Mesajlar siz seçtikten sonra silindi.</string>
<string name="cannot_share_message_alert_title">Mesaj gönderilemedi</string>
<string name="forward_files_not_accepted_receive_files">İndir</string>
<string name="forward_multiple">Mesajları ilet..</string>
<string name="compose_forward_messages_n">%1$s mesaj iletiliyor</string>
<string name="compose_save_messages_n">%1$s Mesaj kayıt ediliyor.</string>
<string name="info_view_connect_button">bağlan</string>
<string name="info_view_call_button">Ara</string>
<string name="info_view_message_button">mesaj</string>
<string name="only_delete_conversation">Sadece sohbeti sil</string>
<string name="info_view_open_button">açık</string>
<string name="info_view_search_button">ara</string>
<string name="switching_profile_error_title">Profil değiştirme sırasında hata oluştu.</string>
<string name="select_chat_profile">Sohbet profili seç</string>
<string name="xftp_servers_configured">XFTP sunucuları yapılandırıldı</string>
<string name="media_and_file_servers">Medya ve dosya sunucuları</string>
<string name="network_proxy_auth_mode_no_auth">Proxy ile bilgeleri kullanma</string>
<string name="network_proxy_incorrect_config_title">Proxy kayıt edilirken hata oluştu.</string>
<string name="network_proxy_incorrect_config_desc">Proxy konfigürasyonunun doğru olduğundan emin olun.</string>
<string name="network_proxy_password">Şifre</string>
<string name="app_check_for_updates_beta">Beta</string>
<string name="app_check_for_updates_download_started">Uygulama güncellemesi indiriliyor, uygulamayı kapatmayın</string>
<string name="app_check_for_updates_installed_successfully_title">Kurulum başarılı</string>
<string name="app_check_for_updates_button_install">Güncellemeyi Kur</string>
<string name="app_check_for_updates_button_open">Dosya konumunu aç</string>
<string name="app_check_for_updates_notice_disable">Devre dışı bırak</string>
<string name="app_check_for_updates_installed_successfully_desc">Lütfen uygulamayı yeniden başlatın.</string>
<string name="invite_friends_short">Davet</string>
<string name="create_address_button">Yarat</string>
<string name="privacy_media_blur_radius_off">Kapalı</string>
<string name="settings_section_title_chat_database">Mesajlaşma Veritabanı</string>
<string name="chat_database_exported_continue">Devam et</string>
<string name="share_text_message_status">Mesaj durumu: %s</string>
<string name="appearance_font_size">Yazı tipi boyutu</string>
<string name="v6_0_new_chat_experience">Yeni bir sohbet deneyimi 🎉</string>
<string name="v6_0_connection_servers_status">Ağınızı kontrol edin</string>
<string name="v6_0_connection_servers_status_descr">Bağlantı ve sunucuların durumu</string>
<string name="v6_0_increase_font_size">Yazı boyutunu arttır</string>
<string name="remote_ctrl_connection_stopped_desc">Lütfen telefonun ve bilgisayarın aynı lokal ağa bağlı olduğundan ve bilgisayar güvenlik duvarının bağlantıya izin verdiğinden emin olun. Lütfen diğer herhangi bir sorunu geliştiricilerle paylaşın.</string>
<string name="servers_info_reset_stats_alert_title">Tüm istatistikler sıfırlansın mı ?</string>
<string name="servers_info_reconnect_server_error">Hata, sunucuya yeniden bağlanılıyor</string>
<string name="servers_info_reconnect_all_servers_button">Tüm sunuculara yeniden bağlan</string>
<string name="duplicates_label">Kopyalar</string>
<string name="chunks_uploaded">Parçalar yüklendi</string>
<string name="downloaded_files">Dosyalar İndirildi</string>
<string name="download_errors">İndirme hataları</string>
<string name="info_row_message_status">Mesaj durumu</string>
<string name="error_parsing_uri_title">Geçersiz link</string>
<string name="error_parsing_uri_desc">Lütfen SimpleX linki doğru mu kontrol edin.</string>
<string name="proxy_destination_error_broker_host">Varış sunucusu ardesi (%1$s) yönlendirme sunucusu (%2$s) ile uyumsuz.</string>
<string name="proxy_destination_error_broker_version">Varış sunucusu sürümü (%1$s) yönlendirme sunucusu (%2$s) ile uyumsuz.</string>
<string name="message_forwarded_title">Mesaj iletildi</string>
<string name="member_inactive_desc">Kullanıcı aktif olursa mesaj iletilebilir.</string>
<string name="compose_message_placeholder">Mesaj</string>
<string name="deleted_chats">Arşivli kişiler</string>
<string name="no_filtered_contacts">Filtrelenmiş kişiler yok</string>
<string name="paste_link">Bağlantıyı yapıştır</string>
<string name="action_button_add_members">Davet et</string>
<string name="member_info_member_disabled">Devre dışı</string>
<string name="member_info_member_inactive">inaktif</string>
<string name="allow_calls_question">Aramalara izin verilsin mi ?</string>
<string name="calls_prohibited_alert_title">Aramalara izin verilmiyor</string>
<string name="cant_call_contact_deleted_alert_text">Kişi silindi.</string>
<string name="cant_send_message_to_member_alert_title">Grup üyesine mesaj gönderilemiyor</string>
<string name="cant_call_member_send_message_alert_text">Çağrıları aktif etmek için mesaj gönder.</string>
<string name="v6_0_your_contacts_descr">Daha sonra görüşmek için kişileri arşivleyin</string>
<string name="v6_0_private_routing_descr">IP adresinizi ve bağlantılarınızı korur.</string>
<string name="v6_0_new_media_options">Yeni medya seçenekleri</string>
<string name="v6_0_privacy_blur">Daha iyi gizlilik için bulanıklaştır.</string>
<string name="v6_0_connect_faster_descr">Arkadaşlarınıza daha hızlı bağlanın</string>
<string name="v6_0_delete_many_messages_descr">Aynı anda yirmiye kadar mesaj silin.</string>
<string name="v6_0_chat_list_media">Sohbet listesinden oynat.</string>
<string name="migrate_from_device_remove_archive_question">Arşiv kaldırılsın mı ?</string>
<string name="servers_info_sessions_connected">Bağlandı</string>
<string name="current_user">Aktif profil</string>
<string name="servers_info_files_tab">Dosyalar</string>
<string name="servers_info_missing">Bilgi yok, yenilemeyi deneyin</string>
<string name="servers_info_sessions_connecting">Bağlanıyor</string>
<string name="servers_info_sessions_errors">Hatalar</string>
<string name="servers_info_messages_received">Mesajlar alındı</string>
<string name="servers_info_messages_sent">Mesajlar gönderildi</string>
<string name="servers_info_reconnect_servers_message">Mesaj iletimine zorlamak için tüm sunuculara yeniden bağlan. Bu ekstra internet kullanılır.</string>
<string name="servers_info_reconnect_server_title">Sunucuya yeniden bağlansın mı ?</string>
<string name="servers_info_reconnect_servers_title">Sunuculara yeniden bağlanılsın mı ?</string>
<string name="servers_info_reconnect_servers_error">Hata sunuculara yeniden bağlanılıyor</string>
<string name="servers_info_reset_stats_alert_confirm">Sıfırla</string>
<string name="connections">Bağlantılar</string>
<string name="created">Yaratıldı</string>
<string name="decryption_errors">Şifre çözme hataları</string>
<string name="other_errors">diğer hatalar</string>
<string name="completed">Tamamlandı</string>
<string name="deleted">Silindi</string>
<string name="deletion_errors">Silme hatası</string>
<string name="open_server_settings_button">Sunucu ayarlarını</string>
<string name="share_text_file_status">Dosya durumu: %s</string>
<string name="new_message">Yeni mesaj</string>
<string name="smp_servers_other">Diğer SMP sunucuları</string>
<string name="reconnect">Yeniden bağlan</string>
<string name="sent_directly">Direkt gönderildi.</string>
<string name="all_users">Tüm Profiller</string>
<string name="cant_call_member_alert_title">Grup üyesi aranamıyor</string>
<string name="chat_database_exported_title">Veritabanı dışa aktarıldı</string>
<string name="chunks_deleted">Parçalar silindi</string>
<string name="chunks_downloaded">Parçalar indirildi</string>
<string name="servers_info_connected_servers_section_header">Sunucuayı bağlandı</string>
<string name="cant_call_contact_connecting_wait_alert_text">Kişiye bağlanılıyor, lütfen bekleyin ya da daha sonra kontrol edin.</string>
<string name="copy_error">Kopyalama hatası</string>
<string name="delete_without_notification">Bildirim göndermeden sil</string>
<string name="v6_0_upgrade_app_descr">Yeni versiyonları GitHub\'dan indirin</string>
<string name="servers_info_reset_stats_alert_error_title">Hata istatistikler sıfırlanıyor</string>
<string name="smp_proxy_error_broker_host">Yönlendirme sunucu adresi (%1$s) ağ ayarlarıyla uyumsuz.</string>
<string name="info_row_file_status">Dosya durumu</string>
<string name="proxy_destination_error_failed_to_connect">Yönlendirme sunucusu (%1$s) varış sunucusuna (%2$s) bağlanamadı. Lütfen daha sonra tekrar deneyin.</string>
<string name="smp_proxy_error_broker_version">Yönlendirme suncusu sürümü ağ ayarlarıyla uyumsuz: %1$s</string>
<string name="servers_info_subscriptions_connections_pending">Bekliyor</string>
<string name="calls_prohibited_ask_to_enable_calls_alert_text">Lütfen kişinizden çağrılara izin vermesini isteyin.</string>
<string name="servers_info_previously_connected_servers_section_header">Önceden bağlanılmış sunucular</string>
<string name="cant_call_contact_alert_title">Kişi aranamıyor</string>
<string name="network_options_save_and_reconnect">Kayıt et ve yeniden bağlan</string>
<string name="delete_messages_cannot_be_undone_warning">Mesajlar silinecek - bu geri alınamaz!</string>
<string name="delete_messages_mark_deleted_warning">Mesajlar silinmek üzere işaretlendi. Alıcı (lar) bu mesajları görebilecek.</string>
<string name="delete_members_messages__question">Üylerin %d mesajı silinsin mi ?</string>
<string name="member_inactive_title">Üye inaktif</string>
<string name="message_forwarded_desc">Henüz direkt bağlantı yok mesaj admin tarafından yönlendirildi.</string>
<string name="message_servers">Mesaj sunucuları</string>
<string name="smp_servers_configured">SMP sunucları yapılandırıldı</string>
<string name="xftp_servers_other">Diğer XFTP sunucuları</string>
<string name="privacy_media_blur_radius">Medyayı bulanıklaştır.</string>
<string name="delete_contact_cannot_undo_warning">Kişiler silinecek - bu geri alınamaz !</string>
<string name="keep_conversation">Sohbeti sakla.</string>
<string name="confirm_delete_contact_question">Kişiyi silmek istediğinizden emin misiniz ?</string>
<string name="conversation_deleted">Sohbet silindi!</string>
<string name="contact_deleted">Kişiler silindi!</string>
<string name="please_try_later">Lütfen sonra tekrar deneyin</string>
<string name="private_routing_error">Gizli yönlendirme hatası</string>
<string name="file_error">Dosya hatası</string>
<string name="servers_info_details">Detaylar</string>
<string name="expired_label">Süresi dolmuş</string>
<string name="send_errors">Gönderme hataları</string>
<string name="attempts_label">denemeler</string>
<string name="servers_info_detailed_statistics">Detaylı istatistikler</string>
<string name="servers_info_downloaded">İndirildi</string>
<string name="other_label">diğer</string>
<string name="servers_info_detailed_statistics_received_messages_header">Alınan mesajlar</string>
<string name="servers_info_detailed_statistics_received_total">Toplam alınan</string>
<string name="select_verb">Seç</string>
<string name="cannot_share_message_alert_text">Seçilen sohbet tercihleri bu mesajı yasakladı.</string>
<string name="servers_info_reset_stats">Tüm istatistikleri sıfırla</string>
<string name="reset_all_hints">Tüm ip uçlarını sıfırla</string>
<string name="n_file_errors">%1$d dosya hata(ları)\n%2$s</string>
<string name="forward_files_in_progress_desc">%1$d dosya(lar) hala indiriliyor.</string>
<string name="forward_files_failed_to_receive_desc">%1$d dosyası (ları) indirilemedi.</string>
<string name="forward_files_missing_desc">%1$d dosyası (ları) silindi.</string>
<string name="n_other_file_errors">%1$d diğer dosya hatası(ları).</string>
<string name="forward_files_not_accepted_desc">%1$d dosyası (ları) indirilemedi.</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s mesajları iletilemedi</string>
<string name="privacy_media_blur_radius_medium">Orta</string>
<string name="servers_info_subscriptions_section_header">Mesaj alındısı</string>
<string name="acknowledged">Onaylandı</string>
<string name="error_initializing_web_view_wrong_arch">WebView başlatılırken hata oluştu. WebView\'in yüklü olduğundan ve desteklenen mimarisinin arm64 olduğundan emin olun.\nHata: %s</string>
<string name="network_session_mode_session_description">Uygulamayı her başlattığınızda yeni SOCKS kimlik bilgileri kullanılacaktır.</string>
<string name="network_session_mode_server_description">Her sunucu için yeni SOCKS kimlik bilgileri kullanılacaktır.</string>
<string name="app_check_for_updates_button_download">İndir %s (%s)</string>
<string name="settings_section_title_message_shape">Mesaj şekli</string>
</resources>
@@ -2086,26 +2086,4 @@
<string name="network_proxy_password">Пароль</string>
<string name="network_proxy_username">Ім\'я користувача</string>
<string name="network_proxy_auth_mode_isolate_by_auth_user">Використовуйте різні облікові дані проксі для кожного профілю.</string>
<string name="n_other_file_errors">%1$d інша(і) помилка(и) файлу.</string>
<string name="forward_files_failed_to_receive_desc">%1$d файл(и) не вдалося завантажити.</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s повідомлень не було переслано.</string>
<string name="forward_files_not_accepted_receive_files">Завантажити</string>
<string name="forward_alert_title_messages_to_forward">Переслати %1$s повідомлень?</string>
<string name="forward_multiple">Пересилаю повідомлення…</string>
<string name="forward_alert_forward_messages_without_files">Переслати повідомлення без файлів?</string>
<string name="forward_alert_title_nothing_to_forward">Немає нічого для пересилання!</string>
<string name="compose_save_messages_n">Зберігаю %1$s повідомлень.</string>
<string name="n_file_errors">%1$d помилка(и) файлу:
\n%2$s</string>
<string name="forward_files_not_accepted_desc">%1$d файл(и) не були завантажені.</string>
<string name="forward_files_in_progress_desc">%1$d файл(и) ще завантажуються.</string>
<string name="forward_files_missing_desc">%1$d файл(и) були видалені.</string>
<string name="compose_forward_messages_n">Пересилаю %1$s повідомлень</string>
<string name="forward_files_messages_deleted_after_selection_desc">Повідомлення були видалені після того, як ви їх вибрали.</string>
<string name="error_forwarding_messages">Помилка при пересиланні повідомлень</string>
<string name="icon_descr_sound_muted">Звук вимкнено</string>
<string name="error_initializing_web_view_wrong_arch">Помилка ініціалізації WebView. Переконайтеся, що WebView встановлено, і його підтримувана архітектура — arm64. \nПомилка: %s</string>
<string name="settings_message_shape_tail">Хвіст</string>
<string name="settings_message_shape_corner">Куточок</string>
<string name="settings_section_title_message_shape">Форма повідомлення</string>
</resources>
@@ -829,59 +829,4 @@
<string name="smp_proxy_error_broker_version">Phiên bản máy chủ chuyển tiếp không tương thích với cài đặt mạng: %1$s.</string>
<string name="section_title_for_console">CHO CONSOLE</string>
<string name="forward_message">Chuyển tiếp tin nhắn…</string>
<string name="v4_6_reduced_battery_usage">Giảm thiểu sử dụng pin hơn nữa</string>
<string name="forward_alert_forward_messages_without_files">Chuyển tiếp tin nhắn mà không có tệp?</string>
<string name="wallpaper_preview_hello_alice">Chào buổi chiều!</string>
<string name="v4_4_french_interface">Giao diện tiếng Pháp</string>
<string name="found_desktop">Đã tìm thấy máy tính</string>
<string name="simplex_link_mode_full">Liên kết đầy đủ</string>
<string name="from_gallery_button">Từ Thư viện</string>
<string name="full_name__field">Tên đầy đủ:</string>
<string name="group_is_decentralized">Tuyệt đối phi tập trung - chỉ hiển thị cho thành viên.</string>
<string name="wallpaper_preview_hello_bob">Chào buổi sáng!</string>
<string name="group_member_status_group_deleted">nhóm đã bị xóa</string>
<string name="group_members_can_add_message_reactions">Các thành viên nhóm có thể thả cảm xúc tin nhắn.</string>
<string name="group_members_can_delete">Các thành viên nhóm có thể xóa theo cách không thể hồi phục các tin nhắn đã gửi. (24 giờ)</string>
<string name="group_members_can_send_voice">Các thành viên trong nhóm có thể gửi tin nhắn thoại.</string>
<string name="permissions_required">Cấp quyền</string>
<string name="group_link">Liên kết nhóm</string>
<string name="info_row_group">Nhóm</string>
<string name="group_full_name_field">Tên đầy đủ nhóm:</string>
<string name="group_members_can_send_disappearing">Các thành viên trong nhóm có thể gửi tin nhắn tự xóa.</string>
<string name="group_members_can_send_files">Các thành viên trong nhóm có thể gửi tệp và phương tiện truyền thông.</string>
<string name="group_members_can_send_simplex_links">Các thành viên trong nhóm có thể gửi liên kết SimpleX.</string>
<string name="icon_descr_group_inactive">Nhóm không hoạt động</string>
<string name="connect_plan_group_already_exists">Nhóm đã tồn tại rồi!</string>
<string name="group_members_can_send_dms">Các thành viên trong nhóm có thể gửi tin nhắn trực tiếp.</string>
<string name="v4_2_group_links">Liên kết nhóm</string>
<string name="group_invitation_expired">Lời mời nhóm đã hết hạn</string>
<string name="alert_message_group_invitation_expired">Lời mới nhóm không còn có hiệu lực, nó đã bị xóa bởi người gửi.</string>
<string name="permissions_grant">Cho phép thực hiện cuộc gọi</string>
<string name="permissions_grant_in_settings">Cấp quyền trong cài đặt</string>
<string name="hide_notification">Ẩn</string>
<string name="icon_descr_hang_up">Ngắt kết nối</string>
<string name="notification_preview_mode_hidden">Ẩn</string>
<string name="hidden_profile_password">Mật khẩu hồ sơ ẩn</string>
<string name="group_profile_is_stored_on_members_devices">Hồ sơ nhóm được lưu trữ tại thiết bị của thành viên, không lưu trữ trên máy chủ.</string>
<string name="snd_group_event_group_profile_updated">hồ sơ nhóm đã được cập nhật</string>
<string name="icon_descr_help">trợ giúp</string>
<string name="hide_dev_options">Ẩn:</string>
<string name="email_invite_body">Xin chào!
\nKết nối với tôi qua SimpleX Chat: %s</string>
<string name="hide_profile">Ẩn hồ sơ</string>
<string name="settings_section_title_help">TRỢ GIÚP</string>
<string name="delete_group_for_all_members_cannot_undo_warning">Nhóm sẽ bị xóa cho tất cả các thành viên - điều này không thể hoàn tác!</string>
<string name="delete_group_for_self_cannot_undo_warning">Nhóm sẽ bị xóa cho bạn - điều này không thể hoàn tác!</string>
<string name="group_preferences">Tùy chọn nhóm</string>
<string name="recent_history_is_not_sent_to_new_members">Lịch sử không được gửi đến các thành viên mới.</string>
<string name="v4_3_improved_privacy_and_security_desc">Ẩn màn hình ứng dụng trong danh sách các ứng dụng gần đây.</string>
<string name="v4_6_group_moderation">Quản trị nhóm</string>
<string name="v4_6_group_welcome_message">Lời chào nhóm</string>
<string name="audio_device_wired_headphones">Tai nghe</string>
<string name="notification_display_mode_hidden_desc">Ẩn liên hệ và tin nhắn</string>
<string name="hide_verb">Ẩn</string>
<string name="user_hide">Ẩn</string>
<string name="edit_history">Lịch sử</string>
<string name="alert_title_no_group">Không tìm thấy nhóm!</string>
<string name="v4_6_hidden_chat_profiles">Hồ sơ trò chuyện ẩn</string>
</resources>
@@ -2087,26 +2087,4 @@
<string name="network_proxy_random_credentials">使用随机凭据</string>
<string name="network_proxy_username">用户名</string>
<string name="new_chat_share_profile">分享配置文件</string>
<string name="error_forwarding_messages">转发消息出错</string>
<string name="forward_files_messages_deleted_after_selection_desc">在你选中消息后这些消息被删除。</string>
<string name="n_file_errors">%1$d 个文件错误:
\n%2$s</string>
<string name="n_other_file_errors">其他 %1$d 个文件错误。</string>
<string name="forward_files_not_accepted_desc">%1$d 个文件未被下载。</string>
<string name="forward_alert_title_messages_to_forward">转发 %1$s 条消息?</string>
<string name="forward_alert_forward_messages_without_files">仅转发消息不转发文件?</string>
<string name="forward_alert_title_nothing_to_forward">没什么可转发的!</string>
<string name="forward_files_in_progress_desc">仍有 %1$d 个文件在下载中。</string>
<string name="forward_files_failed_to_receive_desc">%1$d 个文件下载失败。</string>
<string name="forward_files_missing_desc">%1$d 个文件被删除了。</string>
<string name="forward_files_not_accepted_receive_files">下载</string>
<string name="forward_files_messages_deleted_after_selection_title">%1$s 条消息未被转发</string>
<string name="forward_multiple">转发消息…</string>
<string name="compose_forward_messages_n">转发 %1$s 条消息</string>
<string name="compose_save_messages_n">保存 %1$s 条消息</string>
<string name="icon_descr_sound_muted">已静音</string>
<string name="settings_section_title_message_shape">管理形状</string>
<string name="settings_message_shape_corner">拐角</string>
<string name="settings_message_shape_tail">尾部</string>
<string name="error_initializing_web_view_wrong_arch">初始化 WebView 出错。确保你安装了 WebView 且其支持的架构为 arm64。\n错误:%s</string>
</resources>
@@ -31,7 +31,6 @@ var sendMessageToNative = (msg) => console.log(JSON.stringify(msg));
var toggleScreenShare = async () => { };
var localOrPeerMediaSourcesChanged = (_call) => { };
var inactiveCallMediaSourcesChanged = (_inactiveCallMediaSources) => { };
var failedToGetPermissions = (_title, _description) => { };
// Global object with cryptrographic/encoding functions
const callCrypto = callCryptoFunction();
var TransformOperation;
@@ -160,7 +159,7 @@ const processCommand = (function () {
try {
localStream = (notConnectedCall === null || notConnectedCall === void 0 ? void 0 : notConnectedCall.localStream)
? notConnectedCall.localStream
: await getLocalMediaStream(inactiveCallMediaSources.mic, inactiveCallMediaSources.camera && (await browserHasCamera()), localCamera);
: await getLocalMediaStream(inactiveCallMediaSources.mic, inactiveCallMediaSources.camera, localCamera);
}
catch (e) {
console.log("Error while getting local media stream", e);
@@ -297,7 +296,7 @@ const processCommand = (function () {
endCall();
let localStream = null;
try {
localStream = await getLocalMediaStream(true, command.media == CallMediaType.Video && (await browserHasCamera()), VideoCamera.User);
localStream = await getLocalMediaStream(true, command.media == CallMediaType.Video, VideoCamera.User);
const videos = getVideoElements();
if (videos) {
videos.local.srcObject = localStream;
@@ -305,10 +304,6 @@ const processCommand = (function () {
}
}
catch (e) {
console.log(e);
// Do not allow to continue the call without audio permission
resp = { type: "error", message: "capabilities: no permissions were granted for mic and/or camera" };
break;
localStream = new MediaStream();
// Will be shown on the next stage of call estabilishing, can work without any streams
//desktopShowPermissionsAlert(command.media)
@@ -442,11 +437,6 @@ const processCommand = (function () {
break;
case "media":
if (!activeCall) {
if (!notConnectedCall) {
// call can have a slow startup and be in this place even before "capabilities" stage
resp = { type: "error", message: "media: call has not yet pass capabilities stage" };
break;
}
switch (command.source) {
case CallMediaSource.Mic:
inactiveCallMediaSources.mic = command.enable;
@@ -494,11 +484,8 @@ const processCommand = (function () {
if (!activeCall || !pc) {
if (notConnectedCall) {
recreateLocalStreamWhileNotConnected(command.camera);
resp = { type: "ok" };
}
else {
resp = { type: "error", message: "camera: call has not yet pass capabilities stage" };
}
resp = { type: "ok" };
}
else {
if (await replaceMedia(activeCall, CallMediaSource.Camera, true, command.camera)) {
@@ -528,10 +515,6 @@ const processCommand = (function () {
endCall();
resp = { type: "ok" };
break;
case "permission":
failedToGetPermissions(command.title, permissionDescription(command));
resp = { type: "ok" };
break;
default:
resp = { type: "error", message: "unknown command" };
break;
@@ -901,7 +884,7 @@ const processCommand = (function () {
localStream.getVideoTracks().forEach((elem) => elem.stop());
localStream.getVideoTracks().forEach((elem) => localStream.removeTrack(elem));
}
await getLocalMediaStream(inactiveCallMediaSources.mic && localStream.getAudioTracks().length == 0, inactiveCallMediaSources.camera && (localStream.getVideoTracks().length == 0 || oldCamera != newCamera) && (await browserHasCamera()), newCamera)
await getLocalMediaStream(inactiveCallMediaSources.mic && localStream.getAudioTracks().length == 0, inactiveCallMediaSources.camera && (localStream.getVideoTracks().length == 0 || oldCamera != newCamera), newCamera)
.then((stream) => {
stream.getTracks().forEach((elem) => {
localStream.addTrack(elem);
@@ -1091,18 +1074,6 @@ const processCommand = (function () {
};
return navigator.mediaDevices.getDisplayMedia(constraints);
}
async function browserHasCamera() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const hasCamera = devices.some((elem) => elem.kind == "videoinput");
console.log("Camera is available: " + hasCamera);
return hasCamera;
}
catch (error) {
console.log("Error while enumerating devices: " + error, error);
return false;
}
}
function callMediaConstraints(mic, camera, facingMode) {
return {
audio: mic,
@@ -1316,18 +1287,6 @@ function desktopShowPermissionsAlert(mediaType) {
window.alert("Permissions denied. Please, allow access to mic and camera to make the call working and hit unmute/camera button. Don't reload the page.");
}
}
function permissionDescription(command) {
if (window.safari) {
return command.safari;
}
else if ((navigator.userAgent.includes("Chrome") && navigator.vendor.includes("Google Inc")) ||
navigator.userAgent.includes("Firefox")) {
return command.chrome;
}
else {
return "";
}
}
// Cryptography function - it is loaded both in the main window and in worker context (if the worker is used)
function callCryptoFunction() {
const initialPlainTextRequired = {
@@ -50,12 +50,6 @@
<div id="audio-call-icon">
<img src="/desktop/images/ic_phone_in_talk.svg" />
</div>
<div id="permission-denied">
<p id="permission-denied-title"></p>
<p id="permission-denied-desc"></p>
</div>
<p id="manage-call">
<button id="toggle-screen" onclick="javascript:toggleScreenManually()">
<img src="/desktop/images/ic_screen_share.svg" />
@@ -3,7 +3,6 @@ body {
padding: 0;
margin: 0;
background-color: black;
font-family: Arial, Helvetica, sans-serif;
}
#remote-video-stream.inline {
@@ -158,23 +157,6 @@ body {
animation: spin 2s linear infinite;
}
#permission-denied {
position: absolute;
width: 100%;
text-align: center;
height: 100%;
align-content: center;
color: white;
}
#permission-denied-title {
font-weight: bold;
}
#permission-denied-desc {
font-style: italic;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
@@ -198,6 +180,7 @@ body {
color: white;
opacity: 0.8;
width: 200px;
font-family: Arial, Helvetica, sans-serif;
}
#info-block.audio {
@@ -75,16 +75,6 @@ inactiveCallMediaSourcesChanged = (inactiveCallMediaSources) => {
document.getElementById("info-block").className = className;
// document.getElementById("media-sources")!.innerText = inactiveCallMediaSourcesStatus(inactiveCallMediaSources)
};
failedToGetPermissions = (title, description) => {
document.getElementById("info-block").style.visibility = "hidden";
document.getElementById("progress").style.visibility = "hidden";
document.getElementById("permission-denied-title").innerText = title;
document.getElementById("permission-denied-desc").innerText = description;
document.getElementById("toggle-mic").style.visibility = "hidden";
document.getElementById("toggle-camera").style.visibility = "hidden";
document.getElementById("toggle-screen").style.visibility = "hidden";
document.getElementById("toggle-speaker").style.visibility = "hidden";
};
function enableMicIcon(enabled) {
document.getElementById("toggle-mic").innerHTML = enabled
? '<img src="/desktop/images/ic_mic.svg" />'
@@ -199,13 +199,7 @@ private fun ApplicationScope.AppWindow(closedByError: MutableState<Boolean>) {
768.dp)
Window(state = cWindowState, onCloseRequest = { hiddenUntilRestart = true }, title = stringResource(MR.strings.chat_console)) {
SimpleXTheme {
TerminalView(true) { hiddenUntilRestart = true }
ModalManager.floatingTerminal.showInView()
DisposableEffect(Unit) {
onDispose {
ModalManager.floatingTerminal.closeModals()
}
}
TerminalView(ChatModel) { hiddenUntilRestart = true }
}
}
}
@@ -105,14 +105,6 @@ actual fun ActiveCallView() {
else -> {}
}
is WCallResponse.Error -> {
when (apiMsg.command) {
is WCallCommand.Capabilities -> chatModel.callCommand.add(WCallCommand.Permission(
title = generalGetString(MR.strings.call_desktop_permission_denied_title),
chrome = generalGetString(MR.strings.call_desktop_permission_denied_chrome),
safari = generalGetString(MR.strings.call_desktop_permission_denied_safari)
))
else -> {}
}
Log.e(TAG, "ActiveCallView: command error ${r.message}")
}
}
@@ -62,9 +62,6 @@ fun AppearanceScope.AppearanceLayout(
SectionDividerSpaced()
ThemesSection(systemDarkTheme)
SectionDividerSpaced()
MessageShapeSection()
SectionDividerSpaced()
ProfileImageSection()
+4 -4
View File
@@ -26,11 +26,11 @@ android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.jvm.target=11
android.version_name=6.1-beta.4
android.version_code=245
android.version_name=6.1-beta.3
android.version_code=244
desktop.version_name=6.1-beta.4
desktop.version_code=71
desktop.version_name=6.1-beta.3
desktop.version_code=70
kotlin.version=1.9.23
gradle.plugin.version=8.2.0

Some files were not shown because too many files have changed in this diff Show More