├── .github └── workflows │ ├── publish-release.yml │ └── publish-snapshot.yml ├── .gitignore ├── LICENSE ├── README.md ├── api ├── api-ktor │ ├── build.gradle.kts │ └── src │ │ ├── commonMain │ │ └── kotlin │ │ │ └── app │ │ │ └── meetacy │ │ │ └── sdk │ │ │ ├── MeetacyApi.kt │ │ │ └── engine │ │ │ └── ktor │ │ │ ├── HttpRequestBuilder.kt │ │ │ ├── KtorMeetacyEngine.kt │ │ │ ├── RSocket.kt │ │ │ ├── exception │ │ │ └── getException.kt │ │ │ ├── requests │ │ │ ├── auth │ │ │ │ ├── AuthEngine.kt │ │ │ │ └── telegram │ │ │ │ │ └── AuthTelegramEngine.kt │ │ │ ├── files │ │ │ │ └── FilesEngine.kt │ │ │ ├── friends │ │ │ │ ├── FriendsEngine.kt │ │ │ │ ├── subscribers │ │ │ │ │ └── SubscribersEngine.kt │ │ │ │ └── subscriptions │ │ │ │ │ └── SubscriptionsEngine.kt │ │ │ ├── invitations │ │ │ │ └── InvitationsEngine.kt │ │ │ ├── meetings │ │ │ │ └── MeetingsEngine.kt │ │ │ ├── notifications │ │ │ │ └── NotificationsEngine.kt │ │ │ ├── search │ │ │ │ └── SearchEngine.kt │ │ │ ├── updates │ │ │ │ └── UpdatesEngine.kt │ │ │ └── users │ │ │ │ └── UsersEngine.kt │ │ │ └── response │ │ │ ├── Response.kt │ │ │ └── SimpleResponse.kt │ │ └── jsMain │ │ ├── kotlin │ │ └── Test.kt │ │ └── resources │ │ └── test.html ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ ├── AuthorizedMeetacyApi.kt │ │ ├── MeetacyApi.kt │ │ ├── auth │ │ ├── AuthApi.kt │ │ ├── AuthorizedAuthApi.kt │ │ ├── email │ │ │ ├── AuthEmailApi.kt │ │ │ ├── AuthorizedAuthEmailApi.kt │ │ │ └── LinkEmailResult.kt │ │ └── telegram │ │ │ ├── AuthTelegramApi.kt │ │ │ └── TempAuthRepository.kt │ │ ├── engine │ │ ├── MeetacyRequestsEngine.kt │ │ └── requests │ │ │ ├── AcceptInvitationRequest.kt │ │ │ ├── AddFriendRequest.kt │ │ │ ├── AwaitTelegramAuthRequest.kt │ │ │ ├── CancelInvitationRequest.kt │ │ │ ├── ConfirmEmailRequest.kt │ │ │ ├── CreateInvitationRequest.kt │ │ │ ├── CreateMeetingRequest.kt │ │ │ ├── DeleteFriendRequest.kt │ │ │ ├── DenyInvitationRequest.kt │ │ │ ├── EditMeetingRequest.kt │ │ │ ├── EditUserRequest.kt │ │ │ ├── EmitFriendsLocationRequest.kt │ │ │ ├── EmitUpdatesRequest.kt │ │ │ ├── FinishTelegramAuthRequest.kt │ │ │ ├── FlowRequest.kt │ │ │ ├── GenerateAuthRequest.kt │ │ │ ├── GetFileRequest.kt │ │ │ ├── GetMeRequest.kt │ │ │ ├── GetMeetingRequest.kt │ │ │ ├── GetUserByIdRequest.kt │ │ │ ├── GetUserByUsernameRequest.kt │ │ │ ├── LeaveMeetingRequest.kt │ │ │ ├── LinkEmailRequest.kt │ │ │ ├── ListActiveMeetingsRequest.kt │ │ │ ├── ListFriendsRequest.kt │ │ │ ├── ListMeetingParticipantsRequest.kt │ │ │ ├── ListMeetingsHistoryRequest.kt │ │ │ ├── ListMeetingsMapRequest.kt │ │ │ ├── ListNotificationsRequest.kt │ │ │ ├── ListPastMeetingsRequest.kt │ │ │ ├── ListSubscribersRequest.kt │ │ │ ├── ListSubscriptionsRequest.kt │ │ │ ├── MeetacyRequest.kt │ │ │ ├── ParticipateMeetingRequest.kt │ │ │ ├── PreloginTelegramAuthRequest.kt │ │ │ ├── PushLocationRequest.kt │ │ │ ├── ReadNotificationRequest.kt │ │ │ ├── SearchRequest.kt │ │ │ ├── UploadFileRequest.kt │ │ │ └── UsernameAvailableRequest.kt │ │ ├── exception │ │ ├── MeetacyConnectionException.kt │ │ ├── MeetacyException.kt │ │ ├── MeetacyInternalException.kt │ │ ├── MeetacyResponseException.kt │ │ ├── MeetacyUnauthorizedException.kt │ │ ├── MeetacyUserNotFoundException.kt │ │ ├── MeetacyUsernameAlreadyOccupiedException.kt │ │ └── meetacyApiError.kt │ │ ├── files │ │ ├── AuthorizedFilesApi.kt │ │ ├── DownloadableFile.kt │ │ ├── FileRepository.kt │ │ ├── FilesApi.kt │ │ ├── UploadableFile.kt │ │ └── withCallback.kt │ │ ├── friends │ │ ├── AuthorizedFriendsApi.kt │ │ ├── FriendsApi.kt │ │ ├── location │ │ │ ├── AuthorizedFriendsLocationApi.kt │ │ │ ├── AuthorizedUserLocationSnapshotRepository.kt │ │ │ ├── FriendsLocationApi.kt │ │ │ └── UserLocationSnapshotRepository.kt │ │ ├── subscribers │ │ │ ├── AuthorizedSubscribersApi.kt │ │ │ └── SubscribersApi.kt │ │ └── subscriptions │ │ │ ├── AuthorizedSubscriptionsApi.kt │ │ │ └── SubscriptionsApi.kt │ │ ├── invitations │ │ ├── AuthorizedInvitationRepository.kt │ │ ├── AuthorizedInvitationsApi.kt │ │ ├── InvitationsApi.kt │ │ └── InvitationsRepository.kt │ │ ├── meetings │ │ ├── AuthorizedMeetingRepository.kt │ │ ├── AuthorizedMeetingsApi.kt │ │ ├── MeetingRepository.kt │ │ ├── MeetingsApi.kt │ │ ├── history │ │ │ ├── AuthorizedMeetingsHistoryApi.kt │ │ │ └── MeetingsHistoryApi.kt │ │ ├── map │ │ │ ├── AuthorizedMeetingsMapApi.kt │ │ │ └── MeetingsMapApi.kt │ │ └── participants │ │ │ ├── AuthorizedMeetingParticipantsApi.kt │ │ │ ├── AuthorizedMeetingParticipantsRepository.kt │ │ │ ├── MeetingParticipantsApi.kt │ │ │ └── MeetingParticipantsRepository.kt │ │ ├── notifications │ │ ├── AuthorizedNotificationRepository.kt │ │ ├── AuthorizedNotificationsApi.kt │ │ ├── NotificationRepository.kt │ │ └── NotificationsApi.kt │ │ ├── search │ │ ├── AuthorizedSearchItemRepository.kt │ │ └── SearchItemRepository.kt │ │ ├── updates │ │ ├── AuthorizedUpdateRepository.kt │ │ ├── AuthorizedUpdatesApi.kt │ │ ├── UpdateRepository.kt │ │ └── UpdatesApi.kt │ │ ├── users │ │ ├── AuthorizedRegularUserDetailsRepository.kt │ │ ├── AuthorizedRegularUserRepository.kt │ │ ├── AuthorizedSelfUserDetailsRepository.kt │ │ ├── AuthorizedSelfUserRepository.kt │ │ ├── AuthorizedUserDetailsRepository.kt │ │ ├── AuthorizedUserRepository.kt │ │ ├── AuthorizedUsersApi.kt │ │ ├── RegularUserDetailsRepository.kt │ │ ├── RegularUserRepository.kt │ │ ├── SelfUserDetailsRepository.kt │ │ ├── SelfUserRepository.kt │ │ ├── UserDetailsRepository.kt │ │ ├── UserRepository.kt │ │ ├── UsersApi.kt │ │ ├── subscribers │ │ │ ├── AuthorizedSubscribersRepository.kt │ │ │ └── SubscribersRepository.kt │ │ └── subscriptions │ │ │ ├── AuthorizedSubscriptionsRepository.kt │ │ │ └── SubscriptionsRepository.kt │ │ └── version │ │ └── ApiVersion.kt │ ├── iosMain │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ └── files │ │ ├── AuthorizedFilesApi.kt │ │ ├── DownloadableFile.kt │ │ ├── FilesApi.kt │ │ ├── FilesRepository.kt │ │ └── NSURLExt.kt │ ├── jsMain │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ └── files │ │ ├── AuthorizedFilesApi.kt │ │ └── FilesApi.kt │ └── jvmMain │ └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── files │ ├── AuthorizedFilesApi.kt │ ├── DownloadableFile.kt │ ├── FilesApi.kt │ └── FilesRepository.kt ├── build-logic ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── Version.kt │ ├── example-logic-convention.gradle.kts │ ├── kmp-library-convention.gradle.kts │ ├── print-sdk-version-convention.gradle.kts │ └── publication-convention.gradle.kts ├── build.gradle.kts ├── example ├── iosApp │ ├── iosApp.xcodeproj │ │ └── project.pbxproj │ └── iosApp │ │ ├── ActivityIndicator.swift │ │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ │ ├── AuthScreen.swift │ │ ├── FilesScreen.swift │ │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ │ ├── iosApp.entitlements │ │ └── iosAppApp.swift └── shared │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ └── example │ │ ├── Environment.kt │ │ ├── FilesUseCase.kt │ │ ├── RegisterUseCase.kt │ │ ├── SharedFactory.kt │ │ └── createHttpClientEngine.kt │ └── iosMain │ └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── example │ └── createHttpClientEngine.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── io ├── build.gradle.kts ├── io-ktor │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ └── io │ │ ├── ByteReadChannelExt.kt │ │ ├── InputIntegration.kt │ │ ├── InputSourceIntegration.kt │ │ └── OutputIntegration.kt ├── karma.config.d │ └── page.js └── src │ ├── commonMain │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ └── io │ │ ├── Constants.kt │ │ ├── IOChannel.kt │ │ ├── IOSource.kt │ │ ├── Input.kt │ │ ├── InputSource.kt │ │ ├── Output.kt │ │ ├── OutputSource.kt │ │ ├── annotation │ │ └── IODslMarker.kt │ │ ├── buffer │ │ └── ByteBuffer.kt │ │ ├── bytes │ │ └── ByteArrayView.kt │ │ └── exception │ │ └── MeetacyIOException.kt │ ├── iosMain │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ └── io │ │ ├── FileHandleInput.kt │ │ ├── FileHandleOutput.kt │ │ ├── IosResult.kt │ │ ├── NSError.kt │ │ └── NativeException.kt │ ├── jsMain │ └── kotlin │ │ └── app │ │ └── meetacy │ │ └── sdk │ │ └── io │ │ ├── Blob.kt │ │ ├── Exports.kt │ │ └── Reader.kt │ └── jvmMain │ └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── io │ ├── File.kt │ ├── InputStream.kt │ └── OutputStream.kt ├── kotlinx-datetime ├── build.gradle.kts └── src │ └── commonMain │ └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── types │ └── datetime │ ├── Date.kt │ └── DateTime.kt ├── settings.gradle.kts └── types ├── build.gradle.kts ├── serializable ├── build.gradle.kts └── src │ └── commonMain │ └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── types │ └── serializable │ ├── address │ └── AddressSerializable.kt │ ├── amount │ └── AmountSerializable.kt │ ├── datetime │ ├── DateOrTimeSerializable.kt │ ├── DateSerializable.kt │ └── DateTimeSerializable.kt │ ├── email │ └── EmailSerializable.kt │ ├── file │ └── FileIdSerializable.kt │ ├── invitation │ ├── InvitationId.kt │ └── InvitationSerializable.kt │ ├── location │ └── LocationSerializable.kt │ ├── meeting │ ├── MeetingIdSerializable.kt │ └── MeetingSerializable.kt │ ├── notification │ ├── NotificationIdSerializable.kt │ └── NotificationSerializable.kt │ ├── optional │ └── OptionalSerializable.kt │ ├── paging │ ├── PagingIdSerializable.kt │ └── PagingResponseSerializable.kt │ ├── place │ └── PlaceSerializable.kt │ ├── search │ └── SearchItemSerializable.kt │ └── user │ ├── RelationshipSerializable.kt │ ├── UserDetailsSerializable.kt │ ├── UserIdSerializable.kt │ ├── UserSerializable.kt │ └── UsernameSerializable.kt └── src ├── commonMain └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── types │ ├── address │ └── Address.kt │ ├── amount │ └── Amount.kt │ ├── annotation │ ├── UnsafeConstructor.kt │ └── UnstableApi.kt │ ├── auth │ ├── Token.kt │ └── telegram │ │ ├── SecretTelegramBotKey.kt │ │ ├── TempTelegramAuth.kt │ │ └── TemporalTelegramHash.kt │ ├── datetime │ ├── Date.kt │ ├── DateOrTime.kt │ └── DateTime.kt │ ├── email │ ├── ConfirmEmailHash.kt │ ├── ConfirmEmailStatus.kt │ └── Email.kt │ ├── file │ └── FileId.kt │ ├── invitation │ ├── AcceptationState.kt │ ├── Invitation.kt │ └── InvitationId.kt │ ├── location │ ├── Location.kt │ └── LocationSnapshot.kt │ ├── meeting │ ├── Meeting.kt │ └── MeetingId.kt │ ├── notification │ ├── Notification.kt │ └── NotificationId.kt │ ├── optional │ └── Optional.kt │ ├── paging │ ├── FlatPagingIterator.kt │ ├── FlatPagingSource.kt │ ├── PagingId.kt │ ├── PagingIterator.kt │ ├── PagingRepository.kt │ ├── PagingResponse.kt │ └── PagingSource.kt │ ├── place │ └── Place.kt │ ├── search │ └── SearchItem.kt │ ├── update │ ├── Update.kt │ └── UpdateId.kt │ ├── url │ ├── Parameters.kt │ ├── Url.kt │ └── UrlProtocol.kt │ └── user │ ├── RegularUser.kt │ ├── RegularUserDetails.kt │ ├── Relationship.kt │ ├── SelfUser.kt │ ├── SelfUserDetails.kt │ ├── User.kt │ ├── UserDetails.kt │ ├── UserId.kt │ ├── UserLocationSnapshot.kt │ └── Username.kt ├── iosMain └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── types │ └── datetime │ ├── DateActuals.kt │ ├── Formatter.kt │ └── IosDate.kt ├── iosTest └── kotlin │ └── DateTests.kt ├── jsMain └── kotlin │ └── app │ └── meetacy │ └── sdk │ └── types │ └── datetime │ ├── DateActuals.kt │ └── JsDate.kt └── jvmMain └── kotlin └── app └── meetacy └── sdk └── types └── datetime ├── DateActuals.kt ├── DateFormats.kt ├── JavaDate.kt ├── JavaInstant.kt └── JavaLocalDate.kt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build 3 | .gradle 4 | kotlin-js-store 5 | local.properties 6 | **/.DS_Store 7 | */**/xcuserdata 8 | **/*xcworkspace 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Meetacy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /api/api-ktor/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kmp-library-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | 7 | dependencies { 8 | commonMainImplementation(libs.kotlinxCoroutines) 9 | commonMainImplementation(libs.ktorClient) 10 | commonMainImplementation(libs.kotlinxSerialization) 11 | commonMainImplementation(libs.ktorClientLogging) 12 | commonMainImplementation(libs.ktorClientWebSockets) 13 | commonMainImplementation(libs.rsocketKtorClient) 14 | commonMainImplementation(libs.ktorClientContentNegotiation) 15 | commonMainImplementation(libs.ktorSerializationJson) 16 | 17 | commonMainApi(projects.api) 18 | commonMainImplementation(projects.types.serializable) 19 | 20 | jvmTestImplementation(libs.ktorClientCio) 21 | } 22 | 23 | kotlin.sourceSets.all { 24 | languageSettings { 25 | optIn("app.meetacy.sdk.types.annotation.UnstableApi") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/MeetacyApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk 2 | 3 | import app.meetacy.sdk.engine.ktor.KtorMeetacyEngine 4 | import app.meetacy.sdk.types.url.Url 5 | import app.meetacy.sdk.types.url.url 6 | import io.ktor.client.HttpClient 7 | import io.ktor.client.plugins.logging.* 8 | import kotlinx.serialization.json.Json 9 | 10 | public fun MeetacyApi( 11 | baseUrl: Url, 12 | httpClient: HttpClient = HttpClient(), 13 | json: Json = Json 14 | ): MeetacyApi = MeetacyApi( 15 | engine = KtorMeetacyEngine( 16 | baseUrl = baseUrl, 17 | httpClient = httpClient, 18 | json = json 19 | ) 20 | ) 21 | 22 | public fun MeetacyApi.Companion.production( 23 | httpClient: HttpClient = HttpClient(), 24 | enableLogging: Boolean = false, 25 | json: Json = Json 26 | ): MeetacyApi { 27 | val configuredClient = if (enableLogging) { 28 | httpClient.config { 29 | Logging { 30 | logger = Logger.SIMPLE 31 | level = LogLevel.ALL 32 | } 33 | } 34 | } else httpClient 35 | 36 | return MeetacyApi( 37 | httpClient = configuredClient, 38 | json = json, 39 | baseUrl = "https://api.meetacy.app".url 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/HttpRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.ktor 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.version.ApiVersion 5 | import io.ktor.client.request.* 6 | import io.ktor.http.* 7 | 8 | public fun HttpRequestBuilder.apiVersion(apiVersion: ApiVersion) { 9 | header("Api-Version", apiVersion.int) 10 | } 11 | 12 | public fun HttpRequestBuilder.token(token: Token) { 13 | header(HttpHeaders.Authorization, token.string) 14 | } 15 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/RSocket.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.ktor 2 | 3 | import app.meetacy.sdk.engine.ktor.exception.getException 4 | import app.meetacy.sdk.engine.ktor.response.ServerResponse 5 | import app.meetacy.sdk.exception.MeetacyConnectionException 6 | import app.meetacy.sdk.exception.MeetacyInternalException 7 | import app.meetacy.sdk.exception.meetacyApiError 8 | import io.ktor.client.* 9 | import io.ktor.http.* 10 | import io.ktor.utils.io.errors.* 11 | import io.rsocket.kotlin.RSocket 12 | import io.rsocket.kotlin.RSocketError 13 | import io.rsocket.kotlin.ktor.client.rSocket 14 | import kotlinx.coroutines.CancellationException 15 | import kotlinx.serialization.json.Json 16 | 17 | internal inline fun handleRSocketExceptions( 18 | json: Json, 19 | block: () -> T 20 | ): T { 21 | return try { 22 | block() 23 | } catch (exception: RSocketError) { 24 | when (exception) { 25 | is RSocketError.Custom -> { 26 | val response = json.decodeFromString>( 27 | string = exception.message ?: meetacyApiError(message = "Message should be present when throwing custom error") 28 | ) as ServerResponse.Error 29 | throw getException(response) 30 | } 31 | else -> { 32 | throw MeetacyConnectionException(cause = exception) 33 | } 34 | } 35 | } catch (exception: IOException) { 36 | throw MeetacyConnectionException(cause = exception) 37 | } catch (exception: RuntimeException) { 38 | throw when (exception) { 39 | is CancellationException -> exception 40 | else -> MeetacyInternalException(cause = exception) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/exception/getException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.ktor.exception 2 | 3 | import app.meetacy.sdk.engine.ktor.response.ServerResponse 4 | import app.meetacy.sdk.exception.MeetacyInternalException 5 | import app.meetacy.sdk.exception.MeetacyUnauthorizedException 6 | import app.meetacy.sdk.exception.MeetacyUserNotFoundException 7 | import app.meetacy.sdk.exception.MeetacyUsernameAlreadyOccupiedException 8 | 9 | internal fun getException( 10 | error: ServerResponse.Error 11 | ): Throwable { 12 | return when (error.errorCode) { 13 | MeetacyUnauthorizedException.CODE -> MeetacyUnauthorizedException(error.errorMessage) 14 | MeetacyUsernameAlreadyOccupiedException.CODE -> MeetacyUsernameAlreadyOccupiedException(error.errorMessage) 15 | MeetacyUserNotFoundException.CODE -> MeetacyUserNotFoundException(error.errorMessage) 16 | else -> MeetacyInternalException(error.errorMessage) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/requests/auth/AuthEngine.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.engine.ktor.requests.auth 4 | 5 | import app.meetacy.sdk.engine.ktor.apiVersion 6 | import app.meetacy.sdk.engine.ktor.requests.auth.telegram.AuthTelegramEngine 7 | import app.meetacy.sdk.engine.ktor.response.bodyAsSuccess 8 | import app.meetacy.sdk.engine.requests.GenerateAuthRequest 9 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 10 | import app.meetacy.sdk.types.auth.Token 11 | import app.meetacy.sdk.types.url.Url 12 | import io.ktor.client.HttpClient 13 | import io.ktor.client.request.post 14 | import io.ktor.client.request.setBody 15 | import kotlinx.serialization.Serializable 16 | import kotlinx.serialization.json.Json 17 | 18 | internal class AuthEngine( 19 | baseUrl: Url, 20 | private val httpClient: HttpClient, 21 | rsocketClient: HttpClient, 22 | json: Json 23 | ) { 24 | private val baseUrl: Url = baseUrl / "auth" 25 | 26 | val telegram: AuthTelegramEngine = AuthTelegramEngine( 27 | baseUrl = this.baseUrl, 28 | httpClient = httpClient, 29 | rsocketClient = rsocketClient, 30 | json = json 31 | ) 32 | 33 | @Serializable 34 | private data class GenerateAuthBody(val nickname: String) 35 | 36 | suspend fun generate(request: GenerateAuthRequest): GenerateAuthRequest.Response { 37 | val url = baseUrl / "generate" 38 | val body = GenerateAuthBody(request.nickname) 39 | val response = httpClient 40 | .post(url.string) { 41 | apiVersion(request.apiVersion) 42 | setBody(body) 43 | } 44 | .bodyAsSuccess() 45 | val result = Token(response) 46 | return GenerateAuthRequest.Response(result) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/requests/friends/subscribers/SubscribersEngine.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.ktor.requests.friends.subscribers 2 | 3 | import app.meetacy.sdk.engine.ktor.apiVersion 4 | import app.meetacy.sdk.engine.ktor.response.bodyAsSuccess 5 | import app.meetacy.sdk.engine.ktor.token 6 | import app.meetacy.sdk.engine.requests.ListSubscribersRequest 7 | import app.meetacy.sdk.types.serializable.paging.PagingResponseSerializable 8 | import app.meetacy.sdk.types.serializable.paging.type 9 | import app.meetacy.sdk.types.serializable.user.UserDetailsSerializable 10 | import app.meetacy.sdk.types.serializable.user.UserSerializable 11 | import app.meetacy.sdk.types.serializable.user.type 12 | import app.meetacy.sdk.types.url.Url 13 | import app.meetacy.sdk.types.user.User 14 | import io.ktor.client.* 15 | import io.ktor.client.request.* 16 | 17 | internal class SubscribersEngine( 18 | baseUrl: Url, 19 | private val httpClient: HttpClient 20 | ) { 21 | private val baseUrl = baseUrl / "subscribers" 22 | 23 | suspend fun list(request: ListSubscribersRequest): ListSubscribersRequest.Response { 24 | val url = baseUrl / "list" 25 | 26 | val response = httpClient.get(url.string) { 27 | apiVersion(request.apiVersion) 28 | token(request.token) 29 | parameter("userId", request.userId?.string) 30 | parameter("amount", request.amount.int) 31 | parameter("pagingId", request.pagingId?.string) 32 | }.bodyAsSuccess>() 33 | .type() 34 | .mapItems(UserSerializable::type) 35 | 36 | return ListSubscribersRequest.Response(response) 37 | } 38 | } -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/requests/friends/subscriptions/SubscriptionsEngine.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.ktor.requests.friends.subscriptions 2 | 3 | import app.meetacy.sdk.engine.ktor.apiVersion 4 | import app.meetacy.sdk.engine.ktor.response.bodyAsSuccess 5 | import app.meetacy.sdk.engine.ktor.token 6 | import app.meetacy.sdk.engine.requests.ListSubscriptionsRequest 7 | import app.meetacy.sdk.types.serializable.paging.PagingResponseSerializable 8 | import app.meetacy.sdk.types.serializable.paging.type 9 | import app.meetacy.sdk.types.serializable.user.UserSerializable 10 | import app.meetacy.sdk.types.serializable.user.type 11 | import app.meetacy.sdk.types.url.Url 12 | import io.ktor.client.* 13 | import io.ktor.client.request.* 14 | 15 | internal class SubscriptionsEngine( 16 | baseUrl: Url, 17 | private val httpClient: HttpClient 18 | ) { 19 | private val baseUrl = baseUrl / "subscriptions" 20 | 21 | suspend fun list(request: ListSubscriptionsRequest): ListSubscriptionsRequest.Response { 22 | val url = baseUrl / "list" 23 | 24 | val response = httpClient.get(url.string) { 25 | apiVersion(request.apiVersion) 26 | token(request.token) 27 | parameter("userId", request.userId?.string) 28 | parameter("amount", request.amount.int) 29 | parameter("pagingId", request.pagingId?.string) 30 | }.bodyAsSuccess>() 31 | .type() 32 | .mapItems(UserSerializable::type) 33 | 34 | return ListSubscriptionsRequest.Response(response) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/requests/search/SearchEngine.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.ktor.requests.search 2 | 3 | import app.meetacy.sdk.engine.ktor.apiVersion 4 | import app.meetacy.sdk.engine.ktor.response.bodyAsSuccess 5 | import app.meetacy.sdk.engine.ktor.token 6 | import app.meetacy.sdk.engine.requests.SearchRequest 7 | import app.meetacy.sdk.types.serializable.search.SearchItemSerializable 8 | import app.meetacy.sdk.types.serializable.search.type 9 | import app.meetacy.sdk.types.url.Url 10 | import io.ktor.client.* 11 | import io.ktor.client.request.* 12 | 13 | internal class SearchEngine( 14 | baseUrl: Url, 15 | private val httpClient: HttpClient 16 | ) { 17 | val baseUrl: Url = baseUrl / "search" 18 | 19 | suspend fun search(request: SearchRequest): SearchRequest.Response { 20 | val response = httpClient.get(baseUrl.string) { 21 | apiVersion(request.apiVersion) 22 | token(request.token) 23 | parameter("prompt", request.prompt) 24 | parameter("latitude", request.location?.latitude) 25 | parameter("longitude", request.location?.longitude) 26 | }.bodyAsSuccess>() 27 | return SearchRequest.Response(response.map { it.type() }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/api-ktor/src/commonMain/kotlin/app/meetacy/sdk/engine/ktor/response/SimpleResponse.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.ktor.response 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | internal data class StatusTrueResponse( 8 | @SerialName("status") 9 | val status: Boolean 10 | ) 11 | -------------------------------------------------------------------------------- /api/api-ktor/src/jsMain/kotlin/Test.kt: -------------------------------------------------------------------------------- 1 | import app.meetacy.sdk.MeetacyApi 2 | import app.meetacy.sdk.files.upload 3 | import app.meetacy.sdk.io.asMeetacyInputSource 4 | import app.meetacy.sdk.io.readIterative 5 | import app.meetacy.sdk.io.use 6 | import app.meetacy.sdk.production 7 | import app.meetacy.sdk.types.auth.Token 8 | import kotlinx.browser.document 9 | import kotlinx.coroutines.DelicateCoroutinesApi 10 | import kotlinx.coroutines.GlobalScope 11 | import kotlinx.coroutines.launch 12 | import org.w3c.dom.HTMLElement 13 | import org.w3c.dom.HTMLInputElement 14 | import org.w3c.files.get 15 | 16 | @OptIn(DelicateCoroutinesApi::class) 17 | public fun main() { 18 | val sdk = MeetacyApi.production() 19 | 20 | val node = document.getElementById("input") as HTMLInputElement 21 | val button = document.getElementById("button") as HTMLElement 22 | 23 | button.onclick = { 24 | GlobalScope.launch { 25 | val fileId = sdk.files.upload( 26 | token = Token("66:CttTaeSxGW7V2eW0UNHh1RBGU74xjznSy2kji5KcBFXNpilDCa8DFojTy228mi5Pc3o4xPYWUj0Jt8HbDotx0acmH8eaAmu0dnGp7Kg69Is49p3jogWu5NOdfUm6JVJ63jSUiswN4sXNrP35FghTqDscyFsdYLGOVVIzKBiJmIfS15ZgMymb4VZVGuNeMD0Va53C6Y1CZk8bUDUoCTTQgqYkGCBIDgcyc8ZT1dcPw2xuFl9vNqKnPbPxULW0slTG"), 27 | source = node.files!![0]!! 28 | ) { uploaded, totalBytes -> 29 | println("Uploaded ${uploaded}b / ${totalBytes}b (${uploaded.toDouble() / totalBytes * 100}%)...") 30 | } 31 | 32 | println(fileId) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /api/api-ktor/src/jsMain/resources/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kmp-library-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | 7 | dependencies { 8 | commonMainApi(projects.types) 9 | commonMainApi(projects.io.ioKtor) 10 | commonMainImplementation(libs.kotlinxCoroutines) 11 | } 12 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/auth/AuthApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.auth 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.MeetacyApi 5 | import app.meetacy.sdk.auth.email.AuthEmailApi 6 | import app.meetacy.sdk.auth.telegram.AuthTelegramApi 7 | import app.meetacy.sdk.engine.requests.GenerateAuthRequest 8 | 9 | public class AuthApi(private val api: MeetacyApi) { 10 | public val email: AuthEmailApi = AuthEmailApi(api) 11 | public val telegram: AuthTelegramApi = AuthTelegramApi(api) 12 | 13 | public suspend fun generate(nickname: String): AuthorizedMeetacyApi { 14 | val token = api.engine.execute(GenerateAuthRequest(nickname)).token 15 | return api.authorized(token) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/auth/AuthorizedAuthApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.auth 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.auth.email.AuthorizedAuthEmailApi 5 | import app.meetacy.sdk.types.auth.Token 6 | 7 | public class AuthorizedAuthApi(private val api: AuthorizedMeetacyApi) { 8 | public val token: Token get() = api.token 9 | public val base: AuthApi get() = api.base.auth 10 | 11 | public val email: AuthorizedAuthEmailApi = AuthorizedAuthEmailApi(api) 12 | } 13 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/auth/email/AuthEmailApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.auth.email 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.ConfirmEmailRequest 5 | import app.meetacy.sdk.engine.requests.LinkEmailRequest 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.email.ConfirmEmailHash 8 | import app.meetacy.sdk.types.email.ConfirmEmailStatus 9 | import app.meetacy.sdk.types.email.Email 10 | 11 | public class AuthEmailApi(private val api: MeetacyApi) { 12 | public suspend fun link(token: Token, email: Email): LinkEmailResult { 13 | api.engine.execute(LinkEmailRequest(token, email)) 14 | return LinkEmailResult(emailApi = this, email) 15 | } 16 | 17 | public suspend fun confirm( 18 | email: Email, 19 | confirmHash: ConfirmEmailHash 20 | ): ConfirmEmailStatus { 21 | return api.engine.execute(ConfirmEmailRequest(email, confirmHash)).status 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/auth/email/AuthorizedAuthEmailApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.auth.email 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.email.Email 6 | 7 | public class AuthorizedAuthEmailApi(private val api: AuthorizedMeetacyApi) { 8 | public val token: Token get() = api.token 9 | public val base: AuthEmailApi get() = api.base.auth.email 10 | 11 | public suspend fun link(email: Email): LinkEmailResult = base.link(token, email) 12 | } 13 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/auth/email/LinkEmailResult.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.auth.email 2 | 3 | import app.meetacy.sdk.types.email.ConfirmEmailHash 4 | import app.meetacy.sdk.types.email.ConfirmEmailStatus 5 | import app.meetacy.sdk.types.email.Email 6 | 7 | public class LinkEmailResult( 8 | private val emailApi: AuthEmailApi, 9 | private val email: Email 10 | ) { 11 | public suspend fun confirm(confirmHash: ConfirmEmailHash): ConfirmEmailStatus = 12 | emailApi.confirm(email, confirmHash) 13 | } 14 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/auth/telegram/AuthTelegramApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.auth.telegram 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.MeetacyApi 5 | import app.meetacy.sdk.engine.requests.AwaitTelegramAuthRequest 6 | import app.meetacy.sdk.engine.requests.FinishTelegramAuthRequest 7 | import app.meetacy.sdk.engine.requests.PreloginTelegramAuthRequest 8 | import app.meetacy.sdk.types.auth.Token 9 | import app.meetacy.sdk.types.auth.telegram.SecretTelegramBotKey 10 | import app.meetacy.sdk.types.auth.telegram.TemporalTelegramHash 11 | 12 | public class AuthTelegramApi(private val api: MeetacyApi) { 13 | public suspend fun await(temporalToken: Token): AuthorizedMeetacyApi { 14 | val token = api.engine.execute(AwaitTelegramAuthRequest(temporalToken)).permanentToken 15 | return api.authorized(token) 16 | } 17 | 18 | public suspend fun finish( 19 | temporalHash: TemporalTelegramHash, 20 | secretBotKey: SecretTelegramBotKey, 21 | telegramId: Long, 22 | username: String?, 23 | firstName: String?, 24 | lastName: String?, 25 | ) { 26 | val request = FinishTelegramAuthRequest( 27 | temporalHash = temporalHash, 28 | secretBotKey = secretBotKey, 29 | telegramId = telegramId, 30 | username = username, 31 | firstName = firstName, 32 | lastName = lastName, 33 | ) 34 | api.engine.execute(request) 35 | } 36 | 37 | public suspend fun prelogin(): TempAuthRepository { 38 | val tempAuth = api.engine.execute(PreloginTelegramAuthRequest).tempAuth 39 | return TempAuthRepository(tempAuth, api) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/auth/telegram/TempAuthRepository.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnstableApi::class) 2 | 3 | package app.meetacy.sdk.auth.telegram 4 | 5 | import app.meetacy.sdk.AuthorizedMeetacyApi 6 | import app.meetacy.sdk.MeetacyApi 7 | import app.meetacy.sdk.types.annotation.UnstableApi 8 | import app.meetacy.sdk.types.auth.telegram.TempTelegramAuth 9 | import app.meetacy.sdk.types.auth.Token 10 | import app.meetacy.sdk.types.url.Url 11 | 12 | public class TempAuthRepository( 13 | public val data: TempTelegramAuth, 14 | private val api: MeetacyApi 15 | ) { 16 | public val token: Token get() = data.token 17 | public val botLink: Url get() = data.botLink 18 | 19 | public suspend fun await(): AuthorizedMeetacyApi { 20 | return api.auth.telegram.await(token) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/MeetacyRequestsEngine.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnstableApi::class) 2 | 3 | package app.meetacy.sdk.engine 4 | 5 | import app.meetacy.sdk.engine.requests.MeetacyRequest 6 | import app.meetacy.sdk.types.annotation.UnstableApi 7 | import app.meetacy.sdk.types.file.FileId 8 | import app.meetacy.sdk.types.url.Url 9 | 10 | public interface MeetacyRequestsEngine { 11 | public fun getFileUrl(id: FileId): Url 12 | 13 | /** 14 | * The only possible exception to throw is 15 | * [app.meetacy.sdk.exception.MeetacyException] 16 | * and its inheritors 17 | */ 18 | public suspend fun execute(request: MeetacyRequest): T 19 | } 20 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/AcceptInvitationRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.invitation.InvitationId 5 | 6 | public class AcceptInvitationRequest( 7 | public val token: Token, 8 | public val invitationId: InvitationId 9 | ): SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/AddFriendRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.user.UserId 5 | 6 | public data class AddFriendRequest( 7 | val token: Token, 8 | val friendId: UserId 9 | ) : SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/AwaitTelegramAuthRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | 5 | public class AwaitTelegramAuthRequest( 6 | public val temporalToken: Token 7 | ) : MeetacyRequest { 8 | public data class Response(val permanentToken: Token) 9 | } 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/CancelInvitationRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.invitation.InvitationId 5 | 6 | public class CancelInvitationRequest( 7 | public val token: Token, 8 | public val invitationId: InvitationId 9 | ): SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ConfirmEmailRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.email.ConfirmEmailHash 4 | import app.meetacy.sdk.types.email.ConfirmEmailStatus 5 | import app.meetacy.sdk.types.email.Email 6 | 7 | public data class ConfirmEmailRequest( 8 | val email: Email, 9 | val confirmHash: ConfirmEmailHash 10 | ) : MeetacyRequest { 11 | public data class Response(val status: ConfirmEmailStatus) 12 | } 13 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/CreateInvitationRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.invitation.Invitation 5 | import app.meetacy.sdk.types.meeting.MeetingId 6 | import app.meetacy.sdk.types.user.UserId 7 | 8 | public data class CreateInvitationRequest( 9 | val token: Token, 10 | val usersIds: List, 11 | val meetingId: MeetingId 12 | ) : MeetacyRequest { 13 | public data class Response(val invitations: List) 14 | } 15 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/CreateMeetingRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.datetime.Date 5 | import app.meetacy.sdk.types.file.FileId 6 | import app.meetacy.sdk.types.location.Location 7 | import app.meetacy.sdk.types.meeting.Meeting 8 | 9 | public class CreateMeetingRequest( 10 | public val token: Token, 11 | public val title: String?, 12 | public val date: Date, 13 | public val location: Location, 14 | public val description: String?, 15 | public val visibility: Meeting.Visibility, 16 | public val fileId: FileId? = null 17 | ) : MeetacyRequest { 18 | public data class Response(val meeting: Meeting) 19 | } 20 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/DeleteFriendRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.user.UserId 5 | 6 | public data class DeleteFriendRequest( 7 | val token: Token, 8 | val friendId: UserId 9 | ) : SimpleMeetacyRequest -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/DenyInvitationRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.invitation.InvitationId 5 | 6 | public class DenyInvitationRequest( 7 | public val token: Token, 8 | public val invitationId: InvitationId 9 | ): SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/EditMeetingRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.datetime.Date 5 | import app.meetacy.sdk.types.file.FileId 6 | import app.meetacy.sdk.types.location.Location 7 | import app.meetacy.sdk.types.meeting.Meeting 8 | import app.meetacy.sdk.types.meeting.MeetingId 9 | import app.meetacy.sdk.types.optional.Optional 10 | 11 | public data class EditMeetingRequest( 12 | public val token: Token, 13 | public val meetingId: MeetingId, 14 | public val title: Optional, 15 | public val description: Optional, 16 | public val location: Optional, 17 | public val date: Optional, 18 | public val avatarId: Optional, 19 | public val visibility: Optional 20 | ) : MeetacyRequest { 21 | public data class Response(val meeting: Meeting) 22 | } 23 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/EditUserRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.file.FileId 5 | import app.meetacy.sdk.types.optional.Optional 6 | import app.meetacy.sdk.types.user.SelfUser 7 | import app.meetacy.sdk.types.user.Username 8 | 9 | public data class EditUserRequest( 10 | val token: Token, 11 | val nickname: Optional, 12 | val username: Optional, 13 | val avatarId: Optional 14 | ) : MeetacyRequest { 15 | public data class Response(val user: SelfUser) 16 | } 17 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/EmitFriendsLocationRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.user.UserLocationSnapshot 5 | import app.meetacy.sdk.types.location.Location 6 | import kotlinx.coroutines.flow.Flow 7 | import kotlinx.coroutines.flow.FlowCollector 8 | 9 | public data class EmitFriendsLocationRequest( 10 | public val token: Token, 11 | public val selfLocation: Flow, 12 | override val collector: FlowCollector 13 | ) : FlowMeetacyRequest { 14 | 15 | public data class Update(val user: UserLocationSnapshot) 16 | } 17 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/EmitUpdatesRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.update.UpdateId 5 | import kotlinx.coroutines.flow.FlowCollector 6 | import app.meetacy.sdk.types.update.Update as UpdateType 7 | 8 | public class EmitUpdatesRequest( 9 | public val token: Token, 10 | public val fromId: UpdateId?, 11 | override val collector: FlowCollector 12 | ) : FlowMeetacyRequest { 13 | public data class Update(val update: UpdateType) 14 | } 15 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/FinishTelegramAuthRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.telegram.SecretTelegramBotKey 4 | import app.meetacy.sdk.types.auth.telegram.TemporalTelegramHash 5 | 6 | public data class FinishTelegramAuthRequest( 7 | val temporalHash: TemporalTelegramHash, 8 | val secretBotKey: SecretTelegramBotKey, 9 | val telegramId: Long, 10 | val username: String?, 11 | val firstName: String?, 12 | val lastName: String?, 13 | ) : SimpleMeetacyRequest 14 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/FlowRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import kotlinx.coroutines.flow.FlowCollector 4 | 5 | public sealed interface FlowMeetacyRequest : SimpleMeetacyRequest { 6 | public val collector: FlowCollector 7 | } 8 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/GenerateAuthRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | 5 | public data class GenerateAuthRequest( 6 | val nickname: String 7 | ) : MeetacyRequest{ 8 | public data class Response(val token: Token) 9 | } 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/GetFileRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.files.DownloadableFile 4 | import app.meetacy.sdk.types.file.FileId 5 | 6 | public data class GetFileRequest( 7 | public val fileId: FileId 8 | ) : MeetacyRequest { 9 | public data class Response(public val file: DownloadableFile) 10 | } 11 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/GetMeRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.user.SelfUser 5 | import app.meetacy.sdk.types.user.SelfUserDetails 6 | 7 | public data class GetMeRequest( 8 | val token: Token 9 | ): MeetacyRequest{ 10 | public data class Response(val me: SelfUserDetails) 11 | } 12 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/GetMeetingRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.meeting.Meeting 5 | import app.meetacy.sdk.types.meeting.MeetingId 6 | 7 | public data class GetMeetingRequest( 8 | val token: Token, 9 | val meetingId: MeetingId 10 | ) : MeetacyRequest { 11 | public data class Response(val meeting: Meeting) 12 | } 13 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/GetUserByIdRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.user.UserDetails 5 | import app.meetacy.sdk.types.user.UserId 6 | 7 | public data class GetUserByIdRequest( 8 | val token: Token, 9 | val userId: UserId 10 | ) : MeetacyRequest { 11 | public data class Response(val user: UserDetails) 12 | } 13 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/GetUserByUsernameRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.user.UserDetails 5 | import app.meetacy.sdk.types.user.UserId 6 | import app.meetacy.sdk.types.user.Username 7 | 8 | public data class GetUserByUsernameRequest( 9 | val token: Token, 10 | val username: Username 11 | ) : MeetacyRequest { 12 | public data class Response(val user: UserDetails) 13 | } 14 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/LeaveMeetingRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.meeting.MeetingId 5 | 6 | public data class LeaveMeetingRequest( 7 | val token: Token, 8 | val meetingId: MeetingId 9 | ) : SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/LinkEmailRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.email.Email 5 | 6 | public data class LinkEmailRequest( 7 | val token: Token, 8 | val email: Email 9 | ) : SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListActiveMeetingsRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.meeting.Meeting 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingResponse 8 | 9 | public data class ListActiveMeetingsRequest( 10 | val token: Token, 11 | val amount: Amount, 12 | val pagingId: PagingId? 13 | ) : MeetacyRequest { 14 | public data class Response(val paging: PagingResponse) 15 | } 16 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListFriendsRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.paging.PagingId 6 | import app.meetacy.sdk.types.paging.PagingResponse 7 | import app.meetacy.sdk.types.user.RegularUser 8 | 9 | public data class ListFriendsRequest( 10 | val token: Token, 11 | val amount: Amount, 12 | val pagingId: PagingId? 13 | ) : MeetacyRequest { 14 | public data class Response(val paging: PagingResponse) 15 | } 16 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListMeetingParticipantsRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.meeting.MeetingId 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingResponse 8 | import app.meetacy.sdk.types.user.User 9 | 10 | public data class ListMeetingParticipantsRequest( 11 | val token: Token, 12 | val meetingId: MeetingId, 13 | val amount: Amount, 14 | val pagingId: PagingId? 15 | ) : MeetacyRequest { 16 | public data class Response(val paging: PagingResponse) 17 | } 18 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListMeetingsHistoryRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.meeting.Meeting 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingResponse 8 | 9 | public data class ListMeetingsHistoryRequest( 10 | val token: Token, 11 | val amount: Amount, 12 | val pagingId: PagingId? 13 | ) : MeetacyRequest { 14 | public data class Response(val paging: PagingResponse) 15 | } 16 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListMeetingsMapRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.location.Location 5 | import app.meetacy.sdk.types.meeting.Meeting 6 | 7 | public data class ListMeetingsMapRequest( 8 | val token: Token, 9 | val location: Location 10 | ) : MeetacyRequest { 11 | public data class Response(val meetings: List) 12 | } 13 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListNotificationsRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.notification.Notification 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingResponse 8 | 9 | public data class ListNotificationsRequest( 10 | val token: Token, 11 | val amount: Amount, 12 | val pagingId: PagingId? 13 | ) : MeetacyRequest { 14 | public data class Response(val paging: PagingResponse) 15 | } 16 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListPastMeetingsRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.meeting.Meeting 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingResponse 8 | 9 | public data class ListPastMeetingsRequest( 10 | val token: Token, 11 | val amount: Amount, 12 | val pagingId: PagingId? 13 | ) : MeetacyRequest { 14 | public data class Response(val paging: PagingResponse) 15 | } -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListSubscribersRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.paging.PagingId 6 | import app.meetacy.sdk.types.paging.PagingResponse 7 | import app.meetacy.sdk.types.user.User 8 | import app.meetacy.sdk.types.user.UserDetails 9 | import app.meetacy.sdk.types.user.UserId 10 | 11 | public data class ListSubscribersRequest( 12 | val token: Token, 13 | val amount: Amount, 14 | val pagingId: PagingId?, 15 | val userId: UserId?, 16 | ) : MeetacyRequest { 17 | public data class Response(val paging: PagingResponse) 18 | } 19 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ListSubscriptionsRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.paging.PagingId 6 | import app.meetacy.sdk.types.paging.PagingResponse 7 | import app.meetacy.sdk.types.user.User 8 | import app.meetacy.sdk.types.user.UserDetails 9 | import app.meetacy.sdk.types.user.UserId 10 | 11 | public data class ListSubscriptionsRequest( 12 | val token: Token, 13 | val amount: Amount, 14 | val pagingId: PagingId?, 15 | val userId: UserId?, 16 | ) : MeetacyRequest { 17 | public data class Response(val paging: PagingResponse) 18 | } 19 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/MeetacyRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.version.ApiVersion 4 | 5 | public sealed interface MeetacyRequest { 6 | public val apiVersion: ApiVersion get() = ApiVersion.latest() 7 | } 8 | 9 | public typealias SimpleMeetacyRequest = MeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ParticipateMeetingRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.meeting.MeetingId 5 | 6 | public data class ParticipateMeetingRequest( 7 | val token: Token, 8 | val meetingId: MeetingId 9 | ) : SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/PreloginTelegramAuthRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.telegram.TempTelegramAuth 4 | 5 | public data object PreloginTelegramAuthRequest : MeetacyRequest { 6 | public data class Response( 7 | val tempAuth: TempTelegramAuth 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/PushLocationRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.location.Location 5 | 6 | public data class PushLocationRequest( 7 | val token: Token, 8 | val location: Location 9 | ) : SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/ReadNotificationRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.notification.NotificationId 5 | 6 | public data class ReadNotificationRequest( 7 | val token: Token, 8 | val lastNotificationId: NotificationId 9 | ) : SimpleMeetacyRequest 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/SearchRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | import app.meetacy.sdk.types.location.Location 5 | import app.meetacy.sdk.types.search.SearchItem 6 | 7 | public data class SearchRequest( 8 | val prompt: String, 9 | val token: Token, 10 | val location: Location? 11 | ) : MeetacyRequest { 12 | public data class Response(val items: List) 13 | } 14 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/UploadFileRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.files.UploadableFile 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.file.FileId 6 | 7 | public class UploadFileRequest( 8 | public val token: Token, 9 | public val file: UploadableFile 10 | ) : MeetacyRequest { 11 | public data class Response(val fileId: FileId) 12 | } 13 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/engine/requests/UsernameAvailableRequest.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.engine.requests 2 | 3 | import app.meetacy.sdk.types.user.Username 4 | 5 | public data class UsernameAvailableRequest( 6 | val username: Username 7 | ) : MeetacyRequest { 8 | public data class Response(val username: Username) 9 | } 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/MeetacyConnectionException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public class MeetacyConnectionException( 4 | message: String = "Cannot perform request because of poor connection", 5 | cause: Throwable? = null 6 | ) : MeetacyException(message, cause) 7 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/MeetacyException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public sealed class MeetacyException( 4 | message: String? = null, 5 | cause: Throwable? = null 6 | ) : RuntimeException(message, cause) { 7 | final override val cause: Throwable? get() = super.cause 8 | final override val message: String? get() = super.message 9 | } 10 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/MeetacyInternalException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public class MeetacyInternalException( 4 | message: String = "Unknown exception occurred. Please do not try the request with these parameters again", 5 | cause: Throwable? = null 6 | ) : MeetacyException(message, cause) 7 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/MeetacyResponseException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public open class MeetacyResponseException( 4 | public val code: Int, 5 | message: String, cause: Throwable? 6 | ) : MeetacyException(message, cause) 7 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/MeetacyUnauthorizedException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public class MeetacyUnauthorizedException( 4 | message: String = "Your token was expired or invalid, please generate a new one", 5 | cause: Throwable? = null 6 | ) : MeetacyResponseException( 7 | code = CODE, 8 | message = message, 9 | cause = cause 10 | ) { 11 | public companion object { 12 | public const val CODE: Int = 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/MeetacyUserNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public class MeetacyUserNotFoundException(message: String) : MeetacyResponseException( 4 | code = CODE, 5 | message = message, 6 | cause = null 7 | ) { 8 | public companion object { 9 | public const val CODE: Int = 8 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/MeetacyUsernameAlreadyOccupiedException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public class MeetacyUsernameAlreadyOccupiedException( 4 | message: String, 5 | cause: Throwable? = null 6 | ) : MeetacyResponseException( 7 | code = CODE, 8 | message = message, 9 | cause = cause 10 | ) { 11 | public companion object { 12 | public const val CODE: Int = 6 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/exception/meetacyApiError.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.exception 2 | 3 | public fun meetacyApiError(message: String, cause: Throwable? = null): Nothing { 4 | throw MeetacyInternalException(message, cause) 5 | } 6 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/files/AuthorizedFilesApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.io.InputSource 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.file.FileId 7 | import app.meetacy.sdk.types.url.Url 8 | import kotlin.experimental.ExperimentalObjCRefinement 9 | import kotlin.native.HiddenFromObjC 10 | 11 | public class AuthorizedFilesApi(private val api: AuthorizedMeetacyApi) { 12 | public val token: Token get() = api.token 13 | public val base: FilesApi get() = api.base.files 14 | 15 | public fun getFileUrl(id: FileId): Url { 16 | return base.getUrl(id) 17 | } 18 | 19 | public suspend fun get(fileId: FileId): DownloadableFile { 20 | return api.base.files.get(fileId) 21 | } 22 | 23 | @OptIn(ExperimentalObjCRefinement::class) 24 | @HiddenFromObjC 25 | public suspend fun upload(file: UploadableFile): FileId { 26 | return api.base.files.upload(token, file) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/files/DownloadableFile.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.InputSource 4 | import app.meetacy.sdk.types.file.FileId 5 | 6 | public data class DownloadableFile( 7 | public val id: FileId, 8 | public val size: Long, 9 | public val input: InputSource 10 | ) 11 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/files/FileRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.MeetacyApi 5 | import app.meetacy.sdk.io.Input 6 | import app.meetacy.sdk.io.InputSource 7 | import app.meetacy.sdk.types.file.FileId 8 | import app.meetacy.sdk.types.url.Url 9 | import kotlinx.coroutines.CoroutineScope 10 | 11 | public fun FileRepository(id: FileId?, api: AuthorizedMeetacyApi): FileRepository? { 12 | id ?: return null 13 | return FileRepository(id, api) 14 | } 15 | 16 | public fun FileRepository(id: FileId, api: AuthorizedMeetacyApi): FileRepository { 17 | return FileRepository(id, api.base) 18 | } 19 | 20 | public fun FileRepository(id: FileId?, api: MeetacyApi): FileRepository? { 21 | id ?: return null 22 | return FileRepository(id, api) 23 | } 24 | 25 | public class FileRepository( 26 | public val id: FileId, 27 | public val api: MeetacyApi 28 | ) { 29 | public val url: Url get() = api.files.getUrl(id) 30 | 31 | public val input: InputSource get() = object : InputSource { 32 | override suspend fun open(scope: CoroutineScope): Input { 33 | val file = api.files.get(id) 34 | return file.input.open(scope) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/files/FilesApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.GetFileRequest 5 | import app.meetacy.sdk.engine.requests.UploadFileRequest 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.file.FileId 8 | import app.meetacy.sdk.types.url.Url 9 | 10 | public class FilesApi(private val api: MeetacyApi) { 11 | public fun getUrl(id: FileId): Url { 12 | return api.engine.getFileUrl(id) 13 | } 14 | 15 | public suspend fun get(fileId: FileId): DownloadableFile { 16 | return api.engine.execute(GetFileRequest(fileId)).file 17 | } 18 | 19 | public suspend fun upload( 20 | token: Token, 21 | source: UploadableFile 22 | ): FileId { 23 | return api.engine.execute(UploadFileRequest(token, source)).fileId 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/files/UploadableFile.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.InputSource 4 | 5 | public data class UploadableFile( 6 | public val size: Long, 7 | public val input: InputSource 8 | ) 9 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/files/withCallback.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.Input 4 | import app.meetacy.sdk.io.InputSource 5 | import app.meetacy.sdk.io.bytes.ByteArrayView 6 | import kotlinx.coroutines.CoroutineScope 7 | 8 | @PublishedApi 9 | internal suspend inline fun InputSource.withCallback( 10 | crossinline onUpdate: (read: Long) -> Unit 11 | ): InputSource = object : InputSource { 12 | override suspend fun open(scope: CoroutineScope): Input { 13 | val base = this@withCallback.open(scope) 14 | return base.withCallback(onUpdate) 15 | } 16 | } 17 | 18 | @PublishedApi 19 | internal suspend inline fun Input.withCallback( 20 | crossinline onUpdate: (read: Long) -> Unit 21 | ): Input = object : Input { 22 | private val base = this@withCallback 23 | private var readAccumulator = 0L 24 | 25 | override suspend fun read(destination: ByteArrayView): Int { 26 | onUpdate(readAccumulator) 27 | val read = base.read(destination) 28 | readAccumulator += read 29 | return read 30 | } 31 | 32 | override suspend fun close() { 33 | onUpdate(readAccumulator) 34 | base.close() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/friends/location/AuthorizedFriendsLocationApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.friends.location 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.location.Location 6 | import app.meetacy.sdk.users.AuthorizedRegularUserRepository 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.flow.map 9 | 10 | public class AuthorizedFriendsLocationApi( 11 | private val api: AuthorizedMeetacyApi 12 | ) { 13 | public val token: Token get() = api.token 14 | public val base: FriendsLocationApi get() = api.base.friends.location 15 | 16 | public fun flow(selfLocation: Flow): Flow { 17 | return base.flow(api.token, selfLocation).map { userOnMap -> 18 | AuthorizedUserLocationSnapshotRepository( 19 | user = AuthorizedRegularUserRepository( 20 | data = userOnMap.user.data, 21 | api = api 22 | ), 23 | location = userOnMap.location, 24 | capturedAt = userOnMap.capturedAt 25 | ) 26 | } 27 | } 28 | 29 | public suspend fun push(location: Location) { 30 | base.push(token, location) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/friends/location/AuthorizedUserLocationSnapshotRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.friends.location 2 | 3 | import app.meetacy.sdk.types.datetime.DateTime 4 | import app.meetacy.sdk.types.location.Location 5 | import app.meetacy.sdk.users.AuthorizedRegularUserRepository 6 | 7 | public class AuthorizedUserLocationSnapshotRepository( 8 | public val user: AuthorizedRegularUserRepository, 9 | public val location: Location, 10 | public val capturedAt: DateTime 11 | ) 12 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/friends/location/FriendsLocationApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.friends.location 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.EmitFriendsLocationRequest 5 | import app.meetacy.sdk.engine.requests.PushLocationRequest 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.location.Location 8 | import app.meetacy.sdk.users.RegularUserRepository 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.flow 11 | import kotlinx.coroutines.flow.map 12 | 13 | public class FriendsLocationApi( 14 | private val api: MeetacyApi 15 | ) { 16 | public fun flow( 17 | token: Token, 18 | selfLocation: Flow, 19 | ): Flow = 20 | flow { 21 | val request = EmitFriendsLocationRequest( 22 | token = token, 23 | collector = this, 24 | selfLocation = selfLocation 25 | ) 26 | api.engine.execute(request) 27 | }.map { response -> 28 | val userOnMap = response.user 29 | 30 | UserLocationSnapshotRepository( 31 | user = RegularUserRepository( 32 | data = userOnMap.user, 33 | api = api 34 | ), 35 | location = userOnMap.location, 36 | capturedAt = userOnMap.capturedAt 37 | ) 38 | } 39 | 40 | public suspend fun push( 41 | token: Token, 42 | location: Location 43 | ) { 44 | api.engine.execute(PushLocationRequest(token, location)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/friends/location/UserLocationSnapshotRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.friends.location 2 | 3 | import app.meetacy.sdk.types.datetime.DateTime 4 | import app.meetacy.sdk.types.location.Location 5 | import app.meetacy.sdk.users.RegularUserRepository 6 | 7 | public data class UserLocationSnapshotRepository( 8 | public val user: RegularUserRepository, 9 | public val location: Location, 10 | public val capturedAt: DateTime 11 | ) 12 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/friends/subscribers/AuthorizedSubscribersApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.friends.subscribers 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingRepository 8 | import app.meetacy.sdk.types.paging.PagingSource 9 | import app.meetacy.sdk.types.paging.mapItems 10 | import app.meetacy.sdk.types.user.UserId 11 | import app.meetacy.sdk.users.AuthorizedUserRepository 12 | 13 | /** 14 | * When modifying this class, corresponding classes should be altered: 15 | * - [app.meetacy.sdk.users.subscribers.AuthorizedSubscribersRepository] 16 | */ 17 | public class AuthorizedSubscribersApi(private val api: AuthorizedMeetacyApi) { 18 | public val token: Token get() = api.token 19 | public val base: SubscribersApi get() = api.base.friends.subscribers 20 | 21 | public suspend fun list( 22 | amount: Amount, 23 | pagingId: PagingId? = null, 24 | userId: UserId? = null 25 | ): PagingRepository { 26 | return base.list(token, amount, pagingId, userId).mapItems { user -> 27 | AuthorizedUserRepository.of(user.data, api) 28 | } 29 | } 30 | 31 | public fun paging( 32 | chunkSize: Amount, 33 | startPagingId: PagingId? = null, 34 | limit: Amount? = null, 35 | userId: UserId? = null 36 | ): PagingSource { 37 | return PagingSource( 38 | chunkSize, startPagingId, limit 39 | ) { currentAmount, currentPagingId -> 40 | list(currentAmount, currentPagingId, userId).response 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/friends/subscriptions/AuthorizedSubscriptionsApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.friends.subscriptions 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingRepository 8 | import app.meetacy.sdk.types.paging.PagingSource 9 | import app.meetacy.sdk.types.paging.mapItems 10 | import app.meetacy.sdk.types.user.UserId 11 | import app.meetacy.sdk.users.AuthorizedUserDetailsRepository 12 | import app.meetacy.sdk.users.AuthorizedUserRepository 13 | 14 | public class AuthorizedSubscriptionsApi(private val api: AuthorizedMeetacyApi) { 15 | 16 | public val token: Token get() = api.token 17 | public val base: SubscriptionsApi get() = api.base.friends.subscriptions 18 | 19 | public suspend fun list( 20 | amount: Amount, 21 | pagingId: PagingId? = null, 22 | userId: UserId? = null 23 | ): PagingRepository { 24 | return base.list(token, amount, pagingId, userId).mapItems { user -> 25 | AuthorizedUserRepository.of(user.data, api) 26 | } 27 | } 28 | 29 | public fun paging( 30 | chunkSize: Amount, 31 | startPagingId: PagingId? = null, 32 | limit: Amount? = null, 33 | userId: UserId? = null 34 | ): PagingSource { 35 | return PagingSource( 36 | chunkSize, startPagingId, limit 37 | ) { currentAmount, currentPagingId -> 38 | list(currentAmount, currentPagingId, userId).response 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/friends/subscriptions/SubscriptionsApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.friends.subscriptions 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.ListSubscriptionsRequest 5 | import app.meetacy.sdk.types.amount.Amount 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.paging.PagingId 8 | import app.meetacy.sdk.types.paging.PagingRepository 9 | import app.meetacy.sdk.types.paging.PagingSource 10 | import app.meetacy.sdk.types.user.UserId 11 | import app.meetacy.sdk.users.UserRepository 12 | 13 | /** 14 | * When modifying this class, corresponding classes should be altered: 15 | * - [app.meetacy.sdk.friends.subscriptions.AuthorizedSubscriptionsApi] 16 | */ 17 | public class SubscriptionsApi(private val api: MeetacyApi) { 18 | public suspend fun list( 19 | token: Token, 20 | amount: Amount, 21 | pagingId: PagingId? = null, 22 | userId: UserId? = null 23 | ): PagingRepository = PagingRepository( 24 | amount = amount, 25 | startPagingId = pagingId 26 | ) { currentAmount, currentPagingId -> 27 | api.engine.execute( 28 | request = ListSubscriptionsRequest( 29 | token = token, 30 | userId = userId, 31 | amount = currentAmount, 32 | pagingId = currentPagingId 33 | ) 34 | ).paging.mapItems { user -> UserRepository.of(user, api) } 35 | } 36 | 37 | public fun paging( 38 | token: Token, 39 | chunkSize: Amount, 40 | startPagingId: PagingId? = null, 41 | limit: Amount? = null, 42 | userId: UserId? = null 43 | ): PagingSource { 44 | return PagingSource( 45 | chunkSize, startPagingId, limit 46 | ) { currentAmount, currentPagingId -> 47 | list(token, currentAmount, currentPagingId, userId).response 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/invitations/AuthorizedInvitationRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.invitations 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.datetime.DateTime 5 | import app.meetacy.sdk.types.invitation.Invitation 6 | import app.meetacy.sdk.types.invitation.InvitationId 7 | import app.meetacy.sdk.types.meeting.Meeting 8 | import app.meetacy.sdk.types.user.User 9 | 10 | public class AuthorizedInvitationRepository( 11 | public val data: Invitation, 12 | private val api: AuthorizedMeetacyApi 13 | ) { 14 | private val base: AuthorizedInvitationsApi get() = AuthorizedInvitationsApi(api) 15 | 16 | public val id: InvitationId get() = data.id 17 | public val invitedUser: User get() = data.invitedUser 18 | public val invitorUser: User get() = data.inviterUser 19 | public val meeting: Meeting get() = data.meeting 20 | 21 | public suspend fun accept() { 22 | base.accept(id) 23 | } 24 | } -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/invitations/AuthorizedInvitationsApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.invitations 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.invitation.InvitationId 6 | import app.meetacy.sdk.types.meeting.MeetingId 7 | import app.meetacy.sdk.types.user.UserId 8 | 9 | public class AuthorizedInvitationsApi(private val api: AuthorizedMeetacyApi) { 10 | public val token: Token get() = api.token 11 | public val base: InvitationsApi = api.base.invitations 12 | 13 | public suspend fun create( 14 | usersIds: List, 15 | meetingId: MeetingId 16 | ): List { 17 | val invitations = base.create(token, usersIds, meetingId) 18 | 19 | return invitations.map { AuthorizedInvitationRepository(it.data, api) } 20 | } 21 | 22 | public suspend fun accept(invitationId: InvitationId) { 23 | base.accept(token, invitationId) 24 | } 25 | 26 | public suspend fun deny(invitationId: InvitationId) { 27 | base.deny(token, invitationId) 28 | } 29 | 30 | public suspend fun cancel(invitationId: InvitationId) { 31 | base.cancel(token, invitationId) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/invitations/InvitationsApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.invitations 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.AcceptInvitationRequest 5 | import app.meetacy.sdk.engine.requests.CancelInvitationRequest 6 | import app.meetacy.sdk.engine.requests.CreateInvitationRequest 7 | import app.meetacy.sdk.engine.requests.DenyInvitationRequest 8 | import app.meetacy.sdk.types.auth.Token 9 | import app.meetacy.sdk.types.invitation.InvitationId 10 | import app.meetacy.sdk.types.meeting.MeetingId 11 | import app.meetacy.sdk.types.user.UserId 12 | 13 | public class InvitationsApi(private val api: MeetacyApi) { 14 | public suspend fun create( 15 | token: Token, 16 | usersIds: List, 17 | meetingId: MeetingId 18 | ): List { 19 | val invitations = api.engine.execute( 20 | request = CreateInvitationRequest( 21 | token, 22 | usersIds, 23 | meetingId 24 | ) 25 | ).invitations 26 | 27 | return invitations.map { InvitationsRepository(it, api) } 28 | } 29 | 30 | public suspend fun accept( 31 | token: Token, 32 | invitationId: InvitationId 33 | ) { 34 | api.engine.execute(request = AcceptInvitationRequest(token, invitationId)) 35 | } 36 | 37 | public suspend fun deny( 38 | token: Token, 39 | invitationId: InvitationId 40 | ) { 41 | api.engine.execute(request = DenyInvitationRequest(token, invitationId)) 42 | } 43 | 44 | public suspend fun cancel( 45 | token: Token, 46 | invitationId: InvitationId 47 | ) { 48 | api.engine.execute(request = CancelInvitationRequest(token, invitationId)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/invitations/InvitationsRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.invitations 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.datetime.DateTime 6 | import app.meetacy.sdk.types.invitation.Invitation 7 | import app.meetacy.sdk.types.invitation.InvitationId 8 | import app.meetacy.sdk.types.meeting.Meeting 9 | import app.meetacy.sdk.types.user.User 10 | 11 | public class InvitationsRepository( 12 | public val data: Invitation, 13 | private val api: MeetacyApi 14 | ) { 15 | public val id: InvitationId get() = data.id 16 | public val invitedUser: User get() = data.invitedUser 17 | public val invitorUser: User get() = data.inviterUser 18 | public val meeting: Meeting get() = data.meeting 19 | 20 | public suspend fun accept(token: Token) { 21 | api.invitations.accept(token, id) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/meetings/map/AuthorizedMeetingsMapApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.meetings.map 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.meetings.AuthorizedMeetingRepository 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.location.Location 7 | 8 | public class AuthorizedMeetingsMapApi( 9 | private val api: AuthorizedMeetacyApi 10 | ) { 11 | public val token: Token get() = api.token 12 | public val base: MeetingsMapApi get() = api.base.meetings.map 13 | 14 | public suspend fun list(location: Location): List = base 15 | .list(token, location).map { meeting -> AuthorizedMeetingRepository(meeting.data, api) } 16 | } 17 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/meetings/map/MeetingsMapApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.meetings.map 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.ListMeetingsMapRequest 5 | import app.meetacy.sdk.meetings.MeetingRepository 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.location.Location 8 | 9 | /** 10 | * When modifying this class, corresponding classes should be altered: 11 | * - [AuthorizedMeetingsMapApi] 12 | */ 13 | public class MeetingsMapApi( 14 | private val api: MeetacyApi 15 | ) { 16 | public suspend fun list(token: Token, location: Location): List = 17 | api.engine 18 | .execute(ListMeetingsMapRequest(token, location)) 19 | .meetings.map { meeting -> MeetingRepository(meeting, api) } 20 | } 21 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/meetings/participants/AuthorizedMeetingParticipantsApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.meetings.participants 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.meeting.MeetingId 7 | import app.meetacy.sdk.types.paging.* 8 | import app.meetacy.sdk.users.AuthorizedUserRepository 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.map 11 | 12 | public class AuthorizedMeetingParticipantsApi(private val api: AuthorizedMeetacyApi) { 13 | public val token: Token get() = api.token 14 | public val base: MeetingParticipantsApi get() = api.base.meetings.participants 15 | 16 | public suspend fun list( 17 | meetingId: MeetingId, 18 | amount: Amount, 19 | pagingId: PagingId? = null 20 | ): PagingRepository = base 21 | .list(token, meetingId, amount, pagingId) 22 | .mapItems { user -> 23 | AuthorizedUserRepository.of( 24 | data = user.data, 25 | api = api 26 | ) 27 | } 28 | 29 | public suspend fun paging( 30 | meetingId: MeetingId, 31 | chunkSize: Amount, 32 | startPagingId: PagingId? = null, 33 | limit: Amount? = null 34 | ): PagingSource = base 35 | .paging(token, meetingId, chunkSize, startPagingId, limit) 36 | .mapItems { user -> 37 | AuthorizedUserRepository.of( 38 | data = user.data, 39 | api = api 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/meetings/participants/AuthorizedMeetingParticipantsRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.meetings.participants 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.meeting.MeetingId 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingRepository 8 | import app.meetacy.sdk.types.paging.PagingSource 9 | import app.meetacy.sdk.users.AuthorizedUserRepository 10 | 11 | public class AuthorizedMeetingParticipantsRepository( 12 | private val meetingId: MeetingId, 13 | private val api: AuthorizedMeetacyApi 14 | ) { 15 | public suspend fun list( 16 | amount: Amount, 17 | pagingId: PagingId? = null 18 | ): PagingRepository = api.meetings 19 | .participants 20 | .list(meetingId, amount, pagingId) 21 | 22 | public suspend fun paging( 23 | chunkSize: Amount, 24 | startPagingId: PagingId? = null, 25 | limit: Amount? = null 26 | ): PagingSource = api.meetings 27 | .participants 28 | .paging(meetingId, chunkSize, startPagingId, limit) 29 | } 30 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/meetings/participants/MeetingParticipantsApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.meetings.participants 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.ListMeetingParticipantsRequest 5 | import app.meetacy.sdk.types.amount.Amount 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.meeting.MeetingId 8 | import app.meetacy.sdk.types.paging.PagingId 9 | import app.meetacy.sdk.types.paging.PagingRepository 10 | import app.meetacy.sdk.types.paging.PagingSource 11 | import app.meetacy.sdk.users.UserRepository 12 | 13 | public class MeetingParticipantsApi(private val api: MeetacyApi) { 14 | public suspend fun list( 15 | token: Token, 16 | meetingId: MeetingId, 17 | amount: Amount, 18 | pagingId: PagingId? = null 19 | ): PagingRepository = PagingRepository( 20 | amount = amount, 21 | startPagingId = pagingId 22 | ) { currentAmount, currentPagingId -> 23 | api.engine.execute( 24 | request = ListMeetingParticipantsRequest( 25 | token = token, 26 | meetingId = meetingId, 27 | amount = currentAmount, 28 | pagingId = currentPagingId 29 | ) 30 | ).paging.mapItems { user -> UserRepository.of(user, api) } 31 | } 32 | 33 | public suspend fun paging( 34 | token: Token, 35 | meetingId: MeetingId, 36 | chunkSize: Amount, 37 | startPagingId: PagingId? = null, 38 | limit: Amount? = null 39 | ): PagingSource { 40 | return PagingSource(chunkSize, startPagingId, limit) { currentAmount, currentPagingId -> 41 | list(token, meetingId, currentAmount, currentPagingId).response 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/meetings/participants/MeetingParticipantsRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.meetings.participants 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.meeting.MeetingId 7 | import app.meetacy.sdk.types.paging.PagingId 8 | import app.meetacy.sdk.types.paging.PagingRepository 9 | import app.meetacy.sdk.types.paging.PagingResponse 10 | import app.meetacy.sdk.types.paging.PagingSource 11 | import app.meetacy.sdk.users.UserRepository 12 | import kotlinx.coroutines.flow.Flow 13 | 14 | public class MeetingParticipantsRepository( 15 | private val meetingId: MeetingId, 16 | private val api: MeetacyApi 17 | ) { 18 | public suspend fun list( 19 | token: Token, 20 | amount: Amount, 21 | pagingId: PagingId? = null 22 | ): PagingRepository = api.meetings 23 | .participants 24 | .list(token, meetingId, amount, pagingId) 25 | 26 | public suspend fun paging( 27 | token: Token, 28 | chunkSize: Amount, 29 | startPagingId: PagingId? = null, 30 | limit: Amount? = null 31 | ): PagingSource = api.meetings 32 | .participants 33 | .paging(token, meetingId, chunkSize, startPagingId, limit) 34 | } 35 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/notifications/AuthorizedNotificationsApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.notifications 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.notification.NotificationId 7 | import app.meetacy.sdk.types.paging.PagingId 8 | import app.meetacy.sdk.types.paging.PagingRepository 9 | import app.meetacy.sdk.types.paging.mapItems 10 | 11 | /** 12 | * When modifying this class, corresponding classes should be altered: 13 | * - [app.meetacy.sdk.notifications.AuthorizedNotificationRepository] 14 | */ 15 | public class AuthorizedNotificationsApi( 16 | private val api: AuthorizedMeetacyApi 17 | ) { 18 | public val token: Token get() = api.token 19 | public val base: NotificationsApi get() = api.base.notifications 20 | 21 | public suspend fun read(lastNotificationId: NotificationId) { 22 | base.read(token, lastNotificationId) 23 | } 24 | 25 | public suspend fun list( 26 | amount: Amount, 27 | pagingId: PagingId? = null 28 | ): PagingRepository { 29 | return base.list(token, amount, pagingId).mapItems { notification -> 30 | AuthorizedNotificationRepository.of( 31 | data = notification.data, 32 | api = api 33 | ) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/search/SearchItemRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.search 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.meetings.AuthorizedMeetingRepository 5 | import app.meetacy.sdk.meetings.MeetingRepository 6 | import app.meetacy.sdk.types.place.Place as PlaceData 7 | import app.meetacy.sdk.types.search.SearchItem 8 | import app.meetacy.sdk.users.UserRepository 9 | 10 | /** 11 | * When modifying this class, corresponding classes should be altered: 12 | * - [AuthorizedMeetingRepository] 13 | */ 14 | 15 | public sealed class SearchItemRepository { 16 | public abstract val data: SearchItem 17 | 18 | public class Meeting( 19 | override val data: SearchItem.Meeting, 20 | private val api: MeetacyApi 21 | ) : SearchItemRepository() { 22 | public val meeting: MeetingRepository 23 | get() = MeetingRepository(data.meeting, api) 24 | } 25 | 26 | public class Place( 27 | override val data: SearchItem.Place 28 | ) : SearchItemRepository() { 29 | public val place: PlaceData 30 | get() = data.place 31 | } 32 | 33 | public class User( 34 | override val data: SearchItem.User, 35 | private val api: MeetacyApi 36 | ) : SearchItemRepository() { 37 | public val user: UserRepository 38 | get() = UserRepository.of(data.user, api) 39 | } 40 | 41 | public companion object { 42 | public fun of(data: SearchItem, api: MeetacyApi): SearchItemRepository { 43 | return when (data) { 44 | is SearchItem.Meeting -> Meeting(data, api) 45 | is SearchItem.Place -> Place(data) 46 | is SearchItem.User -> User(data, api) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/updates/AuthorizedUpdateRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.updates 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.notifications.AuthorizedNotificationRepository 5 | import app.meetacy.sdk.types.update.Update 6 | 7 | public sealed class AuthorizedUpdateRepository { 8 | public abstract val data: Update 9 | protected abstract val api: AuthorizedMeetacyApi 10 | 11 | public val base: UpdateRepository get() = UpdateRepository.of(data, api.base) 12 | 13 | public class Notification( 14 | override val data: Update.Notification, 15 | override val api: AuthorizedMeetacyApi 16 | ) : AuthorizedUpdateRepository() { 17 | public val notification: AuthorizedNotificationRepository 18 | get() = AuthorizedNotificationRepository.of(data.notification, api) 19 | } 20 | 21 | public companion object { 22 | public fun of(data: Update, api: AuthorizedMeetacyApi): AuthorizedUpdateRepository = 23 | when (data) { 24 | is Update.Notification -> Notification(data, api) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/updates/AuthorizedUpdatesApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.updates 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.update.Update 6 | import app.meetacy.sdk.types.update.UpdateId 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.flow.map 9 | 10 | public class AuthorizedUpdatesApi( 11 | private val api: AuthorizedMeetacyApi 12 | ) { 13 | public val token: Token get() = api.token 14 | public val base: UpdatesApi get() = api.base.updates 15 | 16 | public fun flow(fromId: UpdateId? = null): Flow { 17 | return base.flow(token, fromId).map { update -> 18 | AuthorizedUpdateRepository.of(update.data, api) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/updates/UpdateRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.updates 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.notifications.NotificationRepository 5 | import app.meetacy.sdk.types.update.Update 6 | import app.meetacy.sdk.types.update.UpdateId 7 | 8 | public sealed class UpdateRepository { 9 | public abstract val data: Update 10 | protected abstract val api: MeetacyApi 11 | 12 | public val id: UpdateId get() = data.id 13 | 14 | public class Notification( 15 | override val data: Update.Notification, 16 | override val api: MeetacyApi 17 | ) : UpdateRepository() { 18 | public val notification: NotificationRepository 19 | get() = NotificationRepository.of(data.notification, api) 20 | } 21 | 22 | public companion object { 23 | public fun of(data: Update, api: MeetacyApi): UpdateRepository { 24 | return when (data) { 25 | is Update.Notification -> Notification(data, api) 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/updates/UpdatesApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.updates 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.engine.requests.EmitUpdatesRequest 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.update.UpdateId 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.flow.flow 9 | import kotlinx.coroutines.flow.map 10 | 11 | public class UpdatesApi(private val api: MeetacyApi) { 12 | public fun flow( 13 | token: Token, 14 | fromId: UpdateId? = null 15 | ): Flow { 16 | return flow { 17 | api.engine.execute( 18 | request = EmitUpdatesRequest( 19 | token = token, 20 | fromId = fromId, 21 | collector = this 22 | ) 23 | ) 24 | }.map { event -> UpdateRepository.of(event.update, api) } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/AuthorizedRegularUserRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.files.FileRepository 5 | import app.meetacy.sdk.types.email.Email 6 | import app.meetacy.sdk.types.user.RegularUser 7 | import app.meetacy.sdk.types.user.Relationship 8 | import app.meetacy.sdk.types.user.UserId 9 | import app.meetacy.sdk.types.user.Username 10 | import app.meetacy.sdk.users.subscribers.AuthorizedSubscribersRepository 11 | import app.meetacy.sdk.users.subscriptions.AuthorizedSubscriptionsRepository 12 | 13 | public class AuthorizedRegularUserRepository( 14 | override val data: RegularUser, 15 | private val api: AuthorizedMeetacyApi 16 | ) : AuthorizedUserRepository { 17 | override val base: RegularUserRepository get() = RegularUserRepository(data, api.base) 18 | 19 | override val id: UserId get() = data.id 20 | override val nickname: String get() = data.nickname 21 | override val avatar: FileRepository? get() = FileRepository(data.avatarId, api) 22 | override val username: Username? get() = data.username 23 | override val relationship: Relationship get() = data.relationship 24 | 25 | override val email: Nothing? get() = null 26 | override val emailVerified: Nothing? get() = null 27 | 28 | override val subscribers: AuthorizedSubscribersRepository = AuthorizedSubscribersRepository(api, id) 29 | override val subscriptions: AuthorizedSubscriptionsRepository = AuthorizedSubscriptionsRepository(api, id) 30 | 31 | public suspend fun addFriend() { 32 | api.friends.add(data.id) 33 | } 34 | 35 | public suspend fun deleteFriend() { 36 | api.friends.delete(data.id) 37 | } 38 | 39 | override suspend fun details(): AuthorizedUserDetailsRepository { 40 | return api.users.get(id) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/AuthorizedUserDetailsRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.files.FileRepository 5 | import app.meetacy.sdk.types.amount.Amount 6 | import app.meetacy.sdk.types.email.Email 7 | import app.meetacy.sdk.types.user.* 8 | import app.meetacy.sdk.users.subscribers.AuthorizedSubscribersRepository 9 | import app.meetacy.sdk.users.subscriptions.AuthorizedSubscriptionsRepository 10 | 11 | public sealed interface AuthorizedUserDetailsRepository { 12 | public val data: UserDetails 13 | public val base: UserDetailsRepository 14 | 15 | public val isSelf: Boolean 16 | public val relationship: Relationship? 17 | public val id: UserId 18 | public val nickname: String 19 | public val username: Username? 20 | public val email: Email? 21 | public val emailVerified: Boolean? 22 | public val avatar: FileRepository? 23 | public val subscribersAmount: Amount.OrZero 24 | public val subscriptionsAmount: Amount.OrZero 25 | 26 | public val subscribers: AuthorizedSubscribersRepository 27 | public val subscriptions: AuthorizedSubscriptionsRepository 28 | 29 | public fun toUser(): AuthorizedUserRepository 30 | public suspend fun updated(): AuthorizedUserDetailsRepository 31 | 32 | public companion object { 33 | public fun of( 34 | data: UserDetails, 35 | api: AuthorizedMeetacyApi 36 | ): AuthorizedUserDetailsRepository = when (data) { 37 | is RegularUserDetails -> AuthorizedRegularUserDetailsRepository(data, api) 38 | is SelfUserDetails -> AuthorizedSelfUserDetailsRepository(data, api) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/AuthorizedUserRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.files.FileRepository 5 | import app.meetacy.sdk.types.email.Email 6 | import app.meetacy.sdk.types.file.FileId 7 | import app.meetacy.sdk.types.user.* 8 | import app.meetacy.sdk.users.subscribers.AuthorizedSubscribersRepository 9 | import app.meetacy.sdk.users.subscribers.SubscribersRepository 10 | import app.meetacy.sdk.users.subscriptions.AuthorizedSubscriptionsRepository 11 | 12 | public sealed interface AuthorizedUserRepository { 13 | public val data: User 14 | public val base: UserRepository 15 | 16 | public val id: UserId 17 | public val email: Email? 18 | public val nickname: String 19 | public val emailVerified: Boolean? 20 | public val username: Username? 21 | public val relationship: Relationship? 22 | public val avatar: FileRepository? 23 | 24 | public val subscribers: AuthorizedSubscribersRepository 25 | public val subscriptions: AuthorizedSubscriptionsRepository 26 | 27 | public suspend fun details(): AuthorizedUserDetailsRepository 28 | 29 | public companion object { 30 | public fun of( 31 | data: User, 32 | api: AuthorizedMeetacyApi 33 | ): AuthorizedUserRepository = when (data) { 34 | is RegularUser -> AuthorizedRegularUserRepository(data, api) 35 | is SelfUser -> AuthorizedSelfUserRepository(data, api) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/RegularUserRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.files.FileRepository 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.user.* 7 | import app.meetacy.sdk.users.subscribers.SubscribersRepository 8 | import app.meetacy.sdk.users.subscriptions.SubscriptionsRepository 9 | 10 | public class RegularUserRepository( 11 | override val data: RegularUser, 12 | private val api: MeetacyApi 13 | ) : UserRepository { 14 | override val id: UserId get() = data.id 15 | override val nickname: String get() = data.nickname 16 | override val avatar: FileRepository? get() = FileRepository(data.avatarId, api) 17 | override val relationship: Relationship get() = data.relationship 18 | override val username: Username? get() = data.username 19 | 20 | override val isSelf: Boolean get() = false 21 | override val email: Nothing? get() = null 22 | override val emailVerified: Nothing? get() = null 23 | 24 | override val subscribers: SubscribersRepository = SubscribersRepository(api, id) 25 | override val subscriptions: SubscriptionsRepository = SubscriptionsRepository(api, id) 26 | 27 | public suspend fun addFriend(token: Token) { 28 | api.friends.add(token, data.id) 29 | } 30 | 31 | public suspend fun deleteFriend(token: Token) { 32 | api.friends.delete(token, data.id) 33 | } 34 | 35 | public suspend fun usernameAvailable(username: Username): Username { 36 | return api.users.usernameAvailable(username) 37 | } 38 | 39 | override suspend fun details(token: Token): UserDetailsRepository { 40 | return api.users.get(token, id) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/UserDetailsRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.files.FileRepository 5 | import app.meetacy.sdk.types.amount.Amount 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.email.Email 8 | import app.meetacy.sdk.types.user.* 9 | import app.meetacy.sdk.users.subscribers.SubscribersRepository 10 | import app.meetacy.sdk.users.subscriptions.SubscriptionsRepository 11 | 12 | public sealed interface UserDetailsRepository { 13 | public val data: UserDetails 14 | 15 | public val isSelf: Boolean 16 | public val relationship: Relationship? 17 | public val id: UserId 18 | public val nickname: String 19 | public val username: Username? 20 | public val email: Email? 21 | public val emailVerified: Boolean? 22 | public val avatar: FileRepository? 23 | public val subscribersAmount: Amount.OrZero 24 | public val subscriptionsAmount: Amount.OrZero 25 | 26 | public val subscribers: SubscribersRepository 27 | public val subscriptions: SubscriptionsRepository 28 | 29 | public suspend fun updated(token: Token): UserDetailsRepository 30 | public fun toUser(): UserRepository 31 | 32 | public companion object { 33 | public fun of( 34 | data: UserDetails, 35 | api: MeetacyApi 36 | ): UserDetailsRepository = when (data) { 37 | is RegularUserDetails -> RegularUserDetailsRepository(data, api) 38 | is SelfUserDetails -> SelfUserDetailsRepository(data, api) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.files.FileRepository 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.email.Email 7 | import app.meetacy.sdk.types.user.* 8 | import app.meetacy.sdk.users.subscribers.SubscribersRepository 9 | import app.meetacy.sdk.users.subscriptions.SubscriptionsRepository 10 | 11 | public sealed interface UserRepository { 12 | public val data: User 13 | 14 | public val isSelf: Boolean 15 | public val id: UserId 16 | public val email: Email? 17 | public val nickname: String 18 | public val emailVerified: Boolean? 19 | public val username: Username? 20 | public val relationship: Relationship? 21 | public val avatar: FileRepository? 22 | 23 | public val subscribers: SubscribersRepository 24 | public val subscriptions: SubscriptionsRepository 25 | 26 | public suspend fun details(token: Token): UserDetailsRepository 27 | 28 | public companion object { 29 | public fun of( 30 | data: User, 31 | api: MeetacyApi 32 | ): UserRepository = when (data) { 33 | is RegularUser -> RegularUserRepository(data, api) 34 | is SelfUser -> SelfUserRepository(data, api) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/subscribers/AuthorizedSubscribersRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users.subscribers 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.paging.PagingId 6 | import app.meetacy.sdk.types.paging.PagingRepository 7 | import app.meetacy.sdk.types.paging.PagingSource 8 | import app.meetacy.sdk.types.user.UserId 9 | import app.meetacy.sdk.users.AuthorizedUserRepository 10 | 11 | public class AuthorizedSubscribersRepository( 12 | private val api: AuthorizedMeetacyApi, 13 | private val userId: UserId 14 | ) { 15 | public suspend fun list( 16 | amount: Amount, 17 | pagingId: PagingId? = null, 18 | ): PagingRepository { 19 | return api.friends.subscribers.list(amount, pagingId, userId) 20 | } 21 | 22 | public fun paging( 23 | chunkSize: Amount, 24 | startPagingId: PagingId? = null, 25 | limit: Amount? = null 26 | ): PagingSource { 27 | return api.friends.subscribers.paging(chunkSize, startPagingId, limit, userId) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/subscribers/SubscribersRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users.subscribers 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingRepository 8 | import app.meetacy.sdk.types.paging.PagingSource 9 | import app.meetacy.sdk.types.user.UserId 10 | import app.meetacy.sdk.users.UserRepository 11 | 12 | public class SubscribersRepository( 13 | private val api: MeetacyApi, 14 | private val userId: UserId 15 | ) { 16 | public suspend fun list( 17 | token: Token, 18 | amount: Amount, 19 | pagingId: PagingId? = null, 20 | ): PagingRepository { 21 | return api.friends.subscribers.list(token, amount, pagingId, userId) 22 | } 23 | 24 | public fun paging( 25 | token: Token, 26 | chunkSize: Amount, 27 | startPagingId: PagingId? = null, 28 | limit: Amount? = null 29 | ): PagingSource { 30 | return api.friends.subscribers.paging(token, chunkSize, startPagingId, limit, userId) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/subscriptions/AuthorizedSubscriptionsRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users.subscriptions 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.paging.PagingId 6 | import app.meetacy.sdk.types.paging.PagingRepository 7 | import app.meetacy.sdk.types.paging.PagingSource 8 | import app.meetacy.sdk.types.user.UserId 9 | import app.meetacy.sdk.users.AuthorizedUserRepository 10 | 11 | public class AuthorizedSubscriptionsRepository( 12 | private val api: AuthorizedMeetacyApi, 13 | private val userId: UserId 14 | ) { 15 | public suspend fun list( 16 | amount: Amount, 17 | pagingId: PagingId? = null, 18 | ): PagingRepository { 19 | return api.friends.subscriptions.list(amount, pagingId, userId) 20 | } 21 | 22 | public fun paging( 23 | chunkSize: Amount, 24 | startPagingId: PagingId? = null, 25 | limit: Amount? = null 26 | ): PagingSource { 27 | return api.friends.subscriptions.paging(chunkSize, startPagingId, limit, userId) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/users/subscriptions/SubscriptionsRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.users.subscriptions 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.types.amount.Amount 5 | import app.meetacy.sdk.types.auth.Token 6 | import app.meetacy.sdk.types.paging.PagingId 7 | import app.meetacy.sdk.types.paging.PagingRepository 8 | import app.meetacy.sdk.types.paging.PagingSource 9 | import app.meetacy.sdk.types.user.UserId 10 | import app.meetacy.sdk.users.UserRepository 11 | 12 | public class SubscriptionsRepository( 13 | private val api: MeetacyApi, 14 | private val userId: UserId 15 | ) { 16 | public suspend fun list( 17 | token: Token, 18 | amount: Amount, 19 | pagingId: PagingId? = null, 20 | ): PagingRepository { 21 | return api.friends.subscriptions.list(token, amount, pagingId, userId) 22 | } 23 | 24 | public fun paging( 25 | token: Token, 26 | chunkSize: Amount, 27 | startPagingId: PagingId? = null, 28 | limit: Amount? = null 29 | ): PagingSource { 30 | return api.friends.subscriptions.paging(token, chunkSize, startPagingId, limit, userId) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/commonMain/kotlin/app/meetacy/sdk/version/ApiVersion.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.version 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class ApiVersion(public val int: Int) { 7 | public companion object { 8 | // First versioning was implemented 9 | public val VersioningFeature: ApiVersion = ApiVersion(0) 10 | 11 | public fun latest(): ApiVersion = VersioningFeature 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /api/src/iosMain/kotlin/app/meetacy/sdk/files/DownloadableFile.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.* 4 | import kotlinx.coroutines.CoroutineScope 5 | import platform.Foundation.NSFileHandle 6 | import platform.Foundation.NSURL 7 | 8 | public suspend inline fun DownloadableFile.download( 9 | destination: NSURL, 10 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 11 | ) { 12 | input.use { input -> 13 | destination.asMeetacyOutputSource().use { output -> 14 | input.transferTo(output) { written -> onUpdate(written, size) } 15 | } 16 | } 17 | } 18 | 19 | public suspend inline fun DownloadableFile.download( 20 | crossinline destination: suspend () -> NSFileHandle, 21 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 22 | ) { 23 | input.use { input -> 24 | object : OutputSource { 25 | override suspend fun open(scope: CoroutineScope) = destination().asMeetacyOutput() 26 | }.use { output -> 27 | input.transferTo(output) { written -> onUpdate(written, size) } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/src/iosMain/kotlin/app/meetacy/sdk/files/FilesRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.* 4 | import kotlinx.coroutines.CoroutineScope 5 | import platform.Foundation.NSFileHandle 6 | import platform.Foundation.NSURL 7 | 8 | public suspend inline fun FileRepository.download( 9 | destination: NSURL, 10 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 11 | ) { 12 | val file = api.files.get(id) 13 | 14 | input.use { input -> 15 | destination.asMeetacyOutputSource().use { output -> 16 | input.transferTo(output) { written -> 17 | onUpdate(written, file.size) 18 | } 19 | } 20 | } 21 | } 22 | 23 | public suspend inline fun FileRepository.download( 24 | crossinline destination: suspend () -> NSFileHandle, 25 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 26 | ) { 27 | val file = api.files.get(id) 28 | 29 | input.use { input -> 30 | object : OutputSource { 31 | override suspend fun open(scope: CoroutineScope) = destination().asMeetacyOutput() 32 | }.use { output -> 33 | input.transferTo(output) { written -> 34 | onUpdate(written, file.size) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /api/src/iosMain/kotlin/app/meetacy/sdk/files/NSURLExt.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import platform.Foundation.NSData 4 | import platform.Foundation.NSURL 5 | import platform.Foundation.create 6 | 7 | public val NSURL.fileSize: ULong? get() = NSData.create(this)?.length 8 | -------------------------------------------------------------------------------- /api/src/jsMain/kotlin/app/meetacy/sdk/files/AuthorizedFilesApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.asMeetacyInputSource 4 | import app.meetacy.sdk.types.file.FileId 5 | import org.w3c.files.Blob 6 | 7 | public suspend inline fun AuthorizedFilesApi.upload( 8 | source: Blob, 9 | crossinline onUpdate: (uploaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 10 | ): FileId { 11 | val fileSize = source.size.toLong() 12 | return upload( 13 | file = UploadableFile( 14 | size = fileSize, 15 | input = source.asMeetacyInputSource().withCallback { read -> onUpdate(read, fileSize) } 16 | ) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /api/src/jsMain/kotlin/app/meetacy/sdk/files/FilesApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.asMeetacyInputSource 4 | import app.meetacy.sdk.types.auth.Token 5 | import app.meetacy.sdk.types.file.FileId 6 | import org.w3c.files.Blob 7 | 8 | public suspend inline fun FilesApi.upload( 9 | token: Token, 10 | source: Blob, 11 | crossinline onUpdate: (uploaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 12 | ): FileId { 13 | val fileSize = source.size.toLong() 14 | 15 | return upload( 16 | token = token, 17 | source = UploadableFile( 18 | size = fileSize, 19 | input = source.asMeetacyInputSource().withCallback { read -> onUpdate(read, fileSize) } 20 | ) 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /api/src/jvmMain/kotlin/app/meetacy/sdk/files/DownloadableFile.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.* 4 | import kotlinx.coroutines.CoroutineScope 5 | import java.io.File 6 | import java.io.OutputStream 7 | 8 | public suspend inline fun DownloadableFile.download( 9 | destination: File, 10 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 11 | ) { 12 | input.use { input -> 13 | destination.asMeetacyOutputSource().use { output -> 14 | input.transferTo(output) { written -> onUpdate(written, size) } 15 | } 16 | } 17 | } 18 | 19 | public suspend inline fun DownloadableFile.download( 20 | crossinline destination: suspend () -> OutputStream, 21 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 22 | ) { 23 | input.use { input -> 24 | object : OutputSource { 25 | override suspend fun open(scope: CoroutineScope) = destination().asMeetacyOutput() 26 | }.use { output -> 27 | input.transferTo(output) { written -> onUpdate(written, size) } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/src/jvmMain/kotlin/app/meetacy/sdk/files/FilesRepository.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.files 2 | 3 | import app.meetacy.sdk.io.* 4 | import kotlinx.coroutines.CoroutineScope 5 | import java.io.File 6 | import java.io.OutputStream 7 | 8 | public suspend inline fun FileRepository.download( 9 | destination: File, 10 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 11 | ) { 12 | val file = api.files.get(id) 13 | 14 | input.use { input -> 15 | destination.asMeetacyOutputSource().use { output -> 16 | input.transferTo(output) { written -> 17 | onUpdate(written, file.size) 18 | } 19 | } 20 | } 21 | } 22 | 23 | public suspend inline fun FileRepository.download( 24 | crossinline destination: suspend () -> OutputStream, 25 | crossinline onUpdate: (downloaded: Long, totalBytes: Long) -> Unit = { _, _ -> } 26 | ) { 27 | val file = api.files.get(id) 28 | 29 | input.use { input -> 30 | object : OutputSource { 31 | override suspend fun open(scope: CoroutineScope) = destination().asMeetacyOutput() 32 | }.use { output -> 33 | input.transferTo(output) { written -> 34 | onUpdate(written, file.size) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /build-logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | plugins { 3 | `kotlin-dsl` 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | google() 9 | gradlePluginPortal() 10 | } 11 | 12 | dependencies { 13 | api(libs.kotlinxSerializationPlugin) 14 | api(libs.kotlinPlugin) 15 | } 16 | -------------------------------------------------------------------------------- /build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories { 3 | mavenCentral() 4 | google() 5 | } 6 | 7 | versionCatalogs { 8 | create("libs") { 9 | from(files("../gradle/libs.versions.toml")) 10 | } 11 | } 12 | } 13 | 14 | include(":library-deploy") 15 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/Version.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Project 2 | 3 | fun Project.versionFromProperties(acceptor: (String) -> Unit) { 4 | afterEvaluate { 5 | acceptor(versionFromProperties()) 6 | } 7 | } 8 | 9 | fun Project.versionFromProperties(): String { 10 | val snapshot = project.findProperty("snapshot")?.toString()?.toBooleanStrict() 11 | if (snapshot == null || !snapshot) return project.version.toString() 12 | 13 | val commit = project.property("commit").toString() 14 | val attempt = project.property("attempt").toString().toInt() 15 | 16 | val version = buildString { 17 | append(project.version) 18 | append("-build") 19 | append(commit.take(n = 7)) 20 | if (attempt > 1) { 21 | append(attempt) 22 | } 23 | } 24 | 25 | return version 26 | } 27 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/example-logic-convention.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.kotlin 2 | 3 | plugins { 4 | kotlin("multiplatform") 5 | } 6 | 7 | kotlin { 8 | iosX64() 9 | iosArm64() 10 | iosSimulatorArm64() 11 | 12 | sourceSets { 13 | val commonMain by getting 14 | val iosX64Main by getting 15 | val iosArm64Main by getting 16 | val iosSimulatorArm64Main by getting 17 | val iosMain by creating { 18 | dependsOn(commonMain) 19 | iosX64Main.dependsOn(this) 20 | iosArm64Main.dependsOn(this) 21 | iosSimulatorArm64Main.dependsOn(this) 22 | } 23 | } 24 | } 25 | 26 | afterEvaluate { 27 | val xcodeDir = File(project.buildDir, "xcode") 28 | 29 | tasks.filterIsInstance() 30 | .forEach { xcFrameworkTask -> 31 | val syncName: String = xcFrameworkTask.name.replace("assemble", "sync") 32 | val xcframeworkDir = 33 | File(xcFrameworkTask.outputDir, xcFrameworkTask.buildType.getName()) 34 | 35 | tasks.create(syncName, Sync::class) { 36 | this.group = "xcode" 37 | 38 | this.from(xcframeworkDir) 39 | this.into(xcodeDir) 40 | 41 | this.dependsOn(xcFrameworkTask) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/kmp-library-convention.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_VARIABLE") 2 | 3 | plugins { 4 | kotlin("multiplatform") 5 | kotlin("plugin.serialization") 6 | id("publication-convention") 7 | } 8 | 9 | kotlin { 10 | jvm { 11 | jvmToolchain(8) 12 | } 13 | js(IR) { 14 | browser() 15 | nodejs() 16 | } 17 | iosArm64() 18 | iosX64() 19 | iosSimulatorArm64() 20 | explicitApi() 21 | 22 | sourceSets { 23 | val commonMain by getting { 24 | dependencies { 25 | implementation(kotlin("stdlib")) 26 | } 27 | } 28 | val commonTest by getting 29 | 30 | val iosX64Main by getting 31 | val iosArm64Main by getting 32 | val iosSimulatorArm64Main by getting 33 | 34 | val iosMain by creating { 35 | dependsOn(commonMain) 36 | iosX64Main.dependsOn(this) 37 | iosArm64Main.dependsOn(this) 38 | iosSimulatorArm64Main.dependsOn(this) 39 | } 40 | 41 | val iosX64Test by getting 42 | val iosArm64Test by getting 43 | val iosSimulatorArm64Test by getting 44 | val iosTest by creating { 45 | dependsOn(commonTest) 46 | iosX64Test.dependsOn(this) 47 | iosArm64Test.dependsOn(this) 48 | iosSimulatorArm64Test.dependsOn(this) 49 | } 50 | 51 | all { 52 | languageSettings.enableLanguageFeature("DataObjects") 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/print-sdk-version-convention.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_VARIABLE") 2 | 3 | import org.gradle.kotlin.dsl.creating 4 | 5 | tasks { 6 | val printSdkVersion by creating { 7 | group = "CI" 8 | 9 | doFirst { 10 | println(versionFromProperties()) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /build-logic/src/main/kotlin/publication-convention.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.maven-publish") 3 | } 4 | 5 | group = "app.meetacy.sdk" 6 | 7 | publishing { 8 | repositories { 9 | maven { 10 | name = "MeetacySdk" 11 | url = uri("https://maven.pkg.github.com/meetacy/sdk") 12 | credentials { 13 | username = System.getenv("GITHUB_USERNAME") 14 | password = System.getenv("GITHUB_TOKEN") 15 | } 16 | } 17 | } 18 | 19 | publications.withType { 20 | versionFromProperties { version -> 21 | this.version = version 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("print-sdk-version-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/ActivityIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActivityIndicator.swift 3 | // iosApp 4 | // 5 | // Created by Mark Dubkov on 04.06.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ActivityIndicator: UIViewRepresentable { 11 | 12 | @Binding var isAnimating: Bool 13 | let style: UIActivityIndicatorView.Style 14 | 15 | init( 16 | isAnimating: Binding = .constant(true), 17 | style: UIActivityIndicatorView.Style = .medium 18 | ) { 19 | _isAnimating = isAnimating 20 | self.style = style 21 | } 22 | 23 | func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView { 24 | return UIActivityIndicatorView(style: style) 25 | } 26 | 27 | func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) { 28 | isAnimating ? uiView.startAnimating() : uiView.stopAnimating() 29 | } 30 | } 31 | 32 | struct ActivityIndicator_Previews: PreviewProvider { 33 | static var previews: some View { 34 | ActivityIndicator(isAnimating: .constant(true), style: .medium) 35 | ActivityIndicator(isAnimating: .constant(false), style: .medium) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "1x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "2x", 16 | "size" : "16x16" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "1x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "2x", 26 | "size" : "32x32" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "2x", 36 | "size" : "128x128" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "1x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "2x", 46 | "size" : "256x256" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "1x", 51 | "size" : "512x512" 52 | }, 53 | { 54 | "idiom" : "mac", 55 | "scale" : "2x", 56 | "size" : "512x512" 57 | } 58 | ], 59 | "info" : { 60 | "author" : "xcode", 61 | "version" : 1 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/AuthScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthScreen.swift 3 | // iosApp 4 | // 5 | // Created by Mark Dubkov on 03.06.2023. 6 | // 7 | 8 | import SwiftUI 9 | import shared 10 | 11 | @MainActor class AuthScreenViewModel: ObservableObject { 12 | 13 | private let useCase = iosApp.factory.createRegisterUseCase() 14 | 15 | @Published var isLoading: Bool = false 16 | @Published var text: String = "" 17 | @Published var error: String? 18 | 19 | func register() { 20 | Task { @MainActor in 21 | await registerAsync() 22 | } 23 | } 24 | 25 | private func registerAsync() async { 26 | if text.isEmpty { return } 27 | isLoading = true 28 | defer { 29 | isLoading = false 30 | } 31 | do { 32 | try await useCase.register(nickName: text) 33 | } catch { 34 | self.error = "Some error" 35 | } 36 | } 37 | } 38 | 39 | struct AuthScreen: View { 40 | @StateObject var viewModel: AuthScreenViewModel = .init() 41 | 42 | var body: some View { 43 | VStack(alignment: .leading) { 44 | HStack { 45 | Text("Nickname") 46 | if viewModel.isLoading { 47 | ActivityIndicator() 48 | } 49 | } 50 | TextField("Name", text: $viewModel.text) 51 | Button { 52 | viewModel.register() 53 | } label: { 54 | Text("Register") 55 | } 56 | 57 | } 58 | .padding() 59 | .navigationTitle("Auth") 60 | } 61 | } 62 | 63 | struct AuthScreen_Previews: PreviewProvider { 64 | static var previews: some View { 65 | AuthScreen() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/iosApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/iosApp/iosApp/iosAppApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iosAppApp.swift 3 | // iosApp 4 | // 5 | // Created by Mark Dubkov on 03.06.2023. 6 | // 7 | 8 | import SwiftUI 9 | import shared 10 | 11 | enum Routes { 12 | case register 13 | case files 14 | } 15 | 16 | @main 17 | struct iosApp: App { 18 | 19 | static var factory: SharedFactory! 20 | 21 | init() { 22 | iosApp.factory = SharedFactory() 23 | } 24 | 25 | var body: some Scene { 26 | WindowGroup { 27 | NavigationStack { 28 | VStack(spacing: 20) { 29 | NavigationLink("Auth", value: Routes.register) 30 | NavigationLink("Files", value: Routes.files) 31 | } 32 | .navigationDestination( 33 | for: Routes.self, 34 | destination: { routes in 35 | switch routes { 36 | case .files: 37 | FilesScreen() 38 | case .register: 39 | AuthScreen() 40 | } 41 | } 42 | ) 43 | } 44 | } 45 | } 46 | } 47 | 48 | private struct MainScreen: View { 49 | 50 | var body: some View { 51 | VStack { 52 | NavigationLink("Auth", value: Routes.files) 53 | NavigationLink("Auth", value: Routes.files) 54 | } 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /example/shared/build.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework 3 | 4 | plugins { 5 | id("example-logic-convention") 6 | } 7 | 8 | dependencies { 9 | commonMainImplementation(libs.kotlinxCoroutines) 10 | commonMainApi(projects.api.apiKtor) 11 | commonMainApi(projects.api) 12 | commonMainApi(projects.io) 13 | 14 | iosMainImplementation(libs.ktorClientDarwin) 15 | } 16 | 17 | kotlin { 18 | val xcf = XCFramework("shared") 19 | listOf( 20 | iosX64(), 21 | iosArm64(), 22 | iosSimulatorArm64() 23 | ).forEach { target -> 24 | target.binaries.framework { 25 | baseName = "shared" 26 | export(projects.api.apiKtor) 27 | export(projects.api) 28 | export(projects.io) 29 | xcf.add(this) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/shared/src/commonMain/kotlin/app/meetacy/sdk/example/Environment.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.example 2 | 3 | import app.meetacy.sdk.types.auth.Token 4 | 5 | interface Environment { 6 | var currentToken: Token? 7 | } -------------------------------------------------------------------------------- /example/shared/src/commonMain/kotlin/app/meetacy/sdk/example/FilesUseCase.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.example 2 | 3 | import app.meetacy.sdk.AuthorizedMeetacyApi 4 | import app.meetacy.sdk.files.AuthorizedFilesApi 5 | import app.meetacy.sdk.files.DownloadableFile 6 | import app.meetacy.sdk.types.file.FileId 7 | import kotlinx.coroutines.CoroutineScope 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.flow.MutableStateFlow 10 | import kotlinx.coroutines.flow.launchIn 11 | import kotlinx.coroutines.flow.onEach 12 | import kotlinx.coroutines.flow.update 13 | import kotlinx.coroutines.withContext 14 | 15 | class FilesUseCase internal constructor( 16 | meetacyApi: AuthorizedMeetacyApi 17 | ) { 18 | 19 | private val useCaseScope = CoroutineScope(Dispatchers.Main) 20 | private val uploadedFileId: MutableStateFlow = MutableStateFlow(null) 21 | 22 | val filesApi: AuthorizedFilesApi = meetacyApi.files 23 | 24 | fun setupFileId(fileId: String) { 25 | uploadedFileId.update { fileId } 26 | } 27 | 28 | fun subscribe(onChange: (DownloadableFile) -> Unit) { 29 | uploadedFileId.onEach { fileId -> 30 | val fileIdNotNull = fileId ?: return@onEach 31 | withContext(Dispatchers.Main) { 32 | filesApi.get(FileId(fileIdNotNull)).also(onChange) 33 | } 34 | }.launchIn(useCaseScope) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/shared/src/commonMain/kotlin/app/meetacy/sdk/example/RegisterUseCase.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.example 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.withContext 6 | 7 | class RegisterUseCase internal constructor( 8 | private val environment: Environment, 9 | private val meetacyApi: MeetacyApi 10 | ) { 11 | 12 | suspend fun register(nickName: String) { 13 | withContext(Dispatchers.Main) { 14 | environment.currentToken = meetacyApi.auth.generate(nickName) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/shared/src/commonMain/kotlin/app/meetacy/sdk/example/SharedFactory.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.example 2 | 3 | import app.meetacy.sdk.MeetacyApi 4 | import app.meetacy.sdk.production 5 | import app.meetacy.sdk.types.auth.Token 6 | import io.ktor.client.HttpClient 7 | import io.ktor.client.plugins.logging.LogLevel 8 | import io.ktor.client.plugins.logging.Logger 9 | import io.ktor.client.plugins.logging.Logging 10 | 11 | class SharedFactory { 12 | 13 | private val httpClient: HttpClient by lazy { 14 | HttpClient(createHttpClientEngine()) { 15 | expectSuccess = false 16 | install(Logging) { 17 | logger = object : Logger { 18 | override fun log(message: String): Unit = 19 | println(message) 20 | } 21 | level = LogLevel.HEADERS 22 | } 23 | } 24 | } 25 | 26 | private val environment = object : Environment { 27 | override var currentToken: Token? = null 28 | } 29 | 30 | private val meetacyApi: MeetacyApi by lazy { 31 | MeetacyApi.production(httpClient = httpClient) 32 | } 33 | 34 | fun createRegisterUseCase() = RegisterUseCase( 35 | environment = environment, 36 | meetacyApi = meetacyApi 37 | ) 38 | 39 | @Throws(IllegalStateException::class) 40 | fun createFilesUseCase() = FilesUseCase( 41 | meetacyApi = environment.currentToken 42 | ?.let { meetacyApi.authorized(it) } 43 | ?: error("Unauthorized") 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /example/shared/src/commonMain/kotlin/app/meetacy/sdk/example/createHttpClientEngine.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.example 2 | 3 | import io.ktor.client.engine.HttpClientEngine 4 | 5 | internal expect fun createHttpClientEngine(): HttpClientEngine 6 | -------------------------------------------------------------------------------- /example/shared/src/iosMain/kotlin/app/meetacy/sdk/example/createHttpClientEngine.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.example 2 | 3 | import io.ktor.client.engine.HttpClientEngine 4 | import io.ktor.client.engine.darwin.DarwinHttpRequestException 5 | import io.ktor.client.engine.darwin.DarwinLegacy 6 | import platform.Foundation.NSError 7 | import kotlin.native.concurrent.AtomicReference 8 | 9 | object ThrowableToNSErrorMapper : (Throwable) -> NSError? { 10 | private val mapperRef: AtomicReference<((Throwable) -> NSError?)?> = AtomicReference(null) 11 | 12 | override fun invoke(throwable: Throwable): NSError? { 13 | @Suppress("MaxLineLength") 14 | return requireNotNull(mapperRef.value) { 15 | "please setup ThrowableToNSErrorMapper by call ThrowableToNSErrorMapper.setup() in iosMain" 16 | }.invoke(throwable) 17 | } 18 | 19 | fun setup(block: (Throwable) -> NSError?) { 20 | mapperRef.value = block 21 | } 22 | } 23 | 24 | internal actual fun createHttpClientEngine(): HttpClientEngine { 25 | // configure darwin throwable mapper 26 | ThrowableToNSErrorMapper.setup { (it as? DarwinHttpRequestException)?.origin } 27 | // configure darwin 28 | return DarwinLegacy.create { 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m 2 | kotlin.code.style=official 3 | kotlin.mpp.enableCInteropCommonization=true 4 | org.gradle.caching=true 5 | org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | 3 | kotlin = "1.9.0" 4 | ktor = "2.3.6" 5 | meetacySdk = "0.0.71" 6 | 7 | # kotlinx 8 | kotlinxCoroutines = "1.6.4" 9 | kotlinxSerialization = "1.6.0" 10 | kotlinxDateTime = "0.4.0" 11 | rsocket = "0.15.4" 12 | 13 | [libraries] 14 | 15 | # ktor 16 | ktorClient = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } 17 | ktorClientContentNegotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } 18 | ktorClientCio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } 19 | ktorClientLogging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } 20 | ktorSerializationJson = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } 21 | ktorClientDarwin = { module = "io.ktor:ktor-client-darwin-legacy", version.ref = "ktor" } 22 | ktorClientMock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } 23 | ktorClientWebSockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" } 24 | 25 | rsocketKtorClient = { module = "io.rsocket.kotlin:rsocket-ktor-client", version.ref = "rsocket" } 26 | 27 | # kotlinx 28 | kotlinxCoroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } 29 | kotlinxCoroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } 30 | kotlinxSerialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } 31 | kotlinxDateTime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDateTime" } 32 | 33 | # gradle plugins 34 | kotlinxSerializationPlugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" } 35 | kotlinPlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } 36 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetacy/sdk/4673093606cacf21f8e850aba221a32487de67a6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /io/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kmp-library-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | 7 | dependencies { 8 | commonMainImplementation(libs.kotlinxCoroutines) 9 | } 10 | -------------------------------------------------------------------------------- /io/io-ktor/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kmp-library-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | 7 | dependencies { 8 | commonMainApi(projects.io) 9 | commonMainImplementation(libs.kotlinxCoroutines) 10 | commonMainImplementation(libs.ktorClient) 11 | } 12 | -------------------------------------------------------------------------------- /io/io-ktor/src/commonMain/kotlin/app/meetacy/sdk/io/ByteReadChannelExt.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.bytes.ByteArrayView 4 | import io.ktor.utils.io.* 5 | 6 | public suspend fun ByteReadChannel.readMaxBytes(destination: ByteArrayView): Int { 7 | return readMaxBytes( 8 | destination = destination, 9 | readAcc = 0 10 | ) 11 | } 12 | 13 | private tailrec suspend fun ByteReadChannel.readMaxBytes(destination: ByteArrayView, readAcc: Int): Int { 14 | val readSize = this.readAvailable( 15 | dst = destination.underlying, 16 | offset = destination.fromIndex + readAcc, 17 | length = destination.size - readAcc 18 | ) 19 | 20 | if (readSize <= 0) return readAcc 21 | 22 | return readMaxBytes(destination, readAcc = readAcc + readSize) 23 | } 24 | -------------------------------------------------------------------------------- /io/io-ktor/src/commonMain/kotlin/app/meetacy/sdk/io/InputIntegration.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.bytes.ByteArrayView 4 | import io.ktor.utils.io.* 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.launch 7 | 8 | public fun ByteReadChannel.asMeetacyInput(): Input = object : Input { 9 | override suspend fun read(destination: ByteArrayView): Int { 10 | return this@asMeetacyInput.readMaxBytes(destination) 11 | } 12 | 13 | override suspend fun close() { 14 | this@asMeetacyInput.cancel() 15 | } 16 | } 17 | 18 | public fun Input.asKtorChannel( 19 | scope: CoroutineScope, 20 | bufferSize: Int = DEFAULT_BUFFER_SIZE 21 | ): ByteReadChannel { 22 | val channel = ByteChannel() 23 | 24 | val job = scope.launch { 25 | this@asKtorChannel.readIterative(bufferSize) { bytes -> 26 | channel.writeFully(bytes.underlying, bytes.fromIndex, bytes.size) 27 | } 28 | channel.close() 29 | } 30 | channel.attachJob(job) 31 | 32 | return channel 33 | } 34 | -------------------------------------------------------------------------------- /io/io-ktor/src/commonMain/kotlin/app/meetacy/sdk/io/InputSourceIntegration.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import io.ktor.client.request.forms.* 4 | import io.ktor.utils.io.* 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.launch 7 | 8 | public fun ChannelProvider.asMeetacyInputSource(): InputSource { 9 | return object : InputSource { 10 | 11 | override suspend fun open(scope: CoroutineScope): Input { 12 | val channel = block() 13 | return channel.asMeetacyInput() 14 | } 15 | } 16 | } 17 | 18 | public fun InputSource.asKtorChannelProvider( 19 | scope: CoroutineScope, 20 | size: Long? = null, 21 | bufferSize: Int = DEFAULT_BUFFER_SIZE 22 | ): ChannelProvider { 23 | return ChannelProvider( 24 | size = size, 25 | block = { 26 | val channel = ByteChannel() 27 | 28 | val job = scope.launch { 29 | this@asKtorChannelProvider.use { input -> 30 | input.asKtorChannel( 31 | scope = this, 32 | bufferSize = bufferSize 33 | ).joinTo( 34 | dst = channel, 35 | closeOnEnd = true 36 | ) 37 | } 38 | } 39 | channel.attachJob(job) 40 | 41 | channel 42 | } 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /io/io-ktor/src/commonMain/kotlin/app/meetacy/sdk/io/OutputIntegration.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.bytes.ByteArrayView 4 | import io.ktor.utils.io.* 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.launch 7 | 8 | public fun ByteWriteChannel.asMeetacyOutput(): Output = object : Output { 9 | override suspend fun write(source: ByteArrayView) { 10 | this@asMeetacyOutput.writeFully(source.underlying, source.fromIndex, source.size) 11 | } 12 | 13 | override suspend fun close() { 14 | this@asMeetacyOutput.close() 15 | } 16 | } 17 | 18 | public fun Output.asKtorChannel( 19 | scope: CoroutineScope, 20 | bufferSize: Int = DEFAULT_BUFFER_SIZE 21 | ): ByteWriteChannel { 22 | val byteChannel = ByteChannel() 23 | 24 | val job = scope.launch { 25 | val buffer = ByteArrayView(bufferSize) 26 | 27 | this@asKtorChannel.writeIterative { 28 | byteChannel.readMaxBytes(buffer) 29 | buffer 30 | } 31 | } 32 | byteChannel.attachJob(job) 33 | 34 | return byteChannel 35 | } 36 | -------------------------------------------------------------------------------- /io/karma.config.d/page.js: -------------------------------------------------------------------------------- 1 | // karma.config.d/page.js 2 | config.set({ 3 | customContextFile: "../../../../../io/src/jsTest/resources/test.html" 4 | }) 5 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/Constants.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | public const val DEFAULT_BUFFER_SIZE: Int = 8 * 1024 4 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/IOSource.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.coroutineScope 5 | 6 | public interface IOSource { 7 | public val input: InputSource 8 | public val output: OutputSource 9 | 10 | public suspend fun open(scope: CoroutineScope): IOChannel 11 | } 12 | 13 | public suspend inline fun IOSource.use( 14 | crossinline block: (IOChannel) -> T 15 | ): T = coroutineScope { 16 | val io = open(scope = this) 17 | try { 18 | block(io) 19 | } finally { 20 | io.close() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/Input.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.annotation.IODslMarker 4 | import app.meetacy.sdk.io.bytes.ByteArrayView 5 | 6 | @IODslMarker 7 | public interface Input { 8 | public suspend fun read(destination: ByteArrayView): Int 9 | public suspend fun close() 10 | } 11 | 12 | public suspend inline fun Input.readIterative(buffer: ByteArrayView, consume: (bytes: ByteArrayView) -> Unit) { 13 | while (true) { 14 | val readSize = read(buffer) 15 | val newBuffer = buffer.adjustBounds(toIndex = buffer.fromIndex + readSize) 16 | consume(newBuffer) 17 | if (readSize != buffer.size) break 18 | } 19 | } 20 | 21 | public suspend inline fun Input.readIterative( 22 | bufferSize: Int = DEFAULT_BUFFER_SIZE, 23 | consume: (bytes: ByteArrayView) -> Unit 24 | ) { 25 | return readIterative(ByteArrayView(bufferSize), consume) 26 | } 27 | 28 | public suspend inline fun Input.transferTo( 29 | output: Output, 30 | bufferSize: Int = DEFAULT_BUFFER_SIZE, 31 | onUpdate: (written: Long) -> Unit = {} 32 | ) { 33 | var acc = 0L 34 | this.readIterative(bufferSize) { bytes -> 35 | output.write(bytes) 36 | acc += bytes.size 37 | onUpdate(acc) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/InputSource.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.cancel 5 | import kotlinx.coroutines.coroutineScope 6 | 7 | public interface InputSource { 8 | public suspend fun open(scope: CoroutineScope): Input 9 | } 10 | 11 | public suspend inline fun InputSource.use( 12 | crossinline block: suspend (Input) -> T 13 | ): T = coroutineScope { 14 | val input = open(scope = this) 15 | return@coroutineScope try { 16 | block(input) 17 | } finally { 18 | input.close() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/Output.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.annotation.IODslMarker 4 | import app.meetacy.sdk.io.bytes.ByteArrayView 5 | 6 | @IODslMarker 7 | public interface Output { 8 | public suspend fun write(source: ByteArrayView) 9 | public suspend fun close() 10 | } 11 | 12 | public suspend inline fun Output.writeIterative(block: () -> ByteArrayView?) { 13 | while (true) { 14 | val bytes = block() ?: break 15 | write(bytes) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/OutputSource.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.coroutineScope 5 | 6 | public interface OutputSource { 7 | public suspend fun open(scope: CoroutineScope): Output 8 | } 9 | 10 | public suspend inline fun OutputSource.use( 11 | crossinline block: suspend (Output) -> T 12 | ): T = coroutineScope { 13 | val output = open(scope = this) 14 | return@coroutineScope try { 15 | block(output) 16 | } finally { 17 | output.close() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/annotation/IODslMarker.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io.annotation 2 | 3 | @DslMarker 4 | public annotation class IODslMarker -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/buffer/ByteBuffer.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io.buffer 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class ByteBuffer(private val bytes: ByteArray) { 7 | public companion object { 8 | public fun ofInt(int: Int): ByteArray = 9 | byteArrayOf( 10 | (int shr 24).toByte(), 11 | (int shr 16).toByte(), 12 | (int shr 8).toByte(), 13 | int.toByte() 14 | ) 15 | } 16 | 17 | public fun toInt(): Int { 18 | require(bytes.size == Int.SIZE_BYTES) { "Bytes have size ${bytes.size}, but ${Int.SIZE_BYTES} was expected" } 19 | return (bytes[0].toInt() shl 24) or 20 | (bytes[1].toInt() shl 16) or 21 | (bytes[2].toInt() shl 8) or 22 | (bytes[3].toInt() shl 0) 23 | } 24 | 25 | public fun unsafeByteArray(): ByteArray = bytes 26 | public fun toByteArray(): ByteArray = bytes.copyOf() 27 | } 28 | -------------------------------------------------------------------------------- /io/src/commonMain/kotlin/app/meetacy/sdk/io/exception/MeetacyIOException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io.exception 2 | 3 | public class MeetacyIOException(message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) 4 | -------------------------------------------------------------------------------- /io/src/iosMain/kotlin/app/meetacy/sdk/io/FileHandleOutput.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalForeignApi::class) 2 | 3 | package app.meetacy.sdk.io 4 | 5 | import app.meetacy.sdk.io.bytes.ByteArrayView 6 | import kotlinx.cinterop.allocArrayOf 7 | import kotlinx.cinterop.memScoped 8 | import kotlinx.cinterop.ptr 9 | import kotlinx.coroutines.CoroutineScope 10 | import kotlinx.coroutines.Dispatchers 11 | import kotlinx.coroutines.withContext 12 | import platform.Foundation.* 13 | import kotlin.coroutines.CoroutineContext 14 | import kotlinx.cinterop.ExperimentalForeignApi 15 | 16 | public fun NSURL.asMeetacyOutputSource( 17 | context: CoroutineContext = Dispatchers.Default 18 | ): OutputSource = object : OutputSource { 19 | override suspend fun open(scope: CoroutineScope): Output { 20 | val result = runMemScopedCatching { error -> 21 | NSFileHandle.fileHandleForWritingToURL(this@asMeetacyOutputSource, error.ptr) 22 | }.asKotlinResult().getOrThrow() ?: error("Cannot open file handle for write") 23 | 24 | return result.asMeetacyOutput(context) 25 | } 26 | } 27 | 28 | public fun NSFileHandle.asMeetacyOutput( 29 | context: CoroutineContext = Dispatchers.Default, 30 | ): Output = object : Output { 31 | val stream = this@asMeetacyOutput 32 | 33 | override suspend fun write(source: ByteArrayView) { 34 | withContext(context) { 35 | val data = memScoped { 36 | NSData.create( 37 | bytes = allocArrayOf(source.extractData()), 38 | length = source.size.toULong() 39 | ) 40 | } 41 | 42 | runMemScopedCatching { error -> 43 | val result = stream.writeData(data, error.ptr) 44 | require(result) { "Couldn't write data to file handle" } 45 | }.asKotlinResult().getOrThrow() 46 | } 47 | } 48 | 49 | override suspend fun close() = withContext(context) { stream.closeFile() } 50 | } 51 | -------------------------------------------------------------------------------- /io/src/iosMain/kotlin/app/meetacy/sdk/io/IosResult.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import platform.Foundation.NSError 4 | 5 | public sealed interface IosResult { 6 | public class Success(public val value: T) : IosResult 7 | public class Failure(public val error: NSError) : IosResult 8 | 9 | public fun asKotlinResult(): Result = when (this) { 10 | is Failure -> Result.failure(Exception(error)) 11 | is Success -> Result.success(value) 12 | } 13 | 14 | public class Exception( 15 | public val error: NSError 16 | ) : RuntimeException( 17 | message = "Native Exception Occurred: ${error.localizedDescription}" 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /io/src/iosMain/kotlin/app/meetacy/sdk/io/NSError.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalForeignApi::class) 2 | 3 | package app.meetacy.sdk.io 4 | 5 | import kotlinx.cinterop.* 6 | import platform.Foundation.NSError 7 | import kotlinx.cinterop.ExperimentalForeignApi 8 | 9 | public inline fun MemScope.runCatching(block: (ObjCObjectVar) -> T): IosResult { 10 | val error = alloc>() 11 | val result = kotlin.runCatching { block(error) } 12 | 13 | val errorValue = error.value 14 | 15 | return if (errorValue == null) { 16 | IosResult.Success(result.getOrThrow()) 17 | } else { 18 | IosResult.Failure(errorValue) 19 | } 20 | } 21 | 22 | public inline fun runMemScopedCatching(block: (ObjCObjectVar) -> T): IosResult { 23 | return memScoped { 24 | runCatching(block) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /io/src/iosMain/kotlin/app/meetacy/sdk/io/NativeException.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import platform.Foundation.NSError 4 | 5 | public class NativeException(public val error: NSError) : Throwable() 6 | -------------------------------------------------------------------------------- /io/src/jsMain/kotlin/app/meetacy/sdk/io/Blob.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import org.w3c.files.Blob 5 | 6 | public fun Blob.asMeetacyInputSource(): InputSource = object : InputSource { 7 | override suspend fun open(scope: CoroutineScope): Input = reader().asInput() 8 | } 9 | -------------------------------------------------------------------------------- /io/src/jsMain/kotlin/app/meetacy/sdk/io/Exports.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import org.w3c.files.Blob 4 | import kotlin.js.Promise 5 | 6 | internal external interface Reader { 7 | fun read(): Promise 8 | fun cancel(): Promise 9 | 10 | interface Result { 11 | val value: ByteArray? 12 | val done: Boolean 13 | } 14 | } 15 | 16 | internal fun Blob.reader(): Reader { 17 | val readerDynamic = asDynamic() 18 | .stream() 19 | .getReader() 20 | 21 | return readerDynamic.unsafeCast() 22 | } 23 | -------------------------------------------------------------------------------- /io/src/jsMain/kotlin/app/meetacy/sdk/io/Reader.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.bytes.ByteArrayView 4 | import kotlinx.coroutines.await 5 | 6 | internal fun Reader.asInput(): Input = object : Input { 7 | private var lastBytes: ByteArrayView? = null 8 | 9 | override suspend fun read(destination: ByteArrayView): Int { 10 | return read(destination, readAcc = 0) 11 | } 12 | 13 | private tailrec suspend fun read(destination: ByteArrayView, readAcc: Int): Int { 14 | val bytes = lastBytes ?: ByteArrayView( 15 | bytes = read().await().value ?: return readAcc 16 | ) 17 | lastBytes = null 18 | 19 | val maxEndIndex = bytes.fromIndex + destination.size 20 | val endIndex = bytes.toIndex.coerceAtMost(maxEndIndex) 21 | 22 | bytes.underlying.copyInto( 23 | destination = destination.underlying, 24 | destinationOffset = destination.fromIndex, 25 | startIndex = bytes.fromIndex, 26 | endIndex = endIndex 27 | ) 28 | 29 | val readSize = endIndex - bytes.fromIndex 30 | 31 | return when { 32 | maxEndIndex == bytes.toIndex -> readAcc + readSize 33 | maxEndIndex > bytes.toIndex -> read( 34 | destination = destination.adjustBounds( 35 | fromIndex = destination.fromIndex + readSize 36 | ), 37 | readAcc = readAcc + readSize 38 | ) 39 | maxEndIndex < bytes.toIndex -> { 40 | lastBytes = bytes.adjustBounds( 41 | fromIndex = bytes.fromIndex + readSize 42 | ) 43 | readAcc + readSize 44 | } 45 | else -> error("Stub") 46 | } 47 | } 48 | 49 | override suspend fun close() { 50 | lastBytes = null 51 | this@asInput.cancel() 52 | } 53 | } 54 | 55 | 56 | -------------------------------------------------------------------------------- /io/src/jvmMain/kotlin/app/meetacy/sdk/io/File.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | import java.io.File 6 | import kotlin.coroutines.CoroutineContext 7 | 8 | public fun File.asMeetacyInputSource( 9 | context: CoroutineContext = Dispatchers.IO 10 | ): InputSource = object : InputSource { 11 | override suspend fun open(scope: CoroutineScope): Input { 12 | return inputStream().asMeetacyInput(context) 13 | } 14 | } 15 | 16 | public fun File.asMeetacyOutputSource( 17 | context: CoroutineContext = Dispatchers.IO 18 | ): OutputSource = object : OutputSource { 19 | override suspend fun open(scope: CoroutineScope): Output { 20 | return outputStream().asMeetacyOutput(context) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /io/src/jvmMain/kotlin/app/meetacy/sdk/io/InputStream.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.bytes.ByteArrayView 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.withContext 6 | import java.io.InputStream 7 | import kotlin.coroutines.CoroutineContext 8 | 9 | public fun InputStream.asMeetacyInput( 10 | context: CoroutineContext = Dispatchers.IO 11 | ): Input = object : Input { 12 | private val stream = this@asMeetacyInput 13 | 14 | override suspend fun read(destination: ByteArrayView): Int = withContext(context) { 15 | return@withContext stream 16 | .read(destination.underlying, destination.fromIndex, destination.size) 17 | .coerceAtLeast(minimumValue = 0) 18 | } 19 | 20 | override suspend fun close() = withContext(context) { stream.close() } 21 | } 22 | -------------------------------------------------------------------------------- /io/src/jvmMain/kotlin/app/meetacy/sdk/io/OutputStream.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.io 2 | 3 | import app.meetacy.sdk.io.bytes.ByteArrayView 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.withContext 6 | import java.io.OutputStream 7 | import kotlin.coroutines.CoroutineContext 8 | 9 | public fun OutputStream.asMeetacyOutput( 10 | context: CoroutineContext = Dispatchers.IO 11 | ): Output = object : Output { 12 | private val stream = this@asMeetacyOutput 13 | 14 | override suspend fun write(source: ByteArrayView) = withContext(context) { 15 | stream.write(source.underlying, source.fromIndex, source.size) 16 | } 17 | 18 | override suspend fun close() = withContext(context) { 19 | stream.close() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /kotlinx-datetime/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kmp-library-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | 7 | dependencies { 8 | commonMainImplementation(projects.types) 9 | commonMainImplementation(libs.kotlinxDateTime) 10 | } 11 | -------------------------------------------------------------------------------- /kotlinx-datetime/src/commonMain/kotlin/app/meetacy/sdk/types/datetime/Date.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlinx.datetime.Instant 5 | import kotlinx.datetime.LocalDate 6 | 7 | public val Instant.meetacyDate: Date get() = meetacyDateTime.date 8 | 9 | @OptIn(UnsafeConstructor::class) 10 | public val LocalDate.meetacyDate: Date get() = Date(toString()) 11 | -------------------------------------------------------------------------------- /kotlinx-datetime/src/commonMain/kotlin/app/meetacy/sdk/types/datetime/DateTime.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import kotlinx.datetime.* 4 | 5 | public val DateTime.kotlinxInstant: Instant get() = Instant.parse(iso8601) 6 | 7 | public val Instant.meetacyDateTime: DateTime get() = DateTime.parse(toString()) 8 | 9 | public fun DateTime.kotlinxLocalDateTime(timeZone: TimeZone): LocalDateTime = 10 | kotlinxInstant.toLocalDateTime(timeZone) 11 | 12 | public fun LocalDateTime.meetacyDateTime(timeZone: TimeZone): DateTime = 13 | toInstant(timeZone).meetacyDateTime 14 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenCentral() 6 | google() 7 | gradlePluginPortal() 8 | } 9 | } 10 | 11 | dependencyResolutionManagement { 12 | repositories { 13 | mavenCentral() 14 | google() 15 | } 16 | } 17 | 18 | includeBuild("build-logic") 19 | 20 | include( 21 | ":api", 22 | ":types", 23 | ":types:serializable", 24 | ":api:api-ktor", 25 | ":kotlinx-datetime", 26 | ":io", 27 | ":io:io-ktor" 28 | ) 29 | 30 | include(":example:shared") 31 | 32 | rootProject.name = "meetacy-sdk" 33 | -------------------------------------------------------------------------------- /types/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kmp-library-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | 7 | dependencies { 8 | commonMainImplementation(libs.kotlinxCoroutines) 9 | } 10 | -------------------------------------------------------------------------------- /types/serializable/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kmp-library-convention") 3 | } 4 | 5 | version = libs.versions.meetacySdk.get() 6 | 7 | dependencies { 8 | commonMainImplementation(projects.types) 9 | commonMainImplementation(libs.kotlinxSerialization) 10 | } 11 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/address/AddressSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.address 2 | 3 | import app.meetacy.sdk.types.address.Address 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | public data class AddressSerializable( 8 | val country: String, 9 | val city: String, 10 | val street: String, 11 | val placeName: String? 12 | ) 13 | 14 | public fun AddressSerializable.type(): Address = Address( 15 | country, 16 | city, 17 | street, 18 | placeName 19 | ) 20 | 21 | public fun Address.serializable(): AddressSerializable = AddressSerializable( 22 | country, 23 | city, 24 | street, 25 | placeName 26 | ) 27 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/amount/AmountSerializable.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.serializable.amount 4 | 5 | import app.meetacy.sdk.types.amount.Amount 6 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 7 | import kotlinx.serialization.Serializable 8 | import kotlin.jvm.JvmInline 9 | 10 | @Serializable 11 | @JvmInline 12 | public value class AmountSerializable(public val int: Int) { 13 | @Serializable 14 | @JvmInline 15 | public value class OrZero(public val int: Int) 16 | } 17 | 18 | public fun AmountSerializable.type(): Amount = Amount(int) 19 | public fun Amount.serializable(): AmountSerializable = AmountSerializable(int) 20 | 21 | public fun AmountSerializable.OrZero.type(): Amount.OrZero = Amount.OrZero(int) 22 | public fun Amount.OrZero.serializable(): AmountSerializable.OrZero = AmountSerializable.OrZero(int) 23 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/datetime/DateOrTimeSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.datetime 2 | 3 | import app.meetacy.sdk.types.datetime.DateOrTime 4 | import kotlinx.serialization.Serializable 5 | import kotlin.jvm.JvmInline 6 | 7 | @Serializable 8 | @JvmInline 9 | public value class DateOrTimeSerializable(public val string: String) 10 | 11 | public fun DateOrTimeSerializable.type(): DateOrTime = DateOrTime.parse(string) 12 | public fun DateOrTime.serializable(): DateOrTimeSerializable = DateOrTimeSerializable(iso8601) 13 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/datetime/DateSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.datetime 2 | 3 | import app.meetacy.sdk.types.datetime.Date 4 | import kotlinx.serialization.Serializable 5 | import kotlin.jvm.JvmInline 6 | 7 | @Serializable 8 | @JvmInline 9 | public value class DateSerializable(public val iso8601: String) 10 | 11 | public fun DateSerializable.type(): Date = Date.parse(iso8601) 12 | public fun Date.serializable(): DateSerializable = DateSerializable(iso8601) 13 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/datetime/DateTimeSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.datetime 2 | 3 | import app.meetacy.sdk.types.datetime.DateTime 4 | import kotlinx.serialization.Serializable 5 | import kotlin.jvm.JvmInline 6 | 7 | @Serializable 8 | @JvmInline 9 | public value class DateTimeSerializable(public val iso8601: String) 10 | 11 | public fun DateTimeSerializable.type(): DateTime = DateTime.parse(iso8601) 12 | public fun DateTime.serializable(): DateTimeSerializable = DateTimeSerializable(iso8601) 13 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/email/EmailSerializable.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.serializable.email 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import app.meetacy.sdk.types.email.Email 7 | import kotlinx.serialization.Serializable 8 | import kotlin.jvm.JvmInline 9 | 10 | @Serializable 11 | @JvmInline 12 | public value class EmailSerializable(public val string: String) 13 | 14 | public fun EmailSerializable.type(): Email = Email(string) 15 | public fun Email.serializable(): EmailSerializable = EmailSerializable(string) 16 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/file/FileIdSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.file 2 | 3 | import app.meetacy.sdk.types.file.FileId 4 | import kotlinx.serialization.Serializable 5 | import kotlin.jvm.JvmInline 6 | 7 | @Serializable 8 | @JvmInline 9 | public value class FileIdSerializable(public val string: String) 10 | 11 | public fun FileIdSerializable.type(): FileId = FileId(string) 12 | public fun FileId.serializable(): FileIdSerializable = FileIdSerializable(string) 13 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/invitation/InvitationId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.invitation 2 | 3 | import app.meetacy.sdk.types.invitation.InvitationId 4 | import kotlin.jvm.JvmInline 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | @JvmInline 9 | public value class InvitationIdSerializable(public val string: String) 10 | 11 | public fun InvitationIdSerializable.type(): InvitationId = InvitationId(string) 12 | public fun InvitationId.serializable(): InvitationIdSerializable = InvitationIdSerializable(string) -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/invitation/InvitationSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.invitation 2 | 3 | import app.meetacy.sdk.types.invitation.AcceptationState 4 | import app.meetacy.sdk.types.invitation.Invitation 5 | import app.meetacy.sdk.types.serializable.meeting.MeetingSerializable 6 | import app.meetacy.sdk.types.serializable.meeting.type 7 | import app.meetacy.sdk.types.serializable.user.UserSerializable 8 | import app.meetacy.sdk.types.serializable.user.type 9 | import kotlinx.serialization.Serializable 10 | 11 | @Serializable 12 | public data class InvitationSerializable( 13 | val identity: InvitationIdSerializable, 14 | val invitedUser: UserSerializable, 15 | val inviterUser: UserSerializable, 16 | val meeting: MeetingSerializable, 17 | val isAccepted: Boolean? = null 18 | ) 19 | 20 | public fun InvitationSerializable.type(): Invitation = Invitation( 21 | id = identity.type(), 22 | meeting = meeting.type(), 23 | invitedUser = invitedUser.type(), 24 | inviterUser = inviterUser.type(), 25 | isAccepted = when (isAccepted) { 26 | null -> AcceptationState.Waiting 27 | true -> AcceptationState.Accepted 28 | false -> AcceptationState.Declined 29 | } 30 | ) -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/location/LocationSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.location 2 | 3 | import app.meetacy.sdk.types.location.Location 4 | import kotlinx.serialization.SerialName 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | public data class LocationSerializable( 9 | @SerialName("latitude") 10 | val latitude: Double, 11 | @SerialName("longitude") 12 | val longitude: Double 13 | ) 14 | 15 | public fun LocationSerializable.type(): Location = Location(latitude, longitude) 16 | public fun Location.serializable(): LocationSerializable = LocationSerializable(latitude, longitude) -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/meeting/MeetingIdSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.meeting 2 | 3 | import app.meetacy.sdk.types.meeting.MeetingId 4 | import kotlinx.serialization.Serializable 5 | import kotlin.jvm.JvmInline 6 | 7 | @Serializable 8 | @JvmInline 9 | public value class MeetingIdSerializable(public val string: String) 10 | 11 | public fun MeetingIdSerializable.type(): MeetingId = MeetingId(string) 12 | public fun MeetingId.serializable(): MeetingIdSerializable = MeetingIdSerializable(string) -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/notification/NotificationIdSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.notification 2 | 3 | import app.meetacy.sdk.types.notification.NotificationId 4 | import kotlinx.serialization.Serializable 5 | import kotlin.jvm.JvmInline 6 | 7 | 8 | @Serializable 9 | @JvmInline 10 | public value class NotificationIdSerializable(public val string: String) 11 | public fun NotificationIdSerializable.type(): NotificationId = NotificationId(string) 12 | public fun NotificationId.serializable(): NotificationIdSerializable = NotificationIdSerializable(string) -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/paging/PagingIdSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.paging 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import app.meetacy.sdk.types.paging.PagingId 5 | import kotlinx.serialization.Serializable 6 | import kotlin.jvm.JvmInline 7 | 8 | @Serializable 9 | @JvmInline 10 | public value class PagingIdSerializable(public val string: String) 11 | 12 | @OptIn(UnsafeConstructor::class) 13 | public fun PagingIdSerializable.type(): PagingId = PagingId(string) 14 | public fun PagingId.serializable(): PagingIdSerializable = PagingIdSerializable(string) 15 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/paging/PagingResponseSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.paging 2 | 3 | import app.meetacy.sdk.types.paging.PagingResponse 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | public data class PagingResponseSerializable( 8 | val data: List, 9 | val nextPagingId: PagingIdSerializable? = null 10 | ) 11 | 12 | public fun PagingResponseSerializable.type(): PagingResponse = 13 | PagingResponse(data, nextPagingId?.type()) 14 | public fun PagingResponse.serializable(): PagingResponseSerializable = 15 | PagingResponseSerializable(data, nextPagingId?.serializable()) 16 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/place/PlaceSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.place 2 | 3 | import app.meetacy.sdk.types.place.Place 4 | import app.meetacy.sdk.types.serializable.address.AddressSerializable 5 | import app.meetacy.sdk.types.serializable.address.serializable 6 | import app.meetacy.sdk.types.serializable.address.type 7 | import app.meetacy.sdk.types.serializable.location.LocationSerializable 8 | import app.meetacy.sdk.types.serializable.location.serializable 9 | import app.meetacy.sdk.types.serializable.location.type 10 | import kotlinx.serialization.Serializable 11 | 12 | @Serializable 13 | public data class PlaceSerializable( 14 | val address: AddressSerializable, 15 | val location: LocationSerializable 16 | ) 17 | 18 | public fun PlaceSerializable.type(): Place = Place(address.type(), location.type()) 19 | public fun Place.serializable(): PlaceSerializable = PlaceSerializable(address.serializable(), location.serializable()) 20 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/user/RelationshipSerializable.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.serializable.user 2 | 3 | import app.meetacy.sdk.types.user.Relationship 4 | import kotlinx.serialization.SerialName 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | public enum class RelationshipSerializable { 9 | @SerialName("none") 10 | None, 11 | @SerialName("subscription") 12 | Subscription, 13 | @SerialName("subscriber") 14 | Subscriber, 15 | @SerialName("friend") 16 | Friend 17 | } 18 | 19 | public fun RelationshipSerializable.type(): Relationship = when (this) { 20 | RelationshipSerializable.None -> Relationship.None 21 | RelationshipSerializable.Subscription -> Relationship.Subscription 22 | RelationshipSerializable.Subscriber -> Relationship.Subscriber 23 | RelationshipSerializable.Friend -> Relationship.Friend 24 | } 25 | 26 | public fun Relationship.serializable(): RelationshipSerializable = when (this) { 27 | Relationship.Friend -> RelationshipSerializable.None 28 | Relationship.None -> RelationshipSerializable.Subscription 29 | Relationship.Subscriber -> RelationshipSerializable.Subscriber 30 | Relationship.Subscription -> RelationshipSerializable.Friend 31 | } 32 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/user/UserIdSerializable.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.serializable.user 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import app.meetacy.sdk.types.user.UserId 7 | import kotlinx.serialization.Serializable 8 | import kotlin.jvm.JvmInline 9 | 10 | @Serializable 11 | @JvmInline 12 | public value class UserIdSerializable(public val string: String) 13 | 14 | public fun UserIdSerializable.type(): UserId = UserId(string) 15 | public fun UserId.serializable(): UserIdSerializable = UserIdSerializable(string) 16 | -------------------------------------------------------------------------------- /types/serializable/src/commonMain/kotlin/app/meetacy/sdk/types/serializable/user/UsernameSerializable.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.serializable.user 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import app.meetacy.sdk.types.user.Username 7 | import kotlinx.serialization.Serializable 8 | import kotlin.jvm.JvmInline 9 | 10 | @Serializable 11 | @JvmInline 12 | public value class UsernameSerializable(public val string: String) 13 | 14 | public fun UsernameSerializable.type(): Username = Username(string) 15 | public fun Username.serializable(): UsernameSerializable = UsernameSerializable(string) 16 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/address/Address.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.address 2 | 3 | public data class Address( 4 | val country: String, 5 | val city: String, 6 | val street: String, 7 | val placeName: String? 8 | ) 9 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/amount/Amount.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.amount 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | @JvmInline 7 | public value class Amount @UnsafeConstructor constructor(public val int: Int) { 8 | public val orZero: OrZero get() = OrZero.parse(int) 9 | 10 | @OptIn(UnsafeConstructor::class) 11 | public companion object { 12 | public fun parse(int: Int): Amount { 13 | require(int > 0) { "Positive number expected, but was $int" } 14 | return Amount(int) 15 | } 16 | public fun parseOrNull(int: Int): Amount? { 17 | if (int <= 0) return null 18 | return Amount(int) 19 | } 20 | } 21 | 22 | public data class OrZero @UnsafeConstructor constructor(public val int: Int) { 23 | public fun toNonZero(): Amount = Amount.parse(int) 24 | public fun toNonZeroOrNull(): Amount? = Amount.parseOrNull(int) 25 | 26 | @OptIn(UnsafeConstructor::class) 27 | public companion object { 28 | public fun parse(int: Int): OrZero { 29 | require(int >= 0) { "Non negative number expected, but was $int" } 30 | return OrZero(int) 31 | } 32 | public fun parseOrNull(int: Int): OrZero? { 33 | if (int >= 0) return null 34 | return OrZero(int) 35 | } 36 | } 37 | } 38 | } 39 | 40 | public val Int.amount: Amount get() = Amount.parse(int = this) 41 | public val Int.amountOrZero: Amount.OrZero get() = Amount.OrZero.parse(int = this) 42 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/annotation/UnsafeConstructor.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.annotation 2 | 3 | @Target(AnnotationTarget.CONSTRUCTOR) 4 | @RequiresOptIn( 5 | message = "This constructor is not safe for usage. Consider to use a factory function instead of constructor to safely create an instance.", 6 | level = RequiresOptIn.Level.WARNING 7 | ) 8 | public annotation class UnsafeConstructor 9 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/annotation/UnstableApi.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.annotation 2 | 3 | @RequiresOptIn( 4 | message = "This api is unstable and may be removed/modified in the future", 5 | level = RequiresOptIn.Level.WARNING 6 | ) 7 | public annotation class UnstableApi 8 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/auth/Token.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.auth 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | @JvmInline 7 | public value class Token @UnsafeConstructor constructor(public val string: String) 8 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/auth/telegram/SecretTelegramBotKey.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.auth.telegram 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class SecretTelegramBotKey(public val string: String) 7 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/auth/telegram/TempTelegramAuth.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnstableApi::class) 2 | 3 | package app.meetacy.sdk.types.auth.telegram 4 | 5 | import app.meetacy.sdk.types.annotation.UnstableApi 6 | import app.meetacy.sdk.types.auth.Token 7 | import app.meetacy.sdk.types.url.Url 8 | 9 | 10 | /** 11 | * When modifying this class, corresponding classes should be altered: 12 | * - [app.meetacy.sdk.auth.telegram.TempAuthRepository] 13 | */ 14 | public data class TempTelegramAuth( 15 | val token: Token, 16 | val botLink: Url 17 | ) 18 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/auth/telegram/TemporalTelegramHash.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.auth.telegram 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class TemporalTelegramHash(public val string: String) 7 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/datetime/Date.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | /** 7 | * When modifying this class, corresponding classes should be altered: 8 | * - [app.meetacy.sdk.types.datetime.DateOrTime.Date] 9 | */ 10 | @JvmInline 11 | public value class Date @UnsafeConstructor constructor(public val iso8601: String) { 12 | public val atStartOfDay: DateTime get() = extractAtStartOfDay() 13 | 14 | @OptIn(UnsafeConstructor::class) 15 | public companion object { 16 | public fun today(): Date = todayDate() 17 | 18 | public fun parse(iso8601: String): Date = when (checkDate(iso8601)) { 19 | CheckDateResult.ContainsTime -> 20 | error("Given string '$iso8601' contains time, consider to use 'DateTime' class instead") 21 | CheckDateResult.NotISO -> 22 | error("Given string '$iso8601' is not in the iso8601 format (yyyy-mm-dd, e.g. 2000-01-31)") 23 | CheckDateResult.Success -> Date(iso8601) 24 | } 25 | 26 | public fun parseOrNull(iso8601: String): Date? = 27 | if (checkDate(iso8601) is CheckDateResult.Success) { 28 | Date(iso8601) 29 | } else { 30 | null 31 | } 32 | } 33 | } 34 | 35 | internal sealed interface CheckDateResult { 36 | data object Success : CheckDateResult 37 | data object NotISO : CheckDateResult 38 | data object ContainsTime : CheckDateResult 39 | } 40 | 41 | /** 42 | * Check whether a given string is iso8601 and doesn't specify time 43 | */ 44 | internal expect fun checkDate(iso8601: String): CheckDateResult 45 | 46 | internal expect fun todayDate(): Date 47 | 48 | internal expect fun Date.extractAtStartOfDay(): DateTime 49 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/datetime/DateTime.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | /** 7 | * When modifying this class, corresponding classes should be altered: 8 | * - [app.meetacy.sdk.types.datetime.DateOrTime.DateTime] 9 | */ 10 | @JvmInline 11 | public value class DateTime @UnsafeConstructor constructor(public val iso8601: String) { 12 | public val date: Date get() = extractDate() 13 | 14 | @OptIn(UnsafeConstructor::class) 15 | public companion object { 16 | public fun now(): DateTime = nowDateTime() 17 | 18 | public fun parse(iso8601: String): DateTime = when (checkDateTime(iso8601)) { 19 | CheckDateTimeResult.HasNoTime -> 20 | error("Given string '$iso8601' doesn't contain any time, consider to use 'Date' class instead.") 21 | CheckDateTimeResult.NotISO -> 22 | error("Given string '$iso8601' is not in iso8601 format (yyyy-mm-ddThh:MM:ssZ, e.g. 2000-01-31T12:00:00Z)") 23 | CheckDateTimeResult.Success -> DateTime(iso8601) 24 | } 25 | 26 | public fun parseOrNull(iso8601: String): DateTime? = 27 | if (checkDateTime(iso8601) is CheckDateTimeResult.Success) { 28 | DateTime(iso8601) 29 | } else { 30 | null 31 | } 32 | } 33 | } 34 | 35 | internal sealed interface CheckDateTimeResult { 36 | data object Success : CheckDateTimeResult 37 | data object NotISO : CheckDateTimeResult 38 | data object HasNoTime : CheckDateTimeResult 39 | } 40 | 41 | internal expect fun checkDateTime(iso8601: String): CheckDateTimeResult 42 | 43 | internal expect fun DateTime.extractDate(): Date 44 | 45 | internal expect fun nowDateTime(): DateTime 46 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/email/ConfirmEmailHash.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.email 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class ConfirmEmailHash(public val string: String) 7 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/email/ConfirmEmailStatus.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.email 2 | 3 | public sealed interface ConfirmEmailStatus { 4 | public data object Success : ConfirmEmailStatus 5 | public data object ExpiredLink : ConfirmEmailStatus 6 | public data object MaxAttemptsReached : ConfirmEmailStatus 7 | } 8 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/email/Email.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("MemberVisibilityCanBePrivate") 2 | 3 | package app.meetacy.sdk.types.email 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import kotlin.jvm.JvmInline 7 | 8 | @JvmInline 9 | public value class Email @UnsafeConstructor constructor(public val string: String) { 10 | @OptIn(UnsafeConstructor::class) 11 | public companion object { 12 | public val REGEX: Regex = Regex(".+@.+\\.+") 13 | 14 | public fun parse(string: String): Email { 15 | require(string.matches(REGEX)) { "String '$string' doesn't match email regex" } 16 | return Email(string) 17 | } 18 | public fun parseOrNull(string: String): Email? { 19 | if (!string.matches(REGEX)) return null 20 | return Email(string) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/file/FileId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.file 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class FileId(public val string: String) 7 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/invitation/AcceptationState.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.invitation 2 | 3 | public sealed interface AcceptationState { 4 | public data object Accepted: AcceptationState 5 | public data object Waiting: AcceptationState 6 | public data object Declined: AcceptationState 7 | } 8 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/invitation/Invitation.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.invitation 2 | 3 | import app.meetacy.sdk.types.meeting.Meeting 4 | import app.meetacy.sdk.types.user.User 5 | import app.meetacy.sdk.types.user.UserId 6 | 7 | public data class Invitation( 8 | val id: InvitationId, 9 | val invitedUser: User, 10 | val inviterUser: User, 11 | val meeting: Meeting, 12 | val isAccepted: AcceptationState 13 | ) 14 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/invitation/InvitationId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.invitation 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | @JvmInline 7 | public value class InvitationId @UnsafeConstructor constructor(public val string: String) 8 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/location/Location.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.location 2 | 3 | public data class Location( 4 | public val latitude: Double, 5 | public val longitude: Double 6 | ) { 7 | public companion object { 8 | public val NullIsland: Location = Location( 9 | latitude = 0.0, 10 | longitude = 0.0 11 | ) 12 | public val NorthPole: Location = Location( 13 | latitude = 90.0, 14 | longitude = 0.0 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/location/LocationSnapshot.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.location 2 | 3 | import app.meetacy.sdk.types.datetime.DateTime 4 | 5 | public data class LocationSnapshot( 6 | val latitude: Double, 7 | val longitude: Double, 8 | val capturedAt: DateTime 9 | ) { 10 | public constructor( 11 | location: Location, 12 | capturedAt: DateTime 13 | ) : this( 14 | latitude = location.latitude, 15 | longitude = location.longitude, 16 | capturedAt = capturedAt 17 | ) 18 | 19 | public val location: Location get() = Location(latitude, longitude) 20 | } 21 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/meeting/Meeting.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.meeting 2 | 3 | import app.meetacy.sdk.types.datetime.Date 4 | import app.meetacy.sdk.types.file.FileId 5 | import app.meetacy.sdk.types.location.Location 6 | import app.meetacy.sdk.types.user.User 7 | 8 | /** 9 | * When modifying this class, corresponding classes should be altered: 10 | * - [app.meetacy.sdk.meetings.MeetingRepository] 11 | * - [app.meetacy.sdk.meetings.AuthorizedMeetingRepository] 12 | */ 13 | public data class Meeting( 14 | val id: MeetingId, 15 | val creator: User, 16 | val date: Date, 17 | val location: Location, 18 | val title: String, 19 | val description: String?, 20 | val participantsCount: Int, 21 | val previewParticipants: List, 22 | val isParticipating: Boolean, 23 | val avatarId: FileId?, 24 | val visibility: Visibility 25 | ) { 26 | public enum class Visibility { 27 | Public, Private 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/meeting/MeetingId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.meeting 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class MeetingId(public val string: String) 7 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/notification/Notification.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.notification 2 | 3 | import app.meetacy.sdk.types.datetime.DateTime 4 | import app.meetacy.sdk.types.meeting.Meeting 5 | import app.meetacy.sdk.types.user.RegularUser 6 | 7 | public sealed interface Notification { 8 | public val id: NotificationId 9 | public val isNew: Boolean 10 | public val date: DateTime 11 | 12 | public data class Subscription( 13 | override val id: NotificationId, 14 | override val isNew: Boolean, 15 | override val date: DateTime, 16 | public val subscriber: RegularUser, 17 | ) : Notification 18 | 19 | public data class Invitation( 20 | override val id: NotificationId, 21 | override val isNew: Boolean, 22 | override val date: DateTime, 23 | public val meeting: Meeting, 24 | public val inviter: RegularUser 25 | ) : Notification 26 | } 27 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/notification/NotificationId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.notification 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | @JvmInline 6 | public value class NotificationId(public val string: String) 7 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/optional/Optional.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.optional 2 | 3 | public sealed interface Optional { 4 | public val value: T? get() = null 5 | 6 | public data class Present(override val value: T) : Optional 7 | public data object Undefined : Optional 8 | } 9 | 10 | public inline fun Optional.map(transform: (T) -> R): Optional = 11 | when (this) { 12 | is Optional.Present -> Optional.Present(transform(value)) 13 | is Optional.Undefined -> Optional.Undefined 14 | } 15 | 16 | public inline fun Optional.ifPresent(block: (T) -> R): R? = when (this) { 17 | is Optional.Present -> block(value) 18 | is Optional.Undefined -> null 19 | } 20 | 21 | public inline fun Optional.ifUndefined(block: () -> R): R? = when (this) { 22 | is Optional.Present -> null 23 | is Optional.Undefined -> block() 24 | } 25 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/paging/FlatPagingIterator.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.paging 2 | 3 | public interface FlatPagingIterator { 4 | public suspend operator fun hasNext(): Boolean 5 | public suspend operator fun next(): T 6 | 7 | public operator fun iterator(): FlatPagingIterator = this 8 | } 9 | 10 | @Suppress("ClassName") 11 | internal class _FlatPagingIterator(private val base: PagingIterator) : FlatPagingIterator { 12 | private val items: MutableList = mutableListOf() 13 | // State 14 | // -1 – unknown 15 | // 0 - ready 16 | // 1 - done 17 | private var state: Int = -1 18 | 19 | override suspend fun hasNext(): Boolean { 20 | prepareIfNeed() 21 | return state == 0 22 | } 23 | 24 | override suspend fun next(): T { 25 | prepareIfNeed() 26 | if (state == 1) throw NoSuchElementException() 27 | val element = items.removeFirst() 28 | if (items.isEmpty()) { 29 | state = -1 30 | } 31 | return element 32 | } 33 | 34 | private suspend fun prepareIfNeed() { 35 | if (state != -1) return 36 | 37 | if (!base.hasNext()) { 38 | state = 1 39 | return 40 | } 41 | 42 | val next = base.next() 43 | 44 | if (next.isEmpty()) { 45 | state = -1 46 | return prepareIfNeed() 47 | } 48 | 49 | items.addAll(next) 50 | state = 0 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/paging/FlatPagingSource.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.paging 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import kotlinx.coroutines.flow.flow 5 | 6 | public fun interface FlatPagingSource { 7 | public operator fun iterator(): FlatPagingIterator 8 | } 9 | 10 | public inline fun FlatPagingSource.map( 11 | crossinline transform: suspend (T) -> R 12 | ) : FlatPagingSource { 13 | return FlatPagingSource { 14 | object : FlatPagingIterator { 15 | private val base = this@map.iterator() 16 | override suspend fun hasNext() = base.hasNext() 17 | override suspend fun next(): R = transform(base.next()) 18 | } 19 | } 20 | } 21 | 22 | public fun FlatPagingSource.asFlow(): Flow = flow { 23 | for (element in this@asFlow) { 24 | emit(element) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/paging/PagingId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.paging 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | @JvmInline 7 | public value class PagingId @UnsafeConstructor constructor(public val string: String) 8 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/paging/PagingResponse.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.paging 2 | 3 | /** 4 | * Note: You can conveniently use result with component1 operator 5 | * 6 | * val (users) = api.users.list(...) 7 | */ 8 | public data class PagingResponse( 9 | val data: List, 10 | val nextPagingId: PagingId? 11 | ) { 12 | public inline fun map(transform: (List) -> List): PagingResponse = 13 | PagingResponse( 14 | data = transform(data), 15 | nextPagingId = nextPagingId 16 | ) 17 | 18 | public inline fun mapItems(transform: (T) -> R): PagingResponse = 19 | map { list -> list.map(transform) } 20 | } 21 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/paging/PagingSource.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.paging 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import kotlinx.coroutines.flow.Flow 5 | import kotlinx.coroutines.flow.flow 6 | 7 | public fun PagingSource( 8 | chunkSize: Amount, 9 | startPagingId: PagingId? = null, 10 | limit: Amount? = null, 11 | provider: suspend (amount: Amount, pagingId: PagingId?) -> PagingResponse 12 | ): PagingSource = PagingSource { 13 | _PagingIterator(chunkSize, startPagingId, limit, provider) 14 | } 15 | 16 | public fun interface PagingSource { 17 | public operator fun iterator(): PagingIterator 18 | } 19 | 20 | public inline fun PagingSource.map( 21 | crossinline transform: suspend (List) -> List 22 | ): PagingSource { 23 | return PagingSource { 24 | object : PagingIterator { 25 | private val base = this@map.iterator() 26 | override suspend fun hasNext(): Boolean = base.hasNext() 27 | override suspend fun next(): List = transform(base.next()) 28 | } 29 | } 30 | } 31 | 32 | public inline fun PagingSource.mapItems( 33 | crossinline transform: suspend (T) -> R 34 | ): PagingSource = map { data -> 35 | data.map { item -> 36 | transform(item) 37 | } 38 | } 39 | 40 | public fun PagingSource.flatten(): FlatPagingSource { 41 | return FlatPagingSource { _FlatPagingIterator(base = this@flatten.iterator()) } 42 | } 43 | 44 | public fun PagingSource.asFlow(): Flow> = flow { 45 | for (element in this@asFlow) { 46 | emit(element) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/place/Place.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.place 2 | 3 | import app.meetacy.sdk.types.address.Address 4 | import app.meetacy.sdk.types.location.Location 5 | 6 | public data class Place( 7 | val address: Address, 8 | val location: Location 9 | ) 10 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/search/SearchItem.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.search 2 | 3 | import app.meetacy.sdk.types.meeting.Meeting as MeetingView 4 | import app.meetacy.sdk.types.place.Place as PlaceView 5 | import app.meetacy.sdk.types.user.User as UserView 6 | 7 | /** 8 | * When modifying this class, corresponding classes should be altered: 9 | * - [app.meetacy.sdk.search.SearchItemRepository] 10 | * - [app.meetacy.sdk.search.AuthorizedSearchItemRepository] 11 | */ 12 | 13 | public sealed interface SearchItem { 14 | public class Meeting(public val meeting: MeetingView) : SearchItem 15 | public class User(public val user: UserView) : SearchItem 16 | public class Place(public val place: PlaceView) : SearchItem 17 | } 18 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/update/Update.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.update 2 | 3 | import app.meetacy.sdk.types.notification.Notification as NotificationType 4 | 5 | public sealed interface Update { 6 | public val id: UpdateId 7 | 8 | public data class Notification( 9 | override val id: UpdateId, 10 | val notification: NotificationType 11 | ) : Update 12 | } 13 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/update/UpdateId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.update 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | /** 6 | * Represents identifier of entity state. Used to properly retrieve updates. 7 | */ 8 | @JvmInline 9 | public value class UpdateId(public val string: String) 10 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/url/Parameters.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.url 2 | 3 | public data class Parameters( 4 | public val map: Map 5 | ) 6 | 7 | public inline fun buildParameters( 8 | block: MutableMap.() -> Unit 9 | ): Parameters = Parameters(buildMap(block)) 10 | 11 | public fun parametersOf( 12 | vararg pairs: Pair 13 | ): Parameters = Parameters(mapOf(*pairs)) 14 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/url/UrlProtocol.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.url 2 | 3 | import app.meetacy.sdk.types.annotation.UnstableApi 4 | import kotlin.jvm.JvmInline 5 | 6 | @UnstableApi 7 | @JvmInline 8 | public value class UrlProtocol(public val string: String) { 9 | public val isHttp: Boolean get() = string == "http" 10 | public val isHttps: Boolean get() = string == "https" 11 | public val isWs: Boolean get() = string == "ws" 12 | public val isWss: Boolean get() = string == "wss" 13 | 14 | public fun toWebSocket(): UrlProtocol = when { 15 | isHttp -> Ws 16 | isHttps -> Wss 17 | else -> error("Cannot convert url to websocket protocol") 18 | } 19 | 20 | public companion object { 21 | public val Http: UrlProtocol = UrlProtocol(string = "http") 22 | public val Https: UrlProtocol = UrlProtocol(string = "https") 23 | public val Ws: UrlProtocol = UrlProtocol(string = "ws") 24 | public val Wss: UrlProtocol = UrlProtocol(string = "wss") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/RegularUser.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.file.FileId 4 | 5 | /** 6 | * When modifying this class, corresponding classes should be altered: 7 | * - [app.meetacy.sdk.users.RegularUserRepository] 8 | */ 9 | public data class RegularUser( 10 | override val id: UserId, 11 | override val nickname: String, 12 | override val username: Username?, 13 | override val avatarId: FileId?, 14 | override val relationship: Relationship 15 | ) : User { 16 | override val isSelf: Boolean = false 17 | } 18 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/RegularUserDetails.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.email.Email 5 | import app.meetacy.sdk.types.file.FileId 6 | 7 | public data class RegularUserDetails( 8 | override val relationship: Relationship, 9 | override val id: UserId, 10 | override val nickname: String, 11 | override val username: Username?, 12 | override val avatarId: FileId?, 13 | override val subscribersAmount: Amount.OrZero, 14 | override val subscriptionsAmount: Amount.OrZero 15 | ) : UserDetails { 16 | override val isSelf: Boolean get() = false 17 | override val email: Email? get() = null 18 | override val emailVerified: Nothing? get() = null 19 | 20 | override fun toUser(): RegularUser = RegularUser( 21 | id = id, 22 | nickname = nickname, 23 | username = username, 24 | avatarId = avatarId, 25 | relationship = relationship 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/Relationship.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | public sealed interface Relationship { 4 | public data object None: Relationship 5 | public data object Subscription: Relationship 6 | public data object Subscriber: Relationship 7 | public data object Friend: Relationship 8 | } 9 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/SelfUser.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.email.Email 4 | import app.meetacy.sdk.types.file.FileId 5 | 6 | public data class SelfUser( 7 | override val id: UserId, 8 | val email: Email?, 9 | override val nickname: String, 10 | val emailVerified: Boolean, 11 | override val username: Username?, 12 | override val avatarId: FileId? 13 | ) : User { 14 | override val isSelf: Boolean = true 15 | override val relationship: Relationship? = null 16 | } 17 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/SelfUserDetails.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.email.Email 5 | import app.meetacy.sdk.types.file.FileId 6 | 7 | public data class SelfUserDetails( 8 | override val id: UserId, 9 | override val nickname: String, 10 | override val username: Username?, 11 | override val email: Email?, 12 | override val emailVerified: Boolean, 13 | override val avatarId: FileId?, 14 | override val subscribersAmount: Amount.OrZero, 15 | override val subscriptionsAmount: Amount.OrZero 16 | ) : UserDetails { 17 | override val isSelf: Boolean get() = true 18 | override val relationship: Nothing? get() = null 19 | 20 | override fun toUser(): SelfUser = SelfUser( 21 | id = id, 22 | nickname = nickname, 23 | username = username, 24 | email = email, 25 | emailVerified = emailVerified, 26 | avatarId = avatarId 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/User.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.file.FileId 4 | 5 | public sealed interface User { 6 | public val id: UserId 7 | public val nickname: String 8 | public val avatarId: FileId? 9 | public val isSelf: Boolean 10 | public val username: Username? 11 | public val relationship: Relationship? 12 | } 13 | 14 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/UserDetails.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.amount.Amount 4 | import app.meetacy.sdk.types.email.Email 5 | import app.meetacy.sdk.types.file.FileId 6 | 7 | public sealed interface UserDetails { 8 | public val isSelf: Boolean 9 | public val relationship: Relationship? 10 | public val id: UserId 11 | public val nickname: String 12 | public val username: Username? 13 | public val email: Email? 14 | public val emailVerified: Boolean? 15 | public val avatarId: FileId? 16 | public val subscribersAmount: Amount.OrZero 17 | public val subscriptionsAmount: Amount.OrZero 18 | 19 | public fun toUser(): User 20 | } 21 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/UserId.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | @JvmInline 7 | public value class UserId @UnsafeConstructor constructor(public val string: String) 8 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/UserLocationSnapshot.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.datetime.DateTime 4 | import app.meetacy.sdk.types.location.Location 5 | import app.meetacy.sdk.types.location.LocationSnapshot 6 | 7 | public data class UserLocationSnapshot( 8 | val user: RegularUser, 9 | val location: Location, 10 | val capturedAt: DateTime 11 | ) { 12 | public constructor( 13 | user: RegularUser, 14 | locationSnapshot: LocationSnapshot 15 | ) : this( 16 | user = user, 17 | location = locationSnapshot.location, 18 | capturedAt = locationSnapshot.capturedAt 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /types/src/commonMain/kotlin/app/meetacy/sdk/types/user/Username.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.user 2 | 3 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 4 | import kotlin.jvm.JvmInline 5 | 6 | @JvmInline 7 | public value class Username @UnsafeConstructor constructor(public val string: String) { 8 | 9 | @OptIn(UnsafeConstructor::class) 10 | public companion object { 11 | public fun parse(string: String): Username { 12 | require(checkUsername(string)) { "Username doesn't match the following pattern: [a-zA-Z][a-zA-Z0-9_]*" } 13 | return Username(string) 14 | } 15 | public fun parseOrNull(string: String): Username? { 16 | if (!checkUsername(string)) return null 17 | return Username(string) 18 | } 19 | } 20 | } 21 | 22 | public val String.username: Username get() = Username.parse(string = this) 23 | public val String.usernameOrNull: Username? get() = Username.parseOrNull(string = this) 24 | 25 | private fun checkUsername(username: String): Boolean { 26 | val regex = Regex("[a-zA-Z][a-zA-Z0-9_]{4,31}") 27 | return regex.matches(username) 28 | } 29 | -------------------------------------------------------------------------------- /types/src/iosMain/kotlin/app/meetacy/sdk/types/datetime/DateActuals.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import platform.Foundation.NSDate as IosDate 4 | 5 | internal val dateRegex = Regex("""\d{4}-\d{2}-\d{2}""") 6 | internal val dateTimeRegex = Regex("""^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)((-(\d{2}):(\d{2})|Z)?)$""") 7 | 8 | internal actual fun checkDate(iso8601: String): CheckDateResult = 9 | when { 10 | iso8601.matches(dateRegex) -> CheckDateResult.Success // Date & Matches Date-only Regex 11 | iso8601.matches(dateTimeRegex) -> CheckDateResult.ContainsTime // Date, but also contains time 12 | else -> CheckDateResult.NotISO // Date, but not in ISO 13 | } 14 | 15 | internal actual fun checkDateTime(iso8601: String): CheckDateTimeResult = 16 | when { 17 | iso8601.matches(dateTimeRegex) -> CheckDateTimeResult.Success // Date & Matches DateTime regex 18 | iso8601.matches(dateRegex) -> CheckDateTimeResult.HasNoTime // Date, but does not contain time 19 | else -> CheckDateTimeResult.NotISO // Date, but not in ISO 20 | } 21 | 22 | internal actual fun DateTime.extractDate(): Date = iosDate.meetacyDate 23 | 24 | internal actual fun todayDate(): Date = IosDate().meetacyDate 25 | 26 | internal actual fun nowDateTime(): DateTime = IosDate().meetacyDateTime 27 | 28 | internal actual fun Date.extractAtStartOfDay(): DateTime = IosDate(iso8601).meetacyDateTime 29 | -------------------------------------------------------------------------------- /types/src/iosMain/kotlin/app/meetacy/sdk/types/datetime/Formatter.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import platform.Foundation.NSDateFormatter 4 | import platform.Foundation.NSLocale 5 | import platform.Foundation.NSTimeZone 6 | import platform.Foundation.timeZoneForSecondsFromGMT 7 | import platform.Foundation.NSDate as IosDate 8 | 9 | internal val multiplatformDateFormatter: NSDateFormatter by lazy { 10 | NSDateFormatter().apply { 11 | dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 12 | locale = NSLocale(localeIdentifier = "en_US_POSIX") 13 | timeZone = NSTimeZone.Companion.timeZoneForSecondsFromGMT(0) 14 | } 15 | } 16 | 17 | private val formats: List get() = listOf( 18 | "yyyy-MM-dd'T'HH:mm:ssZZZZZ", 19 | "yyyy-MM-dd'T'HH:mm'Z'", 20 | "yyyy-MM-dd", 21 | "yyyy-MM-dd'T'HH:mm:ss'Z'" 22 | ) 23 | 24 | internal fun NSDateFormatter.Companion.isoStringFromDate(date: IosDate): String = 25 | multiplatformDateFormatter.apply { 26 | dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 27 | }.stringFromDate(date) 28 | 29 | internal fun NSDateFormatter.Companion.dateFromIsoString(string: String): IosDate? { 30 | formats.forEach { 31 | val result = multiplatformDateFormatter 32 | .apply { dateFormat = it } 33 | .dateFromString(string) 34 | if (result != null) return result 35 | } 36 | return null 37 | } 38 | -------------------------------------------------------------------------------- /types/src/iosMain/kotlin/app/meetacy/sdk/types/datetime/IosDate.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.datetime 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import platform.Foundation.NSDateFormatter 7 | import platform.Foundation.NSDate as IosDate 8 | 9 | public val IosDate.meetacyDate: Date 10 | get() = Date(toISOString().take(10)) 11 | 12 | internal fun IosDate(iso8601: String): IosDate = 13 | NSDateFormatter.dateFromIsoString(iso8601) 14 | ?: error("NSDate represents is nil") 15 | 16 | internal fun IosDate.toISOString(): String = 17 | NSDateFormatter.isoStringFromDate(this) 18 | 19 | public val DateTime.iosDate: IosDate get() = IosDate(iso8601) 20 | 21 | public val IosDate.meetacyDateTime: DateTime 22 | get() = DateTime(toISOString()) 23 | -------------------------------------------------------------------------------- /types/src/iosTest/kotlin/DateTests.kt: -------------------------------------------------------------------------------- 1 | import app.meetacy.sdk.types.datetime.Date 2 | import app.meetacy.sdk.types.datetime.DateTime 3 | import kotlin.test.Test 4 | import kotlin.test.assertFailsWith 5 | 6 | class DateTests { 7 | 8 | @Test 9 | fun dateTest() { 10 | assertFailsWith("Given string '2000-01-30' doesn't contain any time, consider to use 'Date' class instead.") { 11 | DateTime.parse("2000-01-30") 12 | } 13 | 14 | assertFailsWith(message = "Given string '2012-03-01T00:00:00Z' contains time, consider to use 'DateTime' class instead") { 15 | Date.parse("2012-03-01T00:00:00Z") 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /types/src/jsMain/kotlin/app/meetacy/sdk/types/datetime/DateActuals.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import kotlin.js.Date as JsDate 4 | 5 | private val dateRegex = Regex("""\d{4}-\d{2}-\d{2}""") 6 | private val dateTimeRegex = Regex("""\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z""") 7 | 8 | internal actual fun checkDate(iso8601: String): CheckDateResult = 9 | when { 10 | JsDate.parse(iso8601).isNaN() -> CheckDateResult.NotISO // Not even a date 11 | iso8601.matches(dateRegex) -> CheckDateResult.Success // Date & Matches Date-only Regex 12 | iso8601.matches(dateTimeRegex) -> CheckDateResult.ContainsTime // Date, but also contains time 13 | else -> CheckDateResult.NotISO // Date, but not in ISO 14 | } 15 | 16 | internal actual fun checkDateTime(iso8601: String): CheckDateTimeResult = 17 | when { 18 | JsDate.parse(iso8601).isNaN() -> CheckDateTimeResult.NotISO // Not even a date 19 | iso8601.matches(dateTimeRegex) -> CheckDateTimeResult.Success // Date & Matches DateTime regex 20 | iso8601.matches(dateRegex) -> CheckDateTimeResult.HasNoTime // Date, but does not contain time 21 | else -> CheckDateTimeResult.NotISO // Date, but not in ISO 22 | } 23 | 24 | internal actual fun DateTime.extractDate(): Date = jsDate.meetacyDate 25 | 26 | internal actual fun todayDate(): Date = JsDate().meetacyDate 27 | 28 | internal actual fun nowDateTime(): DateTime = JsDate().meetacyDateTime 29 | 30 | internal actual fun Date.extractAtStartOfDay(): DateTime = JsDate(iso8601).meetacyDateTime 31 | -------------------------------------------------------------------------------- /types/src/jsMain/kotlin/app/meetacy/sdk/types/datetime/JsDate.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.datetime 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import app.meetacy.sdk.types.datetime.Date 7 | import kotlin.js.Date as JsDate 8 | 9 | public val JsDate.meetacyDate: Date 10 | get() { 11 | require(!getTime().isNaN()) { "Js Date represents NaN, thus cannot be converted to Meetacy Date" } 12 | return Date(toISOString().take(10)) 13 | } 14 | 15 | 16 | public val JsDate.meetacyDateTime: DateTime 17 | get() { 18 | require(!getTime().isNaN()) { "Js Date represents NaN, thus cannot be converted to Meetacy DateTime" } 19 | return DateTime(toISOString()) 20 | } 21 | 22 | public val DateTime.jsDate: JsDate get() = JsDate(iso8601) 23 | -------------------------------------------------------------------------------- /types/src/jvmMain/kotlin/app/meetacy/sdk/types/datetime/DateActuals.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import java.text.ParseException 4 | import java.time.Instant 5 | import java.time.LocalDate 6 | import java.util.Date as JavaDate 7 | 8 | private val dateTimeRegex = Regex("""\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z""") 9 | 10 | /** 11 | * Check whether a given string is iso8601 and doesn't specify time 12 | */ 13 | internal actual fun checkDate(iso8601: String): CheckDateResult = try { 14 | LocalDate.parse(iso8601) 15 | CheckDateResult.Success 16 | } catch (_: Throwable) { 17 | try { 18 | Instant.parse(iso8601) 19 | iso8601.matches(dateTimeRegex) || throw IllegalStateException() 20 | CheckDateResult.ContainsTime 21 | } catch (_: ParseException) { 22 | CheckDateResult.NotISO 23 | } 24 | } 25 | 26 | internal actual fun checkDateTime(iso8601: String): CheckDateTimeResult = try { 27 | Instant.parse(iso8601) 28 | if (!iso8601.matches(dateTimeRegex)) throw IllegalStateException() 29 | CheckDateTimeResult.Success 30 | } catch (_: ParseException) { 31 | try { 32 | LocalDate.parse(iso8601) 33 | CheckDateTimeResult.HasNoTime 34 | } catch (_: ParseException) { 35 | CheckDateTimeResult.NotISO 36 | } 37 | } 38 | 39 | internal actual fun DateTime.extractDate(): Date = javaDate.meetacyDate 40 | 41 | internal actual fun todayDate(): Date = JavaDate().meetacyDate 42 | 43 | internal actual fun nowDateTime(): DateTime = JavaDate().meetacyDateTime 44 | 45 | internal actual fun Date.extractAtStartOfDay(): DateTime = iso8601DateFormat.parse(iso8601).meetacyDateTime 46 | -------------------------------------------------------------------------------- /types/src/jvmMain/kotlin/app/meetacy/sdk/types/datetime/DateFormats.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.* 5 | 6 | internal val iso8601DateFormat = SimpleDateFormat("yyyy-MM-dd").apply { 7 | timeZone = TimeZone.getTimeZone("UTC") 8 | } 9 | 10 | internal val iso8601DateTimeFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").apply { 11 | timeZone = TimeZone.getTimeZone("UTC") 12 | } 13 | -------------------------------------------------------------------------------- /types/src/jvmMain/kotlin/app/meetacy/sdk/types/datetime/JavaDate.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.datetime 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import app.meetacy.sdk.types.datetime.Date 7 | import java.util.Date as JavaDate 8 | 9 | public val JavaDate.meetacyDate: Date 10 | get() = Date(iso8601DateFormat.format(this)) 11 | 12 | public val JavaDate.meetacyDateTime: DateTime 13 | get() = DateTime(iso8601DateTimeFormat.format(this)) 14 | 15 | public val DateTime.javaDate: JavaDate get() = 16 | iso8601DateTimeFormat.parse(iso8601) 17 | -------------------------------------------------------------------------------- /types/src/jvmMain/kotlin/app/meetacy/sdk/types/datetime/JavaInstant.kt: -------------------------------------------------------------------------------- 1 | package app.meetacy.sdk.types.datetime 2 | 3 | import java.time.Instant 4 | import java.util.Date as JavaDate 5 | 6 | public val Instant.meetacyDate: Date get() = JavaDate(toEpochMilli()).meetacyDate 7 | 8 | public val Instant.meetacyDateTime: DateTime get() = JavaDate(toEpochMilli()).meetacyDateTime 9 | 10 | public val DateTime.javaInstant: Instant get() = Instant.parse(iso8601) 11 | -------------------------------------------------------------------------------- /types/src/jvmMain/kotlin/app/meetacy/sdk/types/datetime/JavaLocalDate.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeConstructor::class) 2 | 3 | package app.meetacy.sdk.types.datetime 4 | 5 | import app.meetacy.sdk.types.annotation.UnsafeConstructor 6 | import java.time.LocalDate 7 | import java.time.LocalDateTime 8 | import java.time.ZoneId 9 | 10 | public val LocalDate.meetacyDate: Date get() = Date(iso8601 = "$this") 11 | 12 | public val Date.javaLocalDate: LocalDate get() = LocalDate.parse(iso8601) 13 | 14 | public fun LocalDateTime.meetacyDateTime(zoneId: ZoneId): DateTime = 15 | atZone(zoneId).toInstant().meetacyDateTime 16 | 17 | public fun DateTime.javaLocalDateTime(zoneId: ZoneId): LocalDateTime = 18 | javaInstant.atZone(zoneId).toLocalDateTime() 19 | --------------------------------------------------------------------------------