mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 12:04:11 +00:00
Merge branch 'master' into master-android
This commit is contained in:
+2
-1
@@ -233,7 +233,8 @@ actual fun getFileName(uri: URI): String? {
|
||||
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
cursor.moveToFirst()
|
||||
// Can make an exception
|
||||
cursor.getString(nameIndex)
|
||||
// the provider controls this value, and callers use it as a bare file name
|
||||
cursor.getString(nameIndex)?.let { File(it).name }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
|
||||
@@ -1293,6 +1293,7 @@ data class User(
|
||||
val sendRcptsContacts: Boolean,
|
||||
val sendRcptsSmallGroups: Boolean,
|
||||
val autoAcceptMemberContacts: Boolean,
|
||||
val autoAcceptGroupInvitations: Boolean,
|
||||
val viewPwdHash: UserPwdHash?,
|
||||
val uiThemes: ThemeModeOverrides? = null,
|
||||
val userChatRelay: Boolean,
|
||||
@@ -1325,6 +1326,7 @@ data class User(
|
||||
sendRcptsContacts = true,
|
||||
sendRcptsSmallGroups = false,
|
||||
autoAcceptMemberContacts = false,
|
||||
autoAcceptGroupInvitations = false,
|
||||
viewPwdHash = null,
|
||||
uiThemes = null,
|
||||
userChatRelay = false,
|
||||
|
||||
+9
@@ -941,6 +941,12 @@ object ChatController {
|
||||
throw Exception("failed to set auto-accept ${r.responseType} ${r.details}")
|
||||
}
|
||||
|
||||
suspend fun apiSetUserAutoAcceptGroupInvitations(u: User, enable: Boolean) {
|
||||
val r = sendCmd(u.remoteHostId, CC.ApiSetUserAutoAcceptGroupInvitations(u.userId, enable))
|
||||
if (r.result is CR.CmdOk) return
|
||||
throw Exception("failed to set auto-accept group invitations ${r.responseType} ${r.details}")
|
||||
}
|
||||
|
||||
suspend fun apiHideUser(u: User, viewPwd: String): User =
|
||||
setUserPrivacy(u.remoteHostId, CC.ApiHideUser(u.userId, viewPwd))
|
||||
|
||||
@@ -3775,6 +3781,7 @@ sealed class CC {
|
||||
class ApiSetUserContactReceipts(val userId: Long, val userMsgReceiptSettings: UserMsgReceiptSettings): CC()
|
||||
class ApiSetUserGroupReceipts(val userId: Long, val userMsgReceiptSettings: UserMsgReceiptSettings): CC()
|
||||
class ApiSetUserAutoAcceptMemberContacts(val userId: Long, val enable: Boolean): CC()
|
||||
class ApiSetUserAutoAcceptGroupInvitations(val userId: Long, val enable: Boolean): CC()
|
||||
class ApiHideUser(val userId: Long, val viewPwd: String): CC()
|
||||
class ApiUnhideUser(val userId: Long, val viewPwd: String): CC()
|
||||
class ApiMuteUser(val userId: Long): CC()
|
||||
@@ -3965,6 +3972,7 @@ sealed class CC {
|
||||
"/_set receipts groups $userId ${onOff(mrs.enable)} clear_overrides=${onOff(mrs.clearOverrides)}"
|
||||
}
|
||||
is ApiSetUserAutoAcceptMemberContacts -> "/_set accept member contacts $userId ${onOff(enable)}"
|
||||
is ApiSetUserAutoAcceptGroupInvitations -> "/_set accept group invitations $userId ${onOff(enable)}"
|
||||
is ApiHideUser -> "/_hide user $userId ${json.encodeToString(viewPwd)}"
|
||||
is ApiUnhideUser -> "/_unhide user $userId ${json.encodeToString(viewPwd)}"
|
||||
is ApiMuteUser -> "/_mute user $userId"
|
||||
@@ -4176,6 +4184,7 @@ sealed class CC {
|
||||
is ApiSetUserContactReceipts -> "apiSetUserContactReceipts"
|
||||
is ApiSetUserGroupReceipts -> "apiSetUserGroupReceipts"
|
||||
is ApiSetUserAutoAcceptMemberContacts -> "apiSetUserAutoAcceptMemberContacts"
|
||||
is ApiSetUserAutoAcceptGroupInvitations -> "apiSetUserAutoAcceptGroupInvitations"
|
||||
is ApiHideUser -> "apiHideUser"
|
||||
is ApiUnhideUser -> "apiUnhideUser"
|
||||
is ApiMuteUser -> "apiMuteUser"
|
||||
|
||||
+8
-5
@@ -502,14 +502,17 @@ fun ChatView(
|
||||
groupMembersJob = scope.launch(Dispatchers.Default) {
|
||||
val r = chatModel.controller.apiGroupMemberInfo(chatRh, groupInfo.groupId, member.groupMemberId)
|
||||
val stats = r?.second
|
||||
val (_, code) = if (member.memberActive) {
|
||||
val (updatedMember, code) = if (member.memberActive) {
|
||||
val memCode = chatModel.controller.apiGetGroupMemberCode(chatRh, groupInfo.apiId, member.groupMemberId)
|
||||
member to memCode?.second
|
||||
(memCode?.first ?: r?.first ?: member) to memCode?.second
|
||||
} else {
|
||||
member to null
|
||||
(r?.first ?: member) to null
|
||||
}
|
||||
if (!isActive || chatModel.chatId.value != groupInfo.id) return@launch
|
||||
// members are not loaded in large groups, so only the opened member is added to the model
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(chatRh, groupInfo, updatedMember)
|
||||
}
|
||||
setGroupMembers(chatRh, groupInfo, chatModel)
|
||||
if (!isActive) return@launch
|
||||
|
||||
if (chatsCtx.secondaryContextFilter == null) {
|
||||
ModalManager.end.closeModals()
|
||||
|
||||
+5
@@ -753,6 +753,11 @@ private fun saveArchiveFromURI(importedArchiveURI: URI): String? {
|
||||
if (inputStream != null && archiveName != null) {
|
||||
val archivePath = "$databaseExportDir${File.separator}$archiveName"
|
||||
val destFile = File(archivePath)
|
||||
// resolves symlinks, so it also catches a final component linking outside the folder
|
||||
if (destFile.canonicalFile.parentFile != databaseExportDir.canonicalFile) {
|
||||
Log.e(TAG, "saveArchiveFromURI path outside of export folder")
|
||||
return null
|
||||
}
|
||||
Files.copy(inputStream, destFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
archivePath
|
||||
} else {
|
||||
|
||||
+2
-1
@@ -31,7 +31,8 @@ fun AppBarTitle(
|
||||
val connection = if (enableAlphaChanges) handler?.connection else null
|
||||
LaunchedEffect(title) {
|
||||
if (enableAlphaChanges) {
|
||||
handler?.title?.value = title
|
||||
// the app bar shows a single line, so the line breaks of the large title are replaced with spaces
|
||||
handler?.title?.value = title.replace("\n", " ")
|
||||
} else {
|
||||
handler?.connection?.scrollTrackingEnabled = false
|
||||
}
|
||||
|
||||
+323
-57
@@ -1,6 +1,10 @@
|
||||
package chat.simplex.common.views.onboarding
|
||||
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.calculatePan
|
||||
import androidx.compose.foundation.gestures.calculateZoom
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
@@ -8,15 +12,38 @@ 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.CornerRadius
|
||||
import androidx.compose.ui.geometry.RoundRect
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.geometry.toRect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.PointerEventPass
|
||||
import androidx.compose.ui.input.pointer.PointerIcon
|
||||
import androidx.compose.ui.input.pointer.pointerHoverIcon
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withLink
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.BuildConfigCommon
|
||||
@@ -35,6 +62,7 @@ import chat.simplex.common.views.usersettings.showAddShortLinkAlert
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@Composable
|
||||
fun ModalData.WhatsNewView(updatedConditions: Boolean = false, viaSettings: Boolean = false, close: () -> Unit) {
|
||||
@@ -914,14 +942,15 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
)
|
||||
),
|
||||
VersionDescription(
|
||||
version = "v7.0",
|
||||
// the trailing space differs from the previously released "v7.0", so that What's new is shown again
|
||||
version = if (isInUs()) "v7.0.1" else "v7.0",
|
||||
post = null,
|
||||
features = listOf(
|
||||
// VersionFeature.FeatureView(
|
||||
// icon = null,
|
||||
// titleId = MR.strings.v7_0_invest,
|
||||
// view = { _ -> InvestInSimpleXChatView() }
|
||||
// ),
|
||||
VersionFeature.FeatureView(
|
||||
icon = null,
|
||||
titleId = MR.strings.v7_0_invest,
|
||||
view = { modalManager -> InvestInSimpleXChatView(modalManager) }
|
||||
),
|
||||
VersionFeature.FeatureDescription(
|
||||
icon = MR.images.ic_alternate_email,
|
||||
titleId = MR.strings.v7_0_simplex_names,
|
||||
@@ -956,57 +985,294 @@ fun shouldShowWhatsNew(m: ChatModel): Boolean {
|
||||
return v != lastVersion
|
||||
}
|
||||
|
||||
// private const val WEFUNDER_URL = "https://wefunder.com/simplexchat"
|
||||
//
|
||||
// @Composable
|
||||
// private fun InvestInSimpleXChatView() {
|
||||
// if (platform.androidIsPlayStoreBuild) {
|
||||
// LaunchedEffect(Unit) { if (androidPlayStoreCountry.value == null) platform.androidLoadPlayStoreCountry() }
|
||||
// if (androidPlayStoreCountry.value != "US") return
|
||||
// }
|
||||
// val uriHandler = LocalUriHandler.current
|
||||
// Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(bottom = 12.dp)) {
|
||||
// Column(modifier = Modifier.weight(1f)) {
|
||||
// Row(
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
// modifier = Modifier.padding(bottom = 4.dp)
|
||||
// ) {
|
||||
// Icon(painterResource(MR.images.ic_redeem), stringResource(MR.strings.v7_0_invest), tint = MaterialTheme.colors.secondary)
|
||||
// Text(
|
||||
// generalGetString(MR.strings.v7_0_invest),
|
||||
// maxLines = 2,
|
||||
// overflow = TextOverflow.Ellipsis,
|
||||
// style = MaterialTheme.typography.h4,
|
||||
// fontWeight = FontWeight.Medium,
|
||||
// modifier = Modifier.padding(bottom = 6.dp)
|
||||
// )
|
||||
// }
|
||||
// Text(generalGetString(MR.strings.v7_0_invest_descr), fontSize = 15.sp, modifier = Modifier.padding(bottom = 4.dp))
|
||||
// Row(
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
// modifier = Modifier
|
||||
// .clickable(
|
||||
// interactionSource = remember { MutableInteractionSource() },
|
||||
// indication = null
|
||||
// ) {
|
||||
// uriHandler.openExternalLink(WEFUNDER_URL)
|
||||
// }
|
||||
// ) {
|
||||
// Text(stringResource(MR.strings.v7_0_invest_learn_more), color = MaterialTheme.colors.primary, fontSize = 15.sp)
|
||||
// Icon(painterResource(MR.images.ic_open_in_new), stringResource(MR.strings.v7_0_invest_learn_more), tint = MaterialTheme.colors.primary)
|
||||
// }
|
||||
// }
|
||||
// if (BuildConfigCommon.SIMPLEX_ASSETS) {
|
||||
// Image(
|
||||
// painterResource(if (isInDarkTheme()) MR.images.own_stake_light else MR.images.own_stake),
|
||||
// contentDescription = null,
|
||||
// modifier = Modifier.width(80.dp)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
private const val WEFUNDER_URL = "https://wefunder.com/simplex.chat"
|
||||
|
||||
private const val CROWDFUNDING_CONTACT_URI = "simplex:/a#JxGcOA1_QhlmVFzYYabloMbvMZk5Y9d9iS3ITDnhzYo?h=smp11.simplex.im"
|
||||
|
||||
// the center modal takes the remaining width of the window, so the image is limited to its design width
|
||||
private val MAX_CROWDFUNDING_IMAGE_WIDTH = DEFAULT_MIN_CENTER_MODAL_WIDTH
|
||||
|
||||
// the width of the page images shipped with the desktop app, so that they are never upscaled
|
||||
private val CROWDFUNDING_PAGE_IMAGE_WIDTH = DEFAULT_MIN_CENTER_MODAL_WIDTH
|
||||
|
||||
// the corner radius the images are designed with, and the same radius as a share of their design width
|
||||
private val CROWDFUNDING_IMAGE_CORNER_RADIUS = 12.dp
|
||||
private const val CROWDFUNDING_IMAGE_CORNER_RADIUS_RATIO = 0.03f
|
||||
|
||||
private class CrowdfundingLayout(
|
||||
val maxImageWidth: Dp,
|
||||
val imageShape: Shape,
|
||||
// the modal manager that shows the page in the center of the window, or null when nothing does
|
||||
private val centerOfWindow: ModalManager?
|
||||
) {
|
||||
fun inCenterOfWindow(modalManager: ModalManager) = modalManager === centerOfWindow
|
||||
}
|
||||
|
||||
// the images are designed for the width of a phone screen, which Android always gives them. On desktop
|
||||
// they are limited to their own width, and their radius is scaled with them, as they are still shown
|
||||
// wider than designed: a fixed radius would not only look almost square, but would also leave the corners
|
||||
// baked into the jpegs visible - they have black behind them, as jpegs have no transparency
|
||||
private val crowdfundingLayout = if (appPlatform.isDesktop)
|
||||
CrowdfundingLayout(CROWDFUNDING_PAGE_IMAGE_WIDTH, object : Shape {
|
||||
override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline =
|
||||
Outline.Rounded(RoundRect(size.toRect(), CornerRadius(size.width * CROWDFUNDING_IMAGE_CORNER_RADIUS_RATIO)))
|
||||
}, ModalManager.center)
|
||||
else
|
||||
CrowdfundingLayout(Dp.Unspecified, RoundedCornerShape(CROWDFUNDING_IMAGE_CORNER_RADIUS), null)
|
||||
|
||||
// Google Play policy restricts promoting investments, so Play builds only show it in the US
|
||||
@Composable
|
||||
fun crowdfundingAvailable(): Boolean {
|
||||
if (!platform.androidIsPlayStoreBuild) return true
|
||||
if (androidPlayStoreCountry.value == null) {
|
||||
LaunchedEffect(Unit) {
|
||||
if (androidPlayStoreCountry.value == null) platform.androidLoadPlayStoreCountry()
|
||||
}
|
||||
}
|
||||
return isInUs()
|
||||
}
|
||||
|
||||
fun isInUs(): Boolean =
|
||||
androidPlayStoreCountry.value == "US"
|
||||
|| androidPlayStoreCountry.value == ""
|
||||
|| androidPlayStoreCountry.value == null
|
||||
|
||||
@Composable
|
||||
private fun InvestInSimpleXChatView(modalManager: ModalManager) {
|
||||
if (!crowdfundingAvailable()) return
|
||||
val showGetStake = { modalManager.showModalCloseable(cardScreen = true) { close -> GetStakeView(fromSettings = false, inCenterOfWindow = crowdfundingLayout.inCenterOfWindow(modalManager), close = close) } }
|
||||
Column(modifier = Modifier.padding(bottom = 12.dp)) {
|
||||
Text(
|
||||
generalGetString(MR.strings.v7_0_invest),
|
||||
style = MaterialTheme.typography.h4,
|
||||
fontWeight = FontWeight.Medium,
|
||||
modifier = Modifier.padding(bottom = 6.dp)
|
||||
)
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append(generalGetString(MR.strings.v7_0_invest_descr))
|
||||
append(" ")
|
||||
withStyle(SpanStyle(color = MaterialTheme.colors.primary)) {
|
||||
append(generalGetString(MR.strings.learn_more))
|
||||
}
|
||||
},
|
||||
fontSize = 15.sp,
|
||||
modifier = Modifier
|
||||
.pointerHoverIcon(PointerIcon.Hand)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = showGetStake
|
||||
)
|
||||
)
|
||||
if (BuildConfigCommon.SIMPLEX_ASSETS) {
|
||||
Image(
|
||||
painterResource(MR.images.crowdfunding_1),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.widthIn(max = MAX_CROWDFUNDING_IMAGE_WIDTH)
|
||||
.fillMaxWidth()
|
||||
.clip(crowdfundingLayout.imageShape)
|
||||
.pointerHoverIcon(PointerIcon.Hand)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = showGetStake
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CrowdfundingSlide(
|
||||
val image: ImageResource,
|
||||
val heading: String,
|
||||
val info: String?,
|
||||
val text: String,
|
||||
)
|
||||
|
||||
// not localized: the page is only shown to US investors, and the text duplicates the images
|
||||
private val getStakeSlides: List<CrowdfundingSlide> = listOf(
|
||||
CrowdfundingSlide(
|
||||
MR.images.crowdfunding_1,
|
||||
"The first and the only messaging network without any user IDs",
|
||||
null,
|
||||
"By investing, you can benefit from the company growth, and help us build the future of private and secure communications."
|
||||
),
|
||||
CrowdfundingSlide(
|
||||
MR.images.crowdfunding_2,
|
||||
"480,000+ users joined on their own",
|
||||
null,
|
||||
"SimpleX users have been more than doubling every year without any paid marketing, and donated over \$650,000."
|
||||
),
|
||||
CrowdfundingSlide(
|
||||
MR.images.crowdfunding_3,
|
||||
"Developers already bet on SimpleX success",
|
||||
"Independent developers created moderation and AI bots, Telegram bridges, and a public server registry.",
|
||||
"Every service developers build on SimpleX Network may increase its value, and bring new users to SimpleX Chat."
|
||||
),
|
||||
CrowdfundingSlide(
|
||||
MR.images.crowdfunding_4,
|
||||
"Revenue plan: free for users, channels & businesses pay",
|
||||
"SimpleX Chat plans to earn from the infrastructure and services that creators, businesses and large communities need as they grow.",
|
||||
"Read about how we plan to make SimpleX Chat and network profitable, and about all the investment terms on Wefunder."
|
||||
),
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun GetStakeView(fromSettings: Boolean, inCenterOfWindow: Boolean = false, close: () -> Unit) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val stopped = chatModel.chatRunning.value == false
|
||||
|
||||
@Composable
|
||||
fun slideImage(slide: CrowdfundingSlide) {
|
||||
if (BuildConfigCommon.SIMPLEX_ASSETS) {
|
||||
Image(
|
||||
painterResource(slide.image),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier
|
||||
.widthIn(max = crowdfundingLayout.maxImageWidth)
|
||||
.fillMaxWidth()
|
||||
.clip(crowdfundingLayout.imageShape)
|
||||
.fullScreenOnClick(slide.image)
|
||||
)
|
||||
} else {
|
||||
Text(slide.heading, style = MaterialTheme.typography.h4, fontWeight = FontWeight.Medium)
|
||||
if (slide.info != null) {
|
||||
Text(slide.info, Modifier.padding(top = 4.dp), lineHeight = 24.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnWithScrollBar(Modifier.pinchZoom().padding(horizontal = DEFAULT_PADDING)) {
|
||||
// in the center of the window the page is wide enough for the title to fit on one line
|
||||
val title = "Get a stake in\nSimpleX Chat"
|
||||
AppBarTitle(if (inCenterOfWindow) title.replace("\n", " ") else title, withPadding = false)
|
||||
// What's new already shows the image of the first slide, above the link that opens this page
|
||||
if (fromSettings) {
|
||||
slideImage(getStakeSlides[0])
|
||||
}
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append(getStakeSlides[0].text)
|
||||
// only the link is clickable, the rest of the paragraph is not
|
||||
withLink(LinkAnnotation.Url(WEFUNDER_URL) { uriHandler.openUriCatching(WEFUNDER_URL) }) {
|
||||
withStyle(SpanStyle(color = MaterialTheme.colors.primary, fontWeight = FontWeight.Bold)) {
|
||||
append(" Learn more and invest on Wefunder.")
|
||||
}
|
||||
}
|
||||
},
|
||||
Modifier.padding(top = if (fromSettings) 8.dp else 0.dp),
|
||||
lineHeight = 24.sp
|
||||
)
|
||||
|
||||
getStakeSlides.drop(1).forEach { slide ->
|
||||
Column(Modifier.padding(top = DEFAULT_PADDING * 1.5f)) {
|
||||
slideImage(slide)
|
||||
Text(slide.text, Modifier.padding(top = 8.dp), lineHeight = 24.sp)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
Modifier.fillMaxWidth().padding(top = DEFAULT_PADDING * 2),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
OnboardingActionButton(
|
||||
if (appPlatform.isAndroid) Modifier.fillMaxWidth() else Modifier.widthIn(min = 300.dp),
|
||||
labelId = MR.strings.v7_0_invest_learn_more,
|
||||
onboarding = null,
|
||||
onclick = { uriHandler.openUriCatching(WEFUNDER_URL) }
|
||||
)
|
||||
if (!chatModel.desktopNoUserNoRemote) {
|
||||
TextButtonBelowOnboardingButton(
|
||||
"or ask SimpleX team",
|
||||
onClick = if (stopped) null else ({
|
||||
close()
|
||||
uriHandler.openVerifiedSimplexUri(CROWDFUNDING_CONTACT_URI)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// there is no pinch gesture with a mouse, so on desktop a slide is opened full screen instead
|
||||
@Composable
|
||||
private fun Modifier.fullScreenOnClick(image: ImageResource): Modifier {
|
||||
if (!appPlatform.isDesktop) return this
|
||||
return pointerHoverIcon(PointerIcon.Hand).clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null
|
||||
) {
|
||||
ModalManager.fullscreen.showCustomModal { close ->
|
||||
BackHandler(onBack = close)
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black)
|
||||
.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = close),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(painterResource(image), contentDescription = null, contentScale = ContentScale.Fit, modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val MAX_PAGE_ZOOM = 5f
|
||||
|
||||
/**
|
||||
* The slide images contain small text that is unreadable at screen width, so the page can be pinch-zoomed.
|
||||
* Android only: pinch is unavailable with a mouse.
|
||||
*/
|
||||
@Composable
|
||||
private fun Modifier.pinchZoom(): Modifier {
|
||||
if (!appPlatform.isAndroid) return this
|
||||
var scale by remember { mutableStateOf(1f) }
|
||||
var offsetX by remember { mutableStateOf(0f) }
|
||||
var offsetY by remember { mutableStateOf(0f) }
|
||||
var size by remember { mutableStateOf(IntSize.Zero) }
|
||||
return this
|
||||
.onGloballyPositioned { size = it.size }
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
translationX = offsetX
|
||||
translationY = offsetY
|
||||
}
|
||||
.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
// the initial pass, as the scroll of the same column is applied after this modifier and would take the gesture first
|
||||
awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial)
|
||||
var taken: Boolean? = null
|
||||
do {
|
||||
val event = awaitPointerEvent(PointerEventPass.Initial)
|
||||
val multiTouch = event.changes.count { it.pressed } > 1
|
||||
if (multiTouch || scale > 1f) {
|
||||
scale = (scale * event.calculateZoom()).coerceIn(1f, MAX_PAGE_ZOOM)
|
||||
val pan = event.calculatePan()
|
||||
// the page is scaled around its center, so it can be panned by half of the overflow in each direction
|
||||
val maxX = size.width * (scale - 1f) / 2
|
||||
val maxY = size.height * (scale - 1f) / 2
|
||||
val pannedY = offsetY + pan.y * scale
|
||||
// the clamp is applied even when the gesture is not taken: at scale 1 both bounds
|
||||
// are 0, which resets the offsets after zooming back out
|
||||
offsetX = (offsetX + pan.x * scale).coerceIn(-maxX, maxX)
|
||||
offsetY = pannedY.coerceIn(-maxY, maxY)
|
||||
// two fingers always mean zoom, taken without a touch slop: waiting for one would let
|
||||
// the scroll reach its own slop first and scroll the page. A one finger drag is left
|
||||
// to the scroll at the edges, decided once so it cannot alternate mid drag
|
||||
if (multiTouch) taken = true
|
||||
else if (taken == null && pan.y != 0f) taken = pannedY.absoluteValue < maxY
|
||||
if (taken == true) event.changes.forEach { if (it.pressed) it.consume() }
|
||||
}
|
||||
} while (event.changes.any { it.pressed })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateUpdateAddressShortLinkView(modalManager: ModalManager) {
|
||||
|
||||
+25
-9
@@ -87,13 +87,19 @@ fun PrivacySettingsView(
|
||||
val currentUser = chatModel.currentUser.value
|
||||
if (currentUser != null && !chatModel.desktopNoUserNoRemote) {
|
||||
SectionDividerSpaced()
|
||||
ContacRequestsFromGroupsSection(
|
||||
AutoAcceptSection(
|
||||
currentUser = currentUser,
|
||||
setAutoAcceptGrpDirectInvs = { enable ->
|
||||
setAutoAcceptMemberContacts = { enable ->
|
||||
withApi {
|
||||
chatModel.controller.apiSetUserAutoAcceptMemberContacts(currentUser, enable)
|
||||
chatModel.currentUser.value = currentUser.copy(autoAcceptMemberContacts = enable)
|
||||
}
|
||||
},
|
||||
setAutoAcceptGroupInvitations = { enable ->
|
||||
withApi {
|
||||
chatModel.controller.apiSetUserAutoAcceptGroupInvitations(currentUser, enable)
|
||||
chatModel.currentUser.value = currentUser.copy(autoAcceptGroupInvitations = enable)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -333,16 +339,26 @@ expect fun PrivacyDeviceSection(
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun ContacRequestsFromGroupsSection(
|
||||
private fun AutoAcceptSection(
|
||||
currentUser: User,
|
||||
setAutoAcceptGrpDirectInvs: (Boolean) -> Unit
|
||||
setAutoAcceptMemberContacts: (Boolean) -> Unit,
|
||||
setAutoAcceptGroupInvitations: (Boolean) -> Unit
|
||||
) {
|
||||
SectionView(stringResource(MR.strings.settings_section_title_contact_requests_from_groups)) {
|
||||
SettingsActionItemWithContent(painterResource(MR.images.ic_check), stringResource(MR.strings.auto_accept_contact)) {
|
||||
// legacy string key names, reused for their values so this section stays translated
|
||||
SectionView(stringResource(MR.strings.auto_accept_contact)) {
|
||||
SettingsActionItemWithContent(painterResource(MR.images.ic_person), stringResource(MR.strings.settings_section_title_contact_requests_from_groups)) {
|
||||
DefaultSwitch(
|
||||
checked = currentUser.autoAcceptMemberContacts,
|
||||
onCheckedChange = { enable ->
|
||||
setAutoAcceptGrpDirectInvs(enable)
|
||||
setAutoAcceptMemberContacts(enable)
|
||||
}
|
||||
)
|
||||
}
|
||||
SettingsActionItemWithContent(painterResource(MR.images.ic_group), stringResource(MR.strings.group_invitations)) {
|
||||
DefaultSwitch(
|
||||
checked = currentUser.autoAcceptGroupInvitations,
|
||||
onCheckedChange = { enable ->
|
||||
setAutoAcceptGroupInvitations(enable)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -350,7 +366,7 @@ private fun ContacRequestsFromGroupsSection(
|
||||
SectionTextFooter(
|
||||
remember(currentUser.displayName) {
|
||||
buildAnnotatedString {
|
||||
append(generalGetString(MR.strings.this_setting_is_for_your_current_profile) + " ")
|
||||
append(generalGetString(MR.strings.these_settings_are_for_your_current_profile) + " ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(currentUser.displayName)
|
||||
}
|
||||
@@ -387,7 +403,7 @@ private fun DeliveryReceiptsSection(
|
||||
SectionTextFooter(
|
||||
remember(currentUser.displayName) {
|
||||
buildAnnotatedString {
|
||||
append(generalGetString(MR.strings.receipts_section_description) + " ")
|
||||
append(generalGetString(MR.strings.these_settings_are_for_your_current_profile) + " ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(currentUser.displayName)
|
||||
}
|
||||
|
||||
+13
@@ -29,8 +29,10 @@ import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.database.DatabaseView
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.migration.MigrateFromDeviceView
|
||||
import chat.simplex.common.views.onboarding.GetStakeView
|
||||
import chat.simplex.common.views.onboarding.SimpleXInfo
|
||||
import chat.simplex.common.views.onboarding.WhatsNewView
|
||||
import chat.simplex.common.views.onboarding.crowdfundingAvailable
|
||||
import chat.simplex.common.views.usersettings.networkAndServers.NetworkAndServersView
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@@ -110,6 +112,17 @@ fun SettingsLayout(
|
||||
AppShutdownItem()
|
||||
AppVersionItem(showVersion)
|
||||
}
|
||||
|
||||
if (crowdfundingAvailable()) {
|
||||
SectionDividerSpaced()
|
||||
SectionView(stringResource(MR.strings.v7_0_invest)) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_redeem),
|
||||
stringResource(MR.strings.v7_0_crowdfunding),
|
||||
{ ModalManager.start.showModalCloseable(cardScreen = true) { close -> GetStakeView(fromSettings = true, close = close) } }
|
||||
)
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1106,7 +1106,7 @@
|
||||
<string name="tap_to_activate_profile">انقر لتنشيط ملف التعريف.</string>
|
||||
<string name="v4_5_transport_isolation">عزل النقل</string>
|
||||
<string name="this_string_is_not_a_connection_link">هذه السلسلة ليست رابط اتصال!</string>
|
||||
<string name="receipts_section_description">هذه الإعدادات لملف تعريفك الحالي</string>
|
||||
<string name="these_settings_are_for_your_current_profile">هذه الإعدادات لملف تعريفك الحالي</string>
|
||||
<string name="receipts_section_description_1">يمكن تجاوزها في إعدادات الاتصال والمجموعة.</string>
|
||||
<string name="network_option_tcp_connection_timeout">انتهت مهلة اتصال TCP</string>
|
||||
<string name="v4_5_private_filenames_descr">لحماية المنطقة الزمنية، تستخدم ملفات الصور / الصوت التوقيت العالمي المنسق (UTC).</string>
|
||||
|
||||
@@ -1206,6 +1206,7 @@
|
||||
<string name="stop_sharing_address">Stop sharing address?</string>
|
||||
<string name="stop_sharing">Stop sharing</string>
|
||||
<string name="auto_accept_contact">Auto-accept</string>
|
||||
<string name="group_invitations">Group invitations</string>
|
||||
<string name="sent_to_your_contact_after_connection">Sent to your contact after connection.</string>
|
||||
<string name="address_welcome_message">Welcome message</string>
|
||||
<string name="enter_welcome_message_optional">Enter welcome message… (optional)</string>
|
||||
@@ -1557,7 +1558,7 @@
|
||||
<string name="if_you_enter_passcode_data_removed">If you enter this passcode when opening the app, all app data will be irreversibly removed!</string>
|
||||
<string name="set_passcode">Set passcode</string>
|
||||
<string name="this_setting_is_for_your_current_profile">This setting is for your current profile</string>
|
||||
<string name="receipts_section_description">These settings are for your current profile</string>
|
||||
<string name="these_settings_are_for_your_current_profile">These settings are for your current profile</string>
|
||||
<string name="receipts_section_description_1">They can be overridden in contact and group settings.</string>
|
||||
<string name="receipts_section_contacts">Contacts</string>
|
||||
<string name="receipts_contacts_title_enable">Enable receipts?</string>
|
||||
@@ -1602,7 +1603,7 @@
|
||||
<string name="settings_section_title_chats">Chats</string>
|
||||
<string name="settings_section_title_files">Files</string>
|
||||
<string name="settings_section_title_delivery_receipts">Send delivery receipts to</string>
|
||||
<string name="settings_section_title_contact_requests_from_groups">Contact requests from groups</string>
|
||||
<string name="settings_section_title_contact_requests_from_groups">Contact requests in groups</string>
|
||||
<string name="settings_section_title_about">About</string>
|
||||
<string name="settings_section_title_contact">Contact</string>
|
||||
<string name="settings_section_title_support_project">Support the project</string>
|
||||
@@ -2736,9 +2737,10 @@
|
||||
<string name="v6_5_safe_web_links_descr">- opt-in to send link previews.\n- use SOCKS proxy if enabled.\n- prevent hyperlink phishing.\n- remove link tracking.</string>
|
||||
<string name="v6_5_non_profit_governance">Non-profit governance</string>
|
||||
<string name="v6_5_non_profit_governance_descr">To make SimpleX Network last.</string>
|
||||
<!-- <string name="v7_0_invest">Invest in SimpleX Chat</string> -->
|
||||
<!-- <string name="v7_0_invest_descr">Equity crowdfunding launched!</string> -->
|
||||
<!-- <string name="v7_0_invest_learn_more">Learn more on Wefunder</string> -->
|
||||
<string name="v7_0_invest" translatable="false">You can now invest in SimpleX Chat! 🚀</string>
|
||||
<string name="v7_0_invest_descr" translatable="false">Crowdfunding on Wefunder.</string>
|
||||
<string name="v7_0_crowdfunding" translatable="false">Crowdfunding on Wefunder</string>
|
||||
<string name="v7_0_invest_learn_more" translatable="false">Learn more on Wefunder</string>
|
||||
<string name="v7_0_simplex_names">SimpleX public names (BETA)</string>
|
||||
<string name="v7_0_simplex_names_descr">Public names for your channel or business.</string>
|
||||
<string name="v7_0_channels">Better channels 📢</string>
|
||||
|
||||
@@ -481,7 +481,7 @@
|
||||
<string name="enter_correct_passphrase">Въведи правилна парола.</string>
|
||||
<string name="feature_enabled_for_you">активирано за вас</string>
|
||||
<string name="enter_password_to_show">Въведи парола в търсенето</string>
|
||||
<string name="receipts_section_description">Тези настройки са за текущия ви профил</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Тези настройки са за текущия ви профил</string>
|
||||
<string name="receipts_section_description_1">Те могат да бъдат променени в настройките за всеки контакт и група.</string>
|
||||
<string name="settings_developer_tools">Инструменти за разработчици</string>
|
||||
<string name="receipts_contacts_disable_for_all">Деактивиране за всички</string>
|
||||
|
||||
@@ -1946,7 +1946,7 @@
|
||||
<string name="if_you_enter_passcode_data_removed">Si introduïu aquesta contrasenya en obrir l\'aplicació, totes les dades de l\'aplicació s\'eliminaran de manera irreversible.</string>
|
||||
<string name="if_you_enter_self_destruct_code">Si introduïu el vostre codi d\'autodestrucció mentre obriu l\'aplicació:</string>
|
||||
<string name="set_passcode">Estableix codi</string>
|
||||
<string name="receipts_section_description">Aquesta configuració és per al vostre perfil actual</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Aquesta configuració és per al vostre perfil actual</string>
|
||||
<string name="receipts_section_description_1">Es pot canviar a la configuració de contacte i grup.</string>
|
||||
<string name="privacy_media_blur_radius_off">No</string>
|
||||
<string name="settings_section_title_settings">Configuració</string>
|
||||
|
||||
@@ -1246,7 +1246,7 @@
|
||||
<string name="snd_conn_event_ratchet_sync_required">vyžadováno opětovné vyjednávání šifrování pro %s</string>
|
||||
<string name="receipts_contacts_override_disabled">Odesílání potvrzení o doručení je vypnuto pro %d kontakty.</string>
|
||||
<string name="sending_delivery_receipts_will_be_enabled_all_profiles">Odesílání potvrzení o doručení bude povoleno pro všechny kontakty ve všech viditelných profilech chatu.</string>
|
||||
<string name="receipts_section_description">Toto nastavení je pro váš aktuální profil</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Toto nastavení je pro váš aktuální profil</string>
|
||||
<string name="conn_event_ratchet_sync_allowed">opětovné vyjednávání šifrování povoleno</string>
|
||||
<string name="snd_conn_event_ratchet_sync_allowed">opětovné vyjednávání šifrování povoleno pro %s</string>
|
||||
<string name="conn_event_ratchet_sync_required">vyžadováno opětovné vyjednávání šifrování</string>
|
||||
|
||||
@@ -1364,7 +1364,7 @@
|
||||
<string name="v5_2_favourites_filter_descr">Nach ungelesenen und favorisierten Chats filtern.</string>
|
||||
<string name="sending_delivery_receipts_will_be_enabled_all_profiles">Das Senden von Empfangsbestätigungen an alle Kontakte in allen sichtbaren Chat-Profilen wird aktiviert.</string>
|
||||
<string name="receipts_contacts_override_disabled">Das Senden von Bestätigungen an %d Kontakte ist deaktiviert</string>
|
||||
<string name="receipts_section_description">Diese Einstellungen gelten für Ihr aktuelles Chat-Profil</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Diese Einstellungen gelten für Ihr aktuelles Chat-Profil</string>
|
||||
<string name="receipts_section_description_1">Sie können in den Kontakt- und Gruppeneinstellungen überschrieben werden.</string>
|
||||
<string name="receipts_section_contacts">Kontakte</string>
|
||||
<string name="receipts_contacts_title_disable">Bestätigungen deaktivieren\?</string>
|
||||
|
||||
@@ -1350,7 +1350,7 @@
|
||||
<string name="the_sender_will_not_be_notified">Ο αποστολέας ΔΕΝ θα ειδοποιηθεί.</string>
|
||||
<string name="smp_servers_per_user">Οι διακομιστές για τις νέες συνδέσεις του τρέχοντος προφίλ συνομιλίας σου</string>
|
||||
<string name="xftp_servers_per_user">Οι διακομιστές για τα νέα αρχεία του τρέχοντος προφίλ συνομιλίας σου</string>
|
||||
<string name="receipts_section_description">Αυτές οι ρυθμίσεις ισχύουν για το τρέχον προφίλ σου</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Αυτές οι ρυθμίσεις ισχύουν για το τρέχον προφίλ σου</string>
|
||||
<string name="the_text_you_pasted_is_not_a_link">Το κείμενο που επικόλλησες δεν είναι σύνδεσμος SimpleX.</string>
|
||||
<string name="migrate_from_device_uploaded_archive_will_be_removed">Το αρχείο της βάσης δεδομένων που μεταφορτώθηκε, θα διαγραφεί οριστικά από τους διακομιστές.</string>
|
||||
<string name="video_decoding_exception_desc">Το βίντεο δεν μπορεί να αποκωδικοποιηθεί. Δοκίμασε ένα άλλο βίντεο ή επικοινώνησε με τους προγραμματιστές.</string>
|
||||
|
||||
@@ -1283,7 +1283,7 @@
|
||||
<string name="receipts_contacts_override_disabled">El envío de confirmaciones está desactivado para %d contactos</string>
|
||||
<string name="receipts_contacts_override_enabled">El envío de confirmaciones está activado para %d contactos</string>
|
||||
<string name="send_receipts">Enviar confirmaciones</string>
|
||||
<string name="receipts_section_description">Esta configuración afecta a tu perfil actual</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Esta configuración afecta a tu perfil actual</string>
|
||||
<string name="enable_receipts_all">Activar</string>
|
||||
<string name="receipts_contacts_title_disable">¿Desactivar confirmaciones\?</string>
|
||||
<string name="receipts_contacts_title_enable">¿Activar confirmaciones\?</string>
|
||||
|
||||
@@ -756,7 +756,7 @@
|
||||
<string name="self_destruct_new_display_name">نام نمایشی جدید:</string>
|
||||
<string name="if_you_enter_self_destruct_code">اگر کد عبور خودتخریبی خود را زمان باز کردن برنامه وارد کنید:</string>
|
||||
<string name="all_app_data_will_be_cleared">تمام اطلاعات برنامه حذف میشود.</string>
|
||||
<string name="receipts_section_description">این تنظیمات برای پروفایل فعلی شما هستند</string>
|
||||
<string name="these_settings_are_for_your_current_profile">این تنظیمات برای پروفایل فعلی شما هستند</string>
|
||||
<string name="receipts_contacts_override_enabled">ارسال رسید برای %d مخاطب فعال است</string>
|
||||
<string name="receipts_contacts_disable_for_all">غیرفعال برای همه</string>
|
||||
<string name="receipts_groups_enable_for_all">فعال برای همه گروهها</string>
|
||||
|
||||
@@ -1272,7 +1272,7 @@
|
||||
<string name="sync_connection_force_confirm">Uudelleenneuvottele</string>
|
||||
<string name="sync_connection_force_question">Uudelleenneuvottele salaus\?</string>
|
||||
<string name="sync_connection_force_desc">Salaus toimii ja uutta salaussopimusta ei tarvita. Tämä voi johtaa yhteysvirheisiin!</string>
|
||||
<string name="receipts_section_description">Nämä asetukset koskevat nykyistä profiiliasi</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Nämä asetukset koskevat nykyistä profiiliasi</string>
|
||||
<string name="receipts_section_description_1">Ne voidaan ohittaa kontakti- ja ryhmäasetuksissa.</string>
|
||||
<string name="conn_event_ratchet_sync_ok">salaus ok</string>
|
||||
<string name="conn_event_ratchet_sync_allowed">salauksen uudelleenneuvottelu sallittu</string>
|
||||
|
||||
@@ -1280,7 +1280,7 @@
|
||||
<string name="rcv_conn_event_verification_code_reset">code de sécurité modifié</string>
|
||||
<string name="sending_delivery_receipts_will_be_enabled_all_profiles">L\'envoi d\'accusés de réception sera activé pour tous les contacts dans tous les profils de chat visibles.</string>
|
||||
<string name="receipts_section_description_1">Ils peuvent être modifiés dans les paramètres des contacts et des groupes.</string>
|
||||
<string name="receipts_section_description">Ces paramètres s\'appliquent à votre profil actuel</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Ces paramètres s\'appliquent à votre profil actuel</string>
|
||||
<string name="you_can_enable_delivery_receipts_later_alert">Vous pouvez les activer ultérieurement via les paramètres de Confidentialité et Sécurité de l\'application.</string>
|
||||
<string name="receipts_contacts_title_enable">Activer les accusés de réception \?</string>
|
||||
<string name="receipts_contacts_title_disable">Désactiver les accusés de réception \?</string>
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
<string name="settings_is_storing_in_clear_text">A jelmondat a beállításokban egyszerű szövegként van tárolva.</string>
|
||||
<string name="terminal_always_visible">Konzol megjelenítése új ablakban</string>
|
||||
<string name="alert_text_msg_bad_hash">Az előző üzenet kivonata különbözik.</string>
|
||||
<string name="receipts_section_description">Ezek a beállítások csak a jelenlegi csevegési profiljára vonatkoznak</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Ezek a beállítások csak a jelenlegi csevegési profiljára vonatkoznak</string>
|
||||
<string name="loading_remote_file_desc">Várjon, amíg a fájl betöltődik a társított hordozható eszközről</string>
|
||||
<string name="read_more_in_github_with_link"><![CDATA[További információkat a <font color="#0088ff">GitHub-tárolónkban</font> talál.]]></string>
|
||||
<string name="error_showing_content">Hiba történt a tartalom megjelenítésekor</string>
|
||||
|
||||
@@ -1206,7 +1206,7 @@
|
||||
<string name="receipts_groups_title_enable">Aktifkan tanda terima untuk grup?</string>
|
||||
<string name="empty_chat_profile_is_created">Profil obrolan kosong dengan nama yang disediakan dibuat, dan aplikasi terbuka seperti biasa.</string>
|
||||
<string name="if_you_enter_passcode_data_removed">Jika Anda memasukkan kode sandi saat membuka aplikasi, semua data aplikasi akan dihapus secara permanen!</string>
|
||||
<string name="receipts_section_description">Pengaturan ini untuk profil Anda saat ini</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Pengaturan ini untuk profil Anda saat ini</string>
|
||||
<string name="receipts_contacts_override_enabled">Kirim tanda terima diaktifkan untuk %d kontak</string>
|
||||
<string name="receipts_contacts_override_disabled">Kirim tanda terima dimatikan untuk %d kontak</string>
|
||||
<string name="error_loading_xftp_servers">Gagal memuat server XFTP</string>
|
||||
|
||||
@@ -1290,7 +1290,7 @@
|
||||
<string name="sending_delivery_receipts_will_be_enabled_all_profiles">L\'invio delle ricevute di consegna sarà attivo per tutti i contatti in tutti i profili di chat visibili.</string>
|
||||
<string name="receipts_contacts_override_disabled">L\'invio di ricevute è disattivato per %d contatti</string>
|
||||
<string name="sync_connection_force_desc">La crittografia funziona e il nuovo accordo sulla crittografia non è richiesto. Potrebbero verificarsi errori di connessione!</string>
|
||||
<string name="receipts_section_description">Queste impostazioni sono per il tuo profilo attuale</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Queste impostazioni sono per il tuo profilo attuale</string>
|
||||
<string name="receipts_section_description_1">Possono essere sovrascritte nelle impostazioni dei contatti e dei gruppi.</string>
|
||||
<string name="receipts_contacts_enable_for_all">Attiva per tutti</string>
|
||||
<string name="receipts_contacts_enable_keep_overrides">Attiva (mantieni sostituzioni)</string>
|
||||
|
||||
@@ -1291,7 +1291,7 @@
|
||||
\n- קבוצות קצת יותר טובות.
|
||||
\n- ועוד!</string>
|
||||
<string name="sending_delivery_receipts_will_be_enabled_all_profiles">שליחת קבלות שליחה תתאפשר עבור כל אנשי הקשר בכל פרופילי הצ\'אט הגלויים.</string>
|
||||
<string name="receipts_section_description">הגדרות אלו מיועדות לפרופיל הנוכחי שלך</string>
|
||||
<string name="these_settings_are_for_your_current_profile">הגדרות אלו מיועדות לפרופיל הנוכחי שלך</string>
|
||||
<string name="receipts_section_description_1">ניתן לעקוף אותם בהגדרות אנשי קשר וקבוצות.</string>
|
||||
<string name="receipts_contacts_override_disabled">שליחת קבלות מושבתת עבור %d אנשי קשר</string>
|
||||
<string name="receipts_contacts_override_enabled">שליחת קבלות מאופשרת עבור %d אנשי קשר</string>
|
||||
|
||||
@@ -1278,7 +1278,7 @@
|
||||
<string name="fix_connection_not_supported_by_group_member">グループメンバーによる修正はサポートされていません</string>
|
||||
<string name="receipts_section_contacts">連絡先</string>
|
||||
<string name="receipts_section_description_1">これらは連絡先とグループの設定が優先されます。</string>
|
||||
<string name="receipts_section_description">これらの設定は現在のプロファイル用です</string>
|
||||
<string name="these_settings_are_for_your_current_profile">これらの設定は現在のプロファイル用です</string>
|
||||
<string name="receipts_contacts_title_enable">配信通知を有効?</string>
|
||||
<string name="sender_at_ts">%s : %s</string>
|
||||
<string name="fix_connection">接続を修正</string>
|
||||
|
||||
@@ -1198,7 +1198,7 @@
|
||||
<string name="connect_plan_this_is_your_link_for_group_vName"><![CDATA[Tai yra jūsų nuoroda grupei <b>%1$s</b>!]]></string>
|
||||
<string name="to_connect_via_link_title">Kad prisijungti su nuoroda</string>
|
||||
<string name="this_link_is_not_a_valid_connection_link">Ši nuoroda nėra tinkama prisijungimo nuoroda!</string>
|
||||
<string name="receipts_section_description">Šie nustatymai yra jūsų dabartiniam profiliui</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Šie nustatymai yra jūsų dabartiniam profiliui</string>
|
||||
<string name="settings_is_storing_in_clear_text">Slaptafrazė saugoma nustatymuose kaip paprastas tekstas.</string>
|
||||
<string name="group_invitation_tap_to_join_incognito">Bakstelėkite, kad prisijungti kaip inkognito</string>
|
||||
<string name="send_receipts_disabled_alert_msg">Ši grupė turi daugiau nei %1$d narių, pristatymo kvitai nėra siunčiami.</string>
|
||||
|
||||
@@ -1607,7 +1607,7 @@
|
||||
<string name="onboarding_network_operators_continue">Ievada tīkla operatori turpināt</string>
|
||||
<string name="incoming_video_call">Ienākošais video zvans</string>
|
||||
<string name="incoming_audio_call">Ienākošais audio zvans</string>
|
||||
<string name="receipts_section_description">Čeki</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Čeki</string>
|
||||
<string name="receipts_section_description_1">Čeku apraksts 1</string>
|
||||
<string name="receipts_section_contacts">Čeku kontakti</string>
|
||||
<string name="receipts_contacts_title_enable">Čeku kontakti iespējot</string>
|
||||
|
||||
@@ -1278,7 +1278,7 @@
|
||||
<string name="receipts_contacts_title_disable">Ontvangst bevestiging uitschakelen\?</string>
|
||||
<string name="receipts_contacts_title_enable">Ontvangst bevestiging inschakelen\?</string>
|
||||
<string name="receipts_contacts_override_enabled">Het verzenden van ontvangst bevestiging is ingeschakeld voor %d-contactpersonen</string>
|
||||
<string name="receipts_section_description">Deze instellingen gelden voor uw huidige profiel</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Deze instellingen gelden voor uw huidige profiel</string>
|
||||
<string name="receipts_contacts_disable_keep_overrides">Uitschakelen (overschrijvingen behouden)</string>
|
||||
<string name="receipts_contacts_enable_for_all">Inschakelen voor iedereen</string>
|
||||
<string name="receipts_contacts_enable_keep_overrides">Inschakelen (overschrijvingen behouden)</string>
|
||||
|
||||
@@ -1287,7 +1287,7 @@
|
||||
<string name="sync_connection_force_question">Renegocjować szyfrowanie\?</string>
|
||||
<string name="receipts_contacts_override_enabled">Wysyłanie potwierdzeń jest włączone dla %d kontaktów</string>
|
||||
<string name="sync_connection_force_desc">Szyfrowanie działa, a nowe uzgodnienie szyfrowania nie jest wymagane. Może to spowodować błędy w połączeniu!</string>
|
||||
<string name="receipts_section_description">Te ustawienia dotyczą Twojego bieżącego profilu</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Te ustawienia dotyczą Twojego bieżącego profilu</string>
|
||||
<string name="receipts_section_description_1">Można je nadpisać w ustawieniach kontaktu i grupy.</string>
|
||||
<string name="conn_event_ratchet_sync_ok">szyfrowanie ok</string>
|
||||
<string name="conn_event_ratchet_sync_allowed">renegocjacja szyfrowania dozwolona</string>
|
||||
|
||||
@@ -1520,7 +1520,7 @@
|
||||
<string name="member_info_member_blocked">bloqueado</string>
|
||||
<string name="code_you_scanned_is_not_simplex_link_qr_code">O código que você escaneou não é um QR code SimpleX.</string>
|
||||
<string name="unable_to_open_browser_desc">O navegador padrão é necessário para chamadas. Configure o navegador padrão no sistema e compartilhe mais informações com os desenvolvedores.</string>
|
||||
<string name="receipts_section_description">Essas configurações são para o seu perfil atual</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Essas configurações são para o seu perfil atual</string>
|
||||
<string name="video_decoding_exception_desc">O vídeo não pode ser decodificado. Por favor, tente com um vídeo diferente ou contate os desenvolvedores.</string>
|
||||
<string name="connect_plan_this_is_your_own_one_time_link">Este é o seu próprio link de uso único!</string>
|
||||
<string name="remote_ctrl_error_timeout">Tempo limite atingido durante a conexão com o desktop</string>
|
||||
|
||||
@@ -1999,7 +1999,7 @@
|
||||
<string name="call_desktop_permission_denied_title">Pentru a efectua apeluri, permiteți utilizarea microfonului. Încheiați apelul și încercați să sunați din nou.</string>
|
||||
<string name="onboarding_network_operators_cant_see_who_talks_to_whom">Când sunt activați mai mulți operatori, niciunul dintre ei nu are metadate pentru a afla cine comunică cu cine.</string>
|
||||
<string name="icon_descr_video_on">Video pornit</string>
|
||||
<string name="receipts_section_description">Aceste setări sunt pentru profilul tău actual</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Aceste setări sunt pentru profilul tău actual</string>
|
||||
<string name="privacy_chat_list_open_links_yes">Da</string>
|
||||
<string name="settings_message_shape_tail">Coada</string>
|
||||
<string name="settings_section_title_use_from_desktop">Utilizare de pe desktop</string>
|
||||
|
||||
@@ -1329,7 +1329,7 @@
|
||||
<string name="receipts_contacts_override_enabled">Отправка отчётов о доставке включена для %d контактов</string>
|
||||
<string name="sending_delivery_receipts_will_be_enabled_all_profiles">Отправка отчётов о доставке будет включена для всех контактов во всех видимых профилях чата.</string>
|
||||
<string name="this_setting_is_for_your_current_profile">Установка для Вашего активного профиля</string>
|
||||
<string name="receipts_section_description">Установки для Вашего активного профиля</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Установки для Вашего активного профиля</string>
|
||||
<string name="receipts_contacts_override_disabled">Отправка отчётов о доставке выключена для %d контактов</string>
|
||||
<string name="sync_connection_force_desc">Шифрование работает, и новое соглашение не требуется. Это может привести к ошибкам соединения!</string>
|
||||
<string name="v5_2_message_delivery_receipts_descr">Вторая галочка - знать, что доставлено! ✅</string>
|
||||
|
||||
@@ -1280,7 +1280,7 @@
|
||||
<string name="in_developing_title">เร็วๆ นี้!</string>
|
||||
<string name="snd_conn_event_ratchet_sync_allowed">อนุญาตให้มีการเจรจา encryption อีกครั้งสําหรับ %s</string>
|
||||
<string name="recipient_colon_delivery_status">%s: %s</string>
|
||||
<string name="receipts_section_description">การตั้งค่าเหล่านี้ใช้สำหรับโปรไฟล์ปัจจุบันของคุณ</string>
|
||||
<string name="these_settings_are_for_your_current_profile">การตั้งค่าเหล่านี้ใช้สำหรับโปรไฟล์ปัจจุบันของคุณ</string>
|
||||
<string name="receipts_section_description_1">สามารถลบล้างได้ในการตั้งค่าผู้ติดต่อและกลุ่ม</string>
|
||||
<string name="receipts_contacts_disable_keep_overrides">ปิดใช้งาน (เก็บการแทนที่)</string>
|
||||
<string name="receipts_contacts_enable_keep_overrides">เปิดใช้งาน (เก็บการแทนที่)</string>
|
||||
|
||||
@@ -1204,7 +1204,7 @@
|
||||
<string name="lock_not_enabled">SimpleX Kilit aktif değil!</string>
|
||||
<string name="chat_lock">SimpleX Kilit</string>
|
||||
<string name="connect_via_member_address_alert_title">Doğrudan bağlanılsın mı?</string>
|
||||
<string name="receipts_section_description">Bu ayarlar mevcut profiliniz içindir</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Bu ayarlar mevcut profiliniz içindir</string>
|
||||
<string name="smp_servers_test_failed">Sunucu testi başarısız!</string>
|
||||
<string name="verify_connection">Bağlantıyı onayla</string>
|
||||
<string name="add_contact_or_create_group">Yeni sohbet başlat</string>
|
||||
|
||||
@@ -1271,7 +1271,7 @@
|
||||
<string name="abort_switch_receiving_address_confirm">Скасувати</string>
|
||||
<string name="choose_file_title">Виберіть файл</string>
|
||||
<string name="receipts_section_contacts">Контакти</string>
|
||||
<string name="receipts_section_description">Ці налаштування стосуються вашого поточного профілю</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Ці налаштування стосуються вашого поточного профілю</string>
|
||||
<string name="receipts_contacts_title_disable">Вимкнути повідомлення про доставку?</string>
|
||||
<string name="receipts_contacts_title_enable">Увімкнути повідомлення про доставку?</string>
|
||||
<string name="conn_event_ratchet_sync_allowed">можлива перезапис шифрування</string>
|
||||
|
||||
@@ -1972,7 +1972,7 @@
|
||||
<string name="the_text_you_pasted_is_not_a_link">Văn bản bạn vừa dán không phải là một đường dẫn SimpleX.</string>
|
||||
<string name="member_role_will_be_changed_with_invitation">Chức vụ sẽ được đổi thành %s. Thành viên sẽ nhận được một lời mời mới.</string>
|
||||
<string name="passphrase_will_be_saved_in_settings">Mật khẩu sẽ được lưu trữ trong cài đặt dưới dạng thuần văn bản sau khi bản đổi nó hoặc khởi động lại ứng dụng.</string>
|
||||
<string name="receipts_section_description">Các cài đặt này là cho hồ sơ trò chuyện hiện tại của bạn</string>
|
||||
<string name="these_settings_are_for_your_current_profile">Các cài đặt này là cho hồ sơ trò chuyện hiện tại của bạn</string>
|
||||
<string name="member_role_will_be_changed_with_notification">Chức vụ sẽ được đổi thành %s. Tất cả mọi người trong nhóm sẽ được thông báo.</string>
|
||||
<string name="migrate_from_device_uploaded_archive_will_be_removed">Bản lưu trữ cơ sở dữ liệu đã được tải lên sẽ bị xóa vĩnh viễn khỏi các máy chủ.</string>
|
||||
<string name="delete_chat_profile_action_cannot_be_undone_warning">Việc này không thể được hoàn tác - hồ sơ, các liên hệ, tin nhắn và tệp của bạn sẽ biến mất mà không thể khôi phục.</string>
|
||||
|
||||
@@ -1344,7 +1344,7 @@
|
||||
<string name="connect_via_member_address_alert_desc">连接请求将发送给该群成员。</string>
|
||||
<string name="settings_is_storing_in_clear_text">密码以明文形式存储在设置中。</string>
|
||||
<string name="error_synchronizing_connection">同步连接时出错</string>
|
||||
<string name="receipts_section_description">这些设置适用于你当前的个人资料</string>
|
||||
<string name="these_settings_are_for_your_current_profile">这些设置适用于你当前的个人资料</string>
|
||||
<string name="snd_conn_event_ratchet_sync_allowed">允许为 %s 重新协商加密</string>
|
||||
<string name="receipts_contacts_enable_for_all">为所有人启用</string>
|
||||
<string name="conn_event_ratchet_sync_required">需要重新协商加密</string>
|
||||
|
||||
@@ -2444,7 +2444,7 @@
|
||||
<string name="without_tor_or_vpn_ip_address_will_be_visible_to_file_servers">未使用 Tor 或 VPN 時,你的 IP 地址會對檔案伺服器可見。</string>
|
||||
<string name="sanitize_links_toggle">移除連結追蹤</string>
|
||||
<string name="this_setting_is_for_your_current_profile">此設定適用於你目前的個人檔案</string>
|
||||
<string name="receipts_section_description">這些設定適用於你目前的個人檔案</string>
|
||||
<string name="these_settings_are_for_your_current_profile">這些設定適用於你目前的個人檔案</string>
|
||||
<string name="receipts_section_description_1">可在聯絡人和群組設定中覆寫這些設定。</string>
|
||||
<string name="receipts_contacts_override_enabled">已為 %d 個聯絡人啟用送達回條</string>
|
||||
<string name="receipts_contacts_override_disabled">已為 %d 個聯絡人停用送達回條</string>
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<rect width="24" height="24" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 175 B |
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<rect width="24" height="24" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 175 B |
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<rect width="24" height="24" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 175 B |
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<rect width="24" height="24" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 175 B |
+13
-3
@@ -23,6 +23,7 @@ import javax.swing.SwingUtilities
|
||||
internal class SkiaBitmapVideoSurface : VideoSurface(VideoSurfaceAdapters.getVideoSurfaceAdapter()) {
|
||||
|
||||
private val videoSurface = SkiaBitmapVideoSurface()
|
||||
@Volatile private var mediaPlayer: MediaPlayer? = null
|
||||
private lateinit var imageInfo: ImageInfo
|
||||
private lateinit var frameBytes: ByteArray
|
||||
private val skiaBitmap: Bitmap = Bitmap()
|
||||
@@ -31,6 +32,7 @@ internal class SkiaBitmapVideoSurface : VideoSurface(VideoSurfaceAdapters.getVid
|
||||
val bitmap: State<ImageBitmap?> = composeBitmap
|
||||
|
||||
override fun attach(mediaPlayer: MediaPlayer) {
|
||||
this.mediaPlayer = mediaPlayer
|
||||
videoSurface.attach(mediaPlayer)
|
||||
}
|
||||
|
||||
@@ -39,9 +41,17 @@ internal class SkiaBitmapVideoSurface : VideoSurface(VideoSurfaceAdapters.getVid
|
||||
private var sourceHeight: Int = 0
|
||||
|
||||
override fun getBufferFormat(sourceWidth: Int, sourceHeight: Int): BufferFormat {
|
||||
this.sourceWidth = sourceWidth
|
||||
this.sourceHeight = sourceHeight
|
||||
return RV32BufferFormat(sourceWidth, sourceHeight)
|
||||
// libvlc passes the size the decoder padded the picture to, not the size of the picture (dav1d
|
||||
// pads to a multiple of 128, so 1920x1080 arrives as 1920x1152), and vlc stretches the picture to
|
||||
// fill whatever size is returned. Ask for the size of the track being played instead. The format
|
||||
// is negotiated more than once, and vlc has not selected the track yet on the first calls
|
||||
val player = mediaPlayer
|
||||
val tracks = player?.media()?.info()?.videoTracks()
|
||||
val playingTrack = player?.video()?.track()
|
||||
val track = tracks?.firstOrNull { it.id() == playingTrack } ?: tracks?.singleOrNull()
|
||||
this.sourceWidth = track?.width()?.takeIf { it > 0 } ?: sourceWidth
|
||||
this.sourceHeight = track?.height()?.takeIf { it > 0 } ?: sourceHeight
|
||||
return RV32BufferFormat(this.sourceWidth, this.sourceHeight)
|
||||
}
|
||||
|
||||
override fun allocatedBuffers(buffers: Array<ByteBuffer>) {
|
||||
|
||||
@@ -345,7 +345,7 @@ class ArchiveConfig(
|
||||
### Import Flow
|
||||
|
||||
1. User selects an archive file.
|
||||
2. UI copies it to a temp location and constructs an `ArchiveConfig`.
|
||||
2. UI copies it into `databaseExportDir` and constructs an `ArchiveConfig`. The destination is confined to that folder: `getFileName` returns a bare file name on every platform, and `saveArchiveFromURI` checks the canonical destination before copying.
|
||||
3. Calls `apiImportArchive(config)` which sends `CC.ApiImportArchive` to the Haskell core.
|
||||
4. The core extracts and replaces both databases.
|
||||
5. Returns `CR.ArchiveImported` with a list of `ArchiveError` (non-fatal issues during import).
|
||||
|
||||
Reference in New Issue
Block a user