diff --git a/.gitignore b/.gitignore index 932b8c2..bfbd8c8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,4 @@ .externalNativeBuild .cxx local.properties -/.idea/ -/gradle/ +/.idea/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..83956dc --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# SnipMeApp +Simple application that allows to save and share code snippets in different languages. + + + diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index edd4b8c..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,147 +0,0 @@ -plugins { - id 'com.android.application' - id 'kotlin-android' - id 'kotlin-kapt' - id 'kotlin-android-extensions' - id 'androidx.navigation.safeargs.kotlin' - id 'com.google.gms.google-services' -} - -def STRING = "String" - -android { - signingConfigs { - release { - // NOTE: Create your own keystore.properties file - def propsFile = rootProject.file('keystore.properties') - if (propsFile.exists()) { - def props = new Properties() - props.load(new FileInputStream(propsFile)) - storeFile = file('../snipplog_keystore.jks') - storePassword = props['storePassword'] - keyAlias = props['keyAlias'] - keyPassword = props['keyPassword'] - } - } - } - - compileSdk 33 - - defaultConfig { - applicationId "pl.tkadziolka.snipmeandroid" - minSdk 21 - targetSdk 33 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - def BASE_URL = "BASE_URL" - def CONTACT_US_PAGE = "CONTACT_US_PAGE" - def FACEBOOK_PAGE = "FACEBOOK_PAGE" - def INSTAGRAM_PAGE = "INSTAGRAM_PAGE" - def GITHUB_PAGE = "GITHUB_PAGE" - def TWITTER_PAGE = "TWITTER_PAGE" - - def PAYPAL_PAGE = "PAYPAL_PAGE" - def CARD_PAGE = "CARD_PAGE" - - def API_BASE_URL = "\"http://91.195.93.3:8000/snip-api/\"" // Must end with '/' - def FACEBOOK_URL = "\"https://www.facebook.com/SnippLog-100576858857140\"" - def INSTAGRAM_URL = "\"https://www.instagram.com/snipp.log\"" - def GITHUB_URL = "\"https://github.com/SnippLog\"" - def TWITTER_URL = "\"https://twitter.com/SnippLog\"" - - def PAYPAL_PAY_URL = "\"https://www.paypal.com/donate?hosted_button_id=WZQNYKQFAHAJG\"" - def CARD_PAY_URL = "\"https://www.paypal.com/biz/fund?id=ACCDJCH2JAHDG\"" - - debug { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - - buildConfigField STRING, BASE_URL, API_BASE_URL - buildConfigField STRING, CONTACT_US_PAGE, FACEBOOK_URL - buildConfigField STRING, FACEBOOK_PAGE, FACEBOOK_URL - buildConfigField STRING, INSTAGRAM_PAGE, INSTAGRAM_URL - buildConfigField STRING, GITHUB_PAGE, GITHUB_URL - buildConfigField STRING, TWITTER_PAGE, TWITTER_URL - buildConfigField STRING, PAYPAL_PAGE, PAYPAL_PAY_URL - buildConfigField STRING, CARD_PAGE, CARD_PAY_URL - } - - release { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - - buildConfigField STRING, BASE_URL, API_BASE_URL - buildConfigField STRING, CONTACT_US_PAGE, FACEBOOK_URL - buildConfigField STRING, FACEBOOK_PAGE, FACEBOOK_URL - buildConfigField STRING, INSTAGRAM_PAGE, INSTAGRAM_URL - buildConfigField STRING, GITHUB_PAGE, GITHUB_URL - buildConfigField STRING, TWITTER_PAGE, TWITTER_URL - buildConfigField STRING, PAYPAL_PAGE, PAYPAL_PAY_URL - buildConfigField STRING, CARD_PAGE, CARD_PAY_URL - signingConfig signingConfigs.release - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = "11" - } - lintOptions { - abortOnError false - } -} - -dependencies { - implementation project(':flutter') - // Kotlin - implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.10" - // KTX - implementation 'androidx.core:core-ktx:1.7.0' - implementation "androidx.fragment:fragment-ktx:1.4.1" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" - implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version" - implementation "androidx.navigation:navigation-runtime-ktx:$navigation_version" - implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version" - implementation "androidx.navigation:navigation-ui-ktx:$navigation_version" - implementation 'com.google.firebase:firebase-analytics-ktx' - // Firebase - implementation platform('com.google.firebase:firebase-bom:28.2.0') - // RX - implementation "io.reactivex.rxjava2:rxkotlin:2.4.0" - implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' - // DI - implementation "io.insert-koin:koin-android:$koin_version" - // Network - implementation "com.squareup.retrofit2:retrofit:$retrofit_version" - implementation "com.squareup.moshi:moshi-kotlin:$moshi_version" - kapt("com.squareup.moshi:moshi-kotlin-codegen:$moshi_version") - implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version" - implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" - implementation "com.squareup.okhttp3:logging-interceptor:4.9.0" - // Design - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - implementation "androidx.recyclerview:recyclerview:1.2.1" - implementation 'androidx.annotation:annotation:1.3.0' - // Utils - implementation 'com.jakewharton.timber:timber:4.7.1' - implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.8' - implementation "com.github.bumptech.glide:glide:$glide_version" - kapt "com.github.bumptech.glide:compiler:$glide_version" - // View - implementation 'com.github.SnippLog:CodeView-Android:1.3.4' - // Test - testImplementation 'junit:junit:4.13.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - testImplementation "io.mockk:mockk:1.10.6" -} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..753034d --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,142 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) + id("com.google.devtools.ksp") +} + +android { + namespace = "dev.snipme.snipmeapp" + compileSdk = 34 + + defaultConfig { + applicationId = "dev.snipme.snipmeapp" + minSdk = 31 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + ndk { + // Filter for architectures supported by Flutter + abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64") + } + } + + buildFeatures{ + android.buildFeatures.buildConfig=true + } + + buildTypes { + val BASE_URL = "BASE_URL" + val API_BASE_URL = "\"http://91.195.93.3:8000/snip-api/\"" // Must end with '/' + val STRING = "String" + + debug { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + + buildConfigField(STRING, BASE_URL, API_BASE_URL) + } + + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + buildConfigField(STRING, BASE_URL, API_BASE_URL) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = "11" + } + + buildFeatures { + compose = true + } +} + +dependencies { + implementation(project(":flutter")) + // Kotlin + implementation(libs.kotlin.stdlib) + // AndroidX Libraries + implementation(libs.androidx.fragment.ktx) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.androidx.lifecycle.reactivestreams.ktx) + implementation(libs.androidx.navigation.runtime.ktx) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.firebase.analytics.ktx) + implementation(platform(libs.firebase.bom)) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.recyclerview) + implementation(libs.androidx.annotation) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + + // RX Libraries + implementation(libs.rxkotlin) + implementation(libs.rxandroid) + + // Dependency Injection + implementation(platform(libs.koin.bom)) + implementation(libs.koin.core) + implementation(libs.koin.android) + + // Network Libraries + implementation(libs.retrofit) + ksp(libs.moshi.kotlin.codegen) + implementation(libs.moshi.kotlin) + implementation(libs.adapter.rxjava2) + implementation(libs.converter.moshi) + implementation(libs.logging.interceptor) + implementation(libs.reactivenetwork.rx2) + + //Local Storage + implementation(libs.androidx.room.runtime) + annotationProcessor(libs.androidx.room.compiler) + ksp(libs.androidx.room.compiler) + implementation(libs.androidx.room.rxjava2) + + + // Utility Libraries + implementation(libs.timber) + implementation(libs.glide) + ksp(libs.compiler) + + //implementation(libs.codeview) + implementation(libs.kodeview) + + // Testing Libraries + testImplementation(libs.mockk) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + + // Debugging Tools + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} diff --git a/app/google-services.json b/app/google-services.json deleted file mode 100644 index 914f772..0000000 --- a/app/google-services.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "project_info": { - "project_number": "556285392569", - "project_id": "snipme-19906", - "storage_bucket": "snipme-19906.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:556285392569:android:16830cff6f8e77a2c98d68", - "android_client_info": { - "package_name": "pl.tkadziolka.snipmeandroid" - } - }, - "oauth_client": [ - { - "client_id": "556285392569-n0m94vrthamlji0tjt08frdrc5aln43t.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyCPlKkKC9iOYPHVV4kcJpr3gIGRnLpu0O4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "556285392569-n0m94vrthamlji0tjt08frdrc5aln43t.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/app/src/androidTest/java/pl/tkadziolka/snipmeandroid/ExampleInstrumentedTest.kt b/app/src/androidTest/java/dev/snipme/snipmeapp/ExampleInstrumentedTest.kt similarity index 83% rename from app/src/androidTest/java/pl/tkadziolka/snipmeandroid/ExampleInstrumentedTest.kt rename to app/src/androidTest/java/dev/snipme/snipmeapp/ExampleInstrumentedTest.kt index f39dac5..491986e 100644 --- a/app/src/androidTest/java/pl/tkadziolka/snipmeandroid/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/dev/snipme/snipmeapp/ExampleInstrumentedTest.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid +package dev.snipme.snipmeapp import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -19,6 +19,6 @@ class ExampleInstrumentedTest { fun useAppContext() { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("pl.tkadziolka.snipmeandroid", appContext.packageName) + assertEquals("dev.snipme.snipmeapp", appContext.packageName) } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd14f53..58b4508 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,42 +1,41 @@ - + - + android:supportsRtl="true"> + android:name=".MainActivity" + android:exported="true" + android:theme="@style/Theme.AppCompat.Light.NoActionBar"> - + android:theme="@style/Theme.AppCompat.Light.NoActionBar" + android:windowSoftInputMode="adjustResize" /> + + + + - - - - - - + \ No newline at end of file diff --git a/app/src/main/assets/app_database.db b/app/src/main/assets/app_database.db new file mode 100644 index 0000000..c233e45 Binary files /dev/null and b/app/src/main/assets/app_database.db differ diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/App.kt b/app/src/main/java/dev/snipme/snipmeapp/App.kt similarity index 72% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/App.kt rename to app/src/main/java/dev/snipme/snipmeapp/App.kt index a4a4773..76e714a 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/App.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/App.kt @@ -1,12 +1,12 @@ -package pl.tkadziolka.snipmeandroid +package dev.snipme.snipmeapp import android.app.Application -import io.github.kbiakov.codeview.classifier.CodeProcessor +import dev.snipme.snipmeapp.BuildConfig.* +import dev.snipme.snipmeapp.di.koinModules +import dev.snipme.snipmeapp.util.CrashReportingTree import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.context.startKoin -import pl.tkadziolka.snipmeandroid.di.koinModules -import pl.tkadziolka.snipmeandroid.util.CrashReportingTree import timber.log.Timber class App : Application() { @@ -18,7 +18,7 @@ class App : Application() { } initLogs() // train classifier on app start - CodeProcessor.init(this) +// CodeProcessor.init(this) startKoin { androidLogger() androidContext(this@App) @@ -27,7 +27,7 @@ class App : Application() { } private fun initLogs() { - if (BuildConfig.DEBUG) { + if (DEBUG) { Timber.plant(Timber.DebugTree()) } else { Timber.plant(CrashReportingTree()) diff --git a/app/src/main/java/dev/snipme/snipmeapp/AppService.kt b/app/src/main/java/dev/snipme/snipmeapp/AppService.kt new file mode 100644 index 0000000..4d1aa21 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/AppService.kt @@ -0,0 +1,107 @@ +package dev.snipme.snipmeapp + +import android.content.ClipData +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.net.Uri +import android.os.Environment +import android.provider.MediaStore +import androidx.core.content.ContextCompat.startActivity +import androidx.core.content.FileProvider +import dev.snipme.snipmeapp.domain.snippets.Snippet +import java.io.File +import java.io.FileNotFoundException +import java.text.DateFormat +import java.util.Date + +const val imageMime = "image/png" + +class AppService(private val context: Context) { + private var imageUri: Uri? = null + + fun getCurrentDateFormatted(): String { + return DateFormat.getDateInstance().format(Date()) + } + + fun storeFile(image: ByteArray, fileName: String, temp: Boolean = false) { + val directoryFile = if (temp) context.cacheDir else context.getExternalFilesDir( + Environment.DIRECTORY_PICTURES + ) + + if (directoryFile == null) throw IllegalStateException("Storage not available") + if (!directoryFile.exists()) { + directoryFile.mkdirs() + } + + val imageFile = File(directoryFile, fileName) + val imageUri = FileProvider.getUriForFile( + context, + "dev.snipme.snipmeapp.fileprovider", + imageFile, + ) + + context.grantUriPermission( + "dev.snipme.snipmeapp.fileprovider", + imageUri, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION + ) + + imageFile.writeBytes(image) + this.imageUri = imageUri + } + + fun launchShareIntent(snippet: Snippet) { + if (imageUri == null) + throw IllegalStateException("Image path is not set. Store image first!") + + val uri = imageUri!! // Store temporary to avoid var change + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + type = imageMime + clipData = ClipData.newRawUri(snippet.title, uri) + setDataAndType(uri, context.contentResolver.getType(uri)); + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + putExtra(Intent.EXTRA_TITLE, snippet.title) + putExtra(Intent.EXTRA_STREAM, uri) + } + + val shareIntent = Intent.createChooser(sendIntent, null) + shareIntent.addFlags(FLAG_ACTIVITY_NEW_TASK) + startActivity(context, shareIntent, null) + } + + fun storeMediaFile(image: ByteArray, name: String) { + val directory = "Pictures/SnipMeApp" + val contentValues = ContentValues().apply { + put(MediaStore.Images.Media.DISPLAY_NAME, name) + put(MediaStore.Images.Media.RELATIVE_PATH, directory) + put(MediaStore.Images.Media.MIME_TYPE, imageMime) + } + + val resolver = context.contentResolver + val existingUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon() + .appendQueryParameter(MediaStore.Images.Media.DISPLAY_NAME, name) + .appendQueryParameter(MediaStore.Images.Media.RELATIVE_PATH, directory) + .build() + + val cursor = + resolver.query(existingUri, arrayOf(MediaStore.Images.Media._ID), null, null, null) + val uri: Uri? = if (cursor != null && cursor.moveToFirst()) { + val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)) + Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id.toString()) + } else { + resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) + } + cursor?.close() + + if (uri != null) { + resolver.openOutputStream(uri)?.use { outputStream -> + outputStream.write(image) + } + } else { + throw FileNotFoundException("Failed to create or update MediaStore record.") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/MainActivity.kt b/app/src/main/java/dev/snipme/snipmeapp/MainActivity.kt new file mode 100644 index 0000000..b74f45a --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/MainActivity.kt @@ -0,0 +1,45 @@ +package dev.snipme.snipmeapp + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import dev.snipme.snipmeapp.channel.EventStreamHandlerPlugin +import dev.snipme.snipmeapp.channel.StateStreamHandlerPlugin +import dev.snipme.snipmeapp.channel.details.DetailsModelPlugin +import dev.snipme.snipmeapp.channel.login.LoginModelPlugin +import dev.snipme.snipmeapp.channel.main.MainModelPlugin +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.embedding.engine.FlutterEngineCache +import io.flutter.embedding.engine.dart.DartExecutor + +class MainActivity : AppCompatActivity() { + // TODO Improve flutter enginge management or remove + private lateinit var flutterEngine: FlutterEngine + + private val cachedEngineId = "ID_CACHED_FLUTTER_ENGINE" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + FlutterEngine(this).apply { + dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault()) + + plugins.add( + setOf( + StateStreamHandlerPlugin(), + EventStreamHandlerPlugin(), + LoginModelPlugin(), + MainModelPlugin(), + DetailsModelPlugin() + ) + ) + FlutterEngineCache.getInstance().put(cachedEngineId, this) + + startActivity( + FlutterActivity.withCachedEngine(cachedEngineId) + .build(baseContext) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/DataModel.g.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/DataModel.g.kt new file mode 100644 index 0000000..1003358 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/DataModel.g.kt @@ -0,0 +1,1055 @@ +// Autogenerated from Pigeon (v24.2.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package dev.snipme.snipmeapp.channel + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private fun wrapResult(result: Any?): List { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class SnippetLanguageType(val raw: Int) { + C(0), + CPP(1), + OBJECTIVE_C(2), + C_SHARP(3), + JAVA(4), + BASH(5), + PYTHON(6), + PERL(7), + RUBY(8), + SWIFT(9), + JAVASCRIPT(10), + KOTLIN(11), + COFFEESCRIPT(12), + RUST(13), + BASIC(14), + CLOJURE(15), + CSS(16), + DART(17), + ERLANG(18), + GO(19), + HASKELL(20), + LISP(21), + LLVM(22), + LUA(23), + MATLAB(24), + ML(25), + MUMPS(26), + NEMERLE(27), + PASCAL(28), + R(29), + RD(30), + SCALA(31), + SQL(32), + TEX(33), + VB(34), + VHDL(35), + TCL(36), + XQUERY(37), + YAML(38), + MARKDOWN(39), + JSON(40), + XML(41), + PROTO(42), + REGEX(43), + UNKNOWN(44); + + companion object { + fun ofRaw(raw: Int): SnippetLanguageType? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class SnippetFilterType(val raw: Int) { + ALL(0), + MINE(1), + SHARED(2); + + companion object { + fun ofRaw(raw: Int): SnippetFilterType? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class UserReaction(val raw: Int) { + NONE(0), + LIKE(1), + DISLIKE(2); + + companion object { + fun ofRaw(raw: Int): UserReaction? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class ModelState(val raw: Int) { + LOADING(0), + LOADED(1), + ERROR(2); + + companion object { + fun ofRaw(raw: Int): ModelState? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class MainModelEvent(val raw: Int) { + NONE(0), + ALERT(1), + LOGOUT(2); + + companion object { + fun ofRaw(raw: Int): MainModelEvent? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class DetailsModelEvent(val raw: Int) { + NONE(0), + ALERT(1), + DELETED(2); + + companion object { + fun ofRaw(raw: Int): DetailsModelEvent? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class LoginModelEvent(val raw: Int) { + NONE(0), + LOGGED(1); + + companion object { + fun ofRaw(raw: Int): LoginModelEvent? { + return values().firstOrNull { it.raw == raw } + } + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class Snippet ( + val uuid: String? = null, + val title: String? = null, + val code: SnippetCode? = null, + val language: SnippetLanguage? = null, + val owner: Owner? = null, + val isOwner: Boolean? = null, + val timeAgo: String? = null, + val voteResult: Long? = null, + val userReaction: UserReaction? = null, + val isPrivate: Boolean? = null, + val isLiked: Boolean? = null, + val isDisliked: Boolean? = null, + val isSaved: Boolean? = null, + val isToDelete: Boolean? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): Snippet { + val uuid = pigeonVar_list[0] as String? + val title = pigeonVar_list[1] as String? + val code = pigeonVar_list[2] as SnippetCode? + val language = pigeonVar_list[3] as SnippetLanguage? + val owner = pigeonVar_list[4] as Owner? + val isOwner = pigeonVar_list[5] as Boolean? + val timeAgo = pigeonVar_list[6] as String? + val voteResult = pigeonVar_list[7] as Long? + val userReaction = pigeonVar_list[8] as UserReaction? + val isPrivate = pigeonVar_list[9] as Boolean? + val isLiked = pigeonVar_list[10] as Boolean? + val isDisliked = pigeonVar_list[11] as Boolean? + val isSaved = pigeonVar_list[12] as Boolean? + val isToDelete = pigeonVar_list[13] as Boolean? + return Snippet(uuid, title, code, language, owner, isOwner, timeAgo, voteResult, userReaction, isPrivate, isLiked, isDisliked, isSaved, isToDelete) + } + } + fun toList(): List { + return listOf( + uuid, + title, + code, + language, + owner, + isOwner, + timeAgo, + voteResult, + userReaction, + isPrivate, + isLiked, + isDisliked, + isSaved, + isToDelete, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class SnippetCode ( + val raw: String? = null, + val tokens: List? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): SnippetCode { + val raw = pigeonVar_list[0] as String? + val tokens = pigeonVar_list[1] as List? + return SnippetCode(raw, tokens) + } + } + fun toList(): List { + return listOf( + raw, + tokens, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class SyntaxToken ( + val start: Long? = null, + val end: Long? = null, + val color: Long? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): SyntaxToken { + val start = pigeonVar_list[0] as Long? + val end = pigeonVar_list[1] as Long? + val color = pigeonVar_list[2] as Long? + return SyntaxToken(start, end, color) + } + } + fun toList(): List { + return listOf( + start, + end, + color, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class SnippetLanguage ( + val raw: String? = null, + val type: SnippetLanguageType? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): SnippetLanguage { + val raw = pigeonVar_list[0] as String? + val type = pigeonVar_list[1] as SnippetLanguageType? + return SnippetLanguage(raw, type) + } + } + fun toList(): List { + return listOf( + raw, + type, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class Owner ( + val id: Long? = null, + val login: String? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): Owner { + val id = pigeonVar_list[0] as Long? + val login = pigeonVar_list[1] as String? + return Owner(id, login) + } + } + fun toList(): List { + return listOf( + id, + login, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class SnippetFilter ( + val languages: List? = null, + val selectedLanguages: List? = null, + val scopes: List? = null, + val selectedScope: String? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): SnippetFilter { + val languages = pigeonVar_list[0] as List? + val selectedLanguages = pigeonVar_list[1] as List? + val scopes = pigeonVar_list[2] as List? + val selectedScope = pigeonVar_list[3] as String? + return SnippetFilter(languages, selectedLanguages, scopes, selectedScope) + } + } + fun toList(): List { + return listOf( + languages, + selectedLanguages, + scopes, + selectedScope, + ) + } +} + +/** + * Generated class from Pigeon that represents data sent in messages. + * This class should not be extended by any user class outside of the generated file. + */ +sealed class ModelStateData +/** + * Generated class from Pigeon that represents data sent in messages. + * This class should not be extended by any user class outside of the generated file. + */ +sealed class ModelEventData +/** Generated class from Pigeon that represents data sent in messages. */ +data class MainModelStateData ( + val state: ModelState? = null, + val isLoading: Boolean? = null, + val data: List? = null, + val filter: SnippetFilter? = null, + val error: String? = null +) : ModelStateData() + { + companion object { + fun fromList(pigeonVar_list: List): MainModelStateData { + val state = pigeonVar_list[0] as ModelState? + val isLoading = pigeonVar_list[1] as Boolean? + val data = pigeonVar_list[2] as List? + val filter = pigeonVar_list[3] as SnippetFilter? + val error = pigeonVar_list[4] as String? + return MainModelStateData(state, isLoading, data, filter, error) + } + } + fun toList(): List { + return listOf( + state, + isLoading, + data, + filter, + error, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class MainModelEventData ( + val event: MainModelEvent? = null, + val message: String? = null +) : ModelEventData() + { + companion object { + fun fromList(pigeonVar_list: List): MainModelEventData { + val event = pigeonVar_list[0] as MainModelEvent? + val message = pigeonVar_list[1] as String? + return MainModelEventData(event, message) + } + } + fun toList(): List { + return listOf( + event, + message, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class DetailsModelStateData ( + val state: ModelState? = null, + val isLoading: Boolean? = null, + val data: Snippet? = null, + val error: String? = null +) : ModelStateData() + { + companion object { + fun fromList(pigeonVar_list: List): DetailsModelStateData { + val state = pigeonVar_list[0] as ModelState? + val isLoading = pigeonVar_list[1] as Boolean? + val data = pigeonVar_list[2] as Snippet? + val error = pigeonVar_list[3] as String? + return DetailsModelStateData(state, isLoading, data, error) + } + } + fun toList(): List { + return listOf( + state, + isLoading, + data, + error, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class DetailsModelEventData ( + val event: DetailsModelEvent? = null, + val value: String? = null +) : ModelEventData() + { + companion object { + fun fromList(pigeonVar_list: List): DetailsModelEventData { + val event = pigeonVar_list[0] as DetailsModelEvent? + val value = pigeonVar_list[1] as String? + return DetailsModelEventData(event, value) + } + } + fun toList(): List { + return listOf( + event, + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class LoginModelStateData ( + val state: ModelState? = null, + val isLoading: Boolean? = null +) : ModelStateData() + { + companion object { + fun fromList(pigeonVar_list: List): LoginModelStateData { + val state = pigeonVar_list[0] as ModelState? + val isLoading = pigeonVar_list[1] as Boolean? + return LoginModelStateData(state, isLoading) + } + } + fun toList(): List { + return listOf( + state, + isLoading, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class LoginModelEventData ( + val event: LoginModelEvent? = null +) : ModelEventData() + { + companion object { + fun fromList(pigeonVar_list: List): LoginModelEventData { + val event = pigeonVar_list[0] as LoginModelEvent? + return LoginModelEventData(event) + } + } + fun toList(): List { + return listOf( + event, + ) + } +} +private open class DataModelPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as Long?)?.let { + SnippetLanguageType.ofRaw(it.toInt()) + } + } + 130.toByte() -> { + return (readValue(buffer) as Long?)?.let { + SnippetFilterType.ofRaw(it.toInt()) + } + } + 131.toByte() -> { + return (readValue(buffer) as Long?)?.let { + UserReaction.ofRaw(it.toInt()) + } + } + 132.toByte() -> { + return (readValue(buffer) as Long?)?.let { + ModelState.ofRaw(it.toInt()) + } + } + 133.toByte() -> { + return (readValue(buffer) as Long?)?.let { + MainModelEvent.ofRaw(it.toInt()) + } + } + 134.toByte() -> { + return (readValue(buffer) as Long?)?.let { + DetailsModelEvent.ofRaw(it.toInt()) + } + } + 135.toByte() -> { + return (readValue(buffer) as Long?)?.let { + LoginModelEvent.ofRaw(it.toInt()) + } + } + 136.toByte() -> { + return (readValue(buffer) as? List)?.let { + Snippet.fromList(it) + } + } + 137.toByte() -> { + return (readValue(buffer) as? List)?.let { + SnippetCode.fromList(it) + } + } + 138.toByte() -> { + return (readValue(buffer) as? List)?.let { + SyntaxToken.fromList(it) + } + } + 139.toByte() -> { + return (readValue(buffer) as? List)?.let { + SnippetLanguage.fromList(it) + } + } + 140.toByte() -> { + return (readValue(buffer) as? List)?.let { + Owner.fromList(it) + } + } + 141.toByte() -> { + return (readValue(buffer) as? List)?.let { + SnippetFilter.fromList(it) + } + } + 142.toByte() -> { + return (readValue(buffer) as? List)?.let { + MainModelStateData.fromList(it) + } + } + 143.toByte() -> { + return (readValue(buffer) as? List)?.let { + MainModelEventData.fromList(it) + } + } + 144.toByte() -> { + return (readValue(buffer) as? List)?.let { + DetailsModelStateData.fromList(it) + } + } + 145.toByte() -> { + return (readValue(buffer) as? List)?.let { + DetailsModelEventData.fromList(it) + } + } + 146.toByte() -> { + return (readValue(buffer) as? List)?.let { + LoginModelStateData.fromList(it) + } + } + 147.toByte() -> { + return (readValue(buffer) as? List)?.let { + LoginModelEventData.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is SnippetLanguageType -> { + stream.write(129) + writeValue(stream, value.raw) + } + is SnippetFilterType -> { + stream.write(130) + writeValue(stream, value.raw) + } + is UserReaction -> { + stream.write(131) + writeValue(stream, value.raw) + } + is ModelState -> { + stream.write(132) + writeValue(stream, value.raw) + } + is MainModelEvent -> { + stream.write(133) + writeValue(stream, value.raw) + } + is DetailsModelEvent -> { + stream.write(134) + writeValue(stream, value.raw) + } + is LoginModelEvent -> { + stream.write(135) + writeValue(stream, value.raw) + } + is Snippet -> { + stream.write(136) + writeValue(stream, value.toList()) + } + is SnippetCode -> { + stream.write(137) + writeValue(stream, value.toList()) + } + is SyntaxToken -> { + stream.write(138) + writeValue(stream, value.toList()) + } + is SnippetLanguage -> { + stream.write(139) + writeValue(stream, value.toList()) + } + is Owner -> { + stream.write(140) + writeValue(stream, value.toList()) + } + is SnippetFilter -> { + stream.write(141) + writeValue(stream, value.toList()) + } + is MainModelStateData -> { + stream.write(142) + writeValue(stream, value.toList()) + } + is MainModelEventData -> { + stream.write(143) + writeValue(stream, value.toList()) + } + is DetailsModelStateData -> { + stream.write(144) + writeValue(stream, value.toList()) + } + is DetailsModelEventData -> { + stream.write(145) + writeValue(stream, value.toList()) + } + is LoginModelStateData -> { + stream.write(146) + writeValue(stream, value.toList()) + } + is LoginModelEventData -> { + stream.write(147) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +val DataModelPigeonMethodCodec = StandardMethodCodec(DataModelPigeonCodec()); + + +private class DataModelPigeonStreamHandler( + val wrapper: DataModelPigeonEventChannelWrapper +) : EventChannel.StreamHandler { + var pigeonSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + pigeonSink = PigeonEventSink(sink) + wrapper.onListen(p0, pigeonSink!!) + } + + override fun onCancel(p0: Any?) { + pigeonSink = null + wrapper.onCancel(p0) + } +} + +interface DataModelPigeonEventChannelWrapper { + open fun onListen(p0: Any?, sink: PigeonEventSink) {} + + open fun onCancel(p0: Any?) {} +} + +class PigeonEventSink(private val sink: EventChannel.EventSink) { + fun success(value: T) { + sink.success(value) + } + + fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + sink.error(errorCode, errorMessage, errorDetails) + } + + fun endOfStream() { + sink.endOfStream() + } +} + +abstract class ChannelStateStreamHandler : DataModelPigeonEventChannelWrapper { + companion object { + fun register(messenger: BinaryMessenger, streamHandler: ChannelStateStreamHandler, instanceName: String = "") { + var channelName: String = "dev.flutter.pigeon.flutter_module.ChannelModelEventApi.channelState" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val internalStreamHandler = DataModelPigeonStreamHandler(streamHandler) + EventChannel(messenger, channelName, DataModelPigeonMethodCodec).setStreamHandler(internalStreamHandler) + } + } +} + +abstract class ChannelEventStreamHandler : DataModelPigeonEventChannelWrapper { + companion object { + fun register(messenger: BinaryMessenger, streamHandler: ChannelEventStreamHandler, instanceName: String = "") { + var channelName: String = "dev.flutter.pigeon.flutter_module.ChannelModelEventApi.channelEvent" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val internalStreamHandler = DataModelPigeonStreamHandler(streamHandler) + EventChannel(messenger, channelName, DataModelPigeonMethodCodec).setStreamHandler(internalStreamHandler) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface ChannelMainModel { + fun resetEvent() + fun initState() + fun filterLanguage(language: String, isSelected: Boolean) + fun filterScope(scope: String) + fun logOut() + + companion object { + /** The codec used by ChannelMainModel. */ + val codec: MessageCodec by lazy { + DataModelPigeonCodec() + } + /** Sets up an instance of `ChannelMainModel` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: ChannelMainModel?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelMainModel.resetEvent$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.resetEvent() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelMainModel.initState$separatedMessageChannelSuffix", codec, taskQueue) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.initState() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelMainModel.filterLanguage$separatedMessageChannelSuffix", codec, taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val languageArg = args[0] as String + val isSelectedArg = args[1] as Boolean + val wrapped: List = try { + api.filterLanguage(languageArg, isSelectedArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelMainModel.filterScope$separatedMessageChannelSuffix", codec, taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val scopeArg = args[0] as String + val wrapped: List = try { + api.filterScope(scopeArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelMainModel.logOut$separatedMessageChannelSuffix", codec, taskQueue) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.logOut() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface ChannelDetailsModel { + fun resetEvent() + fun load(uuid: String) + fun toggleFavorite() + fun saveImage(image: ByteArray) + fun copyToClipboard() + fun shareImage(image: ByteArray) + fun delete() + + companion object { + /** The codec used by ChannelDetailsModel. */ + val codec: MessageCodec by lazy { + DataModelPigeonCodec() + } + /** Sets up an instance of `ChannelDetailsModel` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: ChannelDetailsModel?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelDetailsModel.resetEvent$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.resetEvent() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelDetailsModel.load$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val uuidArg = args[0] as String + val wrapped: List = try { + api.load(uuidArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelDetailsModel.toggleFavorite$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.toggleFavorite() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelDetailsModel.saveImage$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val imageArg = args[0] as ByteArray + val wrapped: List = try { + api.saveImage(imageArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelDetailsModel.copyToClipboard$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.copyToClipboard() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelDetailsModel.shareImage$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val imageArg = args[0] as ByteArray + val wrapped: List = try { + api.shareImage(imageArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelDetailsModel.delete$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.delete() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface ChannelLoginModel { + fun loginOrRegister(email: String, password: String) + fun checkLoginState() + fun resetEvent() + + companion object { + /** The codec used by ChannelLoginModel. */ + val codec: MessageCodec by lazy { + DataModelPigeonCodec() + } + /** Sets up an instance of `ChannelLoginModel` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: ChannelLoginModel?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelLoginModel.loginOrRegister$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val emailArg = args[0] as String + val passwordArg = args[1] as String + val wrapped: List = try { + api.loginOrRegister(emailArg, passwordArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelLoginModel.checkLoginState$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.checkLoginState() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_module.ChannelLoginModel.resetEvent$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.resetEvent() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/EventStreamHandlerPlugin.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/EventStreamHandlerPlugin.kt new file mode 100644 index 0000000..a0230cf --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/EventStreamHandlerPlugin.kt @@ -0,0 +1,48 @@ +package dev.snipme.snipmeapp.channel + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding +import io.flutter.plugin.common.BinaryMessenger +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class FlowChannelEventStreamHandler : ChannelEventStreamHandler() { + private val scope = CoroutineScope(Dispatchers.Main) + private val sinkFlow = MutableSharedFlow( + replay = 3, + onBufferOverflow = DROP_OLDEST + ) + + fun onSetup(messenger: BinaryMessenger) { + register(messenger, this) + } + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + sinkFlow.onEach { sink.success(it) }.launchIn(scope) + } + + override fun onCancel(p0: Any?) { + sinkFlow.resetReplayCache() + } + + fun zip(flow: Flow) { + flow.onEach { sinkFlow.emit(it) }.launchIn(scope) + } +} + +class EventStreamHandlerPlugin : FlutterPlugin, KoinComponent { + private val eventStream by inject() + + override fun onAttachedToEngine(binding: FlutterPluginBinding) { + eventStream.onSetup(binding.binaryMessenger) + } + + override fun onDetachedFromEngine(binding: FlutterPluginBinding) {} +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/ModelPlugin.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/ModelPlugin.kt new file mode 100644 index 0000000..82ae9e5 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/ModelPlugin.kt @@ -0,0 +1,95 @@ +package dev.snipme.snipmeapp.channel + +import android.text.Spanned +import android.text.format.DateUtils +import android.text.style.ForegroundColorSpan +import androidx.core.text.getSpans +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.BinaryMessenger +import org.koin.core.component.KoinComponent +import dev.snipme.snipmeapp.domain.reaction.UserReaction +import dev.snipme.snipmeapp.domain.snippets.* +import java.util.* +import dev.snipme.snipmeapp.channel.Snippet as ChannelSnippet +import dev.snipme.snipmeapp.channel.SnippetCode as ChannelSnippetCode +import dev.snipme.snipmeapp.channel.SnippetLanguage as ChannelSnippetLanguage +import dev.snipme.snipmeapp.channel.SnippetLanguageType as ChannelSnippetLanguageType +import dev.snipme.snipmeapp.channel.UserReaction as ChannelUserReaction +import dev.snipme.snipmeapp.channel.SyntaxToken as ChannelSyntaxToken +import dev.snipme.snipmeapp.channel.Owner as ChannelOwner + +abstract class ModelPlugin : FlutterPlugin, KoinComponent { + + abstract fun onSetup(messenger: BinaryMessenger, channelModel: T?) + + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + onSetup(binding.binaryMessenger, this as T) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + onSetup(binding.binaryMessenger, null) + } +} + +fun Snippet.toModelData(): ChannelSnippet = + ChannelSnippet( + uuid = uuid, + title = title, + code = code.toModelSnippetCode(), + language = language.toModelSnippetLanguage(), + owner = owner.toModelOwner(), + isOwner = isOwner, + voteResult = (numberOfLikes - numberOfDislikes).toLong(), + userReaction = userReaction.toModelUserReaction(), + isLiked = userReaction.toModelReactionState(UserReaction.LIKE), + isDisliked = userReaction.toModelReactionState(UserReaction.DISLIKE), + isPrivate = visibility == SnippetVisibility.PRIVATE, + isSaved = calculateSavedState(isOwner, visibility), + isToDelete = isOwner, + timeAgo = DateUtils.getRelativeTimeSpanString( + modifiedAt.time, + Date().time, + DateUtils.SECOND_IN_MILLIS + ).toString() + ) + +private fun Owner.toModelOwner() = ChannelOwner(id = id.toLong(), login = login) + +private fun SnippetCode.toModelSnippetCode() = + ChannelSnippetCode( + raw = raw, + tokens = highlighted.getSpans().map { span -> + span.toSyntaxToken(highlighted) + }, + ) + +private fun SnippetLanguage.toModelSnippetLanguage() = + ChannelSnippetLanguage( + raw = raw, + type = ChannelSnippetLanguageType.valueOf(type.name), + ) + +private fun UserReaction.toModelUserReaction(): ChannelUserReaction = + when (this) { + UserReaction.LIKE -> ChannelUserReaction.LIKE + UserReaction.DISLIKE -> ChannelUserReaction.DISLIKE + else -> ChannelUserReaction.NONE + } + +private fun UserReaction.toModelReactionState(reaction: UserReaction) = + if (this == UserReaction.NONE) null else this == reaction + +private fun calculateSavedState( + isOwner: Boolean, + visibility: SnippetVisibility +): Boolean? { + if (isOwner.not()) return null + return visibility == SnippetVisibility.PRIVATE +} + +private fun ForegroundColorSpan.toSyntaxToken(spannable: Spanned) = + ChannelSyntaxToken( + start = spannable.getSpanStart(this).toLong(), + end = spannable.getSpanEnd(this).toLong(), + color = foregroundColor.toLong(), + ) diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/StateStreamHandlerPlugin.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/StateStreamHandlerPlugin.kt new file mode 100644 index 0000000..3bd5dc3 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/StateStreamHandlerPlugin.kt @@ -0,0 +1,48 @@ +package dev.snipme.snipmeapp.channel + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding +import io.flutter.plugin.common.BinaryMessenger +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class FlowChannelStateStreamHandler : ChannelStateStreamHandler() { + private val scope = CoroutineScope(Dispatchers.Main) + private val sinkFlow = MutableSharedFlow( + replay = 3, + onBufferOverflow = DROP_OLDEST + ) + + fun onSetup(messenger: BinaryMessenger) { + register(messenger, this) + } + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + sinkFlow.onEach { sink.success(it) }.launchIn(scope) + } + + override fun onCancel(p0: Any?) { + sinkFlow.resetReplayCache() + } + + fun zip(flow: Flow) { + flow.onEach { sinkFlow.emit(it) }.launchIn(scope) + } +} + +class StateStreamHandlerPlugin : FlutterPlugin, KoinComponent { + private val stateStream by inject() + + override fun onAttachedToEngine(binding: FlutterPluginBinding) { + stateStream.onSetup(binding.binaryMessenger) + } + + override fun onDetachedFromEngine(binding: FlutterPluginBinding) {} +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/details/DetailsModel.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/details/DetailsModel.kt new file mode 100644 index 0000000..c97cb3f --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/details/DetailsModel.kt @@ -0,0 +1,167 @@ +package dev.snipme.snipmeapp.channel.details + +import dev.snipme.snipmeapp.channel.error.ErrorParsable +import dev.snipme.snipmeapp.channel.session.SessionModel +import dev.snipme.snipmeapp.domain.clipboard.AddToClipboardUseCase +import dev.snipme.snipmeapp.domain.error.exception.ConnectionException +import dev.snipme.snipmeapp.domain.error.exception.ContentNotFoundException +import dev.snipme.snipmeapp.domain.error.exception.ForbiddenActionException +import dev.snipme.snipmeapp.domain.error.exception.NetworkNotAvailableException +import dev.snipme.snipmeapp.domain.error.exception.NotAuthorizedException +import dev.snipme.snipmeapp.domain.error.exception.RemoteException +import dev.snipme.snipmeapp.domain.error.exception.SessionExpiredException +import dev.snipme.snipmeapp.domain.message.ErrorMessages +import dev.snipme.snipmeapp.domain.reaction.GetTargetUserReactionUseCase +import dev.snipme.snipmeapp.domain.reaction.SetUserReactionUseCase +import dev.snipme.snipmeapp.domain.reaction.UserReaction +import dev.snipme.snipmeapp.domain.share.ShareSnippetUseCase +import dev.snipme.snipmeapp.domain.snippet.DeleteSnippetUseCase +import dev.snipme.snipmeapp.domain.snippet.GetSingleSnippetUseCase +import dev.snipme.snipmeapp.domain.snippet.SaveSnippetUseCase +import dev.snipme.snipmeapp.domain.snippets.Snippet +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxkotlin.subscribeBy +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.flow.MutableStateFlow +import timber.log.Timber + +class DetailsModel( + private val errorMessages: ErrorMessages, + private val getSnippet: GetSingleSnippetUseCase, + private val clipboard: AddToClipboardUseCase, + private val getTargetReaction: GetTargetUserReactionUseCase, + private val setUserReaction: SetUserReactionUseCase, + private val saveSnippet: SaveSnippetUseCase, + private val shareSnippet: ShareSnippetUseCase, + private val deleteSnippet: DeleteSnippetUseCase, + private val session: SessionModel +) : ErrorParsable { + private val disposables = CompositeDisposable() + + private val mutableState = MutableStateFlow(Loading) + val state = mutableState + + private val mutableEvent = MutableStateFlow(Idle) + val event = mutableEvent + + override fun parseError(throwable: Throwable) { + when (throwable) { + is ConnectionException -> setState(Error(errorMessages.parse(throwable))) + is ContentNotFoundException -> setState(Error(errorMessages.parse(throwable))) + is ForbiddenActionException -> setState(Error(errorMessages.parse(throwable))) + is NetworkNotAvailableException -> setState(Error(errorMessages.parse(throwable))) + is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } + is RemoteException -> setState(Error(errorMessages.parse(throwable))) + is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } + else -> setState(Error(errorMessages.parse(throwable))) + } + } + + fun load(uuid: String) { + setState(Loading) + getSnippet(uuid) + .subscribeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { setState(Loaded(it)) }, + onError = { + Timber.e("Couldn't load snippets, error = $it") + parseError(it) + } + ).also { disposables += it } + } + + fun toggleFavorite() { + // TODO Implement + } + + fun copyToClipboard() { + getSnippet()?.let { + clipboard(it.title, it.code.raw) + } + } + + fun save(image: ByteArray) { + Timber.d("Saving snippet image ${image.size}") + try { + getSnippet()?.let { + saveSnippet(image, it) + Timber.d("Snippet ${it.title} saved") + } + mutableEvent.value = Alert("Snippet saved") + } catch (e: Exception) { + Timber.e("Couldn't save snippet, error = $e") + mutableEvent.value = Alert(errorMessages.generic) + } + } + + fun share(image: ByteArray) { + try { + getSnippet()?.let { + shareSnippet(image, it) + } + mutableEvent.value = Alert("Snippet shared") + } catch (e: Exception) { + Timber.e("Couldn't share snippet, error = $e") + mutableEvent.value = Alert(errorMessages.generic) + } + } + + fun delete() { + getSnippet()?.let { + setState(Loading) + deleteSnippet(it.uuid) + .subscribeOn(Schedulers.io()) + .subscribeBy( + onComplete = { mutableEvent.value = Deleted }, + onError = { error -> + Timber.e("Couldn't delete snippet, error = $error") + parseError(error) + } + ).also { disposables += it } + } + } + + private fun changeReaction(newReaction: UserReaction) { + // Immediately show change to user + val previousState = getLoaded() ?: return + val targetReaction = getTargetReaction(previousState.snippet, newReaction) + setState(previousState.run { copy(snippet = snippet.copy(userReaction = targetReaction)) }) + + setUserReaction(previousState.snippet, newReaction) + .subscribeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { snippet -> mutableState.value = Loaded(snippet) }, + onError = { + // Revert changes + Timber.e("Couldn't change user reaction, error = $it") + mutableEvent.value = Alert(errorMessages.generic) + setState(previousState) + } + ).also { disposables += it } + } + + private fun getSnippet(): Snippet? = getLoaded()?.snippet + + private fun getLoaded() = + if (state.value is Loaded) { + (state.value as Loaded) + } else { + null + } + + private fun setState(newState: DetailsViewState?) { + newState?.let { mutableState.value = it } + } +} + +sealed class DetailsViewState +data object Loading : DetailsViewState() +data class Loaded(val snippet: Snippet) : DetailsViewState() +data class Error(val error: String?) : DetailsViewState() + +sealed class DetailsEvent +data object Idle : DetailsEvent() +data object Deleted : DetailsEvent() +data class Alert(val message: String) : DetailsEvent() +data object Logout : DetailsEvent() \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/details/DetailsModelPlugin.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/details/DetailsModelPlugin.kt new file mode 100644 index 0000000..55496d6 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/details/DetailsModelPlugin.kt @@ -0,0 +1,83 @@ +package dev.snipme.snipmeapp.channel.details + +import dev.snipme.snipmeapp.channel.ChannelDetailsModel +import dev.snipme.snipmeapp.channel.FlowChannelEventStreamHandler +import dev.snipme.snipmeapp.channel.FlowChannelStateStreamHandler +import dev.snipme.snipmeapp.channel.ModelPlugin +import dev.snipme.snipmeapp.channel.toModelData +import io.flutter.plugin.common.BinaryMessenger +import kotlinx.coroutines.flow.map +import org.koin.core.component.inject +import dev.snipme.snipmeapp.channel.DetailsModelEvent as ChannelDetailsModelEvent +import dev.snipme.snipmeapp.channel.DetailsModelEventData as ChannelDetailsModelEventData +import dev.snipme.snipmeapp.channel.DetailsModelStateData as ChannelDetailsModelStateData +import dev.snipme.snipmeapp.channel.ModelState as ChannelModelState + +class DetailsModelPlugin : ModelPlugin(), ChannelDetailsModel { + private val model: DetailsModel by inject() + private val channelStateFlow by inject() + private val channelEventFlow by inject() + + override fun onSetup(messenger: BinaryMessenger, channelModel: ChannelDetailsModel?) { + ChannelDetailsModel.setUp(messenger, channelModel) + channelStateFlow.zip(model.state.map { getModelState(it) }) + channelEventFlow.zip(model.event.map { getModelEvent(it) }) + } + + override fun resetEvent() { + model.event.value = Idle + } + + override fun load(uuid: String) { + model.load(uuid) + } + + override fun toggleFavorite() { + model.toggleFavorite() + } + + override fun saveImage(image: ByteArray) { + model.save(image) + } + + override fun copyToClipboard() { + model.copyToClipboard() + } + + override fun shareImage(image: ByteArray) { + model.share(image) + } + + override fun delete() { + model.delete() + } + + private fun getModelState(viewState: DetailsViewState): ChannelDetailsModelStateData { + return ChannelDetailsModelStateData( + state = viewState.toModelState(), + isLoading = viewState is Loading, + data = (viewState as? Loaded)?.snippet?.toModelData(), + ) + } + + private fun getModelEvent(event: DetailsEvent): ChannelDetailsModelEventData { + return ChannelDetailsModelEventData( + event = event.toModelEvent(), + value = (event as? Alert)?.message.orEmpty() + ) + } + + private fun DetailsViewState.toModelState() = + when (this) { + Loading -> ChannelModelState.LOADING + is Loaded -> ChannelModelState.LOADED + else -> ChannelModelState.ERROR + } + + private fun DetailsEvent.toModelEvent() = + when (this) { + is Deleted -> ChannelDetailsModelEvent.DELETED + is Alert -> ChannelDetailsModelEvent.ALERT + else -> ChannelDetailsModelEvent.NONE + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/error/ErrorParsable.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/error/ErrorParsable.kt similarity index 60% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/ui/error/ErrorParsable.kt rename to app/src/main/java/dev/snipme/snipmeapp/channel/error/ErrorParsable.kt index 66ec2d2..6db36db 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/error/ErrorParsable.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/error/ErrorParsable.kt @@ -1,6 +1,5 @@ -package pl.tkadziolka.snipmeandroid.ui.error +package dev.snipme.snipmeapp.channel.error interface ErrorParsable { - fun parseError(throwable: Throwable) } \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/login/LoginModel.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/login/LoginModel.kt new file mode 100644 index 0000000..ed1cc70 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/login/LoginModel.kt @@ -0,0 +1,137 @@ +package dev.snipme.snipmeapp.channel.login + +import dev.snipme.snipmeapp.channel.error.ErrorParsable +import dev.snipme.snipmeapp.domain.auth.InitialLoginUseCase +import dev.snipme.snipmeapp.domain.auth.LoginInteractor +import dev.snipme.snipmeapp.domain.error.exception.ConnectionException +import dev.snipme.snipmeapp.domain.error.exception.ContentNotFoundException +import dev.snipme.snipmeapp.domain.error.exception.ForbiddenActionException +import dev.snipme.snipmeapp.domain.error.exception.NetworkNotAvailableException +import dev.snipme.snipmeapp.domain.error.exception.NotAuthorizedException +import dev.snipme.snipmeapp.domain.error.exception.RemoteException +import dev.snipme.snipmeapp.domain.error.exception.SessionExpiredException +import dev.snipme.snipmeapp.domain.message.ErrorMessages +import dev.snipme.snipmeapp.util.extension.inProgress +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable +import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxkotlin.subscribeBy +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.flow.MutableStateFlow +import timber.log.Timber +import java.util.concurrent.TimeUnit + +class LoginModel( + private val errorMessages: ErrorMessages, + private val interactor: LoginInteractor, + private val initialLogin: InitialLoginUseCase, +) : ErrorParsable { + private val disposables = CompositeDisposable() + private var identifyDisposable: Disposable? = null + private var loginDisposable: Disposable? = null + private var registerDisposable: Disposable? = null + + private val mutableState = MutableStateFlow(Loading) + val state = mutableState + + private val mutableEvent = MutableStateFlow(Idle) + val event = mutableEvent + + override fun parseError(throwable: Throwable) { + when (throwable) { + is ConnectionException -> setEvent(Error(errorMessages.parse(throwable))) + is ContentNotFoundException -> setEvent(Error(errorMessages.parse(throwable))) + is ForbiddenActionException -> setEvent(Error(errorMessages.alreadyRegistered)) + is NetworkNotAvailableException -> setEvent(Error(errorMessages.parse(throwable))) + is NotAuthorizedException -> setEvent(Error(errorMessages.parse(throwable))) + is RemoteException -> setEvent(Error(errorMessages.parse(throwable))) + is SessionExpiredException -> setEvent(Error(errorMessages.parse(throwable))) + else -> setEvent(Error(errorMessages.parse(throwable))) + } + } + + fun init() { + initialLogin() + .delay(3, TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()) + .doOnEvent { setState(Loaded) } + .subscribeBy( + onComplete = { setEvent(Logged) }, + onError = { + if (it !is NotAuthorizedException) { + Timber.e("Couldn't get token or user, error = $it") + } + } + ).also { disposables += it } + } + + fun loginOrRegister(email: String, password: String) { + if (identifyDisposable.inProgress()) return + + identifyDisposable = interactor.identify(email) + .subscribeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { identified -> publishIdentified(email, password, identified) }, + onError = { + Timber.d("Couldn't identify user = $email, error = $it") + parseError(it) + } + ).also { disposables += it } + } + + fun login(email: String, password: String) { + if (loginDisposable.inProgress()) return + + loginDisposable = interactor.login(email, password) + .subscribeOn(Schedulers.io()) + .doOnEvent { setState(Loaded) } + .subscribeBy( + onComplete = { setEvent(Logged) }, + onError = { + Timber.d("Couldn't login user = $email, error = $it") + parseError(it) + } + ).also { disposables += it } + } + + fun register(email: String, password: String) { + if (registerDisposable.inProgress()) return + + registerDisposable = interactor.register(email, password, email) + .subscribeOn(Schedulers.io()) + .doOnEvent { setState(Loaded) } + .subscribeBy( + onComplete = { setEvent(Logged) }, + onError = { + Timber.d("Couldn't register user = $email, error = $it") + parseError(it) + } + ).also { disposables += it } + } + + private fun publishIdentified(email: String, password: String, identified: Boolean) { + if (identified) { + login(email, password) + } else { + register(email, password) + } + } + + private fun setEvent(event: LoginEvent) { + mutableEvent.value = event + } + + private fun setState(state: LoginState) { + mutableState.value = state + } +} + + +sealed class LoginState +data object Loading : LoginState() +data object Loaded : LoginState() + +sealed class LoginEvent +data object Idle : LoginEvent() +data object Logged : LoginEvent() +data class Error(val message: String?) : LoginEvent() diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/login/LoginModelPlugin.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/login/LoginModelPlugin.kt new file mode 100644 index 0000000..e0b319c --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/login/LoginModelPlugin.kt @@ -0,0 +1,62 @@ +package dev.snipme.snipmeapp.channel.login + +import dev.snipme.snipmeapp.channel.ChannelLoginModel +import dev.snipme.snipmeapp.channel.FlowChannelEventStreamHandler +import dev.snipme.snipmeapp.channel.FlowChannelStateStreamHandler +import dev.snipme.snipmeapp.channel.ModelPlugin +import io.flutter.plugin.common.BinaryMessenger +import kotlinx.coroutines.flow.map +import org.koin.core.component.inject +import dev.snipme.snipmeapp.channel.LoginModelEvent as ChannelLoginModelEvent +import dev.snipme.snipmeapp.channel.LoginModelEventData as ChannelLoginModelEventData +import dev.snipme.snipmeapp.channel.LoginModelStateData as ChannelLoginModelStateData +import dev.snipme.snipmeapp.channel.ModelState as ChannelModelState + +class LoginModelPlugin : ModelPlugin(), ChannelLoginModel { + private val model: LoginModel by inject() + private val channelStateFlow by inject() + private val channelEventFlow by inject() + + override fun resetEvent() { + model.event.value = Idle + } + + override fun onSetup(messenger: BinaryMessenger, channelModel: ChannelLoginModel?) { + ChannelLoginModel.setUp(messenger, channelModel) + channelStateFlow.zip(model.state.map { getModelState(it) }) + channelEventFlow.zip(model.event.map { getModelEvent(it) }) + } + + override fun checkLoginState() { + model.init() + } + + override fun loginOrRegister(email: String, password: String) { + model.loginOrRegister(email, password) + } + + private fun getModelEvent(loginEvent: LoginEvent): ChannelLoginModelEventData { + return ChannelLoginModelEventData( + event = loginEvent.toModelLoginEvent() + ) + } + + private fun getModelState(loginState: LoginState): ChannelLoginModelStateData { + return ChannelLoginModelStateData( + state = loginState.toModelLoginState(), + isLoading = loginState is Loading + ) + } + + private fun LoginState.toModelLoginState() = + when (this) { + Loaded -> ChannelModelState.LOADED + else -> ChannelModelState.LOADING + } + + private fun LoginEvent.toModelLoginEvent() = + when (this) { + Logged -> ChannelLoginModelEvent.LOGGED + else -> ChannelLoginModelEvent.NONE + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/main/MainModel.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/main/MainModel.kt new file mode 100644 index 0000000..d8263a8 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/main/MainModel.kt @@ -0,0 +1,179 @@ +package dev.snipme.snipmeapp.channel.main + +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxkotlin.subscribeBy +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.flow.MutableStateFlow +import dev.snipme.snipmeapp.channel.session.SessionModel +import dev.snipme.snipmeapp.domain.error.exception.* +import dev.snipme.snipmeapp.domain.filter.* +import dev.snipme.snipmeapp.domain.message.ErrorMessages +import dev.snipme.snipmeapp.domain.snippet.ObserveSnippetUpdatesUseCase +import dev.snipme.snipmeapp.domain.snippets.* +import dev.snipme.snipmeapp.domain.user.GetSingleUserUseCase +import dev.snipme.snipmeapp.domain.user.User +import dev.snipme.snipmeapp.channel.error.ErrorParsable +import timber.log.Timber + +private const val ONE_PAGE = 1 + +class MainModel( + private val errorMessages: ErrorMessages, + private val getUser: GetSingleUserUseCase, + private val getSnippets: GetSnippetsUseCase, + private val observeUpdates: ObserveSnippetUpdatesUseCase, + private val hasMore: HasMoreSnippetPagesUseCase, + private val getLanguageFilters: GetLanguageFiltersUseCase, + private val filterSnippetsByLanguage: FilterSnippetsByLanguageUseCase, + private val filterSnippetsByScope: FilterSnippetsByScopeUseCase, + private val updateFilterLanguage: UpdateSnippetFiltersLanguageUseCase, + private val session: SessionModel +) : ErrorParsable { + private val disposables = CompositeDisposable() + + private val mutableEvent = MutableStateFlow(Startup) + val event = mutableEvent + + private val mutableState = MutableStateFlow(Loading) + val state = mutableState + + private var cachedSnippets = emptyList() + private var scopedSnippets = emptyList() + private lateinit var filterState: SnippetFilters + + override fun parseError(throwable: Throwable) { + when (throwable) { + is ConnectionException -> mutableState.value = Error(errorMessages.parse(throwable)) + is ContentNotFoundException -> mutableState.value = + Error(errorMessages.parse(throwable)) + is ForbiddenActionException -> mutableState.value = + Error(errorMessages.parse(throwable)) + is NetworkNotAvailableException -> mutableState.value = + Error(errorMessages.parse(throwable)) + is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } + is RemoteException -> mutableState.value = Error(errorMessages.parse(throwable)) + is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } + else -> mutableState.value = Error(errorMessages.parse(throwable)) + } + } + + init { + observeUpdates() + .subscribeOn(Schedulers.io()) + .subscribeBy( + onNext = { initState() }, + onError = { Timber.e("Couldn't refresh snippet updates, error = $it") } + ).also { disposables += it } + } + + fun initState() { + mutableState.value = Loading + filterState = SnippetFilters( + languages = listOf(SNIPPET_FILTER_ALL), + selectedLanguages = listOf(SNIPPET_FILTER_ALL), + scopes = listOf("All", "Private", "Public"), + selectedScope = "All" + ) + + getUser() + .subscribeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { user -> loadSnippets(user) }, + onError = { + Timber.e("Couldn't load user, error = $it") + parseError(it) + } + ).also { disposables += it } + } + + fun filterLanguage(language: String, isSelected: Boolean) { + getLoadedState()?.let { + filterState = updateFilterLanguage(filterState, language, isSelected) + val filteredSnippets = + filterSnippetsByLanguage(scopedSnippets, filterState.selectedLanguages) + state.value = it.copy(snippets = filteredSnippets, filters = filterState) + } + } + + fun filterScope(scope: String) { + getLoadedState()?.let { + filterState = filterState.copy(selectedScope = scope) + scopedSnippets = filterSnippetsByScope(cachedSnippets, scope) + val updatedFilters = getLanguageFilters(scopedSnippets) + filterState = filterState.copy( + languages = updatedFilters, + selectedLanguages = listOf(SNIPPET_FILTER_ALL), + ) + state.value = it.copy(snippets = scopedSnippets, filters = filterState) + } + } + + fun logOut() { + session.logOut { mutableEvent.value = Logout } + } + + private fun loadNextPage() { + getLoadedState()?.let { state -> + hasMore(SnippetScope.ALL, state.pages) + .subscribeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { hasMore -> + if (hasMore) { + loadSnippets(state.user, pages = state.pages + ONE_PAGE) + } + }, + onError = { + Timber.e("Couldn't check next page, error = $it") + mutableEvent.value = Alert(errorMessages.parse(it)) + }) + .also { disposables += it } + } + } + + private fun loadSnippets( + user: User, + pages: Int = 1, + scope: SnippetScope = SnippetScope.ALL + ) { + getSnippets(scope, pages) + .subscribeOn(Schedulers.io()) + .subscribeBy( + onSuccess = { + cachedSnippets = it + scopedSnippets = cachedSnippets + val updatedFilters = getLanguageFilters(cachedSnippets) + filterState = filterState.copy(languages = updatedFilters) + mutableState.value = Loaded( + user, + it, + pages, + filterState + ) + loadNextPage() + }, + onError = { + Timber.e("Couldn't load snippets, error = $it") + parseError(it) + } + ).also { disposables += it } + } + + private fun getLoadedState(): Loaded? = state.value as? Loaded +} + +sealed class MainViewState +data object Loading : MainViewState() +data class Loaded( + val user: User, + val snippets: List, + val pages: Int, + val filters: SnippetFilters +) : MainViewState() + +data class Error(val message: String?) : MainViewState() + +sealed class MainEvent +data object Startup : MainEvent() +data class Alert(val message: String) : MainEvent() +data object Logout : MainEvent() \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/channel/main/MainModelPlugin.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/main/MainModelPlugin.kt new file mode 100644 index 0000000..3075199 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/main/MainModelPlugin.kt @@ -0,0 +1,93 @@ +package dev.snipme.snipmeapp.channel.main + +import dev.snipme.snipmeapp.channel.FlowChannelEventStreamHandler +import dev.snipme.snipmeapp.channel.FlowChannelStateStreamHandler +import dev.snipme.snipmeapp.channel.ModelPlugin +import dev.snipme.snipmeapp.channel.toModelData +import dev.snipme.snipmeapp.channel.ChannelMainModel +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.snippets.SnippetFilters +import io.flutter.plugin.common.BinaryMessenger +import kotlinx.coroutines.flow.map +import org.koin.core.component.inject +import dev.snipme.snipmeapp.channel.MainModelEvent as ChannelMainModelEvent +import dev.snipme.snipmeapp.channel.MainModelEventData as ChannelMainModelEventData +import dev.snipme.snipmeapp.channel.MainModelStateData as ChannelMainModelStateData +import dev.snipme.snipmeapp.channel.ModelState as ChannelModelState +import dev.snipme.snipmeapp.channel.SnippetFilter as ChannelSnippetFilter + +class MainModelPlugin : ModelPlugin(), ChannelMainModel { + private val model: MainModel by inject() + private val channelStateFlow by inject() + private val channelEventFlow by inject() + + override fun onSetup( + messenger: BinaryMessenger, + channelModel: ChannelMainModel? + ) { + ChannelMainModel.setUp(messenger, channelModel) + channelStateFlow.zip(model.state.map { getState(it) }) + channelEventFlow.zip(model.event.map { getEvent(it) }) + } + + override fun resetEvent() { + model.event.value = Startup + } + + override fun initState() { + model.initState() + } + + override fun filterLanguage(language: String, isSelected: Boolean) { + model.filterLanguage(language, isSelected) + } + + override fun filterScope(scope: String) { + model.filterScope(scope) + } + + override fun logOut() { + model.logOut() + } + + private fun getState(viewState: MainViewState): ChannelMainModelStateData { + return ChannelMainModelStateData( + state = viewState.toModelState(), + isLoading = viewState is Loading, + data = (viewState as? Loaded)?.snippets?.toModelData(), + filter = (viewState as? Loaded)?.filters?.toModelFilter(), + ) + } + + private fun getEvent(viewEvent: MainEvent): ChannelMainModelEventData { + return ChannelMainModelEventData( + event = viewEvent.toModelEvent(), + message = (viewEvent as? Alert)?.message, + ) + } + + private fun MainEvent.toModelEvent() = + when (this) { + is Alert -> ChannelMainModelEvent.ALERT + is Logout -> ChannelMainModelEvent.LOGOUT + else -> ChannelMainModelEvent.NONE + } + + private fun MainViewState.toModelState() = + when (this) { + Loading -> ChannelModelState.LOADING + is Loaded -> ChannelModelState.LOADED + is Error -> ChannelModelState.ERROR + } + + private fun List.toModelData() = map { it.toModelData() } + + private fun SnippetFilters.toModelFilter(): ChannelSnippetFilter { + return ChannelSnippetFilter( + languages = languages, + selectedLanguages = selectedLanguages, + scopes = scopes, + selectedScope = selectedScope, + ) + } +} diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/session/SessionModel.kt b/app/src/main/java/dev/snipme/snipmeapp/channel/session/SessionModel.kt similarity index 77% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/session/SessionModel.kt rename to app/src/main/java/dev/snipme/snipmeapp/channel/session/SessionModel.kt index 2474e90..e2c44ce 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/session/SessionModel.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/channel/session/SessionModel.kt @@ -1,11 +1,11 @@ -package pl.tkadziolka.snipmeandroid.bridge.session +package dev.snipme.snipmeapp.channel.session import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable import io.reactivex.rxkotlin.plusAssign import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.domain.auth.LogoutUserUseCase -import pl.tkadziolka.snipmeandroid.util.extension.inProgress +import dev.snipme.snipmeapp.domain.auth.LogoutUserUseCase +import dev.snipme.snipmeapp.util.extension.inProgress class SessionModel( private val logout: LogoutUserUseCase diff --git a/app/src/main/java/dev/snipme/snipmeapp/di/ChannelModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/ChannelModule.kt new file mode 100644 index 0000000..e5e49a7 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/di/ChannelModule.kt @@ -0,0 +1,18 @@ +package dev.snipme.snipmeapp.di + +import dev.snipme.snipmeapp.channel.FlowChannelEventStreamHandler +import dev.snipme.snipmeapp.channel.FlowChannelStateStreamHandler +import dev.snipme.snipmeapp.channel.details.DetailsModel +import dev.snipme.snipmeapp.channel.login.LoginModel +import dev.snipme.snipmeapp.channel.main.MainModel +import dev.snipme.snipmeapp.channel.session.SessionModel +import org.koin.dsl.module + +internal val channelModule = module { + single { FlowChannelStateStreamHandler() } + single { FlowChannelEventStreamHandler() } + single { SessionModel(get()) } + single { LoginModel(get(), get(), get()) } + single { MainModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } + single { DetailsModel(get(), get(), get(), get(), get(), get(), get(), get(), get()) } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/KoinConfig.kt b/app/src/main/java/dev/snipme/snipmeapp/di/KoinConfig.kt similarity index 66% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/di/KoinConfig.kt rename to app/src/main/java/dev/snipme/snipmeapp/di/KoinConfig.kt index 99e88f6..001117a 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/KoinConfig.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/di/KoinConfig.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.di +package dev.snipme.snipmeapp.di val koinModules = listOf( mapperFilterModule, @@ -9,7 +9,5 @@ val koinModules = listOf( utilModule, useCaseModule, interactorModule, - navigatorModule, - viewModelModule, - modelModule + channelModule ) \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/di/MapperFilterModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/MapperFilterModule.kt new file mode 100644 index 0000000..e6fc98d --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/di/MapperFilterModule.kt @@ -0,0 +1,10 @@ +package dev.snipme.snipmeapp.di + +import org.koin.dsl.module +import dev.snipme.snipmeapp.domain.language.AvailableLanguageFilter +import dev.snipme.snipmeapp.domain.snippets.SnippetResponseMapper + +internal val mapperFilterModule = module { + factory { SnippetResponseMapper() } + factory { AvailableLanguageFilter() } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/NetworkModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/NetworkModule.kt similarity index 92% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/di/NetworkModule.kt rename to app/src/main/java/dev/snipme/snipmeapp/di/NetworkModule.kt index 858ab54..a306688 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/NetworkModule.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/di/NetworkModule.kt @@ -1,12 +1,12 @@ -package pl.tkadziolka.snipmeandroid.di +package dev.snipme.snipmeapp.di import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC import okhttp3.logging.HttpLoggingInterceptor.Level.BODY import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.util.AuthInterceptor +import dev.snipme.snipmeapp.BuildConfig +import dev.snipme.snipmeapp.util.AuthInterceptor import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.moshi.MoshiConverterFactory @@ -63,7 +63,7 @@ internal val networkModule = module { single { Retrofit.Builder() - .client(get()) + .client(get()) .baseUrl(BuildConfig.BASE_URL) .addConverterFactory(MoshiConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/PreferenceModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/PreferenceModule.kt similarity index 54% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/di/PreferenceModule.kt rename to app/src/main/java/dev/snipme/snipmeapp/di/PreferenceModule.kt index 8a9d591..5a63f08 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/PreferenceModule.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/di/PreferenceModule.kt @@ -1,9 +1,9 @@ -package pl.tkadziolka.snipmeandroid.di +package dev.snipme.snipmeapp.di import org.koin.android.ext.koin.androidContext import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.infrastructure.local.AuthPreferences -import pl.tkadziolka.snipmeandroid.util.PreferencesUtil +import dev.snipme.snipmeapp.infrastructure.local.AuthPreferences +import dev.snipme.snipmeapp.util.PreferencesUtil val preferenceModule = module { single { PreferencesUtil(androidContext()) } diff --git a/app/src/main/java/dev/snipme/snipmeapp/di/RepositoryModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/RepositoryModule.kt new file mode 100644 index 0000000..5a8e567 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/di/RepositoryModule.kt @@ -0,0 +1,21 @@ +package dev.snipme.snipmeapp.di + +import org.koin.dsl.module +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepositoryReal +import dev.snipme.snipmeapp.domain.repository.language.LanguageRepository +import dev.snipme.snipmeapp.domain.repository.language.LanguageRepositoryReal +import dev.snipme.snipmeapp.domain.repository.networkstate.NetworkStateRepository +import dev.snipme.snipmeapp.domain.repository.networkstate.NetworkStateRepositoryReal +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepositoryReal +import dev.snipme.snipmeapp.domain.repository.user.UserRepository +import dev.snipme.snipmeapp.domain.repository.user.UserRepositoryReal + +internal val repositoryModule = module { + single { NetworkStateRepositoryReal() } + single { AuthRepositoryReal(get(), get(), get()) } + single { UserRepositoryReal(get(), get()) } + single { SnippetRepositoryReal(get(), get(), get()) } + single { LanguageRepositoryReal(get(), get(), get()) } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/di/ServiceModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/ServiceModule.kt new file mode 100644 index 0000000..b7c9db7 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/di/ServiceModule.kt @@ -0,0 +1,28 @@ +package dev.snipme.snipmeapp.di + +import androidx.room.Room +import dev.snipme.snipmeapp.infrastructure.local.AppDatabase +import dev.snipme.snipmeapp.infrastructure.local.SnippetDao +import dev.snipme.snipmeapp.infrastructure.local.UserDao +import org.koin.dsl.module +import dev.snipme.snipmeapp.infrastructure.remote.* +import retrofit2.Retrofit + +internal val serviceModule = module { + single { get().create(AuthService::class.java) } + single { get().create(UserService::class.java) } + single { get().create(SnippetService::class.java) } + single { get().create(LanguageService::class.java) } + single { get().create(ShareService::class.java) } + + + single { + Room.databaseBuilder( + get(), AppDatabase::class.java, "app_database" + ).createFromAsset("app_database.db").build() + } + + single { get().userDao() } + single { get().snippetDao() } + +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/di/UseCaseModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/UseCaseModule.kt new file mode 100644 index 0000000..bec68fc --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/di/UseCaseModule.kt @@ -0,0 +1,63 @@ +package dev.snipme.snipmeapp.di + +import org.koin.dsl.module +import dev.snipme.snipmeapp.domain.auth.* +import dev.snipme.snipmeapp.domain.clipboard.AddToClipboardUseCase +import dev.snipme.snipmeapp.domain.clipboard.GetFromClipboardUseCase +import dev.snipme.snipmeapp.domain.language.GetLanguagesUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.reaction.GetTargetUserReactionUseCase +import dev.snipme.snipmeapp.domain.reaction.SetUserReactionUseCase +import dev.snipme.snipmeapp.domain.share.ShareSnippetUseCase +import dev.snipme.snipmeapp.domain.snippet.* +import dev.snipme.snipmeapp.domain.filter.FilterSnippetsByLanguageUseCase +import dev.snipme.snipmeapp.domain.filter.FilterSnippetsByScopeUseCase +import dev.snipme.snipmeapp.domain.filter.GetLanguageFiltersUseCase +import dev.snipme.snipmeapp.domain.filter.UpdateSnippetFiltersLanguageUseCase +import dev.snipme.snipmeapp.domain.snippets.GetSnippetsUseCase +import dev.snipme.snipmeapp.domain.snippets.HasMoreSnippetPagesUseCase +import dev.snipme.snipmeapp.domain.user.GetSingleUserUseCase + +internal val useCaseModule = module { + // Base + factory { CheckNetworkAvailableUseCase(get()) } + // Auth + factory { IdentifyUserUseCase(get()) } + factory { InitialLoginUseCase(get()) } + factory { LoginUseCase(get()) } + factory { RegisterUseCase(get(), get()) } + factory { LogoutUserUseCase(get()) } + factory { AuthorizationUseCase(get()) } + // User + factory { GetSingleUserUseCase(get(), get(), get(), get()) } + // Snippet + factory { GetSnippetsUseCase(get(), get(), get()) } + factory { GetSingleSnippetUseCase(get(), get(), get()) } + factory { HasMoreSnippetPagesUseCase(get(), get(), get()) } + factory { CreateSnippetUseCase(get(), get(), get()) } + factory { UpdateSnippetUseCase(get(), get(), get()) } + factory { ObserveUpdatedSnippetPageUseCase(get()) } + factory { ObserveSnippetUpdatesUseCase(get()) } + factory { GetTargetUserReactionUseCase() } + factory { SetUserReactionUseCase(get(), get(), get(), get()) } + factory { DeleteSnippetUseCase(get()) } + // Language + factory { GetLanguagesUseCase(get(), get(), get()) } + // Share + factory { ShareSnippetUseCase(get()) } + // Clipboard + single { AddToClipboardUseCase(get()) } + factory { GetFromClipboardUseCase(get()) } + // Save + factory { SaveSnippetUseCase(get()) } + // Filter + factory { GetLanguageFiltersUseCase() } + factory { FilterSnippetsByLanguageUseCase() } + factory { FilterSnippetsByScopeUseCase() } + factory { UpdateSnippetFiltersLanguageUseCase() } +} + +internal val interactorModule = module { + factory { LoginInteractor(get(), get(), get()) } + factory { EditInteractor(get(), get(), get(), get(), get()) } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/di/UtilModule.kt b/app/src/main/java/dev/snipme/snipmeapp/di/UtilModule.kt new file mode 100644 index 0000000..89d5bd9 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/di/UtilModule.kt @@ -0,0 +1,21 @@ +package dev.snipme.snipmeapp.di + +import android.content.ClipboardManager +import android.content.Context.CLIPBOARD_SERVICE +import dev.snipme.snipmeapp.AppService +import dev.snipme.snipmeapp.BuildConfig +import dev.snipme.snipmeapp.domain.error.DebugErrorHandler +import dev.snipme.snipmeapp.domain.error.SafeErrorHandler +import dev.snipme.snipmeapp.domain.message.ErrorMessages +import dev.snipme.snipmeapp.domain.message.RealValidationMessages +import dev.snipme.snipmeapp.domain.message.ValidationMessages +import org.koin.android.ext.koin.androidApplication +import org.koin.dsl.module + +internal val utilModule = module { + factory { if (BuildConfig.DEBUG) DebugErrorHandler() else SafeErrorHandler() } + factory { ErrorMessages(get()) } + factory { RealValidationMessages(get()) } + single { androidApplication().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager } + single { AppService(androidApplication()) } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/AuthorizationUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/AuthorizationUseCase.kt similarity index 70% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/AuthorizationUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/auth/AuthorizationUseCase.kt index a67605f..32ad5ff 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/AuthorizationUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/AuthorizationUseCase.kt @@ -1,8 +1,8 @@ -package pl.tkadziolka.snipmeandroid.domain.auth +package dev.snipme.snipmeapp.domain.auth import io.reactivex.Completable -import pl.tkadziolka.snipmeandroid.domain.error.exception.SessionExpiredException -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepository +import dev.snipme.snipmeapp.domain.error.exception.SessionExpiredException +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository // In future this should check if token is correct and up to date (refresh) class AuthorizationUseCase( diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/auth/IdentifyUserUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/IdentifyUserUseCase.kt new file mode 100644 index 0000000..86e01d7 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/IdentifyUserUseCase.kt @@ -0,0 +1,11 @@ +package dev.snipme.snipmeapp.domain.auth + +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository + +class IdentifyUserUseCase( + private val auth: AuthRepository, +) { + operator fun invoke(login: String) = + auth.identify(login) +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/InitialLoginUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/InitialLoginUseCase.kt similarity index 64% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/InitialLoginUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/auth/InitialLoginUseCase.kt index ff2e915..0129e04 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/InitialLoginUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/InitialLoginUseCase.kt @@ -1,8 +1,8 @@ -package pl.tkadziolka.snipmeandroid.domain.auth +package dev.snipme.snipmeapp.domain.auth import io.reactivex.Completable -import pl.tkadziolka.snipmeandroid.domain.error.exception.NotAuthorizedException -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepository +import dev.snipme.snipmeapp.domain.error.exception.NotAuthorizedException +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository class InitialLoginUseCase(private val auth: AuthRepository) { operator fun invoke() = auth.getToken() diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LoginInteractor.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/LoginInteractor.kt similarity index 89% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LoginInteractor.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/auth/LoginInteractor.kt index 4735c8b..f8e5ba1 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LoginInteractor.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/LoginInteractor.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.auth +package dev.snipme.snipmeapp.domain.auth class LoginInteractor( private val identifyUserUseCase: IdentifyUserUseCase, diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/auth/LoginUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/LoginUseCase.kt new file mode 100644 index 0000000..6153f63 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/LoginUseCase.kt @@ -0,0 +1,15 @@ +package dev.snipme.snipmeapp.domain.auth + +import io.reactivex.Completable +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository + +class LoginUseCase( + private val auth: AuthRepository, +) { + + operator fun invoke(login: String, password: String): Completable = + auth.login(login, password) + .flatMapCompletable { token -> auth.saveToken(token.toString()) } +} + diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LogoutUserUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/LogoutUserUseCase.kt similarity index 50% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LogoutUserUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/auth/LogoutUserUseCase.kt index 5eaded8..4ad59bc 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LogoutUserUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/LogoutUserUseCase.kt @@ -1,6 +1,6 @@ -package pl.tkadziolka.snipmeandroid.domain.auth +package dev.snipme.snipmeapp.domain.auth -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepository +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository class LogoutUserUseCase(private val repository: AuthRepository) { operator fun invoke() = repository.clearToken() diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/auth/RegisterUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/RegisterUseCase.kt new file mode 100644 index 0000000..7925204 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/auth/RegisterUseCase.kt @@ -0,0 +1,19 @@ +package dev.snipme.snipmeapp.domain.auth + +import io.reactivex.Completable +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository + +class RegisterUseCase( + private val auth: AuthRepository, + private val loginUseCase: LoginUseCase, +) { + operator fun invoke(login: String, password: String, email: String): Completable = + register(login, password, email) + + private fun register(login: String, password: String, email: String) = + auth.register(login, password, email).andThen( + loginUseCase(login, password) + ) + +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/clipboard/AddToClipboardUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/clipboard/AddToClipboardUseCase.kt similarity index 84% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/clipboard/AddToClipboardUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/clipboard/AddToClipboardUseCase.kt index dfe7942..b8bd405 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/clipboard/AddToClipboardUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/clipboard/AddToClipboardUseCase.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.clipboard +package dev.snipme.snipmeapp.domain.clipboard import android.content.ClipData import android.content.ClipboardManager diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/clipboard/GetFromClipboardUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/clipboard/GetFromClipboardUseCase.kt similarity index 88% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/clipboard/GetFromClipboardUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/clipboard/GetFromClipboardUseCase.kt index 9595a79..82a5b37 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/clipboard/GetFromClipboardUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/clipboard/GetFromClipboardUseCase.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.clipboard +package dev.snipme.snipmeapp.domain.clipboard import android.content.ClipboardManager import timber.log.Timber diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/DebugErrorHandler.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/DebugErrorHandler.kt similarity index 92% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/DebugErrorHandler.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/DebugErrorHandler.kt index f7b7eee..05b1076 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/DebugErrorHandler.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/DebugErrorHandler.kt @@ -1,6 +1,6 @@ -package pl.tkadziolka.snipmeandroid.domain.error +package dev.snipme.snipmeapp.domain.error -import pl.tkadziolka.snipmeandroid.domain.error.exception.* +import dev.snipme.snipmeapp.domain.error.exception.* import retrofit2.HttpException import timber.log.Timber import java.io.IOException diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/error/ErrorHandler.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/ErrorHandler.kt new file mode 100644 index 0000000..37ef1bd --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/ErrorHandler.kt @@ -0,0 +1,7 @@ +package dev.snipme.snipmeapp.domain.error + +import dev.snipme.snipmeapp.domain.error.exception.SnipException + +interface ErrorHandler { + fun handle(throwable: Throwable): SnipException +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/SafeErrorHandler.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/SafeErrorHandler.kt similarity index 90% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/SafeErrorHandler.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/SafeErrorHandler.kt index 751e26d..d7ba903 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/SafeErrorHandler.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/SafeErrorHandler.kt @@ -1,6 +1,6 @@ -package pl.tkadziolka.snipmeandroid.domain.error +package dev.snipme.snipmeapp.domain.error -import pl.tkadziolka.snipmeandroid.domain.error.exception.* +import dev.snipme.snipmeapp.domain.error.exception.* import retrofit2.HttpException import java.io.IOException import java.net.HttpURLConnection.* diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ConnectionException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ConnectionException.kt similarity index 58% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ConnectionException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ConnectionException.kt index c9021d4..269ac2a 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ConnectionException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ConnectionException.kt @@ -1,3 +1,3 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception class ConnectionException(override val cause: Throwable? = null): SnipException() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ContentNotFoundException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ContentNotFoundException.kt similarity index 59% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ContentNotFoundException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ContentNotFoundException.kt index bb43bad..8050ad1 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ContentNotFoundException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ContentNotFoundException.kt @@ -1,3 +1,3 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception class ContentNotFoundException(override val cause: Throwable? = null): SnipException() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ForbiddenActionException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ForbiddenActionException.kt similarity index 59% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ForbiddenActionException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ForbiddenActionException.kt index e098ec5..a081fdc 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/ForbiddenActionException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/ForbiddenActionException.kt @@ -1,3 +1,3 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception class ForbiddenActionException(override val cause: Throwable? = null): SnipException() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/NetworkNotAvailableException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/NetworkNotAvailableException.kt similarity index 60% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/NetworkNotAvailableException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/NetworkNotAvailableException.kt index add6c2e..01f50b0 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/NetworkNotAvailableException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/NetworkNotAvailableException.kt @@ -1,3 +1,3 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception class NetworkNotAvailableException(override val cause: Throwable? = null): SnipException() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/NotAuthorizedException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/NotAuthorizedException.kt similarity index 59% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/NotAuthorizedException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/NotAuthorizedException.kt index 61def1c..a916c7d 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/NotAuthorizedException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/NotAuthorizedException.kt @@ -1,3 +1,3 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception class NotAuthorizedException(override val cause: Throwable? = null): SnipException() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/RemoteException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/RemoteException.kt similarity index 56% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/RemoteException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/RemoteException.kt index a3a413d..172b4ca 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/RemoteException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/RemoteException.kt @@ -1,3 +1,3 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception class RemoteException(override val cause: Throwable? = null): SnipException() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/SessionExpiredException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/SessionExpiredException.kt similarity index 59% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/SessionExpiredException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/SessionExpiredException.kt index 9851a47..e7316a5 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/SessionExpiredException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/SessionExpiredException.kt @@ -1,3 +1,3 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception class SessionExpiredException(override val cause: Throwable? = null): SnipException() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/SnipException.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/SnipException.kt similarity index 64% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/SnipException.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/SnipException.kt index ec2b88b..0619fad 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/exception/SnipException.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/error/exception/SnipException.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.error.exception +package dev.snipme.snipmeapp.domain.error.exception import java.lang.Exception diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/filter/FilterSnippetsByLanguageUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/FilterSnippetsByLanguageUseCase.kt new file mode 100644 index 0000000..a77ee72 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/FilterSnippetsByLanguageUseCase.kt @@ -0,0 +1,10 @@ +package dev.snipme.snipmeapp.domain.filter + +import dev.snipme.snipmeapp.domain.snippets.Snippet + +class FilterSnippetsByLanguageUseCase { + operator fun invoke(snippets: List, languages: List): List { + if (languages.contains(SNIPPET_FILTER_ALL)) return snippets + return snippets.filter { languages.contains(it.language.raw) } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/filter/FilterSnippetsByScopeUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/FilterSnippetsByScopeUseCase.kt new file mode 100644 index 0000000..762110b --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/FilterSnippetsByScopeUseCase.kt @@ -0,0 +1,11 @@ +package dev.snipme.snipmeapp.domain.filter + +import dev.snipme.snipmeapp.domain.snippets.Snippet + +class FilterSnippetsByScopeUseCase { + operator fun invoke(snippets: List, scope: String): List { + if (scope == SNIPPET_FILTER_ALL) return snippets + + return snippets.filter { it.isOwner && it.visibility.name.equals(scope, ignoreCase = true) } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/filter/GetLanguageFiltersUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/GetLanguageFiltersUseCase.kt new file mode 100644 index 0000000..388444d --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/GetLanguageFiltersUseCase.kt @@ -0,0 +1,18 @@ +package dev.snipme.snipmeapp.domain.filter + +import dev.snipme.snipmeapp.domain.snippets.Snippet + +const val SNIPPET_FILTER_ALL = "All" + +class GetLanguageFiltersUseCase { + + operator fun invoke(snippets: List): List { + return listOf(SNIPPET_FILTER_ALL) + + snippets.groupBy { it.language.raw } + .map { it.key to it.value.count() } + .sortedBy { it.second }.reversed() + .map { it.first } + .filter { it.isNotBlank() } + .distinct() + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/filter/UpdateSnippetFiltersLanguageUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/UpdateSnippetFiltersLanguageUseCase.kt new file mode 100644 index 0000000..3a72c99 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/filter/UpdateSnippetFiltersLanguageUseCase.kt @@ -0,0 +1,24 @@ +package dev.snipme.snipmeapp.domain.filter + +import dev.snipme.snipmeapp.domain.snippets.SnippetFilters + +class UpdateSnippetFiltersLanguageUseCase { + + operator fun invoke( + filter: SnippetFilters, + language: String, + isSelected: Boolean + ): SnippetFilters = when { + language == SNIPPET_FILTER_ALL -> + filter.copy(selectedLanguages = listOf(SNIPPET_FILTER_ALL)) + isSelected.not() -> filter.copy(selectedLanguages = filter.selectedLanguages - language) + else -> + filter.copy( + selectedLanguages = ( + filter.selectedLanguages + - SNIPPET_FILTER_ALL + + language + ).distinct() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/AvailableLanguageFilter.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/language/AvailableLanguageFilter.kt similarity index 51% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/AvailableLanguageFilter.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/language/AvailableLanguageFilter.kt index c80a760..24992ae 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/AvailableLanguageFilter.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/language/AvailableLanguageFilter.kt @@ -1,7 +1,7 @@ -package pl.tkadziolka.snipmeandroid.domain.language +package dev.snipme.snipmeapp.domain.language -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetLanguageType -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetLanguageMapper +import dev.snipme.snipmeapp.domain.snippets.SnippetLanguageType +import dev.snipme.snipmeapp.domain.snippets.SnippetLanguageMapper class AvailableLanguageFilter { diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/language/GetLanguagesUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/language/GetLanguagesUseCase.kt new file mode 100644 index 0000000..673c3a0 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/language/GetLanguagesUseCase.kt @@ -0,0 +1,17 @@ +package dev.snipme.snipmeapp.domain.language + +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.language.LanguageRepository +import dev.snipme.snipmeapp.util.extension.mapItems + +class GetLanguagesUseCase( + private val auth: AuthorizationUseCase, + private val networkState: CheckNetworkAvailableUseCase, + private val language: LanguageRepository +) { + operator fun invoke() = + auth() + .andThen(networkState()) + .andThen(language.languages().mapItems { it.name }) +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/language/SnippetLanguage.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/language/SnippetLanguage.kt new file mode 100644 index 0000000..8668927 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/language/SnippetLanguage.kt @@ -0,0 +1,7 @@ +package dev.snipme.snipmeapp.domain.language + +import dev.snipme.snipmeapp.infrastructure.model.response.LanguageResponse + +data class SnippetLanguage(val name: String) + +fun LanguageResponse.toLanguage(): SnippetLanguage = SnippetLanguage(name) \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/ErrorMessages.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/message/ErrorMessages.kt similarity index 88% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/ErrorMessages.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/message/ErrorMessages.kt index 565d47f..82acd29 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/ErrorMessages.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/message/ErrorMessages.kt @@ -1,8 +1,8 @@ -package pl.tkadziolka.snipmeandroid.domain.message +package dev.snipme.snipmeapp.domain.message import android.content.Context -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.error.exception.* +import dev.snipme.snipmeapp.R +import dev.snipme.snipmeapp.domain.error.exception.* class ErrorMessages(context: Context) { private val res = context.resources diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/RealValidationMessages.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/message/RealValidationMessages.kt similarity index 94% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/RealValidationMessages.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/message/RealValidationMessages.kt index 8711fff..5e67c01 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/RealValidationMessages.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/message/RealValidationMessages.kt @@ -1,7 +1,7 @@ -package pl.tkadziolka.snipmeandroid.domain.message +package dev.snipme.snipmeapp.domain.message import android.content.Context -import pl.tkadziolka.snipmeandroid.R +import dev.snipme.snipmeapp.R class RealValidationMessages(context: Context): ValidationMessages { private val res = context.resources diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/ValidationMessages.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/message/ValidationMessages.kt similarity index 90% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/ValidationMessages.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/message/ValidationMessages.kt index d72162a..0c2dbce 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/message/ValidationMessages.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/message/ValidationMessages.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.message +package dev.snipme.snipmeapp.domain.message interface ValidationMessages { diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/network/CheckNetworkAvailableUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/network/CheckNetworkAvailableUseCase.kt similarity index 67% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/network/CheckNetworkAvailableUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/network/CheckNetworkAvailableUseCase.kt index 0fdf9d4..208a91f 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/network/CheckNetworkAvailableUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/network/CheckNetworkAvailableUseCase.kt @@ -1,8 +1,8 @@ -package pl.tkadziolka.snipmeandroid.domain.network +package dev.snipme.snipmeapp.domain.network import io.reactivex.Completable -import pl.tkadziolka.snipmeandroid.domain.error.exception.NetworkNotAvailableException -import pl.tkadziolka.snipmeandroid.domain.repository.networkstate.NetworkStateRepository +import dev.snipme.snipmeapp.domain.error.exception.NetworkNotAvailableException +import dev.snipme.snipmeapp.domain.repository.networkstate.NetworkStateRepository class CheckNetworkAvailableUseCase(private val networkStateRepository: NetworkStateRepository) { diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/GetTargetUserReactionUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/GetTargetUserReactionUseCase.kt similarity index 67% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/GetTargetUserReactionUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/reaction/GetTargetUserReactionUseCase.kt index 8333e1e..9547c39 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/GetTargetUserReactionUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/GetTargetUserReactionUseCase.kt @@ -1,6 +1,6 @@ -package pl.tkadziolka.snipmeandroid.domain.reaction +package dev.snipme.snipmeapp.domain.reaction -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.snippets.Snippet class GetTargetUserReactionUseCase { operator fun invoke(snippet: Snippet, reaction: UserReaction) = diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/SetUserReactionUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/SetUserReactionUseCase.kt new file mode 100644 index 0000000..855251e --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/SetUserReactionUseCase.kt @@ -0,0 +1,25 @@ +package dev.snipme.snipmeapp.domain.reaction + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.user.GetSingleUserUseCase + +class SetUserReactionUseCase( + private val auth: AuthorizationUseCase, + private val repository: SnippetRepository, + private val getTargetReaction: GetTargetUserReactionUseCase, + private val getSingleUser: GetSingleUserUseCase +) { + operator fun invoke(snippet: Snippet, reaction: UserReaction): Single { + val targetReaction = getTargetReaction(snippet, reaction) + return auth() + .andThen(getSingleUser()) + .flatMap { user -> + repository.reaction(snippet.uuid, user.id, targetReaction) + .andThen(repository.snippet(snippet.uuid, user.id)) + .doOnSuccess { repository.updateListener.onNext(it) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/UserReaction.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/UserReaction.kt new file mode 100644 index 0000000..dc53fac --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/reaction/UserReaction.kt @@ -0,0 +1,7 @@ +package dev.snipme.snipmeapp.domain.reaction + +enum class UserReaction { + DISLIKE, + NONE, + LIKE +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/auth/AuthRepository.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/auth/AuthRepository.kt similarity index 59% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/auth/AuthRepository.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/repository/auth/AuthRepository.kt index 2dfa1e6..ccc413d 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/auth/AuthRepository.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/auth/AuthRepository.kt @@ -1,17 +1,17 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.auth +package dev.snipme.snipmeapp.domain.repository.auth import io.reactivex.Completable import io.reactivex.Maybe import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.RegisterUserResponse +import dev.snipme.snipmeapp.infrastructure.model.response.RegisterUserResponse interface AuthRepository { fun identify(login: String): Single - fun login(login: String, password: String): Single + fun login(login: String, password: String): Single - fun register(login: String, password: String, email: String): Single + fun register(login: String, password: String, email: String): Completable fun saveToken(token: String): Completable diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/auth/AuthRepositoryReal.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/auth/AuthRepositoryReal.kt similarity index 52% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/auth/AuthRepositoryReal.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/repository/auth/AuthRepositoryReal.kt index d99239a..d17d49e 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/auth/AuthRepositoryReal.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/auth/AuthRepositoryReal.kt @@ -1,32 +1,39 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.auth +package dev.snipme.snipmeapp.domain.repository.auth +import android.util.Log import io.reactivex.Completable import io.reactivex.Maybe -import pl.tkadziolka.snipmeandroid.domain.error.ErrorHandler -import pl.tkadziolka.snipmeandroid.infrastructure.local.AuthPreferences -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.IdentifyUserRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.LoginUserRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.RegisterUserRequest -import pl.tkadziolka.snipmeandroid.infrastructure.remote.AuthService -import pl.tkadziolka.snipmeandroid.util.extension.mapError +import dev.snipme.snipmeapp.domain.error.ErrorHandler +import dev.snipme.snipmeapp.infrastructure.local.AuthPreferences +import dev.snipme.snipmeapp.infrastructure.local.UserEntry +import dev.snipme.snipmeapp.infrastructure.local.UserDao +import dev.snipme.snipmeapp.util.extension.mapError +import io.reactivex.Single class AuthRepositoryReal( private val errorHandler: ErrorHandler, - private val service: AuthService, + private val service: UserDao, private val prefs: AuthPreferences ) : AuthRepository { override fun identify(login: String) = - service.identify(IdentifyUserRequest(login)) + service.identify(login) .mapError { errorHandler.handle(it) } + .map { it -> it > 0 } + override fun login(login: String, password: String) = - service.login(LoginUserRequest(login, password)) + service.login(login, password) .mapError { errorHandler.handle(it) } - .map { it.token } + .map { it } override fun register(login: String, password: String, email: String) = - service.register(RegisterUserRequest(login, password, email)) + service.register(UserEntry( + email = email, + password = password, + login = email.substring(0, email.indexOf('@')), + photo = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRrRHyzylz9-x3vO04G9qyWdOwOjtfsmtaubQ&s" + )) .mapError { errorHandler.handle(it) } override fun saveToken(token: String) = Completable.fromCallable { diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/repository/language/LanguageRepository.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/language/LanguageRepository.kt new file mode 100644 index 0000000..d236f8f --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/language/LanguageRepository.kt @@ -0,0 +1,9 @@ +package dev.snipme.snipmeapp.domain.repository.language + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.language.SnippetLanguage + +interface LanguageRepository { + + fun languages(): Single> +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/repository/language/LanguageRepositoryReal.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/language/LanguageRepositoryReal.kt new file mode 100644 index 0000000..bffde26 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/language/LanguageRepositoryReal.kt @@ -0,0 +1,24 @@ +package dev.snipme.snipmeapp.domain.repository.language + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.error.ErrorHandler +import dev.snipme.snipmeapp.domain.language.AvailableLanguageFilter +import dev.snipme.snipmeapp.domain.language.SnippetLanguage +import dev.snipme.snipmeapp.domain.language.toLanguage +import dev.snipme.snipmeapp.infrastructure.remote.LanguageService +import dev.snipme.snipmeapp.util.extension.filterItems +import dev.snipme.snipmeapp.util.extension.mapError +import dev.snipme.snipmeapp.util.extension.mapItems + +class LanguageRepositoryReal( + private val errorHandler: ErrorHandler, + private val service: LanguageService, + private val filterAvailable: AvailableLanguageFilter +): LanguageRepository { + + override fun languages(): Single> = + service.languages() + .mapError { errorHandler.handle(it) } + .filterItems { filterAvailable(it.name) } + .mapItems { it.toLanguage() } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/networkstate/NetworkStateRepository.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/networkstate/NetworkStateRepository.kt similarity index 72% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/networkstate/NetworkStateRepository.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/repository/networkstate/NetworkStateRepository.kt index 69142c3..5aac54a 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/networkstate/NetworkStateRepository.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/networkstate/NetworkStateRepository.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.networkstate +package dev.snipme.snipmeapp.domain.repository.networkstate import io.reactivex.Observable import io.reactivex.Single diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/networkstate/NetworkStateRepositoryReal.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/networkstate/NetworkStateRepositoryReal.kt similarity index 85% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/networkstate/NetworkStateRepositoryReal.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/repository/networkstate/NetworkStateRepositoryReal.kt index 35e2f69..19600c6 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/networkstate/NetworkStateRepositoryReal.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/networkstate/NetworkStateRepositoryReal.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.networkstate +package dev.snipme.snipmeapp.domain.repository.networkstate import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import io.reactivex.Observable diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepository.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepository.kt new file mode 100644 index 0000000..7790765 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepository.kt @@ -0,0 +1,41 @@ +package dev.snipme.snipmeapp.domain.repository.snippet + +import io.reactivex.Completable +import io.reactivex.Single +import io.reactivex.subjects.BehaviorSubject +import dev.snipme.snipmeapp.domain.reaction.UserReaction +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.snippets.SnippetScope +import dev.snipme.snipmeapp.domain.snippets.SnippetVisibility + +interface SnippetRepository { + + val updateListener: BehaviorSubject + + fun snippets(userId: Int): Single> + + fun snippet(uuid: String, userId: Int): Single + + fun create( + title: String, + code: String, + language: String, + visibility: SnippetVisibility, + userId: Int + ): Single + + fun update( + uuid: String, + title: String, + code: String, + language: String, + visibility: SnippetVisibility, + userId: Int + ): Single + + fun count(): Single + + fun reaction(uuid: String, userId: Int, reaction: UserReaction): Completable + + fun delete(uuid: String): Completable +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepositoryReal.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepositoryReal.kt new file mode 100644 index 0000000..9d8e86c --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepositoryReal.kt @@ -0,0 +1,91 @@ +package dev.snipme.snipmeapp.domain.repository.snippet + +import dev.snipme.snipmeapp.domain.error.ErrorHandler +import dev.snipme.snipmeapp.domain.reaction.UserReaction +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.snippets.SnippetResponseMapper +import dev.snipme.snipmeapp.domain.snippets.SnippetVisibility +import dev.snipme.snipmeapp.infrastructure.local.ReactionEntry +import dev.snipme.snipmeapp.infrastructure.local.SnippetDao +import dev.snipme.snipmeapp.infrastructure.local.SnippetEntry +import dev.snipme.snipmeapp.util.extension.mapError +import dev.snipme.snipmeapp.util.extension.mapItems +import io.reactivex.Completable +import io.reactivex.Single +import io.reactivex.subjects.BehaviorSubject +import java.util.Date + +const val PAGE_START = 0 +const val SNIPPET_PAGE_SIZE = 10 +const val ONE_SNIPPET = 1 + +class SnippetRepositoryReal( + private val errorHandler: ErrorHandler, + private val service: SnippetDao, + private val mapper: SnippetResponseMapper +) : SnippetRepository { + override val updateListener = BehaviorSubject.create() + + override fun snippets(userId: Int): Single> = + service.snippets(userId) + .mapError { errorHandler.handle(it) } + .mapItems { mapper(it) } + + override fun snippet(uuid: String, userId: Int): Single = + service.snippet(uuid.toInt(), userId).map { mapper(it) } + .mapError { errorHandler.handle(it) } + + override fun create( + title: String, + code: String, + language: String, + visibility: SnippetVisibility, + userId: Int + ): Single { + return service.create( + SnippetEntry( + title = title, + code = code, + createdAt = Date().toString(), + modifiedAt = Date().toString(), + visibility = visibility.name, + ownerId = userId, + language = language, + ) + ) + .mapError { errorHandler.handle(it) } + .flatMap { newId -> + service.snippet(newId.toInt(), userId) + .mapError { errorHandler.handle(it) } + .map { mapper(it) } + } + } + + override fun update( + uuid: String, + title: String, + code: String, + language: String, + visibility: SnippetVisibility, + userId: Int + ): Single = + service.update(uuid.toInt(), title, code, language, visibility.name) + .mapError { errorHandler.handle(it) } + .andThen( + service.snippet(uuid.toInt(), userId) + .mapError { errorHandler.handle(it) } + .map { mapper(it) } + ) + + override fun delete(uuid: String): Completable = + service.delete(uuid.toInt()).mapError { errorHandler.handle(it) } + + override fun count() = + service.count() + .mapError { errorHandler.handle(it) } + .map { it } + + override fun reaction(uuid: String, userId: Int, reaction: UserReaction): Completable = + service.reaction(ReactionEntry(snippetId = uuid.toInt(), userId = userId, reaction = reaction.ordinal.toShort())) + .mapError { errorHandler.handle(it) } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepositoryTest.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepositoryTest.kt new file mode 100644 index 0000000..8f6d7cd --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/snippet/SnippetRepositoryTest.kt @@ -0,0 +1,167 @@ +//package dev.snipme.snipmeapp.domain.repository.snippet +// +//import android.text.SpannableString +//import io.reactivex.Completable +//import io.reactivex.Single +//import io.reactivex.subjects.BehaviorSubject +//import dev.snipme.snipmeapp.domain.error.ErrorHandler +//import dev.snipme.snipmeapp.domain.reaction.UserReaction +//import dev.snipme.snipmeapp.domain.snippets.* +//import dev.snipme.snipmeapp.util.SyntaxHighlighter.getHighlighted +//import dev.snipme.snipmeapp.util.extension.lines +//import dev.snipme.snipmeapp.util.extension.newLineChar +//import org.koin.core.definition.indexKey +//import java.util.* +// +//private const val PREVIEW_COUNT = 5 +// +//class SnippetRepositoryTest(private val errorHandler: ErrorHandler) : SnippetRepository { +// +// private val uuid +// get() = UUID.randomUUID().toString() +// +// private val list by lazy { +// List(25) { +// Snippet( +// id = it.toLong(), +// title = "Snippet $it", +// code = getMockCode(code), +// language = getMockLanguage(), +// visibility = SnippetVisibility.PUBLIC, +// isOwner = true, +// owner = Owner(it, "User $it"), +// modifiedAt = Date(), +// numberOfLikes = 5, +// numberOfDislikes = 3, +// userReaction = UserReaction.LIKE +// ) +// } +// } +// override val updateListener: BehaviorSubject = BehaviorSubject.create() +// +//// override fun snippets(scope: SnippetScope, page: Int): Single> { +//// return Single.just(list) +//// .map { it.take(SNIPPET_PAGE_SIZE * page) } +//// .onErrorResumeNext { throwable -> Single.error(errorHandler.handle(throwable)) } +//// } +//// +//// override fun snippet(id: String): Single = Single.just(list.find { it.id == id }) +//// +//// override fun create( +//// title: String, +//// code: String, +//// language: String, +//// visibility: SnippetVisibility +//// ): Single = Single.just( +//// Snippet( +//// id = uuid, +//// title = title, +//// code = getMockCode(code), +//// language = getMockLanguage(), +//// visibility = SnippetVisibility.PUBLIC, +//// isOwner = true, +//// owner = Owner(0, "login"), +//// modifiedAt = Date(), +//// numberOfLikes = 0, +//// numberOfDislikes = 0, +//// userReaction = UserReaction.NONE +//// ) +//// ) +//// +//// override fun update( +//// uuid: String, +//// title: String, +//// code: String, +//// language: String, +//// visibility: SnippetVisibility +//// ): Single = Single.just( +//// Snippet( +//// id = uuid, +//// title = title, +//// code = getMockCode(code), +//// language = getMockLanguage(), +//// visibility = SnippetVisibility.PUBLIC, +//// isOwner = true, +//// owner = Owner(0, "login"), +//// modifiedAt = Date(), +//// numberOfLikes = 5, +//// numberOfDislikes = 3, +//// userReaction = UserReaction.LIKE +//// ) +//// ) +//// +//// override fun count(scope: SnippetScope): Single = Single.just(list.size) +//// +//// override fun reaction(uuid: String, reaction: UserReaction): Completable = +//// Completable.complete() +//// +//// override fun delete(uuid: String): Completable = Completable.complete() +//// +//// private fun getPreview(code: String): SpannableString { +//// val preview = code.lines(PREVIEW_COUNT).joinToString(separator = newLineChar) +//// return getHighlighted(preview) +//// } +// +// private fun getMockCode(code: String) = SnippetCode(raw = code, highlighted = getPreview(code)) +// +// private fun getMockLanguage() = SnippetLanguage(raw = "Java", type = SnippetLanguageType.JAVA) +// +// private val code = +// """ +// /* Block comment */ +// import java.util.Date; +// import static AnInterface.CONSTANT; +// import static java.util.Date.parse; +// import static SomeClass.staticField; +// /** +// * Doc comment here for SomeClass +// * @param T type parameter +// * @see Math#sin(double) +// */ +// @Annotation (name=value) +// public class SomeClass { // some comment +// private T field = null; +// private double unusedField = 12345.67890; +// private UnknownType anotherString = "Another\nStrin\g"; +// public static int staticField = 0; +// public final int instanceFinalField = 0; +// +// /** +// * Semantic highlighting: +// * Generated spectrum to pick colors for local variables and parameters: +// * Color#1 SC1.1 SC1.2 SC1.3 SC1.4 Color#2 SC2.1 SC2.2 SC2.3 SC2.4 Color#3 +// * Color#3 SC3.1 SC3.2 SC3.3 SC3.4 Color#4 SC4.1 SC4.2 SC4.3 SC4.4 Color#5 +// * @param param1 +// * @param reassignedParam +// * @param param2 +// * @param param3 +// */ +// public SomeClass(AnInterface param1, int[] reassignedParam, +// int param2 +// int param3) { +// int reassignedValue = this.staticField + param2 + param3; +// long localVar1, localVar2, localVar3, localVar4; +// int localVar = "IntelliJ"; // Error, incompatible types +// System.out.println(anotherString + toString() + localVar); +// long time = parse("1.2.3"); // Method is deprecated +// new Thread().countStackFrames(); // Method is deprecated and marked for removal +// reassignedValue ++; +// field.run(); +// new SomeClass() { +// { +// int a = localVar; +// } +// }; +// reassignedParam = new ArrayList().toArray(new int[CONSTANT]); +// } +// } +// enum AnEnum { CONST1, CONST2 } +// interface AnInterface { +// int CONSTANT = 2; +// void method(); +// } +// abstract class SomeAbstractClass { +// protected int instanceField = staticField; +// } +// """.trimIndent() +//} diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/repository/user/UserRepository.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/user/UserRepository.kt new file mode 100644 index 0000000..f6d8032 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/user/UserRepository.kt @@ -0,0 +1,8 @@ +package dev.snipme.snipmeapp.domain.repository.user + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.user.User + +interface UserRepository { + fun user(id: Int): Single +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/repository/user/UserRepositoryReal.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/user/UserRepositoryReal.kt new file mode 100644 index 0000000..2e70c3c --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/repository/user/UserRepositoryReal.kt @@ -0,0 +1,18 @@ +package dev.snipme.snipmeapp.domain.repository.user + +import dev.snipme.snipmeapp.domain.error.ErrorHandler +import dev.snipme.snipmeapp.domain.user.User +import dev.snipme.snipmeapp.domain.user.toUser +import dev.snipme.snipmeapp.infrastructure.local.UserDao +import dev.snipme.snipmeapp.util.extension.mapError +import io.reactivex.Single + +class UserRepositoryReal( + private val errorHandler: ErrorHandler, + private val service: UserDao +) : UserRepository { + + override fun user(id: Int): Single = service.user(id) + .mapError { errorHandler.handle(it) } + .map { it.toUser() } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/share/ShareSnippetUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/share/ShareSnippetUseCase.kt new file mode 100644 index 0000000..71dde64 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/share/ShareSnippetUseCase.kt @@ -0,0 +1,15 @@ +package dev.snipme.snipmeapp.domain.share + +import dev.snipme.snipmeapp.AppService +import dev.snipme.snipmeapp.domain.snippets.Snippet + +class ShareSnippetUseCase( + private val appService: AppService +) { + + operator fun invoke(image: ByteArray, snippet: Snippet) { + val name = "${appService.getCurrentDateFormatted()}.png" + appService.storeFile(image, name, temp = true) + appService.launchShareIntent(snippet) + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareUser.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/share/ShareUser.kt similarity index 70% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareUser.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/share/ShareUser.kt index a3625ba..7931943 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareUser.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/share/ShareUser.kt @@ -1,7 +1,7 @@ -package pl.tkadziolka.snipmeandroid.domain.share +package dev.snipme.snipmeapp.domain.share -import pl.tkadziolka.snipmeandroid.domain.user.User -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.SharePersonResponse +import dev.snipme.snipmeapp.domain.user.User +import dev.snipme.snipmeapp.infrastructure.model.response.SharePersonResponse data class ShareUser(val user: User, val isShared: Boolean) diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/CreateSnippetUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/CreateSnippetUseCase.kt new file mode 100644 index 0000000..ffc51d2 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/CreateSnippetUseCase.kt @@ -0,0 +1,35 @@ +package dev.snipme.snipmeapp.domain.snippet + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.snippets.SnippetVisibility +import dev.snipme.snipmeapp.domain.user.GetSingleUserUseCase + +class CreateSnippetUseCase( + private val auth: AuthorizationUseCase, + private val snippetRepository: SnippetRepository, + private val getSingleUser: GetSingleUserUseCase +) { + operator fun invoke( + title: String, + code: String, + language: String, + visibility: SnippetVisibility = SnippetVisibility.PUBLIC + ): Single = auth() + .andThen(getSingleUser()) + .flatMap { user -> + snippetRepository.create( + title = title, + code = code, + language = language, + visibility = visibility, + userId = user.id + ) + } + .doOnSuccess { snippet -> + snippetRepository.updateListener.onNext(snippet) + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/DeleteSnippetUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/DeleteSnippetUseCase.kt new file mode 100644 index 0000000..4814d9f --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/DeleteSnippetUseCase.kt @@ -0,0 +1,13 @@ +package dev.snipme.snipmeapp.domain.snippet + +import io.reactivex.Completable +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.snippets.Snippet + +class DeleteSnippetUseCase(private val repository: SnippetRepository) { + + operator fun invoke(uuid: String): Completable = + repository + .delete(uuid) + .doOnComplete { repository.updateListener.onNext(Snippet.EMPTY.copy(uuid)) } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/EditInteractor.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/EditInteractor.kt similarity index 53% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/EditInteractor.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippet/EditInteractor.kt index f23d20d..844994d 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/EditInteractor.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/EditInteractor.kt @@ -1,9 +1,10 @@ -package pl.tkadziolka.snipmeandroid.domain.snippet +package dev.snipme.snipmeapp.domain.snippet import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.clipboard.GetFromClipboardUseCase -import pl.tkadziolka.snipmeandroid.domain.language.GetLanguagesUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.clipboard.GetFromClipboardUseCase +import dev.snipme.snipmeapp.domain.language.GetLanguagesUseCase +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.snippets.SnippetVisibility class EditInteractor( private val getLanguages: GetLanguagesUseCase, @@ -19,8 +20,13 @@ class EditInteractor( fun create(title: String, code: String, language: String): Single = createSnippet(title, code, language) - fun update(uuid: String, title: String, code: String, language: String): Single = - updateSnippet(uuid, title, code, language) + fun update( + uuid: String, + title: String, + code: String, + language: String, + visibility: SnippetVisibility, + ): Single = updateSnippet(uuid, title, code, language, visibility) fun getFromClipboard(): String? = fromClipboard() } \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/GetSingleSnippetUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/GetSingleSnippetUseCase.kt new file mode 100644 index 0000000..1a6d0e7 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/GetSingleSnippetUseCase.kt @@ -0,0 +1,20 @@ +package dev.snipme.snipmeapp.domain.snippet + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.user.GetSingleUserUseCase + +class GetSingleSnippetUseCase( + private val auth: AuthorizationUseCase, + private val getSingleUser: GetSingleUserUseCase, + private val repository: SnippetRepository +) { + + operator fun invoke(uuid: String): Single = + auth() + .andThen(getSingleUser()) + .flatMap { repository.snippet(uuid, it.id) } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/ObserveSnippetUpdatesUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/ObserveSnippetUpdatesUseCase.kt new file mode 100644 index 0000000..ac7ea4b --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/ObserveSnippetUpdatesUseCase.kt @@ -0,0 +1,9 @@ +package dev.snipme.snipmeapp.domain.snippet + +import io.reactivex.Observable +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.snippets.Snippet + +class ObserveSnippetUpdatesUseCase(private val repository: SnippetRepository) { + operator fun invoke(): Observable = repository.updateListener.share() +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/ObserveUpdatedSnippetPageUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/ObserveUpdatedSnippetPageUseCase.kt similarity index 58% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/ObserveUpdatedSnippetPageUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippet/ObserveUpdatedSnippetPageUseCase.kt index 64c983d..4f4c0b9 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/ObserveUpdatedSnippetPageUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/ObserveUpdatedSnippetPageUseCase.kt @@ -1,34 +1,35 @@ -package pl.tkadziolka.snipmeandroid.domain.snippet +package dev.snipme.snipmeapp.domain.snippet import io.reactivex.Observable import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetScope +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.snippets.Snippet +import dev.snipme.snipmeapp.domain.snippets.SnippetScope private const val START_PAGE = 1 class ObserveUpdatedSnippetPageUseCase(private val repository: SnippetRepository) { - operator fun invoke(scope: SnippetScope): Observable = + operator fun invoke(scope: SnippetScope, userId: Int): Observable = repository.updateListener .skipWhile { it == Snippet.EMPTY } .flatMapSingle { updated -> - getPageWithUpdated(scope, updated, START_PAGE) + getPageWithUpdated(scope, updated, START_PAGE, userId) } private fun getPageWithUpdated( scope: SnippetScope, updated: Snippet, - page: Int - ): Single = repository.snippets(scope, page) + page: Int, + userId: Int + ): Single = repository.snippets(userId) .map { snippets -> snippets.contains(updated.uuid) } .flatMap { contains -> if (contains) { Single.just(page) } else { // Be aware of recursion here - getPageWithUpdated(scope, updated, page + 1) + getPageWithUpdated(scope, updated, page + 1, userId) } } diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/SaveSnippetUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/SaveSnippetUseCase.kt new file mode 100644 index 0000000..b1ed75a --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/SaveSnippetUseCase.kt @@ -0,0 +1,15 @@ +package dev.snipme.snipmeapp.domain.snippet + +import dev.snipme.snipmeapp.AppService +import dev.snipme.snipmeapp.domain.snippets.Snippet + +class SaveSnippetUseCase( + private val appService: AppService +) { + operator fun invoke(image: ByteArray, snippet: Snippet): String { + val name = "${snippet.title.replace(" ", "_")}.png" + appService.storeFile(image, name, temp = false) + appService.storeMediaFile(image, name) + return name + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/UpdateSnippetUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/UpdateSnippetUseCase.kt new file mode 100644 index 0000000..ed9c6af --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippet/UpdateSnippetUseCase.kt @@ -0,0 +1,28 @@ +package dev.snipme.snipmeapp.domain.snippet + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.snippets.SnippetVisibility +import dev.snipme.snipmeapp.domain.user.GetSingleUserUseCase + +class UpdateSnippetUseCase( + private val auth: AuthorizationUseCase, + private val getSingleUser: GetSingleUserUseCase, + private val repository: SnippetRepository +) { + operator fun invoke( + uuid: String, + title: String, + code: String, + language: String, + visibility: SnippetVisibility, + ) = auth() + .andThen(getSingleUser()) + .flatMap{repository.update(uuid, title, code, language, visibility, it.id)} + .doOnSuccess() { + repository.updateListener.onNext(it) + Single.just(it) + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/GetSnippetsUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/GetSnippetsUseCase.kt new file mode 100644 index 0000000..20e0106 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/GetSnippetsUseCase.kt @@ -0,0 +1,22 @@ +package dev.snipme.snipmeapp.domain.snippets + +import io.reactivex.Single +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.user.GetSingleUserUseCase + +class GetSnippetsUseCase( + private val auth: AuthorizationUseCase, + private val repository: SnippetRepository, + private val getSingleUser: GetSingleUserUseCase +) { + operator fun invoke(scope: SnippetScope, page: Int): Single> = + auth() + .andThen(getSingleUser()) + .flatMap { + user -> + repository.snippets(user.id) + .map { list -> list.sortedByDescending { it.modifiedAt.time } } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/HasMoreSnippetPagesUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/HasMoreSnippetPagesUseCase.kt similarity index 59% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/HasMoreSnippetPagesUseCase.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippets/HasMoreSnippetPagesUseCase.kt index c742f5b..8bd123c 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/HasMoreSnippetPagesUseCase.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/HasMoreSnippetPagesUseCase.kt @@ -1,10 +1,10 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets +package dev.snipme.snipmeapp.domain.snippets import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SNIPPET_PAGE_SIZE -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.snippet.SNIPPET_PAGE_SIZE +import dev.snipme.snipmeapp.domain.repository.snippet.SnippetRepository private const val NEXT_PAGE = 1 @@ -17,7 +17,7 @@ class HasMoreSnippetPagesUseCase( operator fun invoke(scope: SnippetScope, page: Int): Single = auth() .andThen(networkAvailable()) - .andThen(repository.count(scope).map { count -> page < pageCountFromOverall(count) }) + .andThen(repository.count().map { count -> page < pageCountFromOverall(count) }) private fun pageCountFromOverall(count: Int): Int { val nextPageOffset = count % SNIPPET_PAGE_SIZE diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/Snippet.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/Snippet.kt similarity index 90% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/Snippet.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippets/Snippet.kt index 2bf2dff..c5ade79 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/Snippet.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/Snippet.kt @@ -1,7 +1,7 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets +package dev.snipme.snipmeapp.domain.snippets import android.text.SpannableString -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction +import dev.snipme.snipmeapp.domain.reaction.UserReaction import java.util.* data class Snippet( diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetFilters.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetFilters.kt new file mode 100644 index 0000000..bc7847c --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetFilters.kt @@ -0,0 +1,8 @@ +package dev.snipme.snipmeapp.domain.snippets + +data class SnippetFilters( + val languages: List, + val selectedLanguages: List, + val scopes: List, + val selectedScope: String +) diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetLanguageMapper.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetLanguageMapper.kt similarity index 92% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetLanguageMapper.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetLanguageMapper.kt index 51b8c81..1dd812b 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetLanguageMapper.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetLanguageMapper.kt @@ -1,6 +1,6 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets +package dev.snipme.snipmeapp.domain.snippets -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetLanguageType.* +import dev.snipme.snipmeapp.domain.snippets.SnippetLanguageType.* object SnippetLanguageMapper { diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetLanguageType.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetLanguageType.kt similarity index 94% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetLanguageType.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetLanguageType.kt index 42ff4cd..77dd5ab 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetLanguageType.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetLanguageType.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets +package dev.snipme.snipmeapp.domain.snippets enum class SnippetLanguageType(val fileExtension: String) { C("c"), diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetResponseMapper.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetResponseMapper.kt new file mode 100644 index 0000000..f999162 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetResponseMapper.kt @@ -0,0 +1,59 @@ +package dev.snipme.snipmeapp.domain.snippets + +import android.text.SpannableString +import dev.snipme.snipmeapp.domain.reaction.UserReaction +import dev.snipme.snipmeapp.infrastructure.local.SnippetExtended +import dev.snipme.snipmeapp.util.SyntaxHighlighter.getHighlighted +import dev.snipme.snipmeapp.util.extension.lines +import dev.snipme.snipmeapp.util.extension.newLineChar +import dev.snipme.snipmeapp.util.extension.toDate +import dev.snipme.snipmeapp.util.extension.toSnippetLanguage +import java.util.* + +const val PREVIEW_COUNT = 5 + +class SnippetResponseMapper { + + operator fun invoke(response: SnippetExtended) = with(response.snippet) { + return@with Snippet( + uuid = id.toString(), + title = title, + code = getCode(code), + language = getLanguage(language), + visibility = getVisibility(visibility), + isOwner = response.isOwner, + owner = Owner(ownerId , response.ownerName), + modifiedAt = modifiedAt.toDate(), + numberOfLikes = response.numberOfLikes, + numberOfDislikes = response.numberOfDislikes, + userReaction = getUserReaction(response.userReaction) + ) + } + + private fun getUserReaction(value: String?) = + when { + value.equals("like", ignoreCase = true) -> UserReaction.LIKE + value.equals("dislike", ignoreCase = true) -> UserReaction.DISLIKE + else -> UserReaction.NONE + } + + private fun getCode(code: String) = SnippetCode( + raw = code.orEmpty(), + highlighted = getPreview(code) + ) + + private fun getLanguage(language: String?) = SnippetLanguage( + raw = language.orEmpty(), + type = language.toSnippetLanguage() + ) + + private fun getPreview(code: String): SpannableString { + val preview = code.lines(PREVIEW_COUNT).joinToString(separator = newLineChar) + return getHighlighted(preview) + } + + private fun getVisibility(visibility: String?): SnippetVisibility { + if (visibility == null) return SnippetVisibility.PRIVATE + return SnippetVisibility.valueOf(visibility) + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetScope.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetScope.kt similarity index 69% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetScope.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetScope.kt index 14641c7..be5c944 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetScope.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetScope.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets +package dev.snipme.snipmeapp.domain.snippets enum class SnippetScope { ALL, PUBLIC, OWNED, SHARED_FOR; diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetVisibility.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetVisibility.kt similarity index 50% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetVisibility.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetVisibility.kt index 02865d1..ff41c28 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetVisibility.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/snippets/SnippetVisibility.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets +package dev.snipme.snipmeapp.domain.snippets enum class SnippetVisibility { PUBLIC, PRIVATE diff --git a/app/src/main/java/dev/snipme/snipmeapp/domain/user/GetSingleUserUseCase.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/user/GetSingleUserUseCase.kt new file mode 100644 index 0000000..dc22e81 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/user/GetSingleUserUseCase.kt @@ -0,0 +1,24 @@ +package dev.snipme.snipmeapp.domain.user + +import android.annotation.SuppressLint +import dev.snipme.snipmeapp.domain.auth.AuthorizationUseCase +import dev.snipme.snipmeapp.domain.network.CheckNetworkAvailableUseCase +import dev.snipme.snipmeapp.domain.repository.auth.AuthRepository +import dev.snipme.snipmeapp.domain.repository.user.UserRepository +import io.reactivex.Single + +class GetSingleUserUseCase( + private val auth: AuthorizationUseCase, + private val networkAvailable: CheckNetworkAvailableUseCase, + private val repository: UserRepository, + private val authRepository: AuthRepository, +) { + @SuppressLint("CheckResult") + operator fun invoke(): Single { + var token: Int = 0 + authRepository.getToken().subscribe { value -> token = value.toInt() } + return auth() +// .andThen(networkAvailable()) + .andThen(repository.user(token)) + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/User.kt b/app/src/main/java/dev/snipme/snipmeapp/domain/user/User.kt similarity index 64% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/User.kt rename to app/src/main/java/dev/snipme/snipmeapp/domain/user/User.kt index 082f4df..88804ca 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/User.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/domain/user/User.kt @@ -1,7 +1,8 @@ -package pl.tkadziolka.snipmeandroid.domain.user +package dev.snipme.snipmeapp.domain.user import androidx.annotation.VisibleForTesting -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.PersonResponse +import dev.snipme.snipmeapp.infrastructure.local.UserEntry +import dev.snipme.snipmeapp.infrastructure.model.response.PersonResponse data class User(val id: Int, val login: String, val email: String, val photo: String) { companion object { @@ -15,4 +16,11 @@ fun PersonResponse.toUser() = User( login = username ?: throw IllegalArgumentException("User must have a login!"), email = email ?: "", photo = photo ?: "" +) + +fun UserEntry.toUser() = User( + id = id, + login = login, + email = email, + photo = photo ) \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/AppDatabase.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/AppDatabase.kt new file mode 100644 index 0000000..e77bf49 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/AppDatabase.kt @@ -0,0 +1,28 @@ +package dev.snipme.snipmeapp.infrastructure.local +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import android.content.Context + +@Database(entities = [UserEntry::class, SnippetEntry::class, ReactionEntry::class], version = 1) +abstract class AppDatabase : RoomDatabase() { + abstract fun userDao(): UserDao + abstract fun snippetDao(): SnippetDao + + companion object { + @Volatile + private var INSTANCE: AppDatabase? = null + + fun getDatabase(context: Context): AppDatabase { + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "app_database" + ).build() + INSTANCE = instance + instance + } + } + } +} diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/local/AuthPreferences.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/AuthPreferences.kt similarity index 74% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/local/AuthPreferences.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/AuthPreferences.kt index 5621c85..fae5b0c 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/local/AuthPreferences.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/AuthPreferences.kt @@ -1,11 +1,10 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.local +package dev.snipme.snipmeapp.infrastructure.local -import pl.tkadziolka.snipmeandroid.util.PreferencesUtil +import dev.snipme.snipmeapp.util.PreferencesUtil private const val TOKEN_KEY = "7e3a40ea-32d2-4248-bcfb-297f2f41246e" class AuthPreferences(private val prefs: PreferencesUtil) { - fun saveToken(token: String) { prefs.save(TOKEN_KEY, token) } diff --git a/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/ReactionEntry.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/ReactionEntry.kt new file mode 100644 index 0000000..c832293 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/ReactionEntry.kt @@ -0,0 +1,33 @@ +package dev.snipme.snipmeapp.infrastructure.local + +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = "reactions", + foreignKeys = [ + ForeignKey( + entity = UserEntry::class, + parentColumns = arrayOf("id"), + childColumns = arrayOf("userId"), + onUpdate = ForeignKey.CASCADE, + onDelete = ForeignKey.CASCADE + ), + ForeignKey( + entity = SnippetEntry::class, + parentColumns = arrayOf("id"), + childColumns = arrayOf("snippetId"), + onUpdate = ForeignKey.CASCADE, + onDelete = ForeignKey.CASCADE + ), + ], + indices = [Index(value = ["userId", "snippetId"], unique = true)] +) +data class ReactionEntry( + @PrimaryKey(true) val id: Int = 0, + val userId: Int, + val snippetId: Int, + val reaction: Short // 0 dislike, 1 none, 2 like +) \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/SnippetDao.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/SnippetDao.kt new file mode 100644 index 0000000..8f070fe --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/SnippetDao.kt @@ -0,0 +1,72 @@ +package dev.snipme.snipmeapp.infrastructure.local + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.reactivex.Completable +import io.reactivex.Single + +@Dao +interface SnippetDao { + @Query( + """ + SELECT s.*, u.login as ownerName, + CASE WHEN s.ownerId = :userId THEN 1 ELSE 0 END as isOwner, + CASE WHEN r.reaction = 0 THEN 'DISLIKE' ELSE CASE WHEN r.reaction = 2 THEN 'LIKE' ELSE 'NONE' END END as userReaction, + (Select Count(*) FROM reactions as r where r.snippetId = :uuid and reaction = 2) as numberOfLikes, + (Select Count(*) FROM reactions as r where r.snippetId = :uuid and reaction = 0) as numberOfDislikes + FROM snippets as s + INNER JOIN users as u ON s.ownerId = u.id + LEFT JOIN reactions as r ON r.userId = :userId and r.snippetId = :uuid + WHERE s.id = :uuid + """ + ) + fun snippet(uuid: Int, userId: Int): Single + + @Query( + """ + SELECT s.*, u.login as ownerName, + CASE WHEN s.ownerId = :userId THEN 1 ELSE 0 END as isOwner, + CASE WHEN r.reaction = 0 THEN "DISLIKE" ELSE CASE WHEN r.reaction = 2 THEN "LIKE" ELSE "NONE" END END as userReaction, + (Select Count(*) FROM reactions as r where r.snippetId = s.id and reaction = 2) as numberOfLikes, + (Select Count(*) FROM reactions as r where r.snippetId = s.id and reaction = 0) as numberOfDislikes + FROM snippets as s + INNER JOIN users as u ON s.ownerId = u.id + LEFT JOIN reactions as r ON r.userId = :userId and r.snippetId = s.id + """ + ) + fun snippets(userId: Int): Single> + + @Query("SELECT COUNT(*) FROM snippets") + fun count(): Single + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun create(snippet: SnippetEntry): Single + + @Query( + """ + UPDATE snippets + SET title = :title, + code = :code, + modifiedAt = current_timestamp, + visibility = :visibility, + language = :language + WHERE id = :uuid + """ + ) + fun update( + uuid: Int, + title: String, + code: String, + visibility: String, + language: String, + ): Completable + + @Query("DELETE FROM snippets WHERE id = :uuid") + fun delete(uuid: Int): Completable + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun reaction(reaction: ReactionEntry): Completable + +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/SnippetEntity.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/SnippetEntity.kt new file mode 100644 index 0000000..425f7c2 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/SnippetEntity.kt @@ -0,0 +1,28 @@ +package dev.snipme.snipmeapp.infrastructure.local + +import androidx.room.Embedded +import androidx.room.Entity +import androidx.room.PrimaryKey + + +@Entity(tableName = "snippets") +data class SnippetEntry( + @PrimaryKey(true) val id: Long = 0, + val title: String, + val code: String, + val createdAt: String, + val modifiedAt: String, + val visibility: String, + val ownerId: Int, + val language: String, +) + + +data class SnippetExtended( + @Embedded val snippet: SnippetEntry, + val ownerName: String, + val isOwner: Boolean, + val userReaction: String, + val numberOfLikes: Int, + val numberOfDislikes: Int, +) diff --git a/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/UserDao.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/UserDao.kt new file mode 100644 index 0000000..ad72231 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/UserDao.kt @@ -0,0 +1,24 @@ +package dev.snipme.snipmeapp.infrastructure.local + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.reactivex.Completable +import io.reactivex.Single + +@Dao +interface UserDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun register(user: UserEntry): Completable + + @Query("SELECT id FROM users WHERE email = :email AND password = :password") + fun login(email: String, password: String): Single + + @Query("SELECT COUNT(*) FROM users WHERE email = :email") + fun identify(email: String): Single + + @Query("SELECT * FROM users WHERE id = :id") + fun user(id: Int) : Single + +} \ No newline at end of file diff --git a/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/UserEntry.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/UserEntry.kt new file mode 100644 index 0000000..7fee638 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/local/UserEntry.kt @@ -0,0 +1,13 @@ +package dev.snipme.snipmeapp.infrastructure.local + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "users") +data class UserEntry( + @PrimaryKey(true) val id: Int = 0, + val email: String, + val password: String, + val login: String, + val photo: String +) \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/SnippetPageResponse.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/SnippetPageResponse.kt similarity index 60% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/SnippetPageResponse.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/SnippetPageResponse.kt index 9064120..8a669c4 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/SnippetPageResponse.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/SnippetPageResponse.kt @@ -1,7 +1,7 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model +package dev.snipme.snipmeapp.infrastructure.model import com.squareup.moshi.JsonClass -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.SnippetResponse +import dev.snipme.snipmeapp.infrastructure.model.response.SnippetResponse @JsonClass(generateAdapter = true) data class SnippetPageResponse( diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/CreateSnippetRequest.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/CreateSnippetRequest.kt similarity index 57% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/CreateSnippetRequest.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/CreateSnippetRequest.kt index 6d57f20..a3412ab 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/CreateSnippetRequest.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/CreateSnippetRequest.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.request +package dev.snipme.snipmeapp.infrastructure.model.request import com.squareup.moshi.JsonClass @@ -6,5 +6,6 @@ import com.squareup.moshi.JsonClass data class CreateSnippetRequest( val title: String, val code: String, - val language: String + val language: String, + val visibility: String ) \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/IdentifyUserRequest.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/IdentifyUserRequest.kt similarity index 65% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/IdentifyUserRequest.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/IdentifyUserRequest.kt index 341f5c0..b7246ec 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/IdentifyUserRequest.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/IdentifyUserRequest.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.request +package dev.snipme.snipmeapp.infrastructure.model.request import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/LoginUserRequest.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/LoginUserRequest.kt similarity index 68% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/LoginUserRequest.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/LoginUserRequest.kt index 2a425dd..4dc20da 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/LoginUserRequest.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/LoginUserRequest.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.request +package dev.snipme.snipmeapp.infrastructure.model.request import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/RateSnippetRequest.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/RateSnippetRequest.kt similarity index 68% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/RateSnippetRequest.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/RateSnippetRequest.kt index 733bfa8..dacbcab 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/RateSnippetRequest.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/RateSnippetRequest.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.request +package dev.snipme.snipmeapp.infrastructure.model.request import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/RegisterUserRequest.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/RegisterUserRequest.kt similarity index 71% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/RegisterUserRequest.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/RegisterUserRequest.kt index 5aeea9b..cda564d 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/RegisterUserRequest.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/RegisterUserRequest.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.request +package dev.snipme.snipmeapp.infrastructure.model.request import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/ShareSnippetRequest.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/ShareSnippetRequest.kt similarity index 77% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/ShareSnippetRequest.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/ShareSnippetRequest.kt index e9248af..5d011f6 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/request/ShareSnippetRequest.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/request/ShareSnippetRequest.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.request +package dev.snipme.snipmeapp.infrastructure.model.request import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/LanguageResponse.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/LanguageResponse.kt similarity index 64% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/LanguageResponse.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/LanguageResponse.kt index ae992b0..b302c7c 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/LanguageResponse.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/LanguageResponse.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.response +package dev.snipme.snipmeapp.infrastructure.model.response import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/PersonResponse.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/PersonResponse.kt similarity index 85% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/PersonResponse.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/PersonResponse.kt index 6cbfbaf..4d4aad1 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/PersonResponse.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/PersonResponse.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.response +package dev.snipme.snipmeapp.infrastructure.model.response import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/RegisterUserResponse.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/RegisterUserResponse.kt similarity index 87% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/RegisterUserResponse.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/RegisterUserResponse.kt index 9bbaf54..3e2f976 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/RegisterUserResponse.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/RegisterUserResponse.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.response +package dev.snipme.snipmeapp.infrastructure.model.response import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/SnippetResponse.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/SnippetResponse.kt similarity index 89% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/SnippetResponse.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/SnippetResponse.kt index 1821cf5..6864079 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/SnippetResponse.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/SnippetResponse.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.response +package dev.snipme.snipmeapp.infrastructure.model.response import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/TokenResponse.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/TokenResponse.kt similarity index 63% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/TokenResponse.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/TokenResponse.kt index 15a6f1b..098ab33 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/model/response/TokenResponse.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/model/response/TokenResponse.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.model.response +package dev.snipme.snipmeapp.infrastructure.model.response import com.squareup.moshi.JsonClass diff --git a/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/AuthService.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/AuthService.kt new file mode 100644 index 0000000..a99525c --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/AuthService.kt @@ -0,0 +1,21 @@ +package dev.snipme.snipmeapp.infrastructure.remote + +import io.reactivex.Single +import dev.snipme.snipmeapp.infrastructure.model.request.IdentifyUserRequest +import dev.snipme.snipmeapp.infrastructure.model.request.LoginUserRequest +import dev.snipme.snipmeapp.infrastructure.model.request.RegisterUserRequest +import dev.snipme.snipmeapp.infrastructure.model.response.RegisterUserResponse +import dev.snipme.snipmeapp.infrastructure.model.response.TokenResponse +import retrofit2.http.* + +interface AuthService { + + @POST("check-if-user-exist/") + fun identify(@Body request: IdentifyUserRequest): Single + + @POST("auth-token/") + fun login(@Body request: LoginUserRequest): Single + + @POST("register/") + fun register(@Body request: RegisterUserRequest): Single +} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/LanguageService.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/LanguageService.kt similarity index 53% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/LanguageService.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/LanguageService.kt index d9830a0..d7426c4 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/LanguageService.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/LanguageService.kt @@ -1,7 +1,7 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.remote +package dev.snipme.snipmeapp.infrastructure.remote import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.LanguageResponse +import dev.snipme.snipmeapp.infrastructure.model.response.LanguageResponse import retrofit2.http.GET interface LanguageService { diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/ShareService.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/ShareService.kt similarity index 64% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/ShareService.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/ShareService.kt index f745b54..ea6ae7a 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/ShareService.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/ShareService.kt @@ -1,9 +1,9 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.remote +package dev.snipme.snipmeapp.infrastructure.remote import io.reactivex.Completable import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.ShareSnippetRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.SharePersonResponse +import dev.snipme.snipmeapp.infrastructure.model.request.ShareSnippetRequest +import dev.snipme.snipmeapp.infrastructure.model.response.SharePersonResponse import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/SnippetService.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/SnippetService.kt similarity index 53% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/SnippetService.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/SnippetService.kt index 87c5f80..8ce8dfa 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/SnippetService.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/SnippetService.kt @@ -1,11 +1,11 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.remote +package dev.snipme.snipmeapp.infrastructure.remote import io.reactivex.Completable import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.infrastructure.model.SnippetPageResponse -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.CreateSnippetRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.RateSnippetRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.SnippetResponse +import dev.snipme.snipmeapp.infrastructure.model.SnippetPageResponse +import dev.snipme.snipmeapp.infrastructure.model.request.CreateSnippetRequest +import dev.snipme.snipmeapp.infrastructure.model.request.RateSnippetRequest +import dev.snipme.snipmeapp.infrastructure.model.response.SnippetResponse import retrofit2.http.* private const val PATH_ID = "id" @@ -26,8 +26,14 @@ interface SnippetService { fun create(@Body request: CreateSnippetRequest): Single @PUT("snippet/{$PATH_ID}/") - fun update(@Path(PATH_ID) id: String, @Body request: CreateSnippetRequest): Single + fun update( + @Path(PATH_ID) id: String, + @Body request: CreateSnippetRequest + ): Single @POST("snippet-rate/") fun rate(@Body request: RateSnippetRequest): Completable + + @DELETE("snippet/{$PATH_ID}/") + fun delete(@Path(PATH_ID) id: String): Completable } \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/UserService.kt b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/UserService.kt similarity index 50% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/UserService.kt rename to app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/UserService.kt index 7e8dbc6..b856e69 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/UserService.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/infrastructure/remote/UserService.kt @@ -1,7 +1,7 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.remote +package dev.snipme.snipmeapp.infrastructure.remote import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.PersonResponse +import dev.snipme.snipmeapp.infrastructure.model.response.PersonResponse import retrofit2.http.GET interface UserService { diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/AuthInterceptor.kt b/app/src/main/java/dev/snipme/snipmeapp/util/AuthInterceptor.kt similarity index 79% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/AuthInterceptor.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/AuthInterceptor.kt index 02ebbed..f0154b0 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/AuthInterceptor.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/AuthInterceptor.kt @@ -1,9 +1,9 @@ -package pl.tkadziolka.snipmeandroid.util +package dev.snipme.snipmeapp.util import okhttp3.Interceptor import okhttp3.Response -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.infrastructure.local.AuthPreferences +import dev.snipme.snipmeapp.BuildConfig +import dev.snipme.snipmeapp.infrastructure.local.AuthPreferences import timber.log.Timber private const val KEY_HEADER_AUTHORIZATION = "Authorization" diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/CrashReportingTree.kt b/app/src/main/java/dev/snipme/snipmeapp/util/CrashReportingTree.kt similarity index 82% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/CrashReportingTree.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/CrashReportingTree.kt index 6b38461..21e2b06 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/CrashReportingTree.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/CrashReportingTree.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.util +package dev.snipme.snipmeapp.util import timber.log.Timber diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/PreferencesUtil.kt b/app/src/main/java/dev/snipme/snipmeapp/util/PreferencesUtil.kt similarity index 95% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/PreferencesUtil.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/PreferencesUtil.kt index 87e7e46..6fabaa4 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/PreferencesUtil.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/PreferencesUtil.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.util +package dev.snipme.snipmeapp.util import android.content.Context import android.content.Context.MODE_PRIVATE diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/SyntaxHighlighter.kt b/app/src/main/java/dev/snipme/snipmeapp/util/SyntaxHighlighter.kt similarity index 92% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/SyntaxHighlighter.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/SyntaxHighlighter.kt index 7ce203a..79046af 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/SyntaxHighlighter.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/SyntaxHighlighter.kt @@ -1,20 +1,20 @@ -package pl.tkadziolka.snipmeandroid.util +package dev.snipme.snipmeapp.util import android.graphics.Color import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan import androidx.core.text.set -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.util.SyntaxPhrases.commentTerminators -import pl.tkadziolka.snipmeandroid.util.SyntaxPhrases.keywords -import pl.tkadziolka.snipmeandroid.util.SyntaxPhrases.multilineCommentTerminators -import pl.tkadziolka.snipmeandroid.util.SyntaxPhrases.textTerminators -import pl.tkadziolka.snipmeandroid.util.SyntaxPhrases.wordTerminators -import pl.tkadziolka.snipmeandroid.util.extension.containsDefault -import pl.tkadziolka.snipmeandroid.util.extension.indicesOf -import pl.tkadziolka.snipmeandroid.util.extension.lengthToEOF -import pl.tkadziolka.snipmeandroid.util.extension.opaque +import dev.snipme.snipmeapp.BuildConfig +import dev.snipme.snipmeapp.util.SyntaxPhrases.commentTerminators +import dev.snipme.snipmeapp.util.SyntaxPhrases.keywords +import dev.snipme.snipmeapp.util.SyntaxPhrases.multilineCommentTerminators +import dev.snipme.snipmeapp.util.SyntaxPhrases.textTerminators +import dev.snipme.snipmeapp.util.SyntaxPhrases.wordTerminators +import dev.snipme.snipmeapp.util.extension.containsDefault +import dev.snipme.snipmeapp.util.extension.indicesOf +import dev.snipme.snipmeapp.util.extension.lengthToEOF +import dev.snipme.snipmeapp.util.extension.opaque import timber.log.Timber import java.util.* diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/SyntaxPhrases.kt b/app/src/main/java/dev/snipme/snipmeapp/util/SyntaxPhrases.kt similarity index 98% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/SyntaxPhrases.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/SyntaxPhrases.kt index e88fafe..35e5bd2 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/SyntaxPhrases.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/SyntaxPhrases.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.util +package dev.snipme.snipmeapp.util object SyntaxPhrases { val wordTerminators = arrayOf(" ", ",", ".", ":", "(", ")", "=", "{", "}", "<", ">", "\r", "\n") diff --git a/app/src/main/java/dev/snipme/snipmeapp/util/extension/CollectionExtensions.kt b/app/src/main/java/dev/snipme/snipmeapp/util/extension/CollectionExtensions.kt new file mode 100644 index 0000000..9165e40 --- /dev/null +++ b/app/src/main/java/dev/snipme/snipmeapp/util/extension/CollectionExtensions.kt @@ -0,0 +1,8 @@ +package dev.snipme.snipmeapp.util.extension + +import dev.snipme.snipmeapp.domain.snippets.SnippetLanguageType +import dev.snipme.snipmeapp.domain.snippets.SnippetLanguageMapper + +fun CharSequence.lines(count: Int) = lines().take(count) + +fun String?.toSnippetLanguage(): SnippetLanguageType = SnippetLanguageMapper.fromString(this) \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NetworkExtensions.kt b/app/src/main/java/dev/snipme/snipmeapp/util/extension/NetworkExtensions.kt similarity index 78% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NetworkExtensions.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/extension/NetworkExtensions.kt index 1cf19d0..6e96ce0 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NetworkExtensions.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/extension/NetworkExtensions.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.util.extension +package dev.snipme.snipmeapp.util.extension import retrofit2.HttpException diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NumberExtensions.kt b/app/src/main/java/dev/snipme/snipmeapp/util/extension/NumberExtensions.kt similarity index 88% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NumberExtensions.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/extension/NumberExtensions.kt index 0f37d58..0db4018 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NumberExtensions.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/extension/NumberExtensions.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.util.extension +package dev.snipme.snipmeapp.util.extension import android.graphics.Color diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/RxExtensions.kt b/app/src/main/java/dev/snipme/snipmeapp/util/extension/RxExtensions.kt similarity index 92% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/RxExtensions.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/extension/RxExtensions.kt index af711b0..85e64bf 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/RxExtensions.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/extension/RxExtensions.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.util.extension +package dev.snipme.snipmeapp.util.extension import io.reactivex.Completable import io.reactivex.Single diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/TextExtensions.kt b/app/src/main/java/dev/snipme/snipmeapp/util/extension/TextExtensions.kt similarity index 95% rename from app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/TextExtensions.kt rename to app/src/main/java/dev/snipme/snipmeapp/util/extension/TextExtensions.kt index 53baee9..1737799 100644 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/TextExtensions.kt +++ b/app/src/main/java/dev/snipme/snipmeapp/util/extension/TextExtensions.kt @@ -1,4 +1,4 @@ -package pl.tkadziolka.snipmeandroid.util.extension +package dev.snipme.snipmeapp.util.extension import java.text.SimpleDateFormat import java.util.* diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/Bridge.java b/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/Bridge.java deleted file mode 100644 index 0914b54..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/Bridge.java +++ /dev/null @@ -1,944 +0,0 @@ -// Autogenerated from Pigeon (v4.2.3), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -package pl.tkadziolka.snipmeandroid.bridge; - -import android.util.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import io.flutter.plugin.common.BasicMessageChannel; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MessageCodec; -import io.flutter.plugin.common.StandardMessageCodec; -import java.io.ByteArrayOutputStream; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.HashMap; - -/**Generated class from Pigeon. */ -@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) -public class Bridge { - - public enum SnippetLanguageType { - C(0), - CPP(1), - OBJECTIVE_C(2), - C_SHARP(3), - JAVA(4), - BASH(5), - PYTHON(6), - PERL(7), - RUBY(8), - SWIFT(9), - JAVASCRIPT(10), - KOTLIN(11), - COFFEESCRIPT(12), - RUST(13), - BASIC(14), - CLOJURE(15), - CSS(16), - DART(17), - ERLANG(18), - GO(19), - HASKELL(20), - LISP(21), - LLVM(22), - LUA(23), - MATLAB(24), - ML(25), - MUMPS(26), - NEMERLE(27), - PASCAL(28), - R(29), - RD(30), - SCALA(31), - SQL(32), - TEX(33), - VB(34), - VHDL(35), - TCL(36), - XQUERY(37), - YAML(38), - MARKDOWN(39), - JSON(40), - XML(41), - PROTO(42), - REGEX(43), - UNKNOWN(44); - - private int index; - private SnippetLanguageType(final int index) { - this.index = index; - } - } - - public enum SnippetFilterType { - ALL(0), - MINE(1), - SHARED(2); - - private int index; - private SnippetFilterType(final int index) { - this.index = index; - } - } - - public enum UserReaction { - NONE(0), - LIKE(1), - DISLIKE(2); - - private int index; - private UserReaction(final int index) { - this.index = index; - } - } - - public enum ModelState { - LOADING(0), - LOADED(1), - ERROR(2); - - private int index; - private ModelState(final int index) { - this.index = index; - } - } - - public enum MainModelEvent { - NONE(0), - ALERT(1), - LOGOUT(2); - - private int index; - private MainModelEvent(final int index) { - this.index = index; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class Snippet { - private @Nullable String uuid; - public @Nullable String getUuid() { return uuid; } - public void setUuid(@Nullable String setterArg) { - this.uuid = setterArg; - } - - private @Nullable String title; - public @Nullable String getTitle() { return title; } - public void setTitle(@Nullable String setterArg) { - this.title = setterArg; - } - - private @Nullable SnippetCode code; - public @Nullable SnippetCode getCode() { return code; } - public void setCode(@Nullable SnippetCode setterArg) { - this.code = setterArg; - } - - private @Nullable SnippetLanguage language; - public @Nullable SnippetLanguage getLanguage() { return language; } - public void setLanguage(@Nullable SnippetLanguage setterArg) { - this.language = setterArg; - } - - private @Nullable Owner owner; - public @Nullable Owner getOwner() { return owner; } - public void setOwner(@Nullable Owner setterArg) { - this.owner = setterArg; - } - - private @Nullable Boolean isOwner; - public @Nullable Boolean getIsOwner() { return isOwner; } - public void setIsOwner(@Nullable Boolean setterArg) { - this.isOwner = setterArg; - } - - private @Nullable String timeAgo; - public @Nullable String getTimeAgo() { return timeAgo; } - public void setTimeAgo(@Nullable String setterArg) { - this.timeAgo = setterArg; - } - - private @Nullable Long voteResult; - public @Nullable Long getVoteResult() { return voteResult; } - public void setVoteResult(@Nullable Long setterArg) { - this.voteResult = setterArg; - } - - private @Nullable UserReaction userReaction; - public @Nullable UserReaction getUserReaction() { return userReaction; } - public void setUserReaction(@Nullable UserReaction setterArg) { - this.userReaction = setterArg; - } - - private @Nullable Boolean isPrivate; - public @Nullable Boolean getIsPrivate() { return isPrivate; } - public void setIsPrivate(@Nullable Boolean setterArg) { - this.isPrivate = setterArg; - } - - private @Nullable Boolean isLiked; - public @Nullable Boolean getIsLiked() { return isLiked; } - public void setIsLiked(@Nullable Boolean setterArg) { - this.isLiked = setterArg; - } - - private @Nullable Boolean isDisliked; - public @Nullable Boolean getIsDisliked() { return isDisliked; } - public void setIsDisliked(@Nullable Boolean setterArg) { - this.isDisliked = setterArg; - } - - private @Nullable Boolean isSaved; - public @Nullable Boolean getIsSaved() { return isSaved; } - public void setIsSaved(@Nullable Boolean setterArg) { - this.isSaved = setterArg; - } - - public static final class Builder { - private @Nullable String uuid; - public @NonNull Builder setUuid(@Nullable String setterArg) { - this.uuid = setterArg; - return this; - } - private @Nullable String title; - public @NonNull Builder setTitle(@Nullable String setterArg) { - this.title = setterArg; - return this; - } - private @Nullable SnippetCode code; - public @NonNull Builder setCode(@Nullable SnippetCode setterArg) { - this.code = setterArg; - return this; - } - private @Nullable SnippetLanguage language; - public @NonNull Builder setLanguage(@Nullable SnippetLanguage setterArg) { - this.language = setterArg; - return this; - } - private @Nullable Owner owner; - public @NonNull Builder setOwner(@Nullable Owner setterArg) { - this.owner = setterArg; - return this; - } - private @Nullable Boolean isOwner; - public @NonNull Builder setIsOwner(@Nullable Boolean setterArg) { - this.isOwner = setterArg; - return this; - } - private @Nullable String timeAgo; - public @NonNull Builder setTimeAgo(@Nullable String setterArg) { - this.timeAgo = setterArg; - return this; - } - private @Nullable Long voteResult; - public @NonNull Builder setVoteResult(@Nullable Long setterArg) { - this.voteResult = setterArg; - return this; - } - private @Nullable UserReaction userReaction; - public @NonNull Builder setUserReaction(@Nullable UserReaction setterArg) { - this.userReaction = setterArg; - return this; - } - private @Nullable Boolean isPrivate; - public @NonNull Builder setIsPrivate(@Nullable Boolean setterArg) { - this.isPrivate = setterArg; - return this; - } - private @Nullable Boolean isLiked; - public @NonNull Builder setIsLiked(@Nullable Boolean setterArg) { - this.isLiked = setterArg; - return this; - } - private @Nullable Boolean isDisliked; - public @NonNull Builder setIsDisliked(@Nullable Boolean setterArg) { - this.isDisliked = setterArg; - return this; - } - private @Nullable Boolean isSaved; - public @NonNull Builder setIsSaved(@Nullable Boolean setterArg) { - this.isSaved = setterArg; - return this; - } - public @NonNull Snippet build() { - Snippet pigeonReturn = new Snippet(); - pigeonReturn.setUuid(uuid); - pigeonReturn.setTitle(title); - pigeonReturn.setCode(code); - pigeonReturn.setLanguage(language); - pigeonReturn.setOwner(owner); - pigeonReturn.setIsOwner(isOwner); - pigeonReturn.setTimeAgo(timeAgo); - pigeonReturn.setVoteResult(voteResult); - pigeonReturn.setUserReaction(userReaction); - pigeonReturn.setIsPrivate(isPrivate); - pigeonReturn.setIsLiked(isLiked); - pigeonReturn.setIsDisliked(isDisliked); - pigeonReturn.setIsSaved(isSaved); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("uuid", uuid); - toMapResult.put("title", title); - toMapResult.put("code", (code == null) ? null : code.toMap()); - toMapResult.put("language", (language == null) ? null : language.toMap()); - toMapResult.put("owner", (owner == null) ? null : owner.toMap()); - toMapResult.put("isOwner", isOwner); - toMapResult.put("timeAgo", timeAgo); - toMapResult.put("voteResult", voteResult); - toMapResult.put("userReaction", userReaction == null ? null : userReaction.index); - toMapResult.put("isPrivate", isPrivate); - toMapResult.put("isLiked", isLiked); - toMapResult.put("isDisliked", isDisliked); - toMapResult.put("isSaved", isSaved); - return toMapResult; - } - static @NonNull Snippet fromMap(@NonNull Map map) { - Snippet pigeonResult = new Snippet(); - Object uuid = map.get("uuid"); - pigeonResult.setUuid((String)uuid); - Object title = map.get("title"); - pigeonResult.setTitle((String)title); - Object code = map.get("code"); - pigeonResult.setCode((code == null) ? null : SnippetCode.fromMap((Map)code)); - Object language = map.get("language"); - pigeonResult.setLanguage((language == null) ? null : SnippetLanguage.fromMap((Map)language)); - Object owner = map.get("owner"); - pigeonResult.setOwner((owner == null) ? null : Owner.fromMap((Map)owner)); - Object isOwner = map.get("isOwner"); - pigeonResult.setIsOwner((Boolean)isOwner); - Object timeAgo = map.get("timeAgo"); - pigeonResult.setTimeAgo((String)timeAgo); - Object voteResult = map.get("voteResult"); - pigeonResult.setVoteResult((voteResult == null) ? null : ((voteResult instanceof Integer) ? (Integer)voteResult : (Long)voteResult)); - Object userReaction = map.get("userReaction"); - pigeonResult.setUserReaction(userReaction == null ? null : UserReaction.values()[(int)userReaction]); - Object isPrivate = map.get("isPrivate"); - pigeonResult.setIsPrivate((Boolean)isPrivate); - Object isLiked = map.get("isLiked"); - pigeonResult.setIsLiked((Boolean)isLiked); - Object isDisliked = map.get("isDisliked"); - pigeonResult.setIsDisliked((Boolean)isDisliked); - Object isSaved = map.get("isSaved"); - pigeonResult.setIsSaved((Boolean)isSaved); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class SnippetCode { - private @Nullable String raw; - public @Nullable String getRaw() { return raw; } - public void setRaw(@Nullable String setterArg) { - this.raw = setterArg; - } - - private @Nullable List tokens; - public @Nullable List getTokens() { return tokens; } - public void setTokens(@Nullable List setterArg) { - this.tokens = setterArg; - } - - public static final class Builder { - private @Nullable String raw; - public @NonNull Builder setRaw(@Nullable String setterArg) { - this.raw = setterArg; - return this; - } - private @Nullable List tokens; - public @NonNull Builder setTokens(@Nullable List setterArg) { - this.tokens = setterArg; - return this; - } - public @NonNull SnippetCode build() { - SnippetCode pigeonReturn = new SnippetCode(); - pigeonReturn.setRaw(raw); - pigeonReturn.setTokens(tokens); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("raw", raw); - toMapResult.put("tokens", tokens); - return toMapResult; - } - static @NonNull SnippetCode fromMap(@NonNull Map map) { - SnippetCode pigeonResult = new SnippetCode(); - Object raw = map.get("raw"); - pigeonResult.setRaw((String)raw); - Object tokens = map.get("tokens"); - pigeonResult.setTokens((List)tokens); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class SyntaxToken { - private @Nullable Long start; - public @Nullable Long getStart() { return start; } - public void setStart(@Nullable Long setterArg) { - this.start = setterArg; - } - - private @Nullable Long end; - public @Nullable Long getEnd() { return end; } - public void setEnd(@Nullable Long setterArg) { - this.end = setterArg; - } - - private @Nullable Long color; - public @Nullable Long getColor() { return color; } - public void setColor(@Nullable Long setterArg) { - this.color = setterArg; - } - - public static final class Builder { - private @Nullable Long start; - public @NonNull Builder setStart(@Nullable Long setterArg) { - this.start = setterArg; - return this; - } - private @Nullable Long end; - public @NonNull Builder setEnd(@Nullable Long setterArg) { - this.end = setterArg; - return this; - } - private @Nullable Long color; - public @NonNull Builder setColor(@Nullable Long setterArg) { - this.color = setterArg; - return this; - } - public @NonNull SyntaxToken build() { - SyntaxToken pigeonReturn = new SyntaxToken(); - pigeonReturn.setStart(start); - pigeonReturn.setEnd(end); - pigeonReturn.setColor(color); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("start", start); - toMapResult.put("end", end); - toMapResult.put("color", color); - return toMapResult; - } - static @NonNull SyntaxToken fromMap(@NonNull Map map) { - SyntaxToken pigeonResult = new SyntaxToken(); - Object start = map.get("start"); - pigeonResult.setStart((start == null) ? null : ((start instanceof Integer) ? (Integer)start : (Long)start)); - Object end = map.get("end"); - pigeonResult.setEnd((end == null) ? null : ((end instanceof Integer) ? (Integer)end : (Long)end)); - Object color = map.get("color"); - pigeonResult.setColor((color == null) ? null : ((color instanceof Integer) ? (Integer)color : (Long)color)); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class SnippetLanguage { - private @Nullable String raw; - public @Nullable String getRaw() { return raw; } - public void setRaw(@Nullable String setterArg) { - this.raw = setterArg; - } - - private @Nullable SnippetLanguageType type; - public @Nullable SnippetLanguageType getType() { return type; } - public void setType(@Nullable SnippetLanguageType setterArg) { - this.type = setterArg; - } - - public static final class Builder { - private @Nullable String raw; - public @NonNull Builder setRaw(@Nullable String setterArg) { - this.raw = setterArg; - return this; - } - private @Nullable SnippetLanguageType type; - public @NonNull Builder setType(@Nullable SnippetLanguageType setterArg) { - this.type = setterArg; - return this; - } - public @NonNull SnippetLanguage build() { - SnippetLanguage pigeonReturn = new SnippetLanguage(); - pigeonReturn.setRaw(raw); - pigeonReturn.setType(type); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("raw", raw); - toMapResult.put("type", type == null ? null : type.index); - return toMapResult; - } - static @NonNull SnippetLanguage fromMap(@NonNull Map map) { - SnippetLanguage pigeonResult = new SnippetLanguage(); - Object raw = map.get("raw"); - pigeonResult.setRaw((String)raw); - Object type = map.get("type"); - pigeonResult.setType(type == null ? null : SnippetLanguageType.values()[(int)type]); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class Owner { - private @Nullable Long id; - public @Nullable Long getId() { return id; } - public void setId(@Nullable Long setterArg) { - this.id = setterArg; - } - - private @Nullable String login; - public @Nullable String getLogin() { return login; } - public void setLogin(@Nullable String setterArg) { - this.login = setterArg; - } - - public static final class Builder { - private @Nullable Long id; - public @NonNull Builder setId(@Nullable Long setterArg) { - this.id = setterArg; - return this; - } - private @Nullable String login; - public @NonNull Builder setLogin(@Nullable String setterArg) { - this.login = setterArg; - return this; - } - public @NonNull Owner build() { - Owner pigeonReturn = new Owner(); - pigeonReturn.setId(id); - pigeonReturn.setLogin(login); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("id", id); - toMapResult.put("login", login); - return toMapResult; - } - static @NonNull Owner fromMap(@NonNull Map map) { - Owner pigeonResult = new Owner(); - Object id = map.get("id"); - pigeonResult.setId((id == null) ? null : ((id instanceof Integer) ? (Integer)id : (Long)id)); - Object login = map.get("login"); - pigeonResult.setLogin((String)login); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class SnippetFilter { - private @Nullable SnippetFilterType type; - public @Nullable SnippetFilterType getType() { return type; } - public void setType(@Nullable SnippetFilterType setterArg) { - this.type = setterArg; - } - - public static final class Builder { - private @Nullable SnippetFilterType type; - public @NonNull Builder setType(@Nullable SnippetFilterType setterArg) { - this.type = setterArg; - return this; - } - public @NonNull SnippetFilter build() { - SnippetFilter pigeonReturn = new SnippetFilter(); - pigeonReturn.setType(type); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("type", type == null ? null : type.index); - return toMapResult; - } - static @NonNull SnippetFilter fromMap(@NonNull Map map) { - SnippetFilter pigeonResult = new SnippetFilter(); - Object type = map.get("type"); - pigeonResult.setType(type == null ? null : SnippetFilterType.values()[(int)type]); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class MainModelStateData { - private @Nullable ModelState state; - public @Nullable ModelState getState() { return state; } - public void setState(@Nullable ModelState setterArg) { - this.state = setterArg; - } - - private @Nullable Boolean is_loading; - public @Nullable Boolean getIs_loading() { return is_loading; } - public void setIs_loading(@Nullable Boolean setterArg) { - this.is_loading = setterArg; - } - - private @Nullable List data; - public @Nullable List getData() { return data; } - public void setData(@Nullable List setterArg) { - this.data = setterArg; - } - - private @Nullable String error; - public @Nullable String getError() { return error; } - public void setError(@Nullable String setterArg) { - this.error = setterArg; - } - - public static final class Builder { - private @Nullable ModelState state; - public @NonNull Builder setState(@Nullable ModelState setterArg) { - this.state = setterArg; - return this; - } - private @Nullable Boolean is_loading; - public @NonNull Builder setIs_loading(@Nullable Boolean setterArg) { - this.is_loading = setterArg; - return this; - } - private @Nullable List data; - public @NonNull Builder setData(@Nullable List setterArg) { - this.data = setterArg; - return this; - } - private @Nullable String error; - public @NonNull Builder setError(@Nullable String setterArg) { - this.error = setterArg; - return this; - } - public @NonNull MainModelStateData build() { - MainModelStateData pigeonReturn = new MainModelStateData(); - pigeonReturn.setState(state); - pigeonReturn.setIs_loading(is_loading); - pigeonReturn.setData(data); - pigeonReturn.setError(error); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("state", state == null ? null : state.index); - toMapResult.put("is_loading", is_loading); - toMapResult.put("data", data); - toMapResult.put("error", error); - return toMapResult; - } - static @NonNull MainModelStateData fromMap(@NonNull Map map) { - MainModelStateData pigeonResult = new MainModelStateData(); - Object state = map.get("state"); - pigeonResult.setState(state == null ? null : ModelState.values()[(int)state]); - Object is_loading = map.get("is_loading"); - pigeonResult.setIs_loading((Boolean)is_loading); - Object data = map.get("data"); - pigeonResult.setData((List)data); - Object error = map.get("error"); - pigeonResult.setError((String)error); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static class MainModelEventData { - private @Nullable MainModelEvent event; - public @Nullable MainModelEvent getEvent() { return event; } - public void setEvent(@Nullable MainModelEvent setterArg) { - this.event = setterArg; - } - - private @Nullable String message; - public @Nullable String getMessage() { return message; } - public void setMessage(@Nullable String setterArg) { - this.message = setterArg; - } - - public static final class Builder { - private @Nullable MainModelEvent event; - public @NonNull Builder setEvent(@Nullable MainModelEvent setterArg) { - this.event = setterArg; - return this; - } - private @Nullable String message; - public @NonNull Builder setMessage(@Nullable String setterArg) { - this.message = setterArg; - return this; - } - public @NonNull MainModelEventData build() { - MainModelEventData pigeonReturn = new MainModelEventData(); - pigeonReturn.setEvent(event); - pigeonReturn.setMessage(message); - return pigeonReturn; - } - } - @NonNull Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("event", event == null ? null : event.index); - toMapResult.put("message", message); - return toMapResult; - } - static @NonNull MainModelEventData fromMap(@NonNull Map map) { - MainModelEventData pigeonResult = new MainModelEventData(); - Object event = map.get("event"); - pigeonResult.setEvent(event == null ? null : MainModelEvent.values()[(int)event]); - Object message = map.get("message"); - pigeonResult.setMessage((String)message); - return pigeonResult; - } - } - private static class MainModelBridgeCodec extends StandardMessageCodec { - public static final MainModelBridgeCodec INSTANCE = new MainModelBridgeCodec(); - private MainModelBridgeCodec() {} - @Override - protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { - switch (type) { - case (byte)128: - return MainModelEventData.fromMap((Map) readValue(buffer)); - - case (byte)129: - return MainModelStateData.fromMap((Map) readValue(buffer)); - - case (byte)130: - return Owner.fromMap((Map) readValue(buffer)); - - case (byte)131: - return Snippet.fromMap((Map) readValue(buffer)); - - case (byte)132: - return SnippetCode.fromMap((Map) readValue(buffer)); - - case (byte)133: - return SnippetFilter.fromMap((Map) readValue(buffer)); - - case (byte)134: - return SnippetLanguage.fromMap((Map) readValue(buffer)); - - case (byte)135: - return SyntaxToken.fromMap((Map) readValue(buffer)); - - default: - return super.readValueOfType(type, buffer); - - } - } - @Override - protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof MainModelEventData) { - stream.write(128); - writeValue(stream, ((MainModelEventData) value).toMap()); - } else - if (value instanceof MainModelStateData) { - stream.write(129); - writeValue(stream, ((MainModelStateData) value).toMap()); - } else - if (value instanceof Owner) { - stream.write(130); - writeValue(stream, ((Owner) value).toMap()); - } else - if (value instanceof Snippet) { - stream.write(131); - writeValue(stream, ((Snippet) value).toMap()); - } else - if (value instanceof SnippetCode) { - stream.write(132); - writeValue(stream, ((SnippetCode) value).toMap()); - } else - if (value instanceof SnippetFilter) { - stream.write(133); - writeValue(stream, ((SnippetFilter) value).toMap()); - } else - if (value instanceof SnippetLanguage) { - stream.write(134); - writeValue(stream, ((SnippetLanguage) value).toMap()); - } else - if (value instanceof SyntaxToken) { - stream.write(135); - writeValue(stream, ((SyntaxToken) value).toMap()); - } else -{ - super.writeValue(stream, value); - } - } - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ - public interface MainModelBridge { - @NonNull MainModelStateData getState(); - @NonNull MainModelEventData getEvent(); - void initState(); - void loadNextPage(); - void filter(@NonNull SnippetFilter filter); - void logOut(); - void refreshSnippetUpdates(); - - /** The codec used by MainModelBridge. */ - static MessageCodec getCodec() { - return MainModelBridgeCodec.INSTANCE; } - /**Sets up an instance of `MainModelBridge` to handle messages through the `binaryMessenger`. */ - static void setup(BinaryMessenger binaryMessenger, MainModelBridge api) { - { - BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.getState", getCodec()); - if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - MainModelStateData output = api.getState(); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.getEvent", getCodec()); - if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - MainModelEventData output = api.getEvent(); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); - BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.initState", getCodec(), taskQueue); - if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - api.initState(); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); - BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.loadNextPage", getCodec(), taskQueue); - if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - api.loadNextPage(); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); - BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.filter", getCodec(), taskQueue); - if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - assert args != null; - SnippetFilter filterArg = (SnippetFilter)args.get(0); - if (filterArg == null) { - throw new NullPointerException("filterArg unexpectedly null."); - } - api.filter(filterArg); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); - BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.logOut", getCodec(), taskQueue); - if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - api.logOut(); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); - BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.refreshSnippetUpdates", getCodec(), taskQueue); - if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - api.refreshSnippetUpdates(); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - } - } - @NonNull private static Map wrapError(@NonNull Throwable exception) { - Map errorMap = new HashMap<>(); - errorMap.put("message", exception.toString()); - errorMap.put("code", exception.getClass().getSimpleName()); - errorMap.put("details", "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); - return errorMap; - } -} diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/ModelPlugin.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/ModelPlugin.kt deleted file mode 100644 index 8d8a356..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/ModelPlugin.kt +++ /dev/null @@ -1,26 +0,0 @@ -package pl.tkadziolka.snipmeandroid.bridge - -import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.BinaryMessenger -import org.koin.core.component.KoinComponent - -/* - flutter pub run pigeon \ - --input bridge/main_model.dart \ - --dart_out lib/model/main_model.dart \ - --java_out ../app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/Bridge.java \ - --java_package "pl.tkadziolka.snipmeandroid.bridge" - */ - -abstract class ModelPlugin: FlutterPlugin, KoinComponent { - - abstract fun onSetup(messenger: BinaryMessenger, bridge: T?) - - override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - onSetup(binding.binaryMessenger, this as T) - } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - onSetup(binding.binaryMessenger, null) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/detail/DetailModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/detail/DetailModel.kt deleted file mode 100644 index de9a827..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/detail/DetailModel.kt +++ /dev/null @@ -1,111 +0,0 @@ -package pl.tkadziolka.snipmeandroid.bridge.detail - -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import kotlinx.coroutines.flow.MutableStateFlow -import pl.tkadziolka.snipmeandroid.domain.clipboard.AddToClipboardUseCase -import pl.tkadziolka.snipmeandroid.domain.error.exception.* -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.reaction.GetTargetUserReactionUseCase -import pl.tkadziolka.snipmeandroid.domain.reaction.SetUserReactionUseCase -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippet.GetSingleSnippetUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.ui.detail.* -import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable -import pl.tkadziolka.snipmeandroid.ui.session.SessionViewModel -import timber.log.Timber - -class DetailModel( - private val errorMessages: ErrorMessages, - private val getSnippet: GetSingleSnippetUseCase, - private val clipboard: AddToClipboardUseCase, - private val getTargetReaction: GetTargetUserReactionUseCase, - private val setUserReaction: SetUserReactionUseCase, - private val session: SessionViewModel -) : ErrorParsable { - private val disposables = CompositeDisposable() - - private val mutableState = MutableStateFlow(Loading) - val state = mutableState - - private val mutableEvent = MutableStateFlow(Idle) - val event = mutableEvent - - override fun parseError(throwable: Throwable) { - when (throwable) { - is ConnectionException -> setState(Error(errorMessages.parse(throwable))) - is ContentNotFoundException -> setState(Error(errorMessages.parse(throwable))) - is ForbiddenActionException -> setState(Error(errorMessages.parse(throwable))) - is NetworkNotAvailableException -> setState(Error(errorMessages.parse(throwable))) - is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } - is RemoteException -> setState(Error(errorMessages.parse(throwable))) - is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } - else -> setState(Error(errorMessages.parse(throwable))) - } - } - - fun load(uuid: String) { - setState(Loading) - getSnippet(uuid) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { setState(Loaded(it)) }, - onError = { - Timber.e("Couldn't load snippets, error = $it") - parseError(it) - } - ).also { disposables += it } - } - - fun like() { - changeReaction(UserReaction.LIKE) - } - - fun dislike() { - changeReaction(UserReaction.DISLIKE) - } - - fun copyToClipboard() { - getSnippet()?.let { - clipboard(it.title, it.code.raw) - } - } - - private fun changeReaction(newReaction: UserReaction) { - // Immediately show change to user - val previousState = getLoaded() ?: return - val targetReaction = getTargetReaction(previousState.snippet, newReaction) - setState(previousState.run { copy(snippet = snippet.copy(userReaction = targetReaction)) }) - - setUserReaction(previousState.snippet, newReaction) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { snippet -> mutableState.value = Loaded(snippet) }, - onError = { - // Revert changes - Timber.e("Couldn't change user reaction, error = $it") - mutableEvent.value = Alert(errorMessages.generic) - setState(previousState) - } - ).also { disposables += it } - } - - private fun getSnippet(): Snippet? = getLoaded()?.snippet - - private fun getLoaded() = - if (state.value is Loaded) { - (state.value as Loaded) - } else { - null - } - - private fun setState(newState: DetailViewState?) { - newState?.let { mutableState.value = it } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/main/MainModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/main/MainModel.kt deleted file mode 100644 index 6ee6ddb..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/main/MainModel.kt +++ /dev/null @@ -1,149 +0,0 @@ -package pl.tkadziolka.snipmeandroid.bridge.main - -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import kotlinx.coroutines.flow.MutableStateFlow -import pl.tkadziolka.snipmeandroid.bridge.session.SessionModel -import pl.tkadziolka.snipmeandroid.domain.error.exception.* -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.snippet.ObserveUpdatedSnippetPageUseCase -import pl.tkadziolka.snipmeandroid.domain.snippet.ResetUpdatedSnippetPageUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.GetSnippetsUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.HasMoreSnippetPagesUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetScope -import pl.tkadziolka.snipmeandroid.domain.user.GetSingleUserUseCase -import pl.tkadziolka.snipmeandroid.domain.user.User -import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable -import pl.tkadziolka.snipmeandroid.ui.main.* -import pl.tkadziolka.snipmeandroid.util.view.SnippetFilter -import timber.log.Timber - -private const val ONE_PAGE = 1 - -class MainModel( - private val errorMessages: ErrorMessages, - private val getUser: GetSingleUserUseCase, - private val getSnippets: GetSnippetsUseCase, - private val observeUpdatedPage: ObserveUpdatedSnippetPageUseCase, - private val resetUpdatedPage: ResetUpdatedSnippetPageUseCase, - private val hasMore: HasMoreSnippetPagesUseCase, - private val session: SessionModel -) : ErrorParsable { - private val disposables = CompositeDisposable() - private var shouldRefresh = false - - private val mutableEvent = MutableStateFlow(Startup) - val event = mutableEvent - - private val mutableState = MutableStateFlow(Loading) - val state = mutableState - - override fun parseError(throwable: Throwable) { - when (throwable) { - is ConnectionException -> mutableState.value = Error(errorMessages.parse(throwable)) - is ContentNotFoundException -> mutableState.value = Error(errorMessages.parse(throwable)) - is ForbiddenActionException -> mutableState.value = Error(errorMessages.parse(throwable)) - is NetworkNotAvailableException -> mutableState.value = Error(errorMessages.parse(throwable)) - is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } - is RemoteException -> mutableState.value = Error(errorMessages.parse(throwable)) - is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } - else -> mutableState.value = Error(errorMessages.parse(throwable)) - } - } - - fun initState() { - getUser() - .subscribeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { user -> loadSnippets(user) }, - onError = { - Timber.e("Couldn't load user, error = $it") - parseError(it) - } - ).also { disposables += it } - } - - fun loadNextPage() { - getLoadedState()?.let { state -> - hasMore(state.scope, state.pages) - .subscribeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { hasMore -> - if (hasMore) loadSnippets(state.user, pages = state.pages + ONE_PAGE) - }, - onError = { - Timber.e("Couldn't check next page, error = $it") - mutableEvent.value = Alert(errorMessages.parse(it)) - }) - .also { disposables += it } - } - } - - fun filter(filter: SnippetFilter) { - val scope = filterToScope(filter) - getLoadedState()?.let { state -> - loadSnippets(state.user, pages = ONE_PAGE, scope = scope) - mutableEvent.value = ListRefreshed - } - } - - fun logOut() { - session.logOut { mutableEvent.value = Logout } - } - - fun refreshSnippetUpdates() { - getLoadedState()?.let { - observeUpdatedPage(getScope()) - .subscribeOn(Schedulers.io()) - .subscribeBy( - onNext = { updatedPage -> - shouldRefresh = true - loadSnippets(it.user, updatedPage, getScope()) - resetUpdatedPage() - }, - onError = { Timber.e("Couldn't refresh snippet updates, error = $it") } - ).also { disposables += it } - } - } - - private fun loadSnippets( - user: User, - pages: Int = 1, - scope: SnippetScope = SnippetScope.ALL - ) { - mutableState.value = (Loading) - getSnippets(scope, pages) - .subscribeOn(Schedulers.io()) - .subscribeBy( - onSuccess = { - mutableState.value = (Loaded(user, it, pages, scope)) - if (shouldRefresh) { - mutableEvent.value = ListRefreshed - shouldRefresh = false - } - }, - onError = { - Timber.e("Couldn't load snippets, error = $it") - parseError(it) - } - ).also { disposables += it } - } - - private fun getLoadedState(): Loaded? = state.value as? Loaded - - private fun filterToScope(filter: SnippetFilter) = - when (filter) { - SnippetFilter.ALL -> SnippetScope.ALL - SnippetFilter.MINE -> SnippetScope.OWNED - else -> SnippetScope.SHARED_FOR - } - - private fun getScope(): SnippetScope { - getLoadedState()?.let { - return it.scope - } - return SnippetScope.ALL - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/main/MainModelPlugin.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/main/MainModelPlugin.kt deleted file mode 100644 index bda4a14..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/main/MainModelPlugin.kt +++ /dev/null @@ -1,149 +0,0 @@ -package pl.tkadziolka.snipmeandroid.bridge.main - -import android.text.Spanned -import android.text.format.DateUtils -import android.text.style.ForegroundColorSpan -import androidx.core.text.getSpans -import io.flutter.plugin.common.BinaryMessenger -import org.koin.core.component.inject -import pl.tkadziolka.snipmeandroid.bridge.Bridge -import pl.tkadziolka.snipmeandroid.bridge.ModelPlugin -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippets.* -import pl.tkadziolka.snipmeandroid.ui.main.* -import pl.tkadziolka.snipmeandroid.util.view.SnippetFilter -import java.util.* - -class MainModelPlugin : ModelPlugin(), Bridge.MainModelBridge { - - private val model: MainModel by inject() - - override fun onSetup( - messenger: BinaryMessenger, - bridge: Bridge.MainModelBridge? - ) { - Bridge.MainModelBridge.setup(messenger, bridge) - } - - override fun getState(): Bridge.MainModelStateData = getData(model.state.value) - - override fun getEvent(): Bridge.MainModelEventData = getEvent(model.event.value) - - override fun initState() { - model.initState() - } - - override fun loadNextPage() { - model.loadNextPage() - } - - override fun filter(filter: Bridge.SnippetFilter) { - val type = (filter.type?.name ?: Bridge.SnippetFilterType.ALL.name).uppercase() - val snippetFilter = SnippetFilter.valueOf(type) - model.filter(snippetFilter) - } - - override fun logOut() { - model.logOut() - } - - override fun refreshSnippetUpdates() { - model.refreshSnippetUpdates() - } - - private fun getData(viewState: MainViewState) = Bridge.MainModelStateData().apply { - state = viewState.toModelState() - is_loading = viewState is Loading - data = (viewState as? Loaded)?.snippets?.toModelData() - } - - private fun getEvent(viewEvent: MainEvent) = Bridge.MainModelEventData().apply { - event = viewEvent.toModelEvent() - message = (viewEvent as? Alert)?.message - } - - private fun MainEvent.toModelEvent() = - when (this) { - is Alert -> Bridge.MainModelEvent.ALERT - is Logout -> Bridge.MainModelEvent.LOGOUT - else -> Bridge.MainModelEvent.NONE - } - - private fun MainViewState.toModelState() = - when (this) { - Loading -> Bridge.ModelState.LOADING - is Loaded -> Bridge.ModelState.LOADED - is Error -> Bridge.ModelState.ERROR - } - - private fun List.toModelData() = map { - Bridge.Snippet().apply { - uuid = it.uuid - title = it.title - code = it.code.toModelSnippetCode() - language = it.language.toModelSnippetLanguage() - owner = it.owner.toModelOwner() - isOwner = it.isOwner - voteResult = (it.numberOfLikes - it.numberOfDislikes).toLong() - userReaction = it.userReaction.toModelUserReaction() - isLiked = it.userReaction.toModelReactionState(UserReaction.LIKE) - isDisliked = it.userReaction.toModelReactionState(UserReaction.DISLIKE) - isSaved = calculateSavedState(it.isOwner, it.visibility) - timeAgo = DateUtils.getRelativeTimeSpanString( - it.modifiedAt.time, - Date().time, - DateUtils.SECOND_IN_MILLIS - ).toString() - } - } - - private fun Owner.toModelOwner() = - Bridge.Owner().let { - it.id = id.toLong() - it.login = login - it - } - - private fun SnippetCode.toModelSnippetCode() = - Bridge.SnippetCode().let { - it.raw = raw - it.tokens = highlighted.getSpans().map { span -> - span.toSyntaxToken(highlighted) - } - it - } - - private fun SnippetLanguage.toModelSnippetLanguage() = - Bridge.SnippetLanguage().let { - it.raw = raw - it.type = Bridge.SnippetLanguageType.valueOf(type.name) - it - } - - private fun ForegroundColorSpan.toSyntaxToken(spannable: Spanned) = - Bridge.SyntaxToken().let { - it.start = spannable.getSpanStart(this).toLong() - it.end = spannable.getSpanEnd(this).toLong() - it.color = foregroundColor.toLong() - it - } - - private fun UserReaction.toModelUserReaction(): Bridge.UserReaction = - when (this) { - UserReaction.LIKE -> Bridge.UserReaction.LIKE - UserReaction.DISLIKE -> Bridge.UserReaction.DISLIKE - else -> Bridge.UserReaction.NONE - } - - private fun UserReaction.toModelReactionState(reaction: UserReaction) = - if (this == UserReaction.NONE) null else this == reaction - - private fun calculateSavedState( - isOwner: Boolean, - visibility: SnippetVisibility - ): Boolean? = when { - isOwner && visibility == SnippetVisibility.PUBLIC -> false - isOwner && visibility == SnippetVisibility.PRIVATE -> true - else -> null - } -} diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/MapperFilterModule.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/di/MapperFilterModule.kt deleted file mode 100644 index a77b27e..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/MapperFilterModule.kt +++ /dev/null @@ -1,10 +0,0 @@ -package pl.tkadziolka.snipmeandroid.di - -import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.domain.language.AvailableLanguageFilter -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetResponseMapper - -internal val mapperFilterModule = module { - factory { SnippetResponseMapper() } - factory { AvailableLanguageFilter() } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/NavigatorModule.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/di/NavigatorModule.kt deleted file mode 100644 index 85c4fb7..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/NavigatorModule.kt +++ /dev/null @@ -1,22 +0,0 @@ -package pl.tkadziolka.snipmeandroid.di - -import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.ui.contact.ContactNavigator -import pl.tkadziolka.snipmeandroid.ui.detail.DetailNavigator -import pl.tkadziolka.snipmeandroid.ui.donate.DonateNavigator -import pl.tkadziolka.snipmeandroid.ui.edit.EditNavigator -import pl.tkadziolka.snipmeandroid.ui.login.LoginNavigator -import pl.tkadziolka.snipmeandroid.ui.main.MainNavigator -import pl.tkadziolka.snipmeandroid.ui.preview.PreviewNavigator -import pl.tkadziolka.snipmeandroid.ui.splash.SplashNavigator - -internal val navigatorModule = module { - factory { SplashNavigator() } - factory { LoginNavigator() } - factory { MainNavigator() } - factory { PreviewNavigator() } - factory { DetailNavigator() } - factory { EditNavigator() } - factory { ContactNavigator() } - factory { DonateNavigator() } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/RepositoryModule.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/di/RepositoryModule.kt deleted file mode 100644 index c4cea79..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/RepositoryModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package pl.tkadziolka.snipmeandroid.di - -import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepository -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepositoryReal -import pl.tkadziolka.snipmeandroid.domain.repository.language.LanguageRepository -import pl.tkadziolka.snipmeandroid.domain.repository.language.LanguageRepositoryReal -import pl.tkadziolka.snipmeandroid.domain.repository.networkstate.NetworkStateRepository -import pl.tkadziolka.snipmeandroid.domain.repository.networkstate.NetworkStateRepositoryReal -import pl.tkadziolka.snipmeandroid.domain.repository.share.ShareRepository -import pl.tkadziolka.snipmeandroid.domain.repository.share.ShareRepositoryReal -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepositoryReal -import pl.tkadziolka.snipmeandroid.domain.repository.user.UserRepository -import pl.tkadziolka.snipmeandroid.domain.repository.user.UserRepositoryReal - -internal val repositoryModule = module { - single { NetworkStateRepositoryReal() } - single { AuthRepositoryReal(get(), get(), get()) } - single { UserRepositoryReal(get(), get()) } - single { SnippetRepositoryReal(get(), get(), get()) } - single { LanguageRepositoryReal(get(), get(), get()) } - single { ShareRepositoryReal(get(), get()) } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/ServiceModule.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/di/ServiceModule.kt deleted file mode 100644 index 48e220b..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/ServiceModule.kt +++ /dev/null @@ -1,13 +0,0 @@ -package pl.tkadziolka.snipmeandroid.di - -import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.infrastructure.remote.* -import retrofit2.Retrofit - -internal val serviceModule = module { - single { get().create(AuthService::class.java) } - single { get().create(UserService::class.java) } - single { get().create(SnippetService::class.java) } - single { get().create(LanguageService::class.java) } - single { get().create(ShareService::class.java) } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/UseCaseModule.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/di/UseCaseModule.kt deleted file mode 100644 index b3d9f3e..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/UseCaseModule.kt +++ /dev/null @@ -1,57 +0,0 @@ -package pl.tkadziolka.snipmeandroid.di - -import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.domain.auth.* -import pl.tkadziolka.snipmeandroid.domain.clipboard.AddToClipboardUseCase -import pl.tkadziolka.snipmeandroid.domain.clipboard.GetFromClipboardUseCase -import pl.tkadziolka.snipmeandroid.domain.language.GetLanguagesUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.reaction.GetTargetUserReactionUseCase -import pl.tkadziolka.snipmeandroid.domain.reaction.SetUserReactionUseCase -import pl.tkadziolka.snipmeandroid.domain.share.ClearCachedShareUsersUseCase -import pl.tkadziolka.snipmeandroid.domain.share.ShareInteractor -import pl.tkadziolka.snipmeandroid.domain.share.ShareSnippetUseCase -import pl.tkadziolka.snipmeandroid.domain.snippet.* -import pl.tkadziolka.snipmeandroid.domain.snippets.GetSnippetsUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.HasMoreSnippetPagesUseCase -import pl.tkadziolka.snipmeandroid.domain.user.GetShareUsersUseCase -import pl.tkadziolka.snipmeandroid.domain.user.GetSingleUserUseCase - -internal val useCaseModule = module { - // Base - factory { CheckNetworkAvailableUseCase(get()) } - // Auth - factory { IdentifyUserUseCase(get(), get()) } - factory { InitialLoginUseCase(get()) } - factory { LoginUseCase(get(), get()) } - factory { RegisterUseCase(get(), get()) } - factory { LogoutUserUseCase(get()) } - factory { AuthorizationUseCase(get()) } - // User - factory { GetSingleUserUseCase(get(), get(), get()) } - // Snippet - factory { GetSnippetsUseCase(get(), get(), get()) } - factory { GetSingleSnippetUseCase(get(), get(), get()) } - factory { HasMoreSnippetPagesUseCase(get(), get(), get()) } - factory { CreateSnippetUseCase(get(), get(), get()) } - factory { UpdateSnippetUseCase(get(), get(), get()) } - factory { ObserveUpdatedSnippetPageUseCase(get()) } - factory { ResetUpdatedSnippetPageUseCase(get()) } - factory { GetTargetUserReactionUseCase() } - factory { SetUserReactionUseCase(get(), get(), get()) } - // Language - factory { GetLanguagesUseCase(get(), get(), get()) } - // Share - factory { GetShareUsersUseCase(get(), get(), get(), get()) } - factory { ClearCachedShareUsersUseCase(get()) } - factory { ShareSnippetUseCase(get(), get(), get(), get()) } - // Clipboard - factory { AddToClipboardUseCase(get()) } - factory { GetFromClipboardUseCase(get()) } -} - -internal val interactorModule = module { - factory { LoginInteractor(get(), get(), get()) } - factory { EditInteractor(get(), get(), get(), get(), get()) } - factory { ShareInteractor(get(), get(), get()) } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/UtilModule.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/di/UtilModule.kt deleted file mode 100644 index 20dfeff..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/UtilModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package pl.tkadziolka.snipmeandroid.di - -import android.content.ClipboardManager -import android.content.Context.CLIPBOARD_SERVICE -import org.koin.android.ext.koin.androidContext -import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.domain.error.DebugErrorHandler -import pl.tkadziolka.snipmeandroid.domain.error.SafeErrorHandler -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages -import pl.tkadziolka.snipmeandroid.domain.message.RealValidationMessages -import pl.tkadziolka.snipmeandroid.ui.Dialogs - -internal val utilModule = module { - factory { if (BuildConfig.DEBUG) DebugErrorHandler() else SafeErrorHandler() } - factory { ErrorMessages(get()) } - factory { RealValidationMessages(get()) } - factory { Dialogs(get()) } - factory { androidContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/ViewModelModule.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/di/ViewModelModule.kt deleted file mode 100644 index deb1ac9..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/di/ViewModelModule.kt +++ /dev/null @@ -1,39 +0,0 @@ -package pl.tkadziolka.snipmeandroid.di - -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module -import pl.tkadziolka.snipmeandroid.bridge.main.MainModel -import pl.tkadziolka.snipmeandroid.bridge.session.SessionModel -import pl.tkadziolka.snipmeandroid.ui.detail.DetailViewModel -import pl.tkadziolka.snipmeandroid.ui.donate.DonateViewModel -import pl.tkadziolka.snipmeandroid.ui.edit.EditViewModel -import pl.tkadziolka.snipmeandroid.ui.login.LoginViewModel -import pl.tkadziolka.snipmeandroid.ui.main.MainViewModel -import pl.tkadziolka.snipmeandroid.ui.preview.PreviewViewModel -import pl.tkadziolka.snipmeandroid.ui.session.SessionViewModel -import pl.tkadziolka.snipmeandroid.ui.share.ShareViewModel -import pl.tkadziolka.snipmeandroid.ui.splash.SplashViewModel - -internal val viewModelModule = module { - viewModel { SplashViewModel(get(), get()) } - viewModel { LoginViewModel(get(), get(), get(), get()) } - viewModel { SessionViewModel(get()) } - viewModel { MainViewModel(get(), get(), get(), get(), get(), get(), get(), get()) } - viewModel { PreviewViewModel(get(), get()) } - viewModel { DetailViewModel(get(), get(), get(), get(), get(), get(), get()) } - viewModel { EditViewModel(get(), get(), get(), get(), get(), get()) } - viewModel { ShareViewModel(get(), get(), get()) } - viewModel { DonateViewModel(get()) } -} - -internal val modelModule = module { -// viewModel { SplashViewModel(get(), get()) } -// viewModel { LoginViewModel(get(), get(), get(), get()) } - single { SessionModel(get()) } - single { MainModel(get(), get(), get(), get(), get(), get(), get()) } -// viewModel { PreviewViewModel(get(), get()) } -// viewModel { DetailViewModel(get(), get(), get(), get(), get(), get(), get()) } -// viewModel { EditViewModel(get(), get(), get(), get(), get(), get()) } -// viewModel { ShareViewModel(get(), get(), get()) } -// viewModel { DonateViewModel(get()) } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/IdentifyUserUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/IdentifyUserUseCase.kt deleted file mode 100644 index ac487c6..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/IdentifyUserUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.auth - -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepository - -class IdentifyUserUseCase( - private val auth: AuthRepository, - private val checkNetwork: CheckNetworkAvailableUseCase -) { - operator fun invoke(login: String) = checkNetwork() - .andThen(auth.identify(login)) -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LoginUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LoginUseCase.kt deleted file mode 100644 index ebf34f4..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/LoginUseCase.kt +++ /dev/null @@ -1,17 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.auth - -import io.reactivex.Completable -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepository - -class LoginUseCase( - private val auth: AuthRepository, - private val checkNetwork: CheckNetworkAvailableUseCase -) { - - operator fun invoke(login: String, password: String): Completable = checkNetwork() - .andThen( - auth.login(login, password) - .flatMapCompletable { token -> auth.saveToken(token) } - ) -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/RegisterUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/RegisterUseCase.kt deleted file mode 100644 index 0ce021d..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/auth/RegisterUseCase.kt +++ /dev/null @@ -1,18 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.auth - -import io.reactivex.Completable -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.auth.AuthRepository - -class RegisterUseCase( - private val auth: AuthRepository, - private val checkNetwork: CheckNetworkAvailableUseCase -) { - operator fun invoke(login: String, password: String, email: String): Completable = - checkNetwork().andThen(register(login, password, email)) - - private fun register(login: String, password: String, email: String) = - auth.register(login, password, email) - .flatMap { auth.login(login, password) } - .flatMapCompletable { token -> auth.saveToken(token) } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/ErrorHandler.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/ErrorHandler.kt deleted file mode 100644 index 592eddf..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/error/ErrorHandler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.error - -import pl.tkadziolka.snipmeandroid.domain.error.exception.SnipException - -interface ErrorHandler { - fun handle(throwable: Throwable): SnipException -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/GetLanguagesUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/GetLanguagesUseCase.kt deleted file mode 100644 index 4d56233..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/GetLanguagesUseCase.kt +++ /dev/null @@ -1,17 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.language - -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.language.LanguageRepository -import pl.tkadziolka.snipmeandroid.util.extension.mapItems - -class GetLanguagesUseCase( - private val auth: AuthorizationUseCase, - private val networkState: CheckNetworkAvailableUseCase, - private val language: LanguageRepository -) { - operator fun invoke() = - auth() - .andThen(networkState()) - .andThen(language.languages().mapItems { it.name }) -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/SnippetLanguage.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/SnippetLanguage.kt deleted file mode 100644 index 272db5c..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/language/SnippetLanguage.kt +++ /dev/null @@ -1,7 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.language - -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.LanguageResponse - -data class SnippetLanguage(val name: String) - -fun LanguageResponse.toLanguage(): SnippetLanguage = SnippetLanguage(name) \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/payment/PaymentOption.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/payment/PaymentOption.kt deleted file mode 100644 index 25eac0c..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/payment/PaymentOption.kt +++ /dev/null @@ -1,7 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.payment - -enum class PaymentOption { - GOOGLE_PAY, - PAYPAL, - CREDIT_CARD -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/SetUserReactionUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/SetUserReactionUseCase.kt deleted file mode 100644 index 207c7a6..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/SetUserReactionUseCase.kt +++ /dev/null @@ -1,20 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.reaction - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet - -class SetUserReactionUseCase( - private val auth: AuthorizationUseCase, - private val repository: SnippetRepository, - private val getTargetReaction: GetTargetUserReactionUseCase -) { - operator fun invoke(snippet: Snippet, reaction: UserReaction): Single { - val targetReaction = getTargetReaction(snippet, reaction) - return auth() - .andThen(repository.reaction(snippet.uuid, targetReaction)) - .andThen(repository.snippet(snippet.uuid)) - .doOnSuccess { repository.updateListener.onNext(it) } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/UserReaction.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/UserReaction.kt deleted file mode 100644 index c426f59..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/reaction/UserReaction.kt +++ /dev/null @@ -1,7 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.reaction - -enum class UserReaction { - NONE, - LIKE, - DISLIKE -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/language/LanguageRepository.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/language/LanguageRepository.kt deleted file mode 100644 index 9dcfb12..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/language/LanguageRepository.kt +++ /dev/null @@ -1,9 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.language - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.language.SnippetLanguage - -interface LanguageRepository { - - fun languages(): Single> -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/language/LanguageRepositoryReal.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/language/LanguageRepositoryReal.kt deleted file mode 100644 index 0d5ea7a..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/language/LanguageRepositoryReal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.language - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.error.ErrorHandler -import pl.tkadziolka.snipmeandroid.domain.language.AvailableLanguageFilter -import pl.tkadziolka.snipmeandroid.domain.language.SnippetLanguage -import pl.tkadziolka.snipmeandroid.domain.language.toLanguage -import pl.tkadziolka.snipmeandroid.infrastructure.remote.LanguageService -import pl.tkadziolka.snipmeandroid.util.extension.filterItems -import pl.tkadziolka.snipmeandroid.util.extension.mapError -import pl.tkadziolka.snipmeandroid.util.extension.mapItems - -class LanguageRepositoryReal( - private val errorHandler: ErrorHandler, - private val service: LanguageService, - private val filterAvailable: AvailableLanguageFilter -): LanguageRepository { - - override fun languages(): Single> = - service.languages() - .mapError { errorHandler.handle(it) } - .filterItems { filterAvailable(it.name) } - .mapItems { it.toLanguage() } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/share/ShareRepository.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/share/ShareRepository.kt deleted file mode 100644 index f7827a4..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/share/ShareRepository.kt +++ /dev/null @@ -1,14 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.share - -import io.reactivex.Completable -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.share.ShareUser - -interface ShareRepository { - - fun share(uuid: String, userId: Int): Completable - - fun shareUsers(snippetUuid: String): Single> - - fun clearCachedUsers() -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/share/ShareRepositoryReal.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/share/ShareRepositoryReal.kt deleted file mode 100644 index 6023fe3..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/share/ShareRepositoryReal.kt +++ /dev/null @@ -1,39 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.share - -import io.reactivex.Completable -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.error.ErrorHandler -import pl.tkadziolka.snipmeandroid.domain.share.ShareUser -import pl.tkadziolka.snipmeandroid.domain.share.toShareUser -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.ShareSnippetRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.SharePersonResponse -import pl.tkadziolka.snipmeandroid.infrastructure.remote.ShareService -import pl.tkadziolka.snipmeandroid.util.extension.mapError -import pl.tkadziolka.snipmeandroid.util.extension.mapItems - -class ShareRepositoryReal( - private val errorHandler: ErrorHandler, - private val service: ShareService -) : ShareRepository { - private var cachedPersonList: List? = null - - override fun share(uuid: String, userId: Int): Completable = - service.share(ShareSnippetRequest(snippet = uuid, allowed_user = userId)) - .mapError { errorHandler.handle(it) } - - override fun shareUsers(snippetUuid: String): Single> = - getPersonList(snippetUuid) - .mapError { errorHandler.handle(it) } - .mapItems { it.toShareUser() } - - private fun getPersonList(snippetUuid: String): Single> = - if (cachedPersonList != null) { - Single.just(cachedPersonList) - } else { - service.shareUsers(snippetUuid).map { cachedPersonList = it; it } - } - - override fun clearCachedUsers() { - cachedPersonList = null - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepository.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepository.kt deleted file mode 100644 index 459db3f..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.snippet - -import io.reactivex.Completable -import io.reactivex.Single -import io.reactivex.subjects.BehaviorSubject -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetScope - -interface SnippetRepository { - - val updateListener: BehaviorSubject - - fun snippets(scope: SnippetScope, page: Int): Single> - - fun snippet(id: String): Single - - fun create(title: String, code: String, language: String): Single - - fun update(uuid: String, title: String, code: String, language: String): Single - - fun count(scope: SnippetScope): Single - - fun reaction(uuid: String, reaction: UserReaction): Completable -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepositoryReal.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepositoryReal.kt deleted file mode 100644 index 1b89c9f..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepositoryReal.kt +++ /dev/null @@ -1,68 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.snippet - -import io.reactivex.Single -import io.reactivex.subjects.BehaviorSubject -import pl.tkadziolka.snipmeandroid.domain.error.ErrorHandler -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetResponseMapper -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetScope -import pl.tkadziolka.snipmeandroid.domain.snippets.value -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.CreateSnippetRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.RateSnippetRequest -import pl.tkadziolka.snipmeandroid.infrastructure.remote.SnippetService -import pl.tkadziolka.snipmeandroid.util.extension.mapError -import pl.tkadziolka.snipmeandroid.util.extension.mapItems - -const val PAGE_START = 0 -const val SNIPPET_PAGE_SIZE = 10 -const val ONE_SNIPPET = 1 - -class SnippetRepositoryReal( - private val errorHandler: ErrorHandler, - private val service: SnippetService, - private val mapper: SnippetResponseMapper -) : SnippetRepository { - private var count: Int? = null - - override val updateListener = BehaviorSubject.create() - - override fun snippets(scope: SnippetScope, page: Int): Single> = - service.snippets(scope.value(), PAGE_START, SNIPPET_PAGE_SIZE * page) - .mapError { errorHandler.handle(it) } - .map { count = it.count; it.results } - .mapItems { mapper(it) } - - override fun snippet(id: String): Single = - service.snippet(id).map { mapper(it) } - .mapError { errorHandler.handle(it) } - - override fun create( - title: String, - code: String, - language: String - ): Single = service.create(CreateSnippetRequest(title, code, language)) - .mapError { errorHandler.handle(it) } - .map { mapper(it) } - - override fun update( - uuid: String, - title: String, - code: String, - language: String - ): Single = service.update(uuid, CreateSnippetRequest(title, code, language)) - .mapError { errorHandler.handle(it) } - .map { mapper(it) } - - override fun count(scope: SnippetScope) = - if (count != null) { - Single.just(count!!) - } else { - service.snippets(scope.value(), PAGE_START, ONE_SNIPPET).map { it.count } - .mapError { errorHandler.handle(it) } - } - - override fun reaction(uuid: String, reaction: UserReaction) = - service.rate(RateSnippetRequest(uuid, reaction.name)) - .mapError { errorHandler.handle(it) } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepositoryTest.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepositoryTest.kt deleted file mode 100644 index b4cf489..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/snippet/SnippetRepositoryTest.kt +++ /dev/null @@ -1,161 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.snippet - -import android.text.SpannableString -import io.reactivex.Completable -import io.reactivex.Single -import io.reactivex.subjects.BehaviorSubject -import pl.tkadziolka.snipmeandroid.domain.error.ErrorHandler -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippets.* -import pl.tkadziolka.snipmeandroid.util.SyntaxHighlighter.getHighlighted -import pl.tkadziolka.snipmeandroid.util.extension.lines -import pl.tkadziolka.snipmeandroid.util.extension.newLineChar -import java.util.* - -private const val PREVIEW_COUNT = 5 - -class SnippetRepositoryTest(private val errorHandler: ErrorHandler) : SnippetRepository { - - private val uuid - get() = UUID.randomUUID().toString() - - private val list by lazy { - List(25) { - Snippet( - uuid = uuid, - title = "Snippet $it", - code = getMockCode(code), - language = getMockLanguage(), - visibility = SnippetVisibility.PUBLIC, - isOwner = true, - owner = Owner(it, "User $it"), - modifiedAt = Date(), - numberOfLikes = 5, - numberOfDislikes = 3, - userReaction = UserReaction.LIKE - ) - } - } - override val updateListener: BehaviorSubject = BehaviorSubject.create() - - override fun snippets(scope: SnippetScope, page: Int): Single> { - return Single.just(list) - .map { it.take(SNIPPET_PAGE_SIZE * page) } - .onErrorResumeNext { throwable -> Single.error(errorHandler.handle(throwable)) } - } - - override fun snippet(id: String): Single = Single.just(list.find { it.uuid == id }) - - override fun create( - title: String, - code: String, - language: String - ): Single = Single.just( - Snippet( - uuid = uuid, - title = title, - code = getMockCode(code), - language = getMockLanguage(), - visibility = SnippetVisibility.PUBLIC, - isOwner = true, - owner = Owner(0, "login"), - modifiedAt = Date(), - numberOfLikes = 0, - numberOfDislikes = 0, - userReaction = UserReaction.NONE - ) - ) - - override fun update( - uuid: String, - title: String, - code: String, - language: String - ): Single = Single.just( - Snippet( - uuid = uuid, - title = title, - code = getMockCode(code), - language = getMockLanguage(), - visibility = SnippetVisibility.PUBLIC, - isOwner = true, - owner = Owner(0, "login"), - modifiedAt = Date(), - numberOfLikes = 5, - numberOfDislikes = 3, - userReaction = UserReaction.LIKE - ) - ) - - override fun count(scope: SnippetScope): Single = Single.just(list.size) - - override fun reaction(uuid: String, reaction: UserReaction): Completable = Completable.complete() - - private fun getPreview(code: String): SpannableString { - val preview = code.lines(PREVIEW_COUNT).joinToString(separator = newLineChar) - return getHighlighted(preview) - } - - private fun getMockCode(code: String) = SnippetCode(raw = code, highlighted = getPreview(code)) - - private fun getMockLanguage() = SnippetLanguage(raw = "Java", type = SnippetLanguageType.JAVA) - - private val code = - """ - /* Block comment */ - import java.util.Date; - import static AnInterface.CONSTANT; - import static java.util.Date.parse; - import static SomeClass.staticField; - /** - * Doc comment here for SomeClass - * @param T type parameter - * @see Math#sin(double) - */ - @Annotation (name=value) - public class SomeClass { // some comment - private T field = null; - private double unusedField = 12345.67890; - private UnknownType anotherString = "Another\nStrin\g"; - public static int staticField = 0; - public final int instanceFinalField = 0; - - /** - * Semantic highlighting: - * Generated spectrum to pick colors for local variables and parameters: - * Color#1 SC1.1 SC1.2 SC1.3 SC1.4 Color#2 SC2.1 SC2.2 SC2.3 SC2.4 Color#3 - * Color#3 SC3.1 SC3.2 SC3.3 SC3.4 Color#4 SC4.1 SC4.2 SC4.3 SC4.4 Color#5 - * @param param1 - * @param reassignedParam - * @param param2 - * @param param3 - */ - public SomeClass(AnInterface param1, int[] reassignedParam, - int param2 - int param3) { - int reassignedValue = this.staticField + param2 + param3; - long localVar1, localVar2, localVar3, localVar4; - int localVar = "IntelliJ"; // Error, incompatible types - System.out.println(anotherString + toString() + localVar); - long time = parse("1.2.3"); // Method is deprecated - new Thread().countStackFrames(); // Method is deprecated and marked for removal - reassignedValue ++; - field.run(); - new SomeClass() { - { - int a = localVar; - } - }; - reassignedParam = new ArrayList().toArray(new int[CONSTANT]); - } - } - enum AnEnum { CONST1, CONST2 } - interface AnInterface { - int CONSTANT = 2; - void method(); - } - abstract class SomeAbstractClass { - protected int instanceField = staticField; - } - """.trimIndent() -} diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/user/UserRepository.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/user/UserRepository.kt deleted file mode 100644 index f7226f7..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/user/UserRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.user - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.user.User - -interface UserRepository { - fun user(): Single -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/user/UserRepositoryReal.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/user/UserRepositoryReal.kt deleted file mode 100644 index c27336e..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/repository/user/UserRepositoryReal.kt +++ /dev/null @@ -1,18 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.repository.user - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.error.ErrorHandler -import pl.tkadziolka.snipmeandroid.domain.user.User -import pl.tkadziolka.snipmeandroid.domain.user.toUser -import pl.tkadziolka.snipmeandroid.infrastructure.remote.UserService -import pl.tkadziolka.snipmeandroid.util.extension.mapError - -class UserRepositoryReal( - private val errorHandler: ErrorHandler, - private val service: UserService -) : UserRepository { - - override fun user(): Single = service.user() - .mapError { errorHandler.handle(it) } - .map { it.toUser() } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ClearCachedShareUsersUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ClearCachedShareUsersUseCase.kt deleted file mode 100644 index 0ed8ac2..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ClearCachedShareUsersUseCase.kt +++ /dev/null @@ -1,9 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.share - -import pl.tkadziolka.snipmeandroid.domain.repository.share.ShareRepository - -class ClearCachedShareUsersUseCase(private val shareRepository: ShareRepository) { - operator fun invoke() { - shareRepository.clearCachedUsers() - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareInteractor.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareInteractor.kt deleted file mode 100644 index 06ae5f8..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareInteractor.kt +++ /dev/null @@ -1,16 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.share - -import pl.tkadziolka.snipmeandroid.domain.user.GetShareUsersUseCase - -class ShareInteractor( - private val getUsers: GetShareUsersUseCase, - private val shareSnippet: ShareSnippetUseCase, - private val clearCachedShareUsers: ClearCachedShareUsersUseCase -) { - - fun users(loginPhrase: String, snippetUuid: String) = getUsers(loginPhrase, snippetUuid) - - fun share(snippetUuid: String, userId: Int) = shareSnippet(snippetUuid, userId) - - fun clearCachedUsers() = clearCachedShareUsers() -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareSnippetUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareSnippetUseCase.kt deleted file mode 100644 index 94fd041..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/ShareSnippetUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.share - -import io.reactivex.Completable -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.share.ShareRepository - -class ShareSnippetUseCase( - private val auth: AuthorizationUseCase, - private val repository: ShareRepository, - private val networkAvailable: CheckNetworkAvailableUseCase, - private val clearCachedUsers: ClearCachedShareUsersUseCase -) { - operator fun invoke(snippetUuid: String, userId: Int): Completable = - auth() - .andThen(networkAvailable()) - .andThen(repository.share(snippetUuid, userId)) - .andThen(Completable.fromCallable { clearCachedUsers() }) -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/SnippetAccess.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/SnippetAccess.kt deleted file mode 100644 index 6f6d718..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/share/SnippetAccess.kt +++ /dev/null @@ -1,5 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.share - -enum class SnippetAccess(name: String) { - VIEWER("VIEWER") -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/CreateSnippetUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/CreateSnippetUseCase.kt deleted file mode 100644 index ade33ed..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/CreateSnippetUseCase.kt +++ /dev/null @@ -1,22 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.snippet - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet - -class CreateSnippetUseCase( - private val auth: AuthorizationUseCase, - private val networkAvailable: CheckNetworkAvailableUseCase, - private val snippetRepository: SnippetRepository -) { - operator fun invoke(title: String, code: String, language: String): Single = - auth() - .andThen(networkAvailable()) - .andThen(snippetRepository.create(title, code, language)) - .flatMap { - snippetRepository.updateListener.onNext(it) - Single.just(it) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/GetSingleSnippetUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/GetSingleSnippetUseCase.kt deleted file mode 100644 index 31c8390..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/GetSingleSnippetUseCase.kt +++ /dev/null @@ -1,20 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.snippet - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet - -class GetSingleSnippetUseCase( - private val auth: AuthorizationUseCase, - private val networkAvailable: CheckNetworkAvailableUseCase, - private val repository: SnippetRepository -) { - - operator fun invoke(uuid: String): Single = - auth() - .andThen(networkAvailable()) - .andThen(repository.snippet(uuid)) - -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/ResetUpdatedSnippetPageUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/ResetUpdatedSnippetPageUseCase.kt deleted file mode 100644 index a2dd8ea..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/ResetUpdatedSnippetPageUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.snippet - -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet - -class ResetUpdatedSnippetPageUseCase(private val repository: SnippetRepository) { - operator fun invoke() { - repository.updateListener.onNext(Snippet.EMPTY) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/UpdateSnippetUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/UpdateSnippetUseCase.kt deleted file mode 100644 index e2e9da3..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippet/UpdateSnippetUseCase.kt +++ /dev/null @@ -1,21 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.snippet - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository - -class UpdateSnippetUseCase( - private val auth: AuthorizationUseCase, - private val networkAvailable: CheckNetworkAvailableUseCase, - private val repository: SnippetRepository -) { - operator fun invoke(uuid: String, title: String, code: String, language: String) = - auth() - .andThen(networkAvailable()) - .andThen(repository.update(uuid, title, code, language)) - .flatMap { - repository.updateListener.onNext(it) - Single.just(it) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/GetSnippetsUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/GetSnippetsUseCase.kt deleted file mode 100644 index d6e3333..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/GetSnippetsUseCase.kt +++ /dev/null @@ -1,20 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SnippetRepository - -class GetSnippetsUseCase( - private val auth: AuthorizationUseCase, - private val repository: SnippetRepository, - private val networkAvailable: CheckNetworkAvailableUseCase -) { - operator fun invoke(scope: SnippetScope, page: Int): Single> = - auth() - .andThen(networkAvailable()) - .andThen( - repository.snippets(scope, page) - .map { list -> list.sortedByDescending { it.modifiedAt.time } } - ) -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetResponseMapper.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetResponseMapper.kt deleted file mode 100644 index 1077ef4..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/snippets/SnippetResponseMapper.kt +++ /dev/null @@ -1,60 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.snippets - -import android.text.SpannableString -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.SnippetResponse -import pl.tkadziolka.snipmeandroid.util.SyntaxHighlighter.getHighlighted -import pl.tkadziolka.snipmeandroid.util.extension.lines -import pl.tkadziolka.snipmeandroid.util.extension.newLineChar -import pl.tkadziolka.snipmeandroid.util.extension.toDate -import pl.tkadziolka.snipmeandroid.util.extension.toSnippetLanguage -import java.util.* - -const val PREVIEW_COUNT = 5 - -class SnippetResponseMapper { - - operator fun invoke(response: SnippetResponse) = with(response) { - return@with Snippet( - uuid = id, - title = title.orEmpty(), - code = getCode(this), - language = getLanguage(language), - visibility = getVisibility(visibility), - isOwner = is_owner ?: false, - owner = Owner(owner?.id ?: 0, owner?.username ?: ""), - modifiedAt = modified_at?.toDate() ?: Date(), - numberOfLikes = number_of_likes ?: 0, - numberOfDislikes = number_of_dislikes ?: 0, - userReaction = getUserReaction(user_reaction) - ) - } - - private fun getUserReaction(value: String?) = - when { - value.equals("like", ignoreCase = true) -> UserReaction.LIKE - value.equals("dislike", ignoreCase = true) -> UserReaction.DISLIKE - else -> UserReaction.NONE - } - - private fun getCode(response: SnippetResponse) = SnippetCode( - raw = response.code.orEmpty(), - highlighted = getPreview(response.code.orEmpty()) - ) - - private fun getLanguage(language: String?) = SnippetLanguage( - raw = language.orEmpty(), - type = language.toSnippetLanguage() - ) - - private fun getPreview(code: String): SpannableString { - val preview = code.lines(PREVIEW_COUNT).joinToString(separator = newLineChar) - return getHighlighted(preview) - } - - private fun getVisibility(visibility: String?) = - if (visibility != null) - SnippetVisibility.valueOf(visibility) - else - SnippetVisibility.PRIVATE -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/GetShareUsersUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/GetShareUsersUseCase.kt deleted file mode 100644 index aaba52f..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/GetShareUsersUseCase.kt +++ /dev/null @@ -1,28 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.user - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.share.ShareRepository -import pl.tkadziolka.snipmeandroid.domain.repository.user.UserRepository -import pl.tkadziolka.snipmeandroid.domain.share.ShareUser -import pl.tkadziolka.snipmeandroid.util.extension.filterItems - -class GetShareUsersUseCase( - private val auth: AuthorizationUseCase, - private val networkAvailable: CheckNetworkAvailableUseCase, - private val shareRepository: ShareRepository, - private val userRepository: UserRepository -) { - - operator fun invoke(loginPhrase: String, snippetUuid: String) = - auth() - .andThen(networkAvailable()) - .andThen(shareRepository.shareUsers(snippetUuid)) - .flatMap { users -> filterCurrentUser(users) } - .filterItems { it.user.login.startsWith(loginPhrase, ignoreCase = true) } - - private fun filterCurrentUser(users: List): Single> = - userRepository.user() - .map { user -> users.filterNot { it.user.id == user.id } } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/GetSingleUserUseCase.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/GetSingleUserUseCase.kt deleted file mode 100644 index 4fab18b..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/user/GetSingleUserUseCase.kt +++ /dev/null @@ -1,18 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.user - -import pl.tkadziolka.snipmeandroid.domain.auth.AuthorizationUseCase -import pl.tkadziolka.snipmeandroid.domain.network.CheckNetworkAvailableUseCase -import pl.tkadziolka.snipmeandroid.domain.repository.user.UserRepository - -class GetSingleUserUseCase( - private val auth: AuthorizationUseCase, - private val networkAvailable: CheckNetworkAvailableUseCase, - private val repository: UserRepository -) { - - operator fun invoke() = - auth() - .andThen(networkAvailable()) - .andThen(repository.user()) - -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/BlankValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/BlankValidator.kt deleted file mode 100644 index 94f7420..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/BlankValidator.kt +++ /dev/null @@ -1,8 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -class BlankValidator(messages: ValidationMessages): MultiValidator(messages) { - override fun validate(vararg phrases: String): ValidationResult = - if (phrases.any { it.isBlank() }) Invalid(messages.phrasesBlank) else Valid -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/ContainsSymbolValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/ContainsSymbolValidator.kt deleted file mode 100644 index 7135345..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/ContainsSymbolValidator.kt +++ /dev/null @@ -1,21 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -class ContainsSymbolValidator( - override val messages: ValidationMessages, - private val all: Boolean = true, - vararg symbols: Char -) : Validator(messages) { - private var _symbols: CharArray = symbols - - override fun validate(phrase: String): ValidationResult { - val success = if (all) { - _symbols.all { phrase.contains(it) } - } else { - _symbols.any { phrase.contains(it) } - } - - return if (success) Valid else Invalid("") - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/EmailValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/EmailValidator.kt deleted file mode 100644 index fc5c52a..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/EmailValidator.kt +++ /dev/null @@ -1,26 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -class EmailValidator(messages: ValidationMessages): Validator(messages) { - private val lengthValidator = LengthValidator(messages, min = MIN_LENGTH) - private val structureValidator = ContainsSymbolValidator( - messages = messages, - symbols = *SYMBOLS.toCharArray() - ) - - override fun validate(phrase: String): ValidationResult { - lengthValidator.validate(phrase).messageOrNull() - ?.let { return Invalid(messages.emailTooShort) } - - structureValidator.validate(phrase).messageOrNull() - ?.let { return Invalid(messages.emailWrongStructure) } - - return Valid - } - - companion object { - const val MIN_LENGTH = 5 - private val SYMBOLS = arrayOf('@', '.') - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/EqualsValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/EqualsValidator.kt deleted file mode 100644 index 38b4a55..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/EqualsValidator.kt +++ /dev/null @@ -1,12 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -class EqualsValidator(messages: ValidationMessages) : MultiValidator(messages) { - override fun validate(vararg phrases: String): ValidationResult = - if (phrases.all { it == phrases.first() }) { - Valid - } else { - Invalid(messages.phrasesNotEqual) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/FieldValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/FieldValidator.kt deleted file mode 100644 index 1da808a..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/FieldValidator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -class FieldValidator( - messages: ValidationMessages -) : LengthValidator(messages, min = MIN_LENGTH) { - companion object { - const val MIN_LENGTH = 1 - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/LengthValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/LengthValidator.kt deleted file mode 100644 index 598db65..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/LengthValidator.kt +++ /dev/null @@ -1,16 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -open class LengthValidator( - override val messages: ValidationMessages, - private val min: Int = 1, - private val max: Int = Int.MAX_VALUE -): Validator(messages) { - override fun validate(phrase: String): ValidationResult = - when { - phrase.length < min -> Invalid(messages.phraseTooShort) - phrase.length > max -> Invalid(messages.phraseTooLong) - else -> Valid - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/NoDiacriticsValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/NoDiacriticsValidator.kt deleted file mode 100644 index 785d076..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/NoDiacriticsValidator.kt +++ /dev/null @@ -1,18 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import android.icu.text.Normalizer2 -import android.os.Build -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages -import java.text.Normalizer - -class NoDiacriticsValidator(messages: ValidationMessages) : MultiValidator(messages) { - override fun validate(vararg phrases: String): ValidationResult { - val correct = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - phrases.all { Normalizer2.getNFDInstance().isNormalized(it) } - } else { - phrases.all { Normalizer.isNormalized(it, Normalizer.Form.NFD) } - } - - return if (correct) Valid else Invalid(messages.phraseHasDiacritics) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/PasswordValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/PasswordValidator.kt deleted file mode 100644 index f3df405..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/PasswordValidator.kt +++ /dev/null @@ -1,40 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -class PasswordValidator(messages: ValidationMessages) : Validator(messages) { - private val lengthValidator = LengthValidator(messages, min = MIN_LENGTH) - private val variedCaseValidator = VariedCaseValidator(messages) - private val specialCharValidator = ContainsSymbolValidator( - messages = messages, - all = false, - symbols = *SPECIAL_CHARS.toCharArray() - ) - private val containsDigitValidator = ContainsSymbolValidator( - messages = messages, - all = false, - symbols = *DIGITS.toCharArray() - ) - - override fun validate(phrase: String): ValidationResult { - lengthValidator.validate(phrase).messageOrNull() - ?.let { return Invalid(messages.passwordTooShort) } - - variedCaseValidator.validate(phrase).messageOrNull() - ?.let { return Invalid(messages.passwordNoUpperLower) } - - specialCharValidator.validate(phrase).messageOrNull() - ?.let { return Invalid(messages.passwordNoSpecialChar) } - - containsDigitValidator.validate(phrase).messageOrNull() - ?.let { return Invalid(messages.passwordNoDigit) } - - return Valid - } - - companion object { - const val MIN_LENGTH = 8 - const val SPECIAL_CHARS = "!@#$%^&*()_+=-{}:;<>/`" - const val DIGITS = "1234567890" - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/SignInValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/SignInValidator.kt deleted file mode 100644 index 1156a66..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/SignInValidator.kt +++ /dev/null @@ -1,24 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -// TODO: 20/02/2021 Create unit test -class SignInValidator(private val messages: ValidationMessages) { - - fun validate(login: String, password: String): ValidationResult { - - BlankValidator(messages).validate(login, password).messageOrNull()?.let { message -> - return Invalid(message) - } - - FieldValidator(messages).validate(login).messageOrNull()?.let { message -> - return Invalid(message) - } - - PasswordValidator(messages).validate(password).messageOrNull()?.let { message -> - return Invalid(message) - } - - return Valid - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/SignUpValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/SignUpValidator.kt deleted file mode 100644 index ec49b5f..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/SignUpValidator.kt +++ /dev/null @@ -1,31 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -// TODO: 20/02/2021 Create unit test -class SignUpValidator(private val messages: ValidationMessages) { - fun validate( - login: String, - password: String, - repeatedPassword: String, - email: String - ): ValidationResult { - - BlankValidator(messages).validate(login, password, repeatedPassword, email).messageOrNull() - ?.let { message -> return Invalid(message) } - - NoDiacriticsValidator(messages).validate(login, password, email).messageOrNull() - ?.let { message -> return Invalid(message) } - - SignInValidator(messages).validate(login, password).messageOrNull() - ?.let { message -> return Invalid(message) } - - EqualsValidator(messages).validate(password, repeatedPassword).messageOrNull() - ?.let { return Invalid(messages.passwordNotEqual) } - - EmailValidator(messages).validate(email).messageOrNull() - ?.let { message -> return Invalid(message) } - - return Valid - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/Validator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/Validator.kt deleted file mode 100644 index 4e77b91..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/Validator.kt +++ /dev/null @@ -1,20 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -abstract class Validator(open val messages: ValidationMessages) { - abstract fun validate(phrase: String): ValidationResult -} - -abstract class MultiValidator(open val messages: ValidationMessages) { - abstract fun validate(vararg phrases: String): ValidationResult -} - -fun ValidationResult.messageOrNull(): String? = when(this) { - is Invalid -> this.message - else -> null -} - -sealed class ValidationResult -object Valid: ValidationResult() -data class Invalid(val message: String): ValidationResult() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/VariedCaseValidator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/VariedCaseValidator.kt deleted file mode 100644 index f440f59..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/domain/validation/VariedCaseValidator.kt +++ /dev/null @@ -1,13 +0,0 @@ -package pl.tkadziolka.snipmeandroid.domain.validation - -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages - -class VariedCaseValidator(override val messages: ValidationMessages) : Validator(messages) { - override fun validate(phrase: String): ValidationResult { - val hasLowerCase = phrase.any { it.isLowerCase() } - val hasUpperCase = phrase.any { it.isUpperCase() } - - if (hasLowerCase && hasUpperCase) return Valid - return Invalid(messages.phraseNoUpperLower) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/AuthService.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/AuthService.kt deleted file mode 100644 index edfee79..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/infrastructure/remote/AuthService.kt +++ /dev/null @@ -1,21 +0,0 @@ -package pl.tkadziolka.snipmeandroid.infrastructure.remote - -import io.reactivex.Single -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.IdentifyUserRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.LoginUserRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.request.RegisterUserRequest -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.RegisterUserResponse -import pl.tkadziolka.snipmeandroid.infrastructure.model.response.TokenResponse -import retrofit2.http.* - -interface AuthService { - - @POST("check-if-user-exist/") - fun identify(@Body request: IdentifyUserRequest): Single - - @POST("auth-token/") - fun login(@Body request: LoginUserRequest): Single - - @POST("register/") - fun register(@Body request: RegisterUserRequest): Single -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/Dialogs.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/Dialogs.kt deleted file mode 100644 index 7fe458f..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/Dialogs.kt +++ /dev/null @@ -1,34 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui - -import android.content.Context -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import pl.tkadziolka.snipmeandroid.R - -class Dialogs(private val context: Context) { - val loading by lazy { - MaterialAlertDialogBuilder(context) - .setTitle(R.string.loading) - .setView(R.layout.view_loading) - .setCancelable(false) - } - - val ok by lazy { - MaterialAlertDialogBuilder(context) - .setPositiveButton(R.string.okay) { dialog, _ -> dialog.cancel() } - .setCancelable(true) - } - - val alreadyRegistered by lazy { - MaterialAlertDialogBuilder(context) - .setTitle(R.string.no_account_title) - .setMessage(R.string.no_account_message) - .setCancelable(false) - } - - val quit by lazy { - MaterialAlertDialogBuilder(context) - .setTitle(R.string.quit_title) - .setMessage(R.string.quit_message) - .setCancelable(true) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/contact/ContactFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/contact/ContactFragment.kt deleted file mode 100644 index 8bb11ff..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/contact/ContactFragment.kt +++ /dev/null @@ -1,33 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.contact - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.ui.setupWithNavController -import kotlinx.android.synthetic.main.fragment_contact.* -import org.koin.android.ext.android.inject -import pl.tkadziolka.snipmeandroid.R - -class ContactFragment: Fragment() { - - private val navigator by inject() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View = inflater.inflate(R.layout.fragment_contact, container, false) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - contactToolbar.setupWithNavController(findNavController()) - - contactFacebookAction.setOnClickListener { navigator.goToFacebook(requireContext()) } - contactInstagramAction.setOnClickListener { navigator.goToInstagram(requireContext()) } - contactGithubAction.setOnClickListener { navigator.goToGithub(requireContext()) } - contactTwitterAction.setOnClickListener { navigator.goToTwitter(requireContext()) } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/contact/ContactNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/contact/ContactNavigator.kt deleted file mode 100644 index 17635c3..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/contact/ContactNavigator.kt +++ /dev/null @@ -1,24 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.contact - -import android.content.Context -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.util.extension.safeOpenWebsite - -class ContactNavigator { - - fun goToFacebook(context: Context) { - context.safeOpenWebsite(BuildConfig.FACEBOOK_PAGE) - } - - fun goToInstagram(context: Context) { - context.safeOpenWebsite(BuildConfig.INSTAGRAM_PAGE) - } - - fun goToGithub(context: Context) { - context.safeOpenWebsite(BuildConfig.GITHUB_PAGE) - } - - fun goToTwitter(context: Context) { - context.safeOpenWebsite(BuildConfig.TWITTER_PAGE) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailFragment.kt deleted file mode 100644 index 26165c5..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailFragment.kt +++ /dev/null @@ -1,129 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.detail - -import android.os.Build -import androidx.core.view.isVisible -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import androidx.navigation.ui.setupWithNavController -import kotlinx.android.synthetic.main.fragment_detail.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.ui.viewmodel.ViewModelFragment -import pl.tkadziolka.snipmeandroid.ui.viewmodel.observeNotNull -import pl.tkadziolka.snipmeandroid.util.extension.* - -class DetailFragment : ViewModelFragment(DetailViewModel::class) { - private val args: DetailFragmentArgs by navArgs() - - override val layout: Int = R.layout.fragment_detail - - override fun onViewCreated() { - setupToolbar() - setupActions() - viewModel.load(args.uuid) - } - - override fun observeViewModel() { - viewModel.state.observeNotNull(this) { state -> - when (state) { - is Loading -> showLoading() - is Loaded -> showData(state.snippet) - is Error -> viewModel.goToError(findNavController(), state.error) - } - } - - viewModel.event.observeNotNull(this) { event -> - when (event) { - is Alert -> showToast(event.message) - is Logout -> { - viewModel.goToLogin(findNavController()) - } - } - } - } - - private fun showLoading() { - detailLoading.visible() - } - - private fun showData(snippet: Snippet) { - detailLoading.gone() - with(snippet) { - showActionsForOwner(isOwner) - detailName.text = title - detailLanguage.text = language.raw - detailLikeCounter.text = numberOfLikes.toString() - detailDislikeCounter.text = numberOfDislikes.toString() - setReactionState(snippet.userReaction) - detailCode.setCodeWithTheme(code.raw, language.type.fileExtension) - } - } - - private fun showActionsForOwner(isOwner: Boolean) { - detailToolbar.menu.findItem(R.id.detailMenuShare).isVisible = isOwner - detailEdit.isVisible = isOwner - } - - private fun setupActions() { - detailEdit.setOnClick { - viewModel.goToEdit(findNavController(), args.uuid) - } - - detailLikeAction.setOnClick { - setReactionAvailability(isAvailable = false) - viewModel.like() - } - - detailDislikeAction.setOnClick { - setReactionAvailability(isAvailable = false) - viewModel.dislike() - } - } - - private fun setupToolbar() { - with(detailToolbar) { - setupWithNavController(findNavController()) - inflateMenu(R.menu.detail_menu) - setOnMenuItemClickListener { menu -> - when (menu.itemId) { - R.id.detailMenuCopy -> { - viewModel.copyToClipboard() - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { - // Android 12+ shows message itself - showToast(getString(R.string.message_copied, detailName.text)) - } - true - } - R.id.detailMenuShare -> { - viewModel.goToShare(findNavController(), args.uuid); true - } - else -> false - } - } - } - } - - private fun setReactionState(userReaction: UserReaction) { - setReactionAvailability(isAvailable = true) - when (userReaction) { - UserReaction.LIKE -> { - detailLikeAction.tint(R.color.highlight) - detailDislikeAction.tint(R.color.white) - } - UserReaction.DISLIKE -> { - detailLikeAction.tint(R.color.white) - detailDislikeAction.tint(R.color.highlight) - } - UserReaction.NONE -> { - detailLikeAction.tint(R.color.white) - detailDislikeAction.tint(R.color.white) - } - } - } - - private fun setReactionAvailability(isAvailable: Boolean) { - detailLikeAction.isEnabled = isAvailable - detailLikeAction.isEnabled = isAvailable - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailNavigator.kt deleted file mode 100644 index 4fdefaa..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailNavigator.kt +++ /dev/null @@ -1,22 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.detail - -import androidx.navigation.NavController -import pl.tkadziolka.snipmeandroid.util.extension.navigateSafe - -class DetailNavigator { - fun goToEdit(nav: NavController, snippetUUID: String) { - nav.navigateSafe(DetailFragmentDirections.goToEdit(snippetUUID)) - } - - fun goToShare(nav: NavController, snippetUUID: String) { - nav.navigateSafe(DetailFragmentDirections.goToShare(snippetUUID)) - } - - fun goToLogin(nav: NavController) { - nav.navigateSafe(DetailFragmentDirections.goToLogin()) - } - - fun goToError(nav: NavController, message: String?) { - nav.navigateSafe(DetailFragmentDirections.goToError(message)) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailViewModel.kt deleted file mode 100644 index 6b53bfe..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/detail/DetailViewModel.kt +++ /dev/null @@ -1,130 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.detail - -import androidx.navigation.NavController -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.domain.clipboard.AddToClipboardUseCase -import pl.tkadziolka.snipmeandroid.domain.error.exception.* -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.reaction.GetTargetUserReactionUseCase -import pl.tkadziolka.snipmeandroid.domain.reaction.SetUserReactionUseCase -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippet.GetSingleSnippetUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable -import pl.tkadziolka.snipmeandroid.ui.session.SessionViewModel -import pl.tkadziolka.snipmeandroid.ui.viewmodel.SingleLiveEvent -import pl.tkadziolka.snipmeandroid.ui.viewmodel.StateViewModel -import timber.log.Timber - -class DetailViewModel( - private val errorMessages: ErrorMessages, - private val navigator: DetailNavigator, - private val getSnippet: GetSingleSnippetUseCase, - private val clipboard: AddToClipboardUseCase, - private val getTargetReaction: GetTargetUserReactionUseCase, - private val setUserReaction: SetUserReactionUseCase, - private val session: SessionViewModel -) : StateViewModel(), ErrorParsable { - - private val mutableEvent = SingleLiveEvent() - val event = mutableEvent - - override fun parseError(throwable: Throwable) { - when (throwable) { - is ConnectionException -> setState(Error(errorMessages.parse(throwable))) - is ContentNotFoundException -> setState(Error(errorMessages.parse(throwable))) - is ForbiddenActionException -> setState(Error(errorMessages.parse(throwable))) - is NetworkNotAvailableException -> setState(Error(errorMessages.parse(throwable))) - is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } - is RemoteException -> setState(Error(errorMessages.parse(throwable))) - is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } - else -> setState(Error(errorMessages.parse(throwable))) - } - } - - fun goToLogin(nav: NavController) { - navigator.goToLogin(nav) - } - - fun goToError(nav: NavController, message: String?) { - navigator.goToError(nav, message) - } - - fun load(uuid: String) { - setState(Loading) - getSnippet(uuid) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { setState(Loaded(it)) }, - onError = { - Timber.e("Couldn't load snippets, error = $it") - parseError(it) - } - ).also { disposables += it } - } - - fun like() { - changeReaction(UserReaction.LIKE) - } - - fun dislike() { - changeReaction(UserReaction.DISLIKE) - } - - fun copyToClipboard() { - getSnippet()?.let { - clipboard(it.title, it.code.raw) - } - } - - fun goToEdit(nav: NavController, snippetUUID: String) { - navigator.goToEdit(nav, snippetUUID) - } - - fun goToShare(nav: NavController, snippetUUID: String) { - navigator.goToShare(nav, snippetUUID) - } - - private fun changeReaction(newReaction: UserReaction) { - // Immediately show change to user - val previousState = getLoaded() ?: return - val targetReaction = getTargetReaction(previousState.snippet, newReaction) - setState(previousState.run { copy(snippet = snippet.copy(userReaction = targetReaction)) }) - - setUserReaction(previousState.snippet, newReaction) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { snippet -> mutableState.value = Loaded(snippet) }, - onError = { - // Revert changes - Timber.e("Couldn't change user reaction, error = $it") - mutableEvent.value = Alert(errorMessages.generic) - setState(previousState) - } - ).also { disposables += it } - } - - private fun getSnippet(): Snippet? = getLoaded()?.snippet - - private fun getLoaded() = - if (state.value is Loaded) { - (state.value as Loaded) - } else { - null - } -} - -sealed class DetailViewState -object Loading : DetailViewState() -data class Loaded(val snippet: Snippet) : DetailViewState() -data class Error(val error: String?) : DetailViewState() - -sealed class DetailEvent -object Idle: DetailEvent() -data class Alert(val message: String) : DetailEvent() -object Logout : DetailEvent() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateFragment.kt deleted file mode 100644 index e0854c3..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateFragment.kt +++ /dev/null @@ -1,36 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.donate - -import androidx.navigation.fragment.findNavController -import androidx.navigation.ui.setupWithNavController -import kotlinx.android.synthetic.main.fragment_donate.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.payment.PaymentOption -import pl.tkadziolka.snipmeandroid.ui.viewmodel.ViewModelFragment -import pl.tkadziolka.snipmeandroid.util.extension.setOnClick - -class DonateFragment : ViewModelFragment(DonateViewModel::class) { - - override val layout: Int = R.layout.fragment_donate - - override fun onViewCreated() { - donateToolbar.setupWithNavController(findNavController()) - setupActions() - } - - override fun observeViewModel() = Unit - - private fun setupActions() { - donateOptions.setOnCheckedChangeListener { _, checkedId -> - when (checkedId) { - R.id.donateOptionGooglePay -> Unit - R.id.donateOptionPayPal -> viewModel.setPaymentOption(PaymentOption.PAYPAL) - R.id.donateOptionCreditCard -> viewModel.setPaymentOption(PaymentOption.CREDIT_CARD) - else -> Unit - } - } - - donateAction.setOnClick { - viewModel.proceedToPayment(requireContext()) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateNavigator.kt deleted file mode 100644 index 119f869..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateNavigator.kt +++ /dev/null @@ -1,16 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.donate - -import android.content.Context -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.util.extension.safeOpenWebsite - -class DonateNavigator { - - fun goToPayPal(context: Context) { - context.safeOpenWebsite(BuildConfig.PAYPAL_PAGE) - } - - fun goToCreditCard(context: Context) { - context.safeOpenWebsite(BuildConfig.CARD_PAGE) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateViewModel.kt deleted file mode 100644 index 2463087..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/donate/DonateViewModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.donate - -import android.content.Context -import androidx.lifecycle.ViewModel -import pl.tkadziolka.snipmeandroid.domain.payment.PaymentOption - -class DonateViewModel(private val navigator: DonateNavigator) : ViewModel() { - private var selectedOption = PaymentOption.PAYPAL - - - fun setPaymentOption(option: PaymentOption) { - selectedOption = option - } - - fun proceedToPayment(context: Context) { - when (selectedOption) { - PaymentOption.GOOGLE_PAY -> Unit - PaymentOption.PAYPAL -> navigator.goToPayPal(context) - PaymentOption.CREDIT_CARD -> navigator.goToCreditCard(context) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditFragment.kt deleted file mode 100644 index d9f2829..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditFragment.kt +++ /dev/null @@ -1,214 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.edit - -import android.os.Bundle -import android.view.View -import android.widget.ArrayAdapter -import androidx.appcompat.app.AlertDialog -import androidx.core.view.isVisible -import androidx.core.widget.addTextChangedListener -import androidx.navigation.NavController -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import androidx.navigation.ui.setupWithNavController -import kotlinx.android.synthetic.main.fragment_edit.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.ui.Dialogs -import pl.tkadziolka.snipmeandroid.ui.viewmodel.ViewModelFragment -import pl.tkadziolka.snipmeandroid.ui.viewmodel.observeNotNull -import pl.tkadziolka.snipmeandroid.util.extension.* - -class EditFragment : ViewModelFragment(EditViewModel::class) { - // Update toolbar title when nav controller set up toolbar's destination - private val destinationChange = NavController.OnDestinationChangedListener { _, _, _ -> - editToolbar.title = if (isEdit()) getString(R.string.edit) else getString(R.string.create) - } - - private val args by navArgs() - - override val layout: Int = R.layout.fragment_edit - - private lateinit var dialogs: Dialogs - private var loadingDialog: AlertDialog? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setupExitDialogOnBack() - } - - override fun onViewCreated() { - dialogs = Dialogs(requireContext()) - setupToolbar() - setupActions() - viewModel.load(args.id) - } - - override fun observeViewModel() { - viewModel.state.observeNotNull(this) { state -> - hideLoading() - when (state) { - Loading -> showLoading() - is Loaded -> { - showData(state.languages, state.snippet) - showValidationError(state.error) - } - is Completed -> { - viewModel.goToDetail(findNavController(), state.snippet.uuid) - } - is Error -> { - viewModel.goToError(findNavController(), state.error) - } - } - } - - viewModel.event.observeNotNull(this) { event -> - when (event) { - is Logout -> viewModel.goToLogin(findNavController()) - is Alert -> showToast(event.message) - is PastedFromClipboard -> { - editCode.setText(event.code) - updatePasteButtonVisibility() - } - } - } - } - - private fun showData(languages: List, snippet: Snippet?) { - // Check if data is different - val adapter = editLanguage.adapter - if (adapter == null || adapter.count != languages.size) { - editLanguage.setAdapter(getLanguageAdapter(languages)) - } - - snippet?.let { updatedSnippet -> - getDifferingFields(updatedSnippet).forEach { - if (it.id == R.id.editName) editName.setText(updatedSnippet.title) - if (it.id == R.id.editCode) editCode.setText(updatedSnippet.code.raw) - if (it.id == R.id.editLanguage) editLanguage.setText(updatedSnippet.language.raw) - } - } - - updatePasteButtonVisibility() - } - - private fun showLoading() { - loadingDialog = dialogs.loading.show() - } - - private fun hideLoading() { - loadingDialog?.dismiss() - } - - private fun showValidationError(error: String?) { - with(editError) { - if (error == null) { - gone() - return - } - visible() - text = error - } - } - - private fun applyChange() { - getTextsAndUpdateState() - if (isEdit()) { - viewModel.edit(args.id ?: "") - } else { - viewModel.create() - } - } - - private fun setupActions() { - editPasteFromClipboard.setOnClick { - viewModel.pasteFromClipboard() - } - } - - private fun getTextsAndUpdateState() { - val title = editName.getTyped() - val code = editCode.text.toString() - val language = editLanguage.getTyped() - viewModel.update(title, code, language) - updatePasteButtonVisibility() - } - - private fun getDifferingFields(snip: Snippet): List { - val differingFields = mutableListOf() - - if (editName.getTyped() != snip.title) { - differingFields.add(editName) - } - - if (editCode.text.toString() != snip.code.raw) { - differingFields.add(editCode) - } - - if (editLanguage.getTyped() != snip.language.raw) { - differingFields.add(editLanguage) - } - - return differingFields - } - - - private fun setupToolbar() { - with(editToolbar) { - // setupWithNavController sets action bar with default implementation - setupWithNavController(findNavController()) - // So, each customization must be done after - setNavigationOnClickListener { requireActivity().onBackPressed() } - findNavController().addOnDestinationChangedListener(destinationChange) - inflateMenu(R.menu.edit_menu) - setOnMenuItemClickListener { menu -> - when (menu.itemId) { - R.id.editMenuSave -> { applyChange(); true } - else -> false - } - } - } - } - - private fun isEdit(): Boolean = args.id != null - - private fun getLanguageAdapter(languages: List) = ArrayAdapter( - requireContext(), - android.R.layout.simple_dropdown_item_1line, - languages - ) - - private fun setupExitDialogOnBack() { - onBackPressed { - val snippet = (viewModel.state.value as? Loaded)?.snippet - - when { - snippet == null -> { back(); return@onBackPressed } - isEdit().not() && fieldsAreEmpty() -> { back(); return@onBackPressed } - getDifferingFields(snippet).isEmpty() -> { back(); return@onBackPressed } - } - - dialogs.quit - .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } - .setPositiveButton(R.string.okay) { dialog, _ -> - dialog.dismiss() - back() - }.show() - } - } - - private fun updatePasteButtonVisibility() { - if (editCode.text.toString().isNotBlank()) { - editPasteFromClipboard.gone() - } - } - - private fun fieldsAreEmpty() = - editName.getTyped().isEmpty() - && editCode.getTyped().isEmpty() - && editLanguage.getTyped().isEmpty() - - override fun onStop() { - findNavController().removeOnDestinationChangedListener(destinationChange) - super.onStop() - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditNavigator.kt deleted file mode 100644 index 712d53e..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditNavigator.kt +++ /dev/null @@ -1,19 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.edit - -import androidx.navigation.NavController -import pl.tkadziolka.snipmeandroid.util.extension.navigateSafe - -class EditNavigator { - - fun goToDetail(nav: NavController, snippetId: String) { - nav.navigateSafe(EditFragmentDirections.goToDetail(snippetId)) - } - - fun goToLogin(nav: NavController) { - nav.navigateSafe(EditFragmentDirections.goToLogin()) - } - - fun goToError(nav: NavController, message: String?) { - nav.navigateSafe(EditFragmentDirections.goToError(message)) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditViewModel.kt deleted file mode 100644 index 6a363ee..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/edit/EditViewModel.kt +++ /dev/null @@ -1,234 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.edit - -import android.text.SpannableString -import androidx.navigation.NavController -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.domain.error.exception.* -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippet.EditInteractor -import pl.tkadziolka.snipmeandroid.domain.snippets.* -import pl.tkadziolka.snipmeandroid.domain.validation.FieldValidator -import pl.tkadziolka.snipmeandroid.domain.validation.messageOrNull -import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable -import pl.tkadziolka.snipmeandroid.ui.session.SessionViewModel -import pl.tkadziolka.snipmeandroid.ui.viewmodel.SingleLiveEvent -import pl.tkadziolka.snipmeandroid.ui.viewmodel.StateViewModel -import pl.tkadziolka.snipmeandroid.util.extension.inProgress -import timber.log.Timber -import java.util.* - -private val NO_ERROR: String? = null - -class EditViewModel( - private val messages: ErrorMessages, - private val errorMessages: ErrorMessages, - private val validationMessages: ValidationMessages, - private val navigator: EditNavigator, - private val interactor: EditInteractor, - private val session: SessionViewModel -) : StateViewModel(), ErrorParsable { - private val mutableEvent = SingleLiveEvent() - val event = mutableEvent - - private var createDisposable: Disposable? = null - - override fun parseError(throwable: Throwable) { - when (throwable) { - is ConnectionException -> setState(Error(errorMessages.parse(throwable))) - is ContentNotFoundException -> setState(Error(errorMessages.parse(throwable))) - is ForbiddenActionException -> setState(Error(errorMessages.parse(throwable))) - is NetworkNotAvailableException -> setState(Error(errorMessages.parse(throwable))) - is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } - is RemoteException -> setState(Error(errorMessages.parse(throwable))) - is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } - else -> setState(Error(errorMessages.parse(throwable))) - } - } - - fun load(uuid: String?) { - // Don't load the same state again when fragment is recreated - if (state.value != null) return - - setState(Loading) - - interactor.languages() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { languages -> fetchSnippetOrComplete(languages, uuid) }, - onError = { error -> - Timber.e("Couldn't load languages, ERROR = $error") - parseError(error) - } - ).also { disposables += it } - } - - fun goToDetail(nav: NavController, snippetId: String) { - navigator.goToDetail(nav, snippetId) - } - - fun goToLogin(nav: NavController) { - navigator.goToLogin(nav) - } - - fun goToError(nav: NavController, message: String?) { - navigator.goToError(nav, message) - } - - fun update(title: String, code: String, language: String) { - getLoaded()?.let { - val snip = it.snippet ?: return - if (title != snip.title || code != snip.code.raw || language != snip.language.raw) - setState(it.copy(snippet = getTempSnippet(title, code, language))) - } - } - - fun create() { - if (state.value is Loading || createDisposable.inProgress()) return - - getSnippetOnValid()?.let { snip -> - setState(Loading) - createDisposable = interactor.create(snip.title, snip.code.raw, snip.language.raw) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { snippet -> setState(Completed(snippet)) }, - onError = { error -> - Timber.e("Couldn't create a new snippet, ERROR = $error") - mutableEvent.value = Alert(errorMessages.parse(error)) - } - ).also { disposables += it } - } - } - - fun edit(uuid: String) { - if (state.value is Loading || createDisposable.inProgress()) return - - getSnippetOnValid()?.let { snip -> - setState(Loading) - createDisposable = interactor.update(uuid, snip.title, snip.code.raw, snip.language.raw) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { snippet -> setState(Completed(snippet)) }, - onError = { error -> - Timber.e("Couldn't update the snippet, ERROR = $error") - mutableEvent.value = Alert(errorMessages.parse(error)) - } - ).also { disposables += it } - } - } - - fun pasteFromClipboard() { - getLoaded()?.let { - interactor.getFromClipboard()?.let { codeFromClipboard -> - mutableEvent.value = PastedFromClipboard(codeFromClipboard) - } - } - } - - private fun fetchSnippetOrComplete(languages: List, uuid: String?) { - if (uuid == null) { - setState(Loaded(languages, getTempSnippet("", "", ""), NO_ERROR)) - return - } - - interactor.snippet(uuid) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { snippet -> setState(Loaded(languages, snippet, NO_ERROR)) }, - onError = { error -> - Timber.e("Couldn't update the snippet, ERROR = $error") - parseError(error) - } - ).also { disposables += it } - } - - private fun getSnippetOnValid(): Snippet? { - getLoaded()?.let { - var escape = false - val snip = it.snippet ?: return null - - validateFields(snip.title, snip.code.raw, snip.language.raw) { errorMessage -> - setState(it.copy(error = errorMessage)) - escape = true - } - - if (escape) return null - - if (it.languages.contains(snip.language.raw).not()) { - setState(it.copy(error = messages.unknownLanguage)) - return null - } - - return snip - } - - return null - } - - private fun validateFields( - title: String, - code: String, - language: String, - onError: (String) -> Unit - ) { - FieldValidator(validationMessages).validate(title).messageOrNull()?.let { - onError(validationMessages.phrasesBlank) - return - } - - FieldValidator(validationMessages).validate(code).messageOrNull()?.let { - onError(validationMessages.phrasesBlank) - return - } - - FieldValidator(validationMessages).validate(language).messageOrNull()?.let { - onError(validationMessages.phrasesBlank) - return - } - } - - private fun getLoaded(): Loaded? = - if (state.value is Loaded) { - state.value as Loaded - } else { - null - } - - private fun getTempSnippet(title: String, code: String, language: String) = Snippet( - uuid = "", - title = title, - code = SnippetCode(raw = code, highlighted = SpannableString("")), - language = SnippetLanguage(raw = language, type = SnippetLanguageType.UNKNOWN), - visibility = SnippetVisibility.PUBLIC, - isOwner = false, - owner = Owner(0, ""), - modifiedAt = Date(), - numberOfLikes = 0, - numberOfDislikes = 0, - userReaction = UserReaction.NONE - ) -} - -sealed class EditViewState -object Loading : EditViewState() -data class Loaded( - val languages: List, - val snippet: Snippet?, - val error: String? -) : EditViewState() -data class Completed(val snippet: Snippet) : EditViewState() -data class Error(val error: String?) : EditViewState() - -sealed class EditEvent -object Logout : EditEvent() -data class PastedFromClipboard(val code: String) : EditEvent() -data class Alert(val message: String) : EditEvent() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/error/ErrorFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/error/ErrorFragment.kt deleted file mode 100644 index 2498b41..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/error/ErrorFragment.kt +++ /dev/null @@ -1,49 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.error - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import kotlinx.android.synthetic.main.fragment_error.* -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.util.extension.navigateUpTo -import pl.tkadziolka.snipmeandroid.util.extension.onBackPressed -import pl.tkadziolka.snipmeandroid.util.extension.safeOpenWebsite -import pl.tkadziolka.snipmeandroid.util.extension.setOnClick - -class ErrorFragment: Fragment() { - - private val args: ErrorFragmentArgs by navArgs() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View = inflater.inflate(R.layout.fragment_error, container, false) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - errorMessage.text = args.message ?: getString(R.string.error_message_generic) - - onBackPressed { navigateToPreviousSuccessScreen() } - errorClose.setOnClick { navigateToPreviousSuccessScreen() } - errorContact.setOnClick { requireContext().safeOpenWebsite(BuildConfig.CONTACT_US_PAGE) } - } - - private fun navigateToPreviousSuccessScreen() { - val nav = findNavController() - val previousNavigation = nav.previousBackStackEntry - val errorScreen = previousNavigation?.destination ?: return - when(errorScreen.id) { - // Error on main list blocks all app, so close it - R.id.main -> requireActivity().finishAndRemoveTask() - R.id.share -> nav.navigateUpTo(R.id.detail) - R.id.login -> nav.navigateUp() - else -> nav.navigateUpTo(R.id.main) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginFragment.kt deleted file mode 100644 index bf393ad..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginFragment.kt +++ /dev/null @@ -1,160 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.login - -import android.view.inputmethod.EditorInfo.IME_ACTION_DONE -import android.view.inputmethod.EditorInfo.IME_ACTION_NEXT -import androidx.appcompat.app.AlertDialog -import androidx.core.view.isVisible -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import kotlinx.android.synthetic.main.fragment_login.* -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.ui.Dialogs -import pl.tkadziolka.snipmeandroid.ui.viewmodel.ViewModelFragment -import pl.tkadziolka.snipmeandroid.ui.viewmodel.observeNotNull -import pl.tkadziolka.snipmeandroid.util.extension.* - -class LoginFragment : ViewModelFragment(LoginViewModel::class) { - - private val args: LoginFragmentArgs by navArgs() - - override val layout: Int = R.layout.fragment_login - - private lateinit var dialogs: Dialogs - private var loadingDialog: AlertDialog? = null - - override fun onViewCreated() { - dialogs = Dialogs(requireContext()) - - if (BuildConfig.DEBUG) - loginVersion.text = "v${BuildConfig.VERSION_NAME}" - - setupActions() - viewModel.startup(args.login) - } - - override fun observeViewModel() { - viewModel.state.observeNotNull(this) { state -> - hideLoading() - when (state) { - Loading -> showLoading() - is Startup -> { - showOnlyLogin(state.login) - showValidationError(state.error) - } - is Login -> { - setRegisterViewState(isRegister = false) - showValidationError(state.error) - } - is Register -> { - setRegisterViewState(isRegister = true) - showValidationError(state.error) - } - is UserFound -> showUserFound() - is Completed -> viewModel.goToMain(findNavController()) - } - - viewModel.event.observeNotNull(this) { event -> - hideLoading() - when (event) { - is Alert -> showToast(event.message) - is Error -> viewModel.goToError(findNavController(), event.message) - is Dialog -> showDialog(event.message) - } - } - } - } - - private fun setupActions() { - loginAction.setOnClick { - when (viewModel.state.value) { - is Startup -> viewModel.validateLogin(loginName.text.trim()) - is Login -> viewModel.login( - loginName.text.trim(), - loginPassword.text.trim() - ) - is Register -> viewModel.register( - loginName.text.trim(), - loginPassword.text.trim(), - loginPasswordRepeat.text.trim(), - loginEmail.text.trim() - ) - else -> Unit - } - } - - loginRegisterAction.setOnClick { - when (viewModel.state.value) { - is Startup -> viewModel.register("", "", "", "") - else -> Unit - } - } - } - - private fun showLoading() { - loadingDialog = dialogs.loading.show() - } - - private fun hideLoading() { - loadingDialog?.dismiss() - } - - private fun showOnlyLogin(login: String?) { - val phrase = getString(R.string.call_login) - loginHelper.text = phrase - login?.let { loginName.text = login } - loginName.imeOptions = IME_ACTION_DONE - } - - private fun setRegisterViewState(isRegister: Boolean) { - loginImage.isVisible = isRegister.not() - loginTitle.isVisible = isRegister.not() - loginPassword.isVisible = true - loginPasswordRepeat.isVisible = isRegister - loginEmail.isVisible = isRegister - loginRegisterAction.isVisible = false - - val imeOption = if (isRegister) { - IME_ACTION_NEXT - } else { - IME_ACTION_DONE - } - - val helperText = if (isRegister) { - getString(R.string.call_register) - } else { - getString(R.string.call_login) - } - - loginName.imeOptions = IME_ACTION_NEXT - loginPassword.imeOptions = imeOption - loginHelper.text = helperText - } - - private fun showValidationError(error: String?) { - with(loginError) { - if (error == null) { - gone() - return - } - visible() - text = error - } - } - - private fun showUserFound() { - dialogs.alreadyRegistered - .setPositiveButton(R.string.okay) { dialog, _ -> - viewModel.goToLogin(viewModel.getLogin().orEmpty()) - dialog.dismiss() - } - .setNegativeButton(R.string.cancel) { dialog, _ -> - viewModel.revokeLogin(viewModel.getLogin().orEmpty()) - dialog.dismiss() - }.show() - } - - private fun showDialog(message: String) { - dialogs.ok.setMessage(message).show() - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginNavigator.kt deleted file mode 100644 index 4c5e20b..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginNavigator.kt +++ /dev/null @@ -1,15 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.login - -import androidx.navigation.NavController -import pl.tkadziolka.snipmeandroid.util.extension.navigateSafe - -class LoginNavigator { - - fun goToMain(navController: NavController) { - navController.navigateSafe(LoginFragmentDirections.goToMain()) - } - - fun goToError(nav: NavController, message: String?) { - nav.navigateSafe(LoginFragmentDirections.goToError(message)) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginViewModel.kt deleted file mode 100644 index 8b705fa..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/login/LoginViewModel.kt +++ /dev/null @@ -1,221 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.login - -import androidx.navigation.NavController -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.domain.auth.LoginInteractor -import pl.tkadziolka.snipmeandroid.domain.error.exception.* -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.message.ValidationMessages -import pl.tkadziolka.snipmeandroid.domain.validation.FieldValidator -import pl.tkadziolka.snipmeandroid.domain.validation.SignInValidator -import pl.tkadziolka.snipmeandroid.domain.validation.SignUpValidator -import pl.tkadziolka.snipmeandroid.domain.validation.messageOrNull -import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable -import pl.tkadziolka.snipmeandroid.ui.viewmodel.SingleLiveEvent -import pl.tkadziolka.snipmeandroid.ui.viewmodel.StateViewModel -import pl.tkadziolka.snipmeandroid.util.extension.errorMessage -import pl.tkadziolka.snipmeandroid.util.extension.inProgress -import retrofit2.HttpException -import timber.log.Timber - -private const val DEBUG_PREFIX = "DEBUG: " - -class LoginViewModel( - private val errorMessages: ErrorMessages, - private val messages: ValidationMessages, - private val interactor: LoginInteractor, - private val navigator: LoginNavigator -) : StateViewModel(), ErrorParsable { - private var identifyDisposable: Disposable? = null - private var loginDisposable: Disposable? = null - private var registerDisposable: Disposable? = null - - private val mutableEvent = SingleLiveEvent() - val event = mutableEvent - - override fun parseError(throwable: Throwable) { - when (throwable) { - is ConnectionException -> setEvent(Error(errorMessages.parse(throwable))) - is ContentNotFoundException -> setEvent(Error(errorMessages.parse(throwable))) - is ForbiddenActionException -> setEvent(Dialog(errorMessages.alreadyRegistered)) - is NetworkNotAvailableException -> setEvent(Error(errorMessages.parse(throwable))) - is NotAuthorizedException -> setEvent(Alert(errorMessages.parse(throwable))) - is RemoteException -> setEvent(Error(errorMessages.parse(throwable))) - is SessionExpiredException -> setEvent(Alert(errorMessages.parse(throwable))) - else -> setEvent(Error(errorMessages.parse(throwable))) - } - } - - fun startup(login: String) { - // Don't reset state on configuration changed - if (state.value != null) return - - setState(Startup(login, null)) - } - - fun goToLogin(login: String) { - setState(Login(login, "", null)) - } - - fun goToMain(nav: NavController) { - navigator.goToMain(nav) - } - - fun goToError(nav: NavController, message: String?) { - navigator.goToError(nav, message) - } - - fun revokeLogin(login: String) { - setState(Startup(login, null)) - } - - fun validateLogin(login: String) { - if (identifyDisposable.inProgress()) return - setState(Loading) - - FieldValidator(messages).validate(login).messageOrNull()?.let { message -> - setState(Startup(login, message)) - return - } - - identifyDisposable = interactor.identify(login) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { identified -> publishIdentified(login, identified) }, - onError = { - val errorMessage = getErrorMessage(it) - Timber.d("Couldn't identify user = $login, error = $it") - setState(Startup(login, errorMessage)) - parseError(it) - } - ).also { disposables += it } - } - - fun login(login: String, password: String) { - if (loginDisposable.inProgress()) return - setState(Loading) - - SignInValidator(messages).validate(login, password).messageOrNull()?.let { message -> - setState(Login(login, password, message)) - return - } - - loginDisposable = interactor.login(login, password) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onComplete = { setState(Completed(login)) }, - onError = { - val errorMessage = getErrorMessage(it) - Timber.d("Couldn't login user = $login, error = $it") - setState(Login(login, password, errorMessage)) - parseError(it) - } - ).also { disposables += it } - } - - fun register(login: String, password: String, repeatedPassword: String, email: String) { - if (registerDisposable.inProgress()) return - setState(Loading) - - SignUpValidator(messages).validate(login, password, repeatedPassword, email).messageOrNull() - ?.let { message -> - setState(Register(login, password, repeatedPassword, email, message)) - return - } - - registerDisposable = interactor.register(login, password, email) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onComplete = { setState(Completed(login)) }, - onError = { - val errorMessage = getErrorMessage(it) - Timber.d("Couldn't register user = $login, error = $it") - setState(Register(login, password, repeatedPassword, email, errorMessage)) - parseError(it) - } - ).also { disposables += it } - } - - fun getLogin(): String? = when (state.value) { - is Startup -> (state.value as Startup).login - is Login -> (state.value as Login).login - is Register -> (state.value as Register).login - is UserFound -> (state.value as UserFound).login - is Completed -> (state.value as Completed).login - else -> null - } - - private fun publishIdentified(login: String, identified: Boolean) { - if (identified) { - setState(UserFound(login)) - } else { - setState(Register(login, "", "", "", null)) - } - } - - private fun setEvent(event: LoginEvent) { - mutableEvent.value = event - } - - private fun getErrorMessage(throwable: Throwable): String? { - // When detailed error message comes from API - val snipException = throwable as? SnipException - val httpException = snipException?.cause as? HttpException - httpException?.errorMessage?.let { detailedMessage -> - return detailedMessage - } - - // Otherwise show only in debug mode - if (BuildConfig.DEBUG.not()) { - return null - } - - return when (throwable) { - is SnipException -> DEBUG_PREFIX + throwable.cause?.message - else -> DEBUG_PREFIX + throwable.message - } - } -} - -sealed class LoginViewState -object Loading : LoginViewState() - -data class Startup( - val login: String, - val error: String? -) : LoginViewState() - -data class Login( - val login: String, - val password: String, - val error: String? -) : LoginViewState() - -data class Register( - val login: String, - val password: String, - val repeatedPassword: String, - val email: String, - val error: String? -) : LoginViewState() - -data class UserFound( - val login: String -) : LoginViewState() - -data class Completed( - val login: String -) : LoginViewState() - -sealed class LoginEvent -data class Error(val message: String?) : LoginEvent() -data class Alert(val message: String) : LoginEvent() -data class Dialog(val message: String) : LoginEvent() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainActivity.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainActivity.kt deleted file mode 100644 index 11459af..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainActivity.kt +++ /dev/null @@ -1,42 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.main - -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle -import io.flutter.embedding.android.FlutterActivity -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.embedding.engine.FlutterEngineCache -import io.flutter.embedding.engine.dart.DartExecutor -import pl.tkadziolka.snipmeandroid.bridge.main.MainModelPlugin -import pl.tkadziolka.snipmeandroid.R - -class MainActivity : AppCompatActivity() { - private lateinit var flutterEngine : FlutterEngine - - private val cachedEngineId = "ENGINE_1" - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - // Instantiate a FlutterEngine. - flutterEngine = FlutterEngine(this) - - // Start executing Dart code to pre-warm the FlutterEngine. - flutterEngine.dartExecutor.executeDartEntrypoint( - DartExecutor.DartEntrypoint.createDefault() - ) - - flutterEngine.plugins.add(MainModelPlugin()) - - // Cache the FlutterEngine to be used by FlutterActivity. - FlutterEngineCache - .getInstance() - .put(cachedEngineId, flutterEngine) - - startActivity( - FlutterActivity - .withCachedEngine(cachedEngineId) - .build(this) - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainFragment.kt deleted file mode 100644 index b729cb5..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainFragment.kt +++ /dev/null @@ -1,110 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.main - -import androidx.annotation.LayoutRes -import androidx.navigation.fragment.findNavController -import kotlinx.android.synthetic.main.fragment_main.* -import pl.tkadziolka.snipmeandroid.BuildConfig -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.repository.snippet.SNIPPET_PAGE_SIZE -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.domain.user.User -import pl.tkadziolka.snipmeandroid.ui.viewmodel.ViewModelFragment -import pl.tkadziolka.snipmeandroid.ui.viewmodel.observeNotNull -import pl.tkadziolka.snipmeandroid.util.extension.* -import pl.tkadziolka.snipmeandroid.util.view.LoadMoreListener - -private const val TOP_OF_LIST = 0 - -class MainFragment : ViewModelFragment(MainViewModel::class) { - - private val loadMore = LoadMoreListener(SNIPPET_PAGE_SIZE) { - viewModel.loadNextPage() - } - - private val mainAdapter by lazy { - SnippetAdapter( - clickListener = { snippet -> - viewModel.goToDetail(findNavController(), snippet.uuid) - }, - pressListener = { snippet -> - with(snippet) { - viewModel.goToPreview(findNavController(), title, uuid, code.raw, language.raw) - } - } - ) - } - - @LayoutRes - override val layout: Int = R.layout.fragment_main - - override fun onViewCreated() { - if (BuildConfig.DEBUG) - mainVersion.text = "v${BuildConfig.VERSION_NAME}" - - setupMenu() - setupActions() - showLogin("") - mainList.adapter = mainAdapter - viewModel.init() - viewModel.refreshSnippetUpdates() - } - - override fun observeViewModel() { - viewModel.state.observeNotNull(this) { state -> - when (state) { - Loading -> showLoading() - is Loaded -> showData(state.user, state.snippets) - is Error -> viewModel.goToError(findNavController(), state.message) - } - } - - viewModel.event.observeNotNull(this) { event -> - when (event) { - is ListRefreshed -> { - mainList.smoothScrollToPosition(TOP_OF_LIST) - mainAppBar.setExpanded(true) - } - is Alert -> showToast(event.message) - is Logout -> viewModel.goToLogin(findNavController()) - } - } - } - - private fun showLoading() { - mainLoading.visible() - } - - private fun showData(user: User, snippets: List) { - mainLoading.gone() - showLogin(user.login) - mainImage.loadWithFallback(user.photo) - with(mainList) { - visible() - mainAdapter.submitList(snippets) - } - } - - private fun showLogin(login: String) { - mainLogin.text = login - } - - private fun setupActions() { - mainAdd.setOnClick { viewModel.goToEdit(findNavController()) } - mainFilters.filterListener = { viewModel.filter(it) } - mainList.addOnScrollListener(loadMore) - } - - private fun setupMenu() { - with(mainToolbar) { - inflateMenu(R.menu.main_menu) - setOnMenuItemClickListener { item -> - when (item.itemId) { - R.id.mainMenuDonate -> { viewModel.goToDonate(findNavController()); true } - R.id.mainMenuContact -> { viewModel.goToContact(findNavController()); true } - R.id.mainMenuLogout -> { viewModel.logOut(); true } - else -> false - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainNavigator.kt deleted file mode 100644 index 03b6829..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainNavigator.kt +++ /dev/null @@ -1,35 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.main - -import androidx.navigation.NavController -import pl.tkadziolka.snipmeandroid.util.extension.navigateSafe - -class MainNavigator { - - fun goToDetail(nav: NavController, snippetId: String) { - nav.navigateSafe(MainFragmentDirections.goToDetail(snippetId)) - } - - fun goToPreview(nav: NavController, title: String, uuid: String, code: String, language: String) { - nav.navigateSafe(MainFragmentDirections.goToPreview(title, uuid, code, language)) - } - - fun goToEdit(nav: NavController) { - nav.navigateSafe(MainFragmentDirections.goToEdit()) - } - - fun goToLogin(nav: NavController) { - nav.navigateSafe(MainFragmentDirections.goToLogin()) - } - - fun goToError(nav: NavController, message: String?) { - nav.navigateSafe(MainFragmentDirections.goToError(message)) - } - - fun goToContact(nav: NavController) { - nav.navigateSafe(MainFragmentDirections.goToContact()) - } - - fun goToDonate(nav: NavController) { - nav.navigateSafe(MainFragmentDirections.goToDonate()) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainViewModel.kt deleted file mode 100644 index d5d3668..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/MainViewModel.kt +++ /dev/null @@ -1,204 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.main - -import androidx.navigation.NavController -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.domain.error.exception.* -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.snippet.ObserveUpdatedSnippetPageUseCase -import pl.tkadziolka.snipmeandroid.domain.snippet.ResetUpdatedSnippetPageUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.GetSnippetsUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.HasMoreSnippetPagesUseCase -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetScope -import pl.tkadziolka.snipmeandroid.domain.user.GetSingleUserUseCase -import pl.tkadziolka.snipmeandroid.domain.user.User -import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable -import pl.tkadziolka.snipmeandroid.ui.session.SessionViewModel -import pl.tkadziolka.snipmeandroid.ui.viewmodel.PersistedStateViewModel -import pl.tkadziolka.snipmeandroid.ui.viewmodel.SingleLiveEvent -import pl.tkadziolka.snipmeandroid.util.view.SnippetFilter -import timber.log.Timber - -private const val ONE_PAGE = 1 - -class MainViewModel( - private val navigator: MainNavigator, - private val errorMessages: ErrorMessages, - private val getUser: GetSingleUserUseCase, - private val getSnippets: GetSnippetsUseCase, - private val observeUpdatedPage: ObserveUpdatedSnippetPageUseCase, - private val resetUpdatedPage: ResetUpdatedSnippetPageUseCase, - private val hasMore: HasMoreSnippetPagesUseCase, - private val session: SessionViewModel -) : PersistedStateViewModel(), ErrorParsable { - private var shouldRefresh = false - - private val mutableEvent = SingleLiveEvent() - val event = mutableEvent - - override fun parseError(throwable: Throwable) { - when (throwable) { - is ConnectionException -> setState(Error(errorMessages.parse(throwable))) - is ContentNotFoundException -> setState(Error(errorMessages.parse(throwable))) - is ForbiddenActionException -> setState(Error(errorMessages.parse(throwable))) - is NetworkNotAvailableException -> setState(Error(errorMessages.parse(throwable))) - is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } - is RemoteException -> setState(Error(errorMessages.parse(throwable))) - is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } - else -> setState(Error(errorMessages.parse(throwable))) - } - } - - override fun initState() { - setState(Loading) - - getUser() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { user -> loadSnippets(user) }, - onError = { - Timber.e("Couldn't load user, error = $it") - parseError(it) - } - ).also { disposables += it } - } - - fun goToDetail(navController: NavController, snippetId: String) { - navigator.goToDetail(navController, snippetId) - } - - fun goToPreview( - nav: NavController, - title: String, - uuid: String, - code: String, - language: String - ) { - navigator.goToPreview(nav, title, uuid, code, language) - } - - fun goToEdit(nav: NavController) { - navigator.goToEdit(nav) - } - - fun goToLogin(nav: NavController) { - navigator.goToLogin(nav) - } - - fun goToError(nav: NavController, message: String?) { - navigator.goToError(nav, message) - } - - fun goToContact(nav: NavController) { - navigator.goToContact(nav) - } - - fun goToDonate(nav: NavController) { - navigator.goToDonate(nav) - } - - fun loadNextPage() { - getLoadedState()?.let { state -> - hasMore(state.scope, state.pages) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { hasMore -> - if (hasMore) loadSnippets(state.user, pages = state.pages + ONE_PAGE) - }, - onError = { - Timber.e("Couldn't check next page, error = $it") - mutableEvent.value = Alert(errorMessages.parse(it)) - }) - .also { disposables += it } - } - } - - fun filter(filter: SnippetFilter) { - val scope = filterToScope(filter) - getLoadedState()?.let { state -> - loadSnippets(state.user, pages = ONE_PAGE, scope = scope) - mutableEvent.value = ListRefreshed - } - } - - fun logOut() { - session.logOut { mutableEvent.value = Logout } - } - - fun refreshSnippetUpdates() { - getLoadedState()?.let { - observeUpdatedPage(getScope()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onNext = { updatedPage -> - shouldRefresh = true - loadSnippets(it.user, updatedPage, getScope()) - resetUpdatedPage() - }, - onError = { Timber.e("Couldn't refresh snippet updates, error = $it") } - ).also { disposables += it } - } - } - - private fun loadSnippets( - user: User, - pages: Int = 1, - scope: SnippetScope = SnippetScope.ALL - ) { - setState(Loading) - getSnippets(scope, pages) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { - setState(Loaded(user, it, pages, scope)) - if (shouldRefresh) { - mutableEvent.value = ListRefreshed - shouldRefresh = false - } - }, - onError = { - Timber.e("Couldn't load snippets, error = $it") - parseError(it) - } - ).also { disposables += it } - } - - private fun getLoadedState(): Loaded? = state.value as? Loaded - - private fun filterToScope(filter: SnippetFilter) = - when (filter) { - SnippetFilter.ALL -> SnippetScope.ALL - SnippetFilter.MINE -> SnippetScope.OWNED - else -> SnippetScope.SHARED_FOR - } - - private fun getScope(): SnippetScope { - getLoadedState()?.let { - return it.scope - } - return SnippetScope.ALL - } -} - -sealed class MainViewState -object Loading : MainViewState() -data class Loaded( - val user: User, - val snippets: List, - val pages: Int, - val scope: SnippetScope -) : MainViewState() -data class Error(val message: String?) : MainViewState() - -sealed class MainEvent -object Startup : MainEvent() -object ListRefreshed : MainEvent() -data class Alert(val message: String) : MainEvent() -object Logout : MainEvent() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/SnippetAdapter.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/SnippetAdapter.kt deleted file mode 100644 index 61e2371..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/main/SnippetAdapter.kt +++ /dev/null @@ -1,78 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.main - -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.view_item_snippet.view.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.reaction.UserReaction -import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet -import pl.tkadziolka.snipmeandroid.util.extension.inflate -import pl.tkadziolka.snipmeandroid.util.extension.setOnClick -import pl.tkadziolka.snipmeandroid.util.extension.tint - -typealias SnippetListener = (Snippet) -> Unit - -class SnippetAdapter( - private val clickListener: SnippetListener, - private val pressListener: SnippetListener -) : ListAdapter(SNIPPET_DIFF) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SnippetHolder = - SnippetHolder(parent.inflate(R.layout.view_item_snippet), clickListener, pressListener) - - override fun onBindViewHolder(holder: SnippetHolder, position: Int) { - holder.bind(getItem(position)) - } - - class SnippetHolder( - view: View, - private val clickListener: SnippetListener, - private val pressListener: SnippetListener - ) : RecyclerView.ViewHolder(view) { - - private fun ImageView.accented() = tint(R.color.highlight) - - private fun ImageView.regular() = tint(R.color.white) - - fun bind(snippet: Snippet) = with(itemView) { - setOnClick { clickListener(snippet) } - setOnLongClickListener { pressListener(snippet); true } - snippetTitle.text = snippet.title - snippetLanguage.text = snippet.language.raw - with(snippetCode) { text = snippet.code.highlighted } - bindReactions(snippet) - } - - private fun View.bindReactions(snippet: Snippet) = with(snippet) { - val likeDiff = numberOfLikes - numberOfDislikes - snippetLikeCounter.text = likeDiff.toString() - when (snippet.userReaction) { - UserReaction.LIKE -> { - snippetLikeIndicator.accented() - snippetDislikeIndicator.regular() - } - UserReaction.DISLIKE -> { - snippetLikeIndicator.regular() - snippetDislikeIndicator.accented() - } - UserReaction.NONE -> { - snippetLikeIndicator.regular() - snippetDislikeIndicator.regular() - } - } - } - } -} - -private val SNIPPET_DIFF = object : DiffUtil.ItemCallback() { - - override fun areItemsTheSame(oldItem: Snippet, newItem: Snippet): Boolean = - oldItem.uuid == newItem.uuid - - override fun areContentsTheSame(oldItem: Snippet, newItem: Snippet): Boolean = - oldItem == newItem -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewFragment.kt deleted file mode 100644 index fedaae5..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewFragment.kt +++ /dev/null @@ -1,97 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.preview - -import android.os.Bundle -import android.view.* -import androidx.fragment.app.DialogFragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import io.github.kbiakov.codeview.highlight.ColorThemeData -import io.github.kbiakov.codeview.highlight.SyntaxColors -import kotlinx.android.synthetic.main.fragment_preview.* -import org.koin.android.ext.android.inject -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.util.SyntaxTheme -import pl.tkadziolka.snipmeandroid.util.SyntaxWindowTheme -import pl.tkadziolka.snipmeandroid.util.extension.* - -private const val HALF = 0.5 -private const val ONE_THIRD = 0.3 - -class PreviewFragment : DialogFragment() { - - private val viewModel: PreviewViewModel by inject() - - private val args: PreviewFragmentArgs by navArgs() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View = inflater.inflate(R.layout.fragment_preview, container) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - showTransparentBackground() - showFullscreen() - - previewContainer.setOnOutsideClick(R.dimen.spacing_medium) { - dismiss() - } - - setupTitle() - setupCodeView() - setupActions() - } - - private fun setupTitle() { - with(previewTitle) { - text = args.title - maxWidth = getMaxWidthPercent(percent = HALF) - } - - with(previewLanguage) { - text = args.language - maxWidth = getMaxWidthPercent(percent = ONE_THIRD) - } - } - - private fun setupCodeView() { - previewCode.setCodeWithTheme(args.code, args.language) - } - - private fun setupActions() { - previewTitle.setOnClick { viewModel.goToDetail(findNavController(), args.uuid) } - previewCopy.setOnClick { - viewModel.copyToClipboard(args.title, args.code) - if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.R) { - // Android 12+ shows message itself - showToast(getString(R.string.message_copied, args.title)) - } - } - } - - private fun getMaxWidthPercent(percent: Double = HALF): Int { - val inset = resources.getDimensionPixelSize(R.dimen.spacing_medium) - val viewWidth = requireActivity().screenWidth - return ((viewWidth - 2 * inset) * percent).toInt() - } -} - -fun SyntaxTheme.toThemeData(window: SyntaxWindowTheme) = ColorThemeData( - syntaxColors = SyntaxColors( - type = keyword, - keyword = keyword, - literal = literal, - comment = comment, - string = text, - punctuation = keyword, - plain = code, - tag = code, - attrName = code, - attrValue = code, - declaration = code - ), - numColor = window.number, - noteColor = window.note, - bgNum = window.numberBackground, - bgContent = window.contentBackground -) \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewNavigator.kt deleted file mode 100644 index 3ff0e74..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewNavigator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.preview - -import androidx.navigation.NavController -import pl.tkadziolka.snipmeandroid.util.extension.navigateSafe - -class PreviewNavigator { - - fun goToDetail(navController: NavController, snippetId: String) { - navController.navigateSafe(PreviewFragmentDirections.goToDetail(snippetId)) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewViewModel.kt deleted file mode 100644 index 1ce814d..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/preview/PreviewViewModel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.preview - -import androidx.lifecycle.ViewModel -import androidx.navigation.NavController -import pl.tkadziolka.snipmeandroid.domain.clipboard.AddToClipboardUseCase - -class PreviewViewModel( - private val navigator: PreviewNavigator, - private val clipboard: AddToClipboardUseCase -): ViewModel() { - - fun goToDetail(nav: NavController, snippetId: String) { - navigator.goToDetail(nav, snippetId) - } - - fun copyToClipboard(label: String, code: String) { - clipboard(label, code) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/session/SessionViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/session/SessionViewModel.kt deleted file mode 100644 index 98d9274..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/session/SessionViewModel.kt +++ /dev/null @@ -1,32 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.session - -import androidx.lifecycle.ViewModel -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.domain.auth.LogoutUserUseCase -import pl.tkadziolka.snipmeandroid.util.extension.inProgress - -class SessionViewModel( - private val logout: LogoutUserUseCase -): ViewModel() { - private val disposables = CompositeDisposable() - private var logoutDisposable: Disposable? = null - - fun logOut(onComplete: () -> Unit) { - if (logoutDisposable.inProgress()) return - - logoutDisposable = logout() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { onComplete() } - .also { disposables += it } - } - - override fun onCleared() { - disposables.dispose() - super.onCleared() - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareFragment.kt deleted file mode 100644 index 9a0b193..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareFragment.kt +++ /dev/null @@ -1,103 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.share - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.DialogFragment -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import kotlinx.android.synthetic.main.fragment_share.* -import org.koin.android.ext.android.inject -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.share.ShareUser -import pl.tkadziolka.snipmeandroid.ui.viewmodel.observeNotNull -import pl.tkadziolka.snipmeandroid.util.extension.* - -class ShareFragment : DialogFragment() { - - private val viewModel: ShareViewModel by inject() - - private val args: ShareFragmentArgs by navArgs() - - private val adapter by lazy { - ShareUserAdapter { shareUser -> - viewModel.share(args.uuid, shareUser.user.id) - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View = inflater.inflate(R.layout.fragment_share, container) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - showTransparentBackground() - showFullscreen() - - shareContainer.setOnOutsideClick(R.dimen.spacing_medium) { - dismiss() - } - - shareList.adapter = adapter - shareName.addTextChangedListener { editable -> - viewModel.search(editable.toString(), args.uuid) - } - - shareCloseAction.setOnClick { - dismiss() - } - - observeViewModel() - } - - private fun observeViewModel() { - viewModel.state.observeNotNull(this) { state -> - hideLoading() - when (state) { - is Loading -> showLoading() - is Loaded -> showData(state.users) - is Shared -> { - showToast(getString(R.string.message_shared, state.shareUser.user.login)) - dismiss() - } - is Error -> { - showToast(state.message ?: getString(R.string.error_message_generic)) - dismiss() - } - } - } - - viewModel.event.observeNotNull(this) { event -> - when (event) { - is Alert -> showToast(event.message) - is Logout -> viewModel.goToLogin(findNavController()) - } - } - } - - private fun showLoading() { - shareLoading.visible() - shareHelperImage.gone() - shareHelper.gone() - } - - private fun hideLoading() { - shareLoading.gone() - } - - private fun showData(users: List?) { - if (users == null) { - shareHelperImage.visible() - shareHelper.visible() - adapter.submitList(null) - return - } - - shareHelperImage.gone() - shareHelper.gone() - adapter.submitList(users) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareUserAdapter.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareUserAdapter.kt deleted file mode 100644 index cb10dec..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareUserAdapter.kt +++ /dev/null @@ -1,62 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.share - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.view_item_user_share.view.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.domain.share.ShareUser -import pl.tkadziolka.snipmeandroid.util.extension.inflate -import pl.tkadziolka.snipmeandroid.util.extension.loadWithFallback - -typealias ShareActionListener = (ShareUser) -> Unit - -class ShareUserAdapter( - private val shareListener: ShareActionListener -) : ListAdapter(SHARE_DIFF) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShareUserHolder = - ShareUserHolder(parent.inflate(R.layout.view_item_user_share), shareListener) - - override fun onBindViewHolder(holder: ShareUserHolder, position: Int) { - holder.bind(getItem(position)) - } - - class ShareUserHolder( - view: View, - private val shareListener: ShareActionListener - ) : RecyclerView.ViewHolder(view) { - fun bind(shareUser: ShareUser) = with(itemView) { - shareUserName.text = shareUser.user.login - setupShareAction(shareUser) - shareUserImage.loadWithFallback(shareUser.user.photo) - } - - private fun View.setupShareAction(user: ShareUser) { - with(shareUserAction) { - val shared = user.isShared - text = getActionText(shared, context) - isEnabled = shared.not() - setOnClickListener { shareListener(user) } - } - } - - private fun getActionText(shared: Boolean, context: Context) = - if (shared) - context.getString(R.string.shared) - else - context.getString(R.string.share) - } -} - -private val SHARE_DIFF = object : DiffUtil.ItemCallback() { - - override fun areItemsTheSame(oldItem: ShareUser, newItem: ShareUser): Boolean = - oldItem.user.login == newItem.user.login - - override fun areContentsTheSame(oldItem: ShareUser, newItem: ShareUser): Boolean = - oldItem == newItem -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareViewModel.kt deleted file mode 100644 index 1e65317..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/share/ShareViewModel.kt +++ /dev/null @@ -1,102 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.share - -import androidx.navigation.NavController -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.domain.error.exception.* -import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages -import pl.tkadziolka.snipmeandroid.domain.share.ShareInteractor -import pl.tkadziolka.snipmeandroid.domain.share.ShareUser -import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable -import pl.tkadziolka.snipmeandroid.ui.session.SessionViewModel -import pl.tkadziolka.snipmeandroid.ui.viewmodel.SingleLiveEvent -import pl.tkadziolka.snipmeandroid.ui.viewmodel.StateViewModel -import pl.tkadziolka.snipmeandroid.util.extension.inProgress -import pl.tkadziolka.snipmeandroid.util.extension.navigateSafe - -class ShareViewModel( - private val errorMessages: ErrorMessages, - private val interactor: ShareInteractor, - private val session: SessionViewModel -) : StateViewModel(), ErrorParsable { - private var usersDisposable: Disposable? = null - private var shareDisposable: Disposable? = null - - private val mutableEvent = SingleLiveEvent() - val event = mutableEvent - - override fun parseError(throwable: Throwable) { - when (throwable) { - is ConnectionException -> setState(Error(errorMessages.parse(throwable))) - is ContentNotFoundException -> setState(Error(errorMessages.parse(throwable))) - is ForbiddenActionException -> setState(Error(errorMessages.parse(throwable))) - is NetworkNotAvailableException -> setState(Error(errorMessages.parse(throwable))) - is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout } - is RemoteException -> setState(Error(errorMessages.parse(throwable))) - is SessionExpiredException -> session.logOut { mutableEvent.value = Logout } - else -> setState(Error(errorMessages.parse(throwable))) - } - } - - init { - interactor.clearCachedUsers() - } - - fun goToLogin(nav: NavController) { - nav.navigateSafe(ShareFragmentDirections.goToLogin()) - } - - fun search(loginPhrase: String, snippetUuid: String) { - usersDisposable?.dispose() - - setState(Loading) - - if (loginPhrase.isBlank()) { - setState(Loaded(null)) - return - } - - usersDisposable = interactor.users(loginPhrase, snippetUuid) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { setState(Loaded(it)) }, - onError = { parseError(it) } - ).also { disposables += it } - } - - fun share(snippetUuid: String, userId: Int) { - if (state.value is Loading || shareDisposable.inProgress()) return - - getUser(userId)?.let { user -> - setState(Loading) - shareDisposable = interactor.share(snippetUuid, userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onComplete = { setState(Shared(user)) }, - onError = { mutableEvent.value = Alert(errorMessages.parse(it)) } - ).also { disposables += it } - } - } - - private fun getUser(id: Int): ShareUser? = - if (state.value is Loaded) { - (state.value as Loaded).users?.find { it.user.id == id } - } else { - null - } -} - -sealed class ShareViewState -object Loading : ShareViewState() -data class Loaded(val users: List?) : ShareViewState() -data class Shared(val shareUser: ShareUser) : ShareViewState() -data class Error(val message: String?) : ShareViewState() - -sealed class ShareEvent -data class Alert(val message: String) : ShareEvent() -object Logout : ShareEvent() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashFragment.kt deleted file mode 100644 index a9c1911..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashFragment.kt +++ /dev/null @@ -1,24 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.splash - -import androidx.navigation.fragment.findNavController -import pl.tkadziolka.snipmeandroid.ui.viewmodel.ViewModelFragment -import pl.tkadziolka.snipmeandroid.ui.viewmodel.observeNotNull - -class SplashFragment : ViewModelFragment(SplashViewModel::class) { - - override val layout: Int = pl.tkadziolka.snipmeandroid.R.layout.fragment_splash - - override fun onViewCreated() { - viewModel.init() - } - - override fun observeViewModel() { - viewModel.state.observeNotNull(this) { state -> - when(state) { - is Logged -> viewModel.goToMain(findNavController()) - else -> viewModel.goToLogin(findNavController()) - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashNavigator.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashNavigator.kt deleted file mode 100644 index 2021a87..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashNavigator.kt +++ /dev/null @@ -1,15 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.splash - -import androidx.navigation.NavController -import pl.tkadziolka.snipmeandroid.util.extension.navigateSafe - -class SplashNavigator { - - fun goToLogin(nav: NavController) { - nav.navigateSafe(SplashFragmentDirections.goToLogin()) - } - - fun goToMain(nav: NavController) { - nav.navigateSafe(SplashFragmentDirections.goToMain()) - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashViewModel.kt deleted file mode 100644 index 1555c2f..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/splash/SplashViewModel.kt +++ /dev/null @@ -1,45 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.splash - -import androidx.navigation.NavController -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers -import pl.tkadziolka.snipmeandroid.domain.auth.InitialLoginUseCase -import pl.tkadziolka.snipmeandroid.domain.error.exception.NotAuthorizedException -import pl.tkadziolka.snipmeandroid.ui.viewmodel.StateViewModel -import timber.log.Timber -import java.util.concurrent.TimeUnit - -class SplashViewModel( - private val initialLogin: InitialLoginUseCase, - private val navigator: SplashNavigator -): StateViewModel() { - - override fun init() { - initialLogin() - .delay(1, TimeUnit.SECONDS) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onComplete = { setState(Logged) }, - onError = { - if (it !is NotAuthorizedException) - Timber.e("Couldn't get token or user, error = $it") - setState(NotAuthorized) - } - ).also { disposables += it } - } - - fun goToLogin(nav: NavController) { - navigator.goToLogin(nav) - } - - fun goToMain(nav: NavController) { - navigator.goToMain(nav) - } -} - -sealed class SplashViewState -object NotAuthorized: SplashViewState() -object Logged: SplashViewState() \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/PersistedStateViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/PersistedStateViewModel.kt deleted file mode 100644 index 7b58cae..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/PersistedStateViewModel.kt +++ /dev/null @@ -1,20 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.viewmodel - -import androidx.lifecycle.LiveData -import androidx.lifecycle.SavedStateHandle - -const val STATE_KEY = "state" - -abstract class PersistedStateViewModel: StateViewModel() { - protected val stateHandle = SavedStateHandle() - - final override val mutableState = stateHandle.getLiveData(STATE_KEY) - override val state: LiveData = mutableState - - override fun init() { - if (stateHandle.contains(STATE_KEY).not()) - initState() - } - - abstract fun initState() -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/SingleLiveEvent.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/SingleLiveEvent.kt deleted file mode 100644 index 607c268..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/SingleLiveEvent.kt +++ /dev/null @@ -1,40 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.viewmodel - -import androidx.annotation.MainThread -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Observer -import timber.log.Timber -import java.util.concurrent.atomic.AtomicBoolean - -class SingleLiveEvent : MutableLiveData() { - private val mPending = AtomicBoolean(false) - - @MainThread - override fun observe(owner: LifecycleOwner, observer: Observer) { - if (hasActiveObservers()) { - Timber.w("Multiple observers registered but only one will be notified.") - } - // Observe the internal MutableLiveData - super.observe(owner, Observer { t -> - if (mPending.compareAndSet(true, false)) { - observer.onChanged(t) - } - }) - } - - @MainThread - override fun setValue(t: T?) { - mPending.set(true) - super.setValue(t) - } - - /** - * Used for cases where T is Void, to make calls cleaner. - */ - - @MainThread - fun call() { - value = null - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/StateViewModel.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/StateViewModel.kt deleted file mode 100644 index ebacd79..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/StateViewModel.kt +++ /dev/null @@ -1,25 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.viewmodel - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import io.reactivex.disposables.CompositeDisposable - -@Suppress("ImplicitThis") -abstract class StateViewModel : ViewModel() { - protected val disposables = CompositeDisposable() - - protected open val mutableState = MutableLiveData() - open val state: LiveData = mutableState - - open fun init() = Unit - - fun setState(newState: T) { - newState?.let { mutableState.value = it } - } - - override fun onCleared() { - disposables.clear() - super.onCleared() - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/ViewModelFragment.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/ViewModelFragment.kt deleted file mode 100644 index 107b5a1..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/ui/viewmodel/ViewModelFragment.kt +++ /dev/null @@ -1,34 +0,0 @@ -package pl.tkadziolka.snipmeandroid.ui.viewmodel - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.lifecycle.* -import org.koin.androidx.viewmodel.ext.android.getViewModel -import kotlin.reflect.KClass - -abstract class ViewModelFragment(private val clazz: KClass) : Fragment() { - protected lateinit var viewModel: T - - abstract val layout: Int - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View = inflater.inflate(layout, container, false) - - protected abstract fun onViewCreated() - protected abstract fun observeViewModel() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - viewModel = getViewModel(null, clazz) - observeViewModel() - onViewCreated() - } -} - -fun LiveData.observeNotNull(owner: LifecycleOwner, callback: (T) -> Unit) { - observe(owner, Observer { callback(it!!) }) -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/AndroidExtensions.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/AndroidExtensions.kt deleted file mode 100644 index 111b2f5..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/AndroidExtensions.kt +++ /dev/null @@ -1,76 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.extension - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.util.DisplayMetrics -import android.util.TypedValue -import android.view.ViewGroup -import android.widget.Toast -import androidx.annotation.AttrRes -import androidx.annotation.DrawableRes -import androidx.core.content.ContextCompat -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import pl.tkadziolka.snipmeandroid.util.SyntaxWindowTheme - -val Activity.screenWidth: Int get() { - val displayMetrics = DisplayMetrics() - windowManager.defaultDisplay.getMetrics(displayMetrics) - return displayMetrics.widthPixels -} - -val Context.syntaxWindowTheme get() = SyntaxWindowTheme( - number = getColorAttr(android.R.attr.textColorPrimary), - note = getColorAttr(android.R.attr.textColorPrimary), - numberBackground = getColorAttr(android.R.attr.colorBackgroundFloating), - contentBackground = getColorAttr(android.R.attr.colorBackgroundFloating) -) - -fun Context.drawable(@DrawableRes res: Int) = ContextCompat.getDrawable(this, res) - -fun Context.showToast(message: String) { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show() -} - -fun Context.safeOpenWebsite(url: String?) { - if (url == null) return - if (!url.startsWith("http://") && !url.startsWith("https://")) return - - val page: Uri = Uri.parse(url) - val browser = Intent(Intent.ACTION_VIEW, page) - if (browser.resolveActivity(packageManager) != null) { - startActivity(browser) - } -} - -fun Fragment.showToast(message: String) { - requireActivity().showToast(message) -} - -fun Context.getColorAttr(@AttrRes attrRes: Int): Int { - val typedValue = TypedValue() - theme.resolveAttribute(attrRes, typedValue, true) - return typedValue.data -} - -fun Fragment.back() { - findNavController().navigateUp() -} - -fun DialogFragment.showTransparentBackground() { - dialog?.window?.setBackgroundDrawableResource(android.R.color.transparent) -} - -// This doesn't work with drawable background with insets -fun DialogFragment.showCancelable(cancelable: Boolean = true) { - isCancelable = cancelable - this.dialog?.setCanceledOnTouchOutside(cancelable) -} - -fun DialogFragment.showFullscreen() { - val matchParent = ViewGroup.LayoutParams.MATCH_PARENT - dialog?.window?.setLayout(matchParent, matchParent) -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/CollectionExtensions.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/CollectionExtensions.kt deleted file mode 100644 index aef794e..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/CollectionExtensions.kt +++ /dev/null @@ -1,8 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.extension - -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetLanguageType -import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetLanguageMapper - -fun CharSequence.lines(count: Int) = lines().take(count) - -fun String?.toSnippetLanguage(): SnippetLanguageType = SnippetLanguageMapper.fromString(this) \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NavigationExtensions.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NavigationExtensions.kt deleted file mode 100644 index 433ceb2..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/NavigationExtensions.kt +++ /dev/null @@ -1,28 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.extension - -import androidx.activity.addCallback -import androidx.annotation.IdRes -import androidx.fragment.app.Fragment -import androidx.navigation.NavController -import androidx.navigation.NavDirections - -fun NavController.navigateUpTo(@IdRes resId: Int) { - if (currentDestination == null) return // On not null we can't decide - if (currentDestination!!.id == resId) return // Wanted destination was reached - navigateUp() // Go back - navigateUpTo(resId) -} - -fun NavController.navigateSafe(navDirections: NavDirections, retryCount: Int = 1) { - try { - navigate(navDirections) - } catch (e: Exception) { - if (retryCount > 1) navigateSafe(navDirections, retryCount - 1) - } -} - -fun Fragment.onBackPressed(action: () -> Unit) { - requireActivity().onBackPressedDispatcher.addCallback(this) { - action() - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/ViewExtensions.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/ViewExtensions.kt deleted file mode 100644 index 9dee79a..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/extension/ViewExtensions.kt +++ /dev/null @@ -1,133 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.extension - -import android.content.res.TypedArray -import android.os.Parcelable -import android.util.AttributeSet -import android.util.SparseArray -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.ViewGroup.MarginLayoutParams -import android.widget.EditText -import android.widget.ImageView -import androidx.annotation.ColorRes -import androidx.annotation.DimenRes -import androidx.annotation.LayoutRes -import androidx.core.content.ContextCompat -import androidx.core.view.children -import androidx.core.view.isInvisible -import androidx.core.view.isVisible -import com.bumptech.glide.Glide -import io.github.kbiakov.codeview.CodeView -import kotlinx.android.synthetic.main.fragment_preview.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.ui.preview.toThemeData -import pl.tkadziolka.snipmeandroid.util.SyntaxWindowTheme -import pl.tkadziolka.snipmeandroid.util.view.DebouncingOnClickListener - -fun ViewGroup.first(): View = getChildAt(0) - -fun View.setMargins( - left: Int? = null, - top: Int? = null, - right: Int? = null, - bottom: Int? = null -) { - if (layoutParams is MarginLayoutParams) { - val layoutParams = layoutParams as MarginLayoutParams - layoutParams.setMargins(left ?: 0, top ?: 0, right ?: 0, bottom ?: 0) - requestLayout() - } -} - -fun ViewGroup.saveChildViewStates(): SparseArray { - val childViewStates = SparseArray() - children.forEach { child -> child.saveHierarchyState(childViewStates) } - return childViewStates -} - -fun ViewGroup.restoreChildViewStates(childViewStates: SparseArray) { - children.forEach { child -> child.restoreHierarchyState(childViewStates) } -} - -fun View.getStyledAttributes(attrs: AttributeSet?, res: IntArray): TypedArray = - context.theme.obtainStyledAttributes(attrs, res, 0, 0) - -fun ViewGroup.inflate(@LayoutRes resource: Int, attachToRoot: Boolean = false): View { - return LayoutInflater.from(this.context).inflate(resource, this, attachToRoot)!! -} - -fun View.visible() { - isVisible = true -} - -fun View.invisible() { - isInvisible = true -} - -fun View.gone() { - isVisible = false -} - -fun EditText.getTyped() = text.toString() - -fun View.setOnClick(intervalMillis: Long = 300, doClick: (View) -> Unit) = - setOnClickListener( - DebouncingOnClickListener( - intervalMillis = intervalMillis, - doClick = doClick - ) - ) - -fun CodeView.setCodeWithTheme(code: String, language: String?, theme: SyntaxWindowTheme? = null) { - with(this) { - if (language != null) setCode(code, language) else setCode(code) - setOptions( - getOptions()!!.copy( - code = code, - language = language, - animateOnHighlight = false, - theme = pl.tkadziolka.snipmeandroid.util.SyntaxTheme() - .toThemeData(theme ?: context.syntaxWindowTheme) - ) - ) - } -} - -fun ImageView.loadWithFallback(image: String) { - Glide.with(this) - .load(image) - .fallback(R.drawable.placeholder) - .placeholder(R.drawable.placeholder) - .error(R.drawable.placeholder) - .into(this) -} - -fun ImageView.tint(@ColorRes res: Int) { - setColorFilter( - ContextCompat.getColor(context, res), - android.graphics.PorterDuff.Mode.SRC_IN - ) -} - -fun View.setOnOutsideClick(@DimenRes marginRes: Int, action: () -> Unit) { - val marginPx = resources.getDimensionPixelSize(marginRes) - setOnTouchListener { view, event -> - view.performClick() - return@setOnTouchListener when { - event.x <= marginPx -> { - action(); true - } - event.y <= marginPx -> { - action(); true - } - view.width - event.x <= marginPx -> { - action(); true - } - view.height - event.y <= marginPx -> { - action(); true - } - else -> false - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/CodeEditText.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/CodeEditText.kt deleted file mode 100644 index ac2f738..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/CodeEditText.kt +++ /dev/null @@ -1,29 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.view - -import android.content.Context -import android.text.Editable -import android.util.AttributeSet -import androidx.appcompat.widget.AppCompatEditText -import androidx.core.widget.addTextChangedListener -import pl.tkadziolka.snipmeandroid.util.SyntaxHighlighter - -class CodeEditText : AppCompatEditText { - // Default constructor with @JvmOverloads doesn't work - constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - - private var previous: String = "" - private var cursorPosition: Int = 0 - - init { - addTextChangedListener { text: Editable? -> - if (text.toString() != previous) { - previous = text.toString() - cursorPosition = selectionEnd - setText(SyntaxHighlighter.getHighlighted(text.toString(), ignoreError = true)) - setSelection(cursorPosition) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/DebouncingOnClickListener.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/DebouncingOnClickListener.kt deleted file mode 100644 index 9f83499..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/DebouncingOnClickListener.kt +++ /dev/null @@ -1,24 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.view - -import android.view.View - -class DebouncingOnClickListener( - private val intervalMillis: Long, - private val doClick: ((View) -> Unit) -) : View.OnClickListener { - - override fun onClick(v: View) { - if (enabled) { - enabled = false - v.postDelayed(ENABLE_AGAIN, intervalMillis) - doClick(v) - } - } - - companion object { - @JvmStatic - var enabled = true - private val ENABLE_AGAIN = - Runnable { enabled = true } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/LoadMoreListener.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/LoadMoreListener.kt deleted file mode 100644 index e3a2d11..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/LoadMoreListener.kt +++ /dev/null @@ -1,26 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.view - -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView - -private const val NO_ITEM = 0 -private const val ZERO_INDEX = 1 - -class LoadMoreListener( - private val itemsPerPage: Int = 1, - private val action: (Int) -> Unit -): RecyclerView.OnScrollListener() { - - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - if (dy > 0) { - // Scroll down - val linearLayoutManager = recyclerView.layoutManager as LinearLayoutManager - val lastItem = linearLayoutManager.findLastCompletelyVisibleItemPosition() - val itemsLeft = (lastItem + ZERO_INDEX) % itemsPerPage - if (lastItem > RecyclerView.NO_POSITION && itemsLeft == NO_ITEM) { - action(lastItem) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/OutlinedTextInput.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/OutlinedTextInput.kt deleted file mode 100644 index cee04d6..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/OutlinedTextInput.kt +++ /dev/null @@ -1,106 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.view - -import android.content.Context -import android.os.Bundle -import android.os.Parcelable -import android.text.InputType.TYPE_NULL -import android.util.AttributeSet -import android.util.SparseArray -import android.view.inputmethod.EditorInfo -import android.view.inputmethod.EditorInfo.IME_NULL -import android.widget.EditText -import android.widget.FrameLayout -import com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE -import kotlinx.android.synthetic.main.view_outlined_text_input.view.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.util.extension.getStyledAttributes -import pl.tkadziolka.snipmeandroid.util.extension.restoreChildViewStates -import pl.tkadziolka.snipmeandroid.util.extension.saveChildViewStates -import timber.log.Timber -import java.util.* -import kotlin.random.Random - -private const val SKIP_ANIMATION = true - -class OutlinedTextInput : FrameLayout { - private val editText: EditText? get() = outlinedTextField.editText - - private val randomId get() = Random(Date().time).nextInt(0, 10) - - constructor(context: Context) : this(context, null) - - constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) - - constructor(context: Context, attrs: AttributeSet?, style: Int) : super(context, attrs, style) { - inflate(context, R.layout.view_outlined_text_input, this) - editText?.id = randomId - getStyledAttributes(attrs, R.styleable.OutlinedTextInput).apply { - try { - with(outlinedTextField) { - hint = getString(R.styleable.OutlinedTextInput_android_hint) - editText?.setText(getString(R.styleable.OutlinedTextInput_android_text)) - editText?.inputType = - getInteger(R.styleable.OutlinedTextInput_android_inputType, TYPE_NULL) - editText?.imeOptions = - getInteger(R.styleable.OutlinedTextInput_android_imeOptions, IME_NULL) - editText?.isSingleLine = - getBoolean(R.styleable.OutlinedTextInput_android_singleLine, true) - - val isPassword = - getBoolean(R.styleable.OutlinedTextInput_android_password, false) - if (isPassword) { - endIconMode = END_ICON_PASSWORD_TOGGLE - passwordVisibilityToggleRequested(SKIP_ANIMATION) - editText?.inputType = EditorInfo.TYPE_TEXT_VARIATION_PASSWORD - } - } - } catch (e: Exception) { - Timber.e("Error during resolving style attributes, error = $e") - } finally { - recycle() - } - } - } - - var text: String - get() = editText?.text?.toString().orEmpty() - set(value) { - editText?.setText(value) - } - - var imeOptions: Int - get() = editText?.imeOptions ?: IME_NULL - set(options) { - editText?.imeOptions = options - } - - override fun dispatchSaveInstanceState(container: SparseArray) { - dispatchFreezeSelfOnly(container) - } - - override fun dispatchRestoreInstanceState(container: SparseArray) { - dispatchThawSelfOnly(container) - } - - override fun onSaveInstanceState(): Parcelable { - return Bundle().apply { - putParcelable(SUPER_STATE_KEY, super.onSaveInstanceState()) - putSparseParcelableArray(SPARSE_STATE_KEY, saveChildViewStates()) - } - } - - override fun onRestoreInstanceState(state: Parcelable?) { - var newState = state - if (newState is Bundle) { - val childrenState = newState.getSparseParcelableArray(SPARSE_STATE_KEY) - childrenState?.let { restoreChildViewStates(it) } - newState = newState.getParcelable(SUPER_STATE_KEY) - } - super.onRestoreInstanceState(newState) - } - - companion object { - private const val SPARSE_STATE_KEY = "SPARSE_STATE_KEY" - private const val SUPER_STATE_KEY = "SUPER_STATE_KEY" - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/SavedStateView.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/SavedStateView.kt deleted file mode 100644 index f43b3c7..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/SavedStateView.kt +++ /dev/null @@ -1,30 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.view - -import android.os.Bundle -import android.os.Parcelable - -private const val SUPER_STATE_KEY = "super_state_key" - -interface SavedStateView { - - val onSaveState: (Bundle) -> Bundle - - val onRestoreState: (Bundle) -> Unit - - // Call as onSaveInstance(super.onSaveInstanceState()) - fun onSaveInstance(superState: Parcelable?): Parcelable { - val bundle = Bundle() - bundle.putParcelable(SUPER_STATE_KEY, superState) - return onSaveState(bundle) - } - - // Call as super.onRestoreInstanceState(onRestoreInstance(state)) - fun onRestoreInstance(state: Parcelable?): Parcelable? { - var newState = state - if (state is Bundle) { - onRestoreState(state) - newState = state.getParcelable(SUPER_STATE_KEY) - } - return newState - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/SnippetsFilterView.kt b/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/SnippetsFilterView.kt deleted file mode 100644 index 2915613..0000000 --- a/app/src/main/java/pl/tkadziolka/snipmeandroid/util/view/SnippetsFilterView.kt +++ /dev/null @@ -1,102 +0,0 @@ -package pl.tkadziolka.snipmeandroid.util.view - -import android.content.Context -import android.os.Bundle -import android.os.Parcelable -import android.util.AttributeSet -import android.view.ViewGroup -import android.widget.TextView -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.view.forEach -import kotlinx.android.synthetic.main.view_snippets_filter.view.* -import pl.tkadziolka.snipmeandroid.R -import pl.tkadziolka.snipmeandroid.util.extension.drawable -import pl.tkadziolka.snipmeandroid.util.extension.first -import pl.tkadziolka.snipmeandroid.util.extension.setMargins -import pl.tkadziolka.snipmeandroid.util.extension.setOnClick -import pl.tkadziolka.snipmeandroid.util.view.SnippetFilter.* - -typealias FilterListener = (SnippetFilter) -> Unit - -enum class SnippetFilter { ALL, MINE, SHARED } - -class SnippetsFilterView : ConstraintLayout, SavedStateView { - private var shouldNotify = true - - var selectedFilter = ALL - - var filterListener: FilterListener? = null - - override val onSaveState: (Bundle) -> Bundle = { - it.apply { putString(SNIPPET_FILTER_KEY, selectedFilter.name) } - } - - override val onRestoreState: (Bundle) -> Unit = { - markFilter(valueOf(it.getString(SNIPPET_FILTER_KEY, ""))) - } - - constructor(context: Context) : this(context, null) - - constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) - - constructor(context: Context, attrs: AttributeSet?, style: Int) : super(context, attrs, style) { - inflate(context, R.layout.view_snippets_filter, this) - setupActions() - markFilter(selectedFilter) - } - - override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - super.onLayout(changed, left, top, right, bottom) - val child = filterViewScrollView.first() as ViewGroup - - if (child.width >= filterViewScrollView.width) { - val startMargin = resources.getDimensionPixelSize(R.dimen.spacing_big) - child.first().setMargins(left = startMargin) - } - } - - fun setFilter(filter: SnippetFilter) { - resetSelection() - selectedFilter = filter - val view = when (filter) { - ALL -> viewAllAction - MINE -> viewMineAction - SHARED -> viewSharedAction - } - view.setTextAppearance(R.style.TextAppearance_Filter_Selected) - view.background = context.drawable(R.drawable.filter_background_selected) - - if (shouldNotify) - filterListener?.invoke(filter) - } - - override fun onSaveInstanceState(): Parcelable? = onSaveInstance(super.onSaveInstanceState()) - - override fun onRestoreInstanceState(state: Parcelable?) = - super.onRestoreInstanceState(onRestoreInstance(state)) - - private fun markFilter(filter: SnippetFilter) { - shouldNotify = false - setFilter(filter) - shouldNotify = true - } - - private fun setupActions() { - viewAllAction.setOnClick { setFilter(ALL) } - viewMineAction.setOnClick { setFilter(MINE) } - viewSharedAction.setOnClick { setFilter(SHARED) } - } - - private fun resetSelection() { - filterViewContainer.forEach { view -> - (view as? TextView)?.let { - it.setTextAppearance(R.style.TextAppearance_Filter) - it.background = context.drawable(R.drawable.filter_background) - } - } - } - - private companion object { - private const val SNIPPET_FILTER_KEY = "snippet_filter_key" - } -} \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi/arrow_right.xml b/app/src/main/res/drawable-anydpi/arrow_right.xml deleted file mode 100644 index 62c4fac..0000000 --- a/app/src/main/res/drawable-anydpi/arrow_right.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable-hdpi/arrow_right.png b/app/src/main/res/drawable-hdpi/arrow_right.png deleted file mode 100644 index ab3a444..0000000 Binary files a/app/src/main/res/drawable-hdpi/arrow_right.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/warning_background.xml b/app/src/main/res/drawable-hdpi/warning_background.xml deleted file mode 100644 index 16cb219..0000000 --- a/app/src/main/res/drawable-hdpi/warning_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable-mdpi/arrow_right.png b/app/src/main/res/drawable-mdpi/arrow_right.png deleted file mode 100644 index 6553520..0000000 Binary files a/app/src/main/res/drawable-mdpi/arrow_right.png and /dev/null differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/arrow_right.png b/app/src/main/res/drawable-xhdpi/arrow_right.png deleted file mode 100644 index 8723439..0000000 Binary files a/app/src/main/res/drawable-xhdpi/arrow_right.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/arrow_right.png b/app/src/main/res/drawable-xxhdpi/arrow_right.png deleted file mode 100644 index bdf8b0f..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/arrow_right.png and /dev/null differ diff --git a/app/src/main/res/drawable/add.xml b/app/src/main/res/drawable/add.xml deleted file mode 100644 index 70046c4..0000000 --- a/app/src/main/res/drawable/add.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/check.xml b/app/src/main/res/drawable/check.xml deleted file mode 100644 index 2501e9f..0000000 --- a/app/src/main/res/drawable/check.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/close.xml b/app/src/main/res/drawable/close.xml deleted file mode 100644 index 70db409..0000000 --- a/app/src/main/res/drawable/close.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/copy.xml b/app/src/main/res/drawable/copy.xml deleted file mode 100644 index 0fb13c2..0000000 --- a/app/src/main/res/drawable/copy.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/dialog_background.xml b/app/src/main/res/drawable/dialog_background.xml deleted file mode 100644 index a97a82e..0000000 --- a/app/src/main/res/drawable/dialog_background.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/dialog_background_code.xml b/app/src/main/res/drawable/dialog_background_code.xml deleted file mode 100644 index c1095fd..0000000 --- a/app/src/main/res/drawable/dialog_background_code.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/dialog_background_inset.xml b/app/src/main/res/drawable/dialog_background_inset.xml deleted file mode 100644 index 6d5b2b8..0000000 --- a/app/src/main/res/drawable/dialog_background_inset.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/donate_option_background.xml b/app/src/main/res/drawable/donate_option_background.xml deleted file mode 100644 index 96a899b..0000000 --- a/app/src/main/res/drawable/donate_option_background.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/donate_option_background_disabled.xml b/app/src/main/res/drawable/donate_option_background_disabled.xml deleted file mode 100644 index 6ac7c4a..0000000 --- a/app/src/main/res/drawable/donate_option_background_disabled.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/donate_option_background_normal.xml b/app/src/main/res/drawable/donate_option_background_normal.xml deleted file mode 100644 index 504283f..0000000 --- a/app/src/main/res/drawable/donate_option_background_normal.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/donate_option_background_selected.xml b/app/src/main/res/drawable/donate_option_background_selected.xml deleted file mode 100644 index e083a3f..0000000 --- a/app/src/main/res/drawable/donate_option_background_selected.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/edit.xml b/app/src/main/res/drawable/edit.xml deleted file mode 100644 index faddfce..0000000 --- a/app/src/main/res/drawable/edit.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/error_illustration.xml b/app/src/main/res/drawable/error_illustration.xml deleted file mode 100644 index 97c0e78..0000000 --- a/app/src/main/res/drawable/error_illustration.xml +++ /dev/null @@ -1,706 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/filter_background.xml b/app/src/main/res/drawable/filter_background.xml deleted file mode 100644 index be101f7..0000000 --- a/app/src/main/res/drawable/filter_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/filter_background_selected.xml b/app/src/main/res/drawable/filter_background_selected.xml deleted file mode 100644 index dbad624..0000000 --- a/app/src/main/res/drawable/filter_background_selected.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/link_button_focused.xml b/app/src/main/res/drawable/link_button_focused.xml deleted file mode 100644 index c9430b4..0000000 --- a/app/src/main/res/drawable/link_button_focused.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/link_button_normal.xml b/app/src/main/res/drawable/link_button_normal.xml deleted file mode 100644 index 62547b9..0000000 --- a/app/src/main/res/drawable/link_button_normal.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/link_button_selector.xml b/app/src/main/res/drawable/link_button_selector.xml deleted file mode 100644 index ffacc06..0000000 --- a/app/src/main/res/drawable/link_button_selector.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/logo.png b/app/src/main/res/drawable/logo.png index 3f6efa6..10560be 100644 Binary files a/app/src/main/res/drawable/logo.png and b/app/src/main/res/drawable/logo.png differ diff --git a/app/src/main/res/drawable/more.xml b/app/src/main/res/drawable/more.xml deleted file mode 100644 index 6a7f274..0000000 --- a/app/src/main/res/drawable/more.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/outlined_input_background.xml b/app/src/main/res/drawable/outlined_input_background.xml deleted file mode 100644 index b014e64..0000000 --- a/app/src/main/res/drawable/outlined_input_background.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/placeholder.png b/app/src/main/res/drawable/placeholder.png deleted file mode 100644 index 4465a27..0000000 Binary files a/app/src/main/res/drawable/placeholder.png and /dev/null differ diff --git a/app/src/main/res/drawable/save.xml b/app/src/main/res/drawable/save.xml deleted file mode 100644 index 999020d..0000000 --- a/app/src/main/res/drawable/save.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/search.xml b/app/src/main/res/drawable/search.xml deleted file mode 100644 index e2dd96c..0000000 --- a/app/src/main/res/drawable/search.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/share.xml b/app/src/main/res/drawable/share.xml deleted file mode 100644 index 9300daf..0000000 --- a/app/src/main/res/drawable/share.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/share_placeholder.png b/app/src/main/res/drawable/share_placeholder.png deleted file mode 100644 index 577e5e0..0000000 Binary files a/app/src/main/res/drawable/share_placeholder.png and /dev/null differ diff --git a/app/src/main/res/drawable/thumb_down.xml b/app/src/main/res/drawable/thumb_down.xml deleted file mode 100644 index ab3f398..0000000 --- a/app/src/main/res/drawable/thumb_down.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/thumb_up.xml b/app/src/main/res/drawable/thumb_up.xml deleted file mode 100644 index 04078a6..0000000 --- a/app/src/main/res/drawable/thumb_up.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/warning.xml b/app/src/main/res/drawable/warning.xml deleted file mode 100644 index b9e2789..0000000 --- a/app/src/main/res/drawable/warning.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/font/comfortaa.ttf b/app/src/main/res/font/comfortaa.ttf deleted file mode 100644 index ace7263..0000000 Binary files a/app/src/main/res/font/comfortaa.ttf and /dev/null differ diff --git a/app/src/main/res/font/comfortaa_bold.ttf b/app/src/main/res/font/comfortaa_bold.ttf deleted file mode 100644 index 63fa94d..0000000 Binary files a/app/src/main/res/font/comfortaa_bold.ttf and /dev/null differ diff --git a/app/src/main/res/font/comfortaa_family.xml b/app/src/main/res/font/comfortaa_family.xml deleted file mode 100644 index d1180be..0000000 --- a/app/src/main/res/font/comfortaa_family.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/font/comfortaa_light.ttf b/app/src/main/res/font/comfortaa_light.ttf deleted file mode 100644 index 5f119c2..0000000 Binary files a/app/src/main/res/font/comfortaa_light.ttf and /dev/null differ diff --git a/app/src/main/res/font/inconsolata_medium.ttf b/app/src/main/res/font/inconsolata_medium.ttf deleted file mode 100644 index 3643283..0000000 Binary files a/app/src/main/res/font/inconsolata_medium.ttf and /dev/null differ diff --git a/app/src/main/res/font/raleway_medium.ttf b/app/src/main/res/font/raleway_medium.ttf deleted file mode 100644 index 5428c9c..0000000 Binary files a/app/src/main/res/font/raleway_medium.ttf and /dev/null differ diff --git a/app/src/main/res/font/raleway_thin.ttf b/app/src/main/res/font/raleway_thin.ttf deleted file mode 100644 index b8e542a..0000000 Binary files a/app/src/main/res/font/raleway_thin.ttf and /dev/null differ diff --git a/app/src/main/res/ic_launcher-playstore.png b/app/src/main/res/ic_launcher-playstore.png deleted file mode 100644 index 0e7f359..0000000 Binary files a/app/src/main/res/ic_launcher-playstore.png and /dev/null differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index afc75e1..cd378c8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,20 +1,13 @@ - + android:layout_height="match_parent" + android:background="@android:color/white"> - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_contact.xml b/app/src/main/res/layout/fragment_contact.xml deleted file mode 100644 index 5b0e881..0000000 --- a/app/src/main/res/layout/fragment_contact.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - -