mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Implement proper iOS structure
This commit is contained in:
Generated
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="XcodeMetaData" PROJECT_DIR="$PROJECT_DIR$/iosApp" PROJECT_FILE="$PROJECT_DIR$/iosApp/iosApp.xcodeproj" />
|
||||
</project>
|
||||
@@ -129,7 +129,7 @@ dependencies {
|
||||
}
|
||||
|
||||
compose.resources {
|
||||
publicResClass = false
|
||||
publicResClass = true
|
||||
packageOfResClass = "ru.fromchat"
|
||||
generateResClass = auto
|
||||
}
|
||||
|
||||
@@ -15,3 +15,4 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,3 +14,4 @@ object AndroidKtorLogger : Logger {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,3 +19,4 @@ actual object Logger {
|
||||
Log.e(tag, message, throwable)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">FromChat</string>
|
||||
<string name="register">Регистрация</string>
|
||||
<string name="register_d">Создайте новый аккаунт</string>
|
||||
<string name="register_button">Зарегистрироваться</string>
|
||||
<string name="username">Имя пользователя</string>
|
||||
<string name="password">Пароль</string>
|
||||
<string name="confirm_password">Подтвердите пароль</string>
|
||||
<string name="login">Войти</string>
|
||||
<string name="login_d">Войдите в аккаунт</string>
|
||||
<string name="welcome">Добро пожаловать!</string>
|
||||
<string name="fill_all_fields">Пожалуйста, заполните все поля</string>
|
||||
<string name="username_length_error">Имя пользователя должно быть от 3 до 20 символов</string>
|
||||
<string name="password_length_error">Пароль должен быть от 5 до 50 символов</string>
|
||||
<string name="passwords_dont_match">Пароли не совпадают</string>
|
||||
<string name="chats">Чаты</string>
|
||||
<string name="contacts">Контакты</string>
|
||||
<string name="dms">ЛС</string>
|
||||
<string name="coming_soon">Скоро будет...</string>
|
||||
<string name="public_chat">Общий чат</string>
|
||||
<string name="chat_last_mesaage">Вы: Последние сообщение</string>
|
||||
<string name="message_placeholder">Введите сообщение</string>
|
||||
<string name="app_name" translatable="false">FromChat</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,44 @@
|
||||
<resources>
|
||||
<!-- App -->
|
||||
<string name="app_name">FromChat</string>
|
||||
|
||||
<!-- Navigation -->
|
||||
<string name="back">Back</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="about">About app</string>
|
||||
|
||||
<!-- Authentication -->
|
||||
<string name="welcome">Welcome!</string>
|
||||
<string name="login">Sign In</string>
|
||||
<string name="login_d">Sign in to your account</string>
|
||||
<string name="register">Registration</string>
|
||||
<string name="register_d">Create a new account</string>
|
||||
<string name="register_button">Register</string>
|
||||
<string name="username">Username</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="confirm_password">Confirm Password</string>
|
||||
<string name="display_name">Display Name</string>
|
||||
<string name="display_name_error">Display name must be between 1 and 64 characters</string>
|
||||
|
||||
<!-- Validation Errors -->
|
||||
<string name="fill_all_fields">Please fill in all fields</string>
|
||||
<string name="username_length_error">Username must be between 3 and 20 characters</string>
|
||||
<string name="password_length_error">Password must be between 5 and 50 characters</string>
|
||||
<string name="passwords_dont_match">Passwords do not match</string>
|
||||
|
||||
<!-- Main Screen -->
|
||||
<string name="chats">Chats</string>
|
||||
<string name="contacts">Contacts</string>
|
||||
<string name="dms">Direct Messages</string>
|
||||
<string name="coming_soon">Coming soon...</string>
|
||||
<string name="public_chat">General Chat</string>
|
||||
<string name="chat_last_mesaage">You: Last message</string>
|
||||
<string name="message_placeholder">Type a message...</string>
|
||||
|
||||
<!-- Error Messages -->
|
||||
<string name="error_unexpected">Unexpected error</string>
|
||||
<string name="error_invalid_credentials">Invalid username or password</string>
|
||||
<string name="error_connection">Connection error</string>
|
||||
<string name="error_unknown">An unknown error occurred</string>
|
||||
</resources>
|
||||
@@ -5,6 +5,7 @@ import io.ktor.client.call.body
|
||||
import io.ktor.client.plugins.ClientRequestException
|
||||
|
||||
suspend inline fun <Response> apiRequest(
|
||||
unexpectedError: String,
|
||||
onError: (String, Exception) -> Unit = { _, _ -> },
|
||||
onSuccess: (Response) -> Unit = {},
|
||||
request: suspend () -> Response
|
||||
@@ -17,7 +18,7 @@ suspend inline fun <Response> apiRequest(
|
||||
val message = if (e.response.status.value in arrayOf(401, 403)) {
|
||||
e.response.body<ErrorResponse>().detail
|
||||
} else {
|
||||
"Unexpected error"
|
||||
unexpectedError
|
||||
}
|
||||
|
||||
Logger.e("API", "API request failed: $message", e)
|
||||
@@ -26,7 +27,7 @@ suspend inline fun <Response> apiRequest(
|
||||
return Result.failure(e)
|
||||
} catch (e: Exception) {
|
||||
Logger.e("API", "API request failed: ${e.message}", e)
|
||||
onError("Unexpected error", e)
|
||||
onError(unexpectedError, e)
|
||||
return Result.failure(e)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package ru.fromchat.api
|
||||
|
||||
import ru.fromchat.core.Logger
|
||||
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
|
||||
import io.ktor.client.plugins.websocket.webSocket
|
||||
import io.ktor.client.request.url
|
||||
@@ -10,6 +9,7 @@ import io.ktor.websocket.readText
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -20,6 +20,8 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import kotlinx.serialization.json.Json
|
||||
import ru.fromchat.WS_API_HOST
|
||||
import ru.fromchat.core.Logger
|
||||
import kotlin.concurrent.Volatile
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
object WebSocketManager {
|
||||
@@ -41,7 +43,8 @@ object WebSocketManager {
|
||||
}
|
||||
|
||||
// State
|
||||
@Volatile private var connecting: Boolean = false
|
||||
@Volatile
|
||||
private var connecting: Boolean = false
|
||||
@Volatile private var session: DefaultClientWebSocketSession? = null
|
||||
|
||||
fun connect() {
|
||||
|
||||
@@ -6,3 +6,4 @@ expect object Logger {
|
||||
fun w(tag: String, message: String, throwable: Throwable? = null)
|
||||
fun e(tag: String, message: String, throwable: Throwable? = null)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,10 @@ fun App() {
|
||||
) {
|
||||
composable("login") {
|
||||
LoginScreen(
|
||||
onLoginSuccess = { navController.navigate("chat") },
|
||||
onLoginSuccess = {
|
||||
navController.navigate("chat")
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavigateToRegister = { navController.navigate("register") }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,22 +24,32 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pr0gramm3r101.utils.crypto.deriveAuthSecret
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.fromchat.R
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.fromchat.Res
|
||||
import ru.fromchat.api.ApiClient
|
||||
import ru.fromchat.api.LoginRequest
|
||||
import ru.fromchat.api.apiRequest
|
||||
import ru.fromchat.error_unexpected
|
||||
import ru.fromchat.fill_all_fields
|
||||
import ru.fromchat.login
|
||||
import ru.fromchat.login_d
|
||||
import ru.fromchat.password
|
||||
import ru.fromchat.register_button
|
||||
import ru.fromchat.ui.RowHeader
|
||||
import com.pr0gramm3r101.utils.crypto.deriveAuthSecret
|
||||
import ru.fromchat.username
|
||||
import ru.fromchat.welcome
|
||||
|
||||
@Composable
|
||||
fun LoginScreen(
|
||||
onLoginSuccess: () -> Unit,
|
||||
onNavigateToRegister: () -> Unit
|
||||
) {
|
||||
val errorUnexpected = stringResource(Res.string.error_unexpected)
|
||||
|
||||
Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->
|
||||
var username by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
@@ -63,8 +73,8 @@ fun LoginScreen(
|
||||
) {
|
||||
RowHeader(
|
||||
icon = Icons.AutoMirrored.Filled.Login,
|
||||
title = stringResource(R.string.welcome),
|
||||
subtitle = stringResource(R.string.login_d)
|
||||
title = stringResource(Res.string.welcome),
|
||||
subtitle = stringResource(Res.string.login_d)
|
||||
)
|
||||
|
||||
if (alert != null) {
|
||||
@@ -74,25 +84,25 @@ fun LoginScreen(
|
||||
OutlinedTextField(
|
||||
value = username,
|
||||
onValueChange = { username = it },
|
||||
label = { Text(stringResource(R.string.username)) },
|
||||
label = { Text(stringResource(Res.string.username)) },
|
||||
singleLine = true
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
label = { Text(stringResource(R.string.password)) },
|
||||
label = { Text(stringResource(Res.string.password)) },
|
||||
singleLine = true,
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
val alert_error_filling = stringResource(R.string.fill_all_fields)
|
||||
val alertErrorFilling = stringResource(Res.string.fill_all_fields)
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
if (username.isBlank() || password.isBlank()) {
|
||||
alert = alert_error_filling
|
||||
alert = alertErrorFilling
|
||||
return@Button
|
||||
}
|
||||
|
||||
@@ -101,6 +111,7 @@ fun LoginScreen(
|
||||
val derived = deriveAuthSecret(username.trim(), password.trim())
|
||||
|
||||
apiRequest(
|
||||
unexpectedError = errorUnexpected,
|
||||
onError = { message, _ ->
|
||||
alert = message
|
||||
},
|
||||
@@ -111,11 +122,11 @@ fun LoginScreen(
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.login))
|
||||
Text(stringResource(Res.string.login))
|
||||
}
|
||||
|
||||
Button(onClick = onNavigateToRegister) {
|
||||
Text(stringResource(R.string.register_button))
|
||||
Text(stringResource(Res.string.register_button))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,22 +27,38 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pr0gramm3r101.utils.crypto.deriveAuthSecret
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.fromchat.R
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.fromchat.Res
|
||||
import ru.fromchat.api.ApiClient
|
||||
import ru.fromchat.api.RegisterRequest
|
||||
import ru.fromchat.api.apiRequest
|
||||
import ru.fromchat.back
|
||||
import ru.fromchat.confirm_password
|
||||
import ru.fromchat.display_name
|
||||
import ru.fromchat.display_name_error
|
||||
import ru.fromchat.error_unexpected
|
||||
import ru.fromchat.fill_all_fields
|
||||
import ru.fromchat.password
|
||||
import ru.fromchat.password_length_error
|
||||
import ru.fromchat.passwords_dont_match
|
||||
import ru.fromchat.register
|
||||
import ru.fromchat.register_button
|
||||
import ru.fromchat.register_d
|
||||
import ru.fromchat.ui.LocalNavController
|
||||
import ru.fromchat.ui.RowHeader
|
||||
import com.pr0gramm3r101.utils.crypto.deriveAuthSecret
|
||||
import ru.fromchat.username
|
||||
import ru.fromchat.username_length_error
|
||||
|
||||
@Composable
|
||||
fun RegisterScreen(
|
||||
onRegistered: () -> Unit
|
||||
) {
|
||||
val errorUnexpected = stringResource(Res.string.error_unexpected)
|
||||
|
||||
Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->
|
||||
var username by remember { mutableStateOf("") }
|
||||
var displayName by remember { mutableStateOf("") }
|
||||
@@ -69,8 +85,8 @@ fun RegisterScreen(
|
||||
) {
|
||||
RowHeader(
|
||||
icon = Icons.Filled.PersonAdd,
|
||||
title = stringResource(R.string.register),
|
||||
subtitle = stringResource(R.string.register_d)
|
||||
title = stringResource(Res.string.register),
|
||||
subtitle = stringResource(Res.string.register_d)
|
||||
)
|
||||
|
||||
if (alert != null) {
|
||||
@@ -80,21 +96,21 @@ fun RegisterScreen(
|
||||
OutlinedTextField(
|
||||
value = username,
|
||||
onValueChange = { username = it },
|
||||
label = { Text(stringResource(R.string.username)) },
|
||||
label = { Text(stringResource(Res.string.username)) },
|
||||
singleLine = true
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = displayName,
|
||||
onValueChange = { displayName = it },
|
||||
label = { Text("Display Name") },
|
||||
label = { Text(stringResource(Res.string.display_name)) },
|
||||
singleLine = true
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
label = { Text(stringResource(R.string.password)) },
|
||||
label = { Text(stringResource(Res.string.password)) },
|
||||
singleLine = true,
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
@@ -102,23 +118,24 @@ fun RegisterScreen(
|
||||
OutlinedTextField(
|
||||
value = confirmPassword,
|
||||
onValueChange = { confirmPassword = it },
|
||||
label = { Text(stringResource(R.string.confirm_password)) },
|
||||
label = { Text(stringResource(Res.string.confirm_password)) },
|
||||
singleLine = true,
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
val alert_error_filling = stringResource(R.string.fill_all_fields)
|
||||
val alert_error_password_little = stringResource(R.string.password_length_error)
|
||||
val alert_error_name_little = stringResource(R.string.username_length_error)
|
||||
val alert_error_password_confrim = stringResource(R.string.passwords_dont_match)
|
||||
val alertErrorFilling = stringResource(Res.string.fill_all_fields)
|
||||
val alertErrorPasswordLittle = stringResource(Res.string.password_length_error)
|
||||
val alertErrorNameLittle = stringResource(Res.string.username_length_error)
|
||||
val alertErrorPasswordConfrim = stringResource(Res.string.passwords_dont_match)
|
||||
val alertErrorDisplayName = stringResource(Res.string.display_name_error)
|
||||
|
||||
IconButton(
|
||||
onClick = { navController.navigateUp() }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back"
|
||||
contentDescription = stringResource(Res.string.back)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -126,23 +143,23 @@ fun RegisterScreen(
|
||||
onClick = {
|
||||
// Checks
|
||||
if (username.isBlank() || displayName.isBlank() || password.isBlank() || confirmPassword.isBlank()) {
|
||||
alert = alert_error_filling
|
||||
alert = alertErrorFilling
|
||||
return@Button
|
||||
}
|
||||
if (password != confirmPassword) {
|
||||
alert = alert_error_password_confrim
|
||||
alert = alertErrorPasswordConfrim
|
||||
return@Button
|
||||
}
|
||||
if (username.length !in 3..20) {
|
||||
alert = alert_error_name_little
|
||||
alert = alertErrorNameLittle
|
||||
return@Button
|
||||
}
|
||||
if (displayName.isBlank() || displayName.length > 64) {
|
||||
alert = "Display name must be between 1 and 64 characters"
|
||||
alert = alertErrorDisplayName
|
||||
return@Button
|
||||
}
|
||||
if (password.length !in 5..50) {
|
||||
alert = alert_error_password_little
|
||||
alert = alertErrorPasswordLittle
|
||||
return@Button
|
||||
}
|
||||
|
||||
@@ -151,6 +168,7 @@ fun RegisterScreen(
|
||||
val derived = deriveAuthSecret(username.trim(), password)
|
||||
|
||||
apiRequest(
|
||||
unexpectedError = errorUnexpected,
|
||||
onError = { message, _ ->
|
||||
alert = message
|
||||
},
|
||||
@@ -168,7 +186,7 @@ fun RegisterScreen(
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.register_button))
|
||||
Text(stringResource(Res.string.register_button))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,12 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import ru.fromchat.R
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.fromchat.Res
|
||||
import ru.fromchat.chat_last_mesaage
|
||||
import ru.fromchat.chats
|
||||
import ru.fromchat.public_chat
|
||||
import ru.fromchat.ui.LocalNavController
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -28,7 +31,7 @@ fun ChatsTab() {
|
||||
MediumTopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.chats),
|
||||
text = stringResource(Res.string.chats),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
@@ -40,8 +43,8 @@ fun ChatsTab() {
|
||||
LazyColumn(contentPadding = innerPadding) {
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.public_chat)) },
|
||||
supportingContent = { Text(stringResource(R.string.chat_last_mesaage)) },
|
||||
headlineContent = { Text(stringResource(Res.string.public_chat)) },
|
||||
supportingContent = { Text(stringResource(Res.string.chat_last_mesaage)) },
|
||||
modifier = Modifier.clickable {
|
||||
navController.navigate("chats/publicChat")
|
||||
}
|
||||
|
||||
@@ -22,10 +22,15 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import ru.fromchat.R
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.fromchat.Res
|
||||
import ru.fromchat.chats
|
||||
import ru.fromchat.coming_soon
|
||||
import ru.fromchat.contacts
|
||||
import ru.fromchat.dms
|
||||
import ru.fromchat.utils.exclude
|
||||
|
||||
@Suppress("AssignedValueIsNeverRead")
|
||||
@Composable
|
||||
fun MainScreen() {
|
||||
var selectedTab by remember { mutableStateOf("chats") }
|
||||
@@ -36,19 +41,19 @@ fun MainScreen() {
|
||||
NavigationBarItem(
|
||||
selected = selectedTab == "chats",
|
||||
onClick = { selectedTab = "chats" },
|
||||
label = { Text(stringResource(R.string.chats)) },
|
||||
label = { Text(stringResource(Res.string.chats)) },
|
||||
icon = { Icon(Icons.AutoMirrored.Filled.Chat, contentDescription = null) }
|
||||
)
|
||||
NavigationBarItem(
|
||||
selected = selectedTab == "contacts",
|
||||
onClick = { selectedTab = "contacts" },
|
||||
label = { Text(stringResource(R.string.contacts)) },
|
||||
label = { Text(stringResource(Res.string.contacts)) },
|
||||
icon = { Icon(Icons.Filled.Contacts, contentDescription = null) }
|
||||
)
|
||||
NavigationBarItem(
|
||||
selected = selectedTab == "dms",
|
||||
onClick = { selectedTab = "dms" },
|
||||
label = { Text(stringResource(R.string.dms)) },
|
||||
label = { Text(stringResource(Res.string.dms)) },
|
||||
icon = { Icon(Icons.Filled.Mail, contentDescription = null) }
|
||||
)
|
||||
}
|
||||
@@ -64,10 +69,10 @@ fun MainScreen() {
|
||||
when (selectedTab) {
|
||||
"chats" -> ChatsTab()
|
||||
"contacts" -> {
|
||||
Text(stringResource(R.string.coming_soon))
|
||||
Text(stringResource(Res.string.coming_soon))
|
||||
}
|
||||
"dms" -> {
|
||||
Text(stringResource(R.string.coming_soon))
|
||||
Text(stringResource(Res.string.coming_soon))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.exclude
|
||||
import androidx.compose.foundation.layout.only
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun WindowInsets.exclude(sides: WindowInsetsSides) = exclude(this.only(sides))
|
||||
@@ -4,3 +4,4 @@ import ru.fromchat.ui.App
|
||||
import androidx.compose.ui.window.ComposeUIViewController
|
||||
|
||||
fun MainViewController() = ComposeUIViewController { App() }
|
||||
|
||||
|
||||
@@ -19,3 +19,4 @@ actual object Logger {
|
||||
NSLog("ERROR: [%s] %s %s", tag, message, throwable?.message ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
TEAM_ID=82MWK23YNM
|
||||
BUNDLE_ID=ru.fromchat.app
|
||||
APP_NAME=FromChat
|
||||
@@ -0,0 +1,407 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 56;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
|
||||
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
|
||||
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
|
||||
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
||||
7555FF7B242A565900829871 /* FromChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FromChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
B92378962B6B1156000C7307 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
058557D7273AAEEB004C7B11 /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
42799AB246E5F90AF97AA0EF /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7555FF72242A565900829871 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AB1DB47929225F7C00F7AF9C /* Configuration */,
|
||||
7555FF7D242A565900829871 /* iosApp */,
|
||||
7555FF7C242A565900829871 /* Products */,
|
||||
42799AB246E5F90AF97AA0EF /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7555FF7C242A565900829871 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7555FF7B242A565900829871 /* FromChat.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7555FF7D242A565900829871 /* iosApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
058557BA273AAA24004C7B11 /* Assets.xcassets */,
|
||||
7555FF82242A565900829871 /* ContentView.swift */,
|
||||
7555FF8C242A565B00829871 /* Info.plist */,
|
||||
2152FB032600AC8F00CF470E /* iOSApp.swift */,
|
||||
058557D7273AAEEB004C7B11 /* Preview Content */,
|
||||
);
|
||||
path = iosApp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AB1DB47929225F7C00F7AF9C /* Configuration */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AB3632DC29227652001CCB65 /* Config.xcconfig */,
|
||||
);
|
||||
path = Configuration;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
7555FF7A242A565900829871 /* iosApp */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
|
||||
buildPhases = (
|
||||
F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */,
|
||||
7555FF77242A565900829871 /* Sources */,
|
||||
B92378962B6B1156000C7307 /* Frameworks */,
|
||||
7555FF79242A565900829871 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = iosApp;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = iosApp;
|
||||
productReference = 7555FF7B242A565900829871 /* FromChat.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
7555FF73242A565900829871 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastSwiftUpdateCheck = 1130;
|
||||
LastUpgradeCheck = 2610;
|
||||
ORGANIZATIONNAME = orgName;
|
||||
TargetAttributes = {
|
||||
7555FF7A242A565900829871 = {
|
||||
CreatedOnToolsVersion = 11.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 7555FF72242A565900829871;
|
||||
packageReferences = (
|
||||
);
|
||||
productRefGroup = 7555FF7C242A565900829871 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
7555FF7A242A565900829871 /* iosApp */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
7555FF79242A565900829871 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
|
||||
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Compile Kotlin Framework";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
7555FF77242A565900829871 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
|
||||
7555FF83242A565900829871 /* ContentView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
7555FFA3242A565B00829871 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7555FFA4242A565B00829871 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
7555FFA6242A565B00829871 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 82MWK23YNM;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
|
||||
);
|
||||
INFOPLIST_FILE = iosApp/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FromChat;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
|
||||
PRODUCT_NAME = "${APP_NAME}";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7555FFA7242A565B00829871 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 82MWK23YNM;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
|
||||
);
|
||||
INFOPLIST_FILE = iosApp/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FromChat;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
|
||||
PRODUCT_NAME = "${APP_NAME}";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7555FFA3242A565B00829871 /* Debug */,
|
||||
7555FFA4242A565B00829871 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7555FFA6242A565B00829871 /* Debug */,
|
||||
7555FFA7242A565B00829871 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 7555FF73242A565900829871 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
BIN
Binary file not shown.
+5
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict />
|
||||
</plist>
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7555FF7A242A565900829871"
|
||||
BuildableName = "FromChat.app"
|
||||
BlueprintName = "iosApp"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7555FF7A242A565900829871"
|
||||
BuildableName = "FromChat.app"
|
||||
BlueprintName = "iosApp"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>iOS.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>7555FF7A242A565900829871</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true />
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "logo_square.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import ComposeApp
|
||||
|
||||
struct ComposeView: UIViewControllerRepresentable {
|
||||
func makeUIViewController(context: Context) -> UIViewController {
|
||||
MainViewControllerKt.MainViewController()
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
ComposeView()
|
||||
.ignoresSafeArea(.all) // Compose has own keyboard handler
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict/>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct iOSApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,33 @@
|
||||
package com.pr0gramm3r101.utils.crypto
|
||||
|
||||
import kotlinx.cinterop.BetaInteropApi
|
||||
import kotlinx.cinterop.ByteVar
|
||||
import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import kotlinx.cinterop.addressOf
|
||||
import kotlinx.cinterop.get
|
||||
import kotlinx.cinterop.reinterpret
|
||||
import kotlinx.cinterop.usePinned
|
||||
import platform.Foundation.NSData
|
||||
import platform.Foundation.base64EncodedStringWithOptions
|
||||
import platform.Foundation.create
|
||||
|
||||
actual object Base64 {
|
||||
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
|
||||
actual fun encode(bytes: ByteArray): String {
|
||||
return bytes.usePinned { pinned ->
|
||||
val nsData = NSData.create(bytes = pinned.addressOf(0), length = bytes.size.toULong())
|
||||
nsData.base64EncodedStringWithOptions(0u)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(BetaInteropApi::class, ExperimentalForeignApi::class)
|
||||
actual fun decode(base64: String): ByteArray {
|
||||
val nsData = NSData.create(base64EncodedString = base64, options = 0u) ?: return ByteArray(0)
|
||||
val length = nsData.length.toInt()
|
||||
val bytes = nsData.bytes ?: return ByteArray(0)
|
||||
val bytesPtr = nsData.bytes ?: return ByteArray(0)
|
||||
val bytePtr = bytesPtr.reinterpret<ByteVar>()
|
||||
return ByteArray(length) { index ->
|
||||
bytes.reinterpret<ByteVar>()[index]
|
||||
bytePtr[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.pr0gramm3r101.utils.crypto
|
||||
|
||||
import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import kotlinx.cinterop.addressOf
|
||||
import kotlinx.cinterop.usePinned
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import platform.CommonCrypto.CCHmac
|
||||
import platform.CommonCrypto.kCCHmacAlgSHA256
|
||||
import platform.CoreCrypto.CCHmac
|
||||
import platform.CoreCrypto.kCCHmacAlgSHA256
|
||||
|
||||
actual object Hmac {
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
actual suspend fun hmacSha256(key: ByteArray, data: ByteArray): ByteArray = withContext(Dispatchers.Default) {
|
||||
val result = ByteArray(32)
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@ package com.pr0gramm3r101.utils.settings
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import platform.Foundation.NSUserDefaults
|
||||
import platform.Foundation.NSBundle
|
||||
|
||||
class IosAsyncSettings : AsyncSettings {
|
||||
private val defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults
|
||||
|
||||
override suspend fun putString(key: String, value: String) = withContext(Dispatchers.Default) {
|
||||
override suspend fun putString(key: String, value: String): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.setObject(value, key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -16,7 +17,7 @@ class IosAsyncSettings : AsyncSettings {
|
||||
defaults.stringForKey(key) ?: default
|
||||
}
|
||||
|
||||
override suspend fun putInt(key: String, value: Int) = withContext(Dispatchers.Default) {
|
||||
override suspend fun putInt(key: String, value: Int): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.setInteger(value.toLong(), key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -26,7 +27,7 @@ class IosAsyncSettings : AsyncSettings {
|
||||
if (value != null) value.toInt() else default
|
||||
}
|
||||
|
||||
override suspend fun putLong(key: String, value: Long) = withContext(Dispatchers.Default) {
|
||||
override suspend fun putLong(key: String, value: Long): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.setObject(value, key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -35,7 +36,7 @@ class IosAsyncSettings : AsyncSettings {
|
||||
(defaults.objectForKey(key) as? platform.Foundation.NSNumber)?.longValue ?: default
|
||||
}
|
||||
|
||||
override suspend fun putFloat(key: String, value: Float) = withContext(Dispatchers.Default) {
|
||||
override suspend fun putFloat(key: String, value: Float): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.setFloat(value, key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -45,7 +46,7 @@ class IosAsyncSettings : AsyncSettings {
|
||||
if (value != null) value.toFloat() else default
|
||||
}
|
||||
|
||||
override suspend fun putBoolean(key: String, value: Boolean) = withContext(Dispatchers.Default) {
|
||||
override suspend fun putBoolean(key: String, value: Boolean): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.setBool(value, key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -58,7 +59,7 @@ class IosAsyncSettings : AsyncSettings {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun putStringSet(key: String, value: Set<String>) = withContext(Dispatchers.Default) {
|
||||
override suspend fun putStringSet(key: String, value: Set<String>): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.setObject(value.toList(), key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -68,7 +69,7 @@ class IosAsyncSettings : AsyncSettings {
|
||||
list.filterIsInstance<String>().toSet()
|
||||
}
|
||||
|
||||
override suspend fun putStringList(key: String, value: List<String>) = withContext(Dispatchers.Default) {
|
||||
override suspend fun putStringList(key: String, value: List<String>): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.setObject(value, key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -78,7 +79,7 @@ class IosAsyncSettings : AsyncSettings {
|
||||
list.filterIsInstance<String>()
|
||||
}
|
||||
|
||||
override suspend fun remove(key: String) = withContext(Dispatchers.Default) {
|
||||
override suspend fun remove(key: String): Unit = withContext(Dispatchers.Default) {
|
||||
defaults.removeObjectForKey(key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
@@ -87,8 +88,9 @@ class IosAsyncSettings : AsyncSettings {
|
||||
defaults.objectForKey(key) != null
|
||||
}
|
||||
|
||||
override suspend fun clear() = withContext(Dispatchers.Default) {
|
||||
val domain = defaults.persistentDomainForName(defaults.bundleIdentifier() ?: "")
|
||||
override suspend fun clear(): Unit = withContext(Dispatchers.Default) {
|
||||
val bundleId = NSBundle.mainBundle.bundleIdentifier ?: ""
|
||||
val domain = defaults.persistentDomainForName(bundleId)
|
||||
domain?.keys?.forEach { key ->
|
||||
defaults.removeObjectForKey(key as String)
|
||||
}
|
||||
@@ -96,4 +98,4 @@ class IosAsyncSettings : AsyncSettings {
|
||||
}
|
||||
}
|
||||
|
||||
actual val asyncSettings: AsyncSettings = IosAsyncSettings()
|
||||
actual val asyncSettings: AsyncSettings = IosAsyncSettings()
|
||||
Reference in New Issue
Block a user