diff --git a/README.md b/README.md
index 9a0f094..95e8ae5 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,40 @@
-This is gallery app
+
Gallery App
+
+GalleryApp is application show image from Unsplash API, based on MVVM architecture.
+
-# GalleryApp
+
+
+
+
+## Download
+Go to the [Releases](https://github.com/LNMCode/GalleryApp/releases) to download the latest APK.
+
+## UI Application
+
+[UI Application](https://www.figma.com/file/abtgGeg11LmHEAgyWqTfTg/Art-gallery-app-UI-(Community)?node-id=0%3A1) - UI of application based on ui shared in Figma.
+
+## Screenshots
+
+
+
+
+
+
+## Tech stack & Open-source libraries
+- Minimum SDK level 21
+- 100% [Kotlin](https://kotlinlang.org/) based + [Coroutines](https://github.com/Kotlin/kotlinx.coroutines) + [Flow](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/) for asynchronous.
+- JetPack
+ - [Lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle) - perform action when lifecycle state changes.
+ - [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) - store and manage UI-related data in a lifecycle conscious way.
+ - [Room](https://developer.android.com/topic/libraries/architecture/room) - a persistence library provides an abstraction layer over SQLite.
+- Architecture
+ - MVVM Architecture (View - DataBinding - ViewModel - Model)
+ - Repository pattern
+ - [Koin](https://github.com/InsertKoinIO/koin) - dependency injection
+- Material Design & Animations
+- [Retrofit2 & Gson](https://github.com/square/retrofit) - constructing the REST API
+- [OkHttp3](https://github.com/square/okhttp) - implementing interceptor, logging and mocking web server
+- [Glide](https://github.com/bumptech/glide) - loading images
+- [Timber](https://github.com/JakeWharton/timber) - logging
+- Shared element container transform/transition between fragments
diff --git a/app/README.md b/app/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/app/README.rm b/app/README.rm
new file mode 100644
index 0000000..e69de29
diff --git a/app/build.gradle b/app/build.gradle
index ed3e46a..24b604f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -40,10 +40,13 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.10"
+ implementation "org.jetbrains.kotlin:kotlin-reflect:1.6.21"
+
implementation 'androidx.core:core-ktx:1.7.0'
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.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
@@ -55,6 +58,7 @@ dependencies {
def retrofit2_version = "2.9.0"
implementation "com.squareup.retrofit2:retrofit:$retrofit2_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version"
+ implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
// -- Room
def room_version = "2.4.2"
@@ -67,13 +71,13 @@ dependencies {
def nav_version = "2.4.2"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
- implementation "androidx.navigation:navigation-runtime:$nav_version"
+ implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
def material_version = "1.5.0"
implementation "com.google.android.material:material:$material_version"
//glide
- def glide_version = "4.12.0"
+ def glide_version = "4.13.0"
implementation "com.github.bumptech.glide:glide:$glide_version"
kapt "com.github.bumptech.glide:compiler:$glide_version"
@@ -91,6 +95,11 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.2'
- implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
+ // architecture components
+ def lifecycle_version = "2.4.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
+ // debugging
+ implementation "com.jakewharton.timber:timber:5.0.1"
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8201d2b..a73485a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,8 +2,10 @@
+
+
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingActivity.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingActivity.kt
new file mode 100644
index 0000000..d784725
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingActivity.kt
@@ -0,0 +1,44 @@
+package com.lnmcode.galleryapp.bindables
+
+import androidx.annotation.LayoutRes
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingComponent
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ViewDataBinding
+
+abstract class BindingActivity constructor(
+ @LayoutRes private val contentLayoutId: Int
+) : AppCompatActivity() {
+
+ /** This interface is generated during compilation to contain getters for all used instance `BindingAdapters`. */
+ protected var bindingComponent: DataBindingComponent? = DataBindingUtil.getDefaultComponent()
+
+ /**
+ * A data-binding property will be initialized before being called [onCreate].
+ * And inflates using the [contentLayoutId] as a content view for activities.
+ */
+ @BindingOnly
+ protected val binding: T by lazy(LazyThreadSafetyMode.NONE) {
+ DataBindingUtil.setContentView(this, contentLayoutId, bindingComponent)
+ }
+
+ /**
+ * An executable inline binding function that receives a binding receiver in lambda.
+ *
+ * @param block A lambda block will be executed with the binding receiver.
+ * @return T A generic class that extends [ViewDataBinding] and generated by DataBinding on compile time.
+ */
+ @BindingOnly
+ protected inline fun binding(block: T.() -> Unit): T {
+ return binding.apply(block)
+ }
+
+ /**
+ * Ensures the [binding] property should be executed before being called [onCreate].
+ */
+ init {
+ addOnContextAvailableListener {
+ binding.notifyChange()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingExtensions.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingExtensions.kt
new file mode 100644
index 0000000..13287f0
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingExtensions.kt
@@ -0,0 +1,67 @@
+package com.lnmcode.galleryapp.bindables
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ViewDataBinding
+import kotlin.reflect.KFunction
+import kotlin.reflect.KProperty
+
+/**
+ *
+ * A binding extension for inflating a [layoutRes] and returns a DataBinding type [T].
+ *
+ * @param layoutRes The layout resource ID of the layout to inflate.
+ * @param attachToParent Whether the inflated hierarchy should be attached to the parent parameter.
+ *
+ * @return T A DataBinding class that inflated using the [layoutRes].
+ */
+@BindingOnly
+fun ViewGroup.binding(
+ @LayoutRes layoutRes: Int,
+ attachToParent: Boolean = false
+): T {
+ return DataBindingUtil.inflate(
+ LayoutInflater.from(context), layoutRes, this, attachToParent
+ )
+}
+
+/**
+ *
+ * A binding extension for inflating a [layoutRes] and returns a DataBinding type [T] with a receiver.
+ *
+ * @param layoutRes The layout resource ID of the layout to inflate.
+ * @param attachToParent Whether the inflated hierarchy should be attached to the parent parameter.
+ * @param block A DataBinding receiver lambda.
+ *
+ * @return T A DataBinding class that inflated using the [layoutRes].
+ */
+@BindingOnly
+fun ViewGroup.binding(
+ @LayoutRes layoutRes: Int,
+ attachToParent: Boolean = false,
+ block: T.() -> Unit
+): T {
+ return binding(layoutRes, attachToParent).apply(block)
+}
+
+/**
+ *
+ * Returns a binding ID by a [KProperty].
+ *
+ * @return A binding resource ID.
+ */
+internal fun KProperty<*>.bindingId(): Int {
+ return BindingManager.getBindingIdByProperty(this)
+}
+
+/**
+ *
+ * Returns a binding ID by a [KFunction].
+ *
+ * @return A binding resource ID.
+ */
+internal fun KFunction<*>.bindingId(): Int {
+ return BindingManager.getBindingIdByFunction(this)
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingFragment.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingFragment.kt
new file mode 100644
index 0000000..18cfe1e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingFragment.kt
@@ -0,0 +1,65 @@
+package com.lnmcode.galleryapp.bindables
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.CallSuper
+import androidx.annotation.LayoutRes
+import androidx.databinding.DataBindingComponent
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ViewDataBinding
+import androidx.fragment.app.Fragment
+
+abstract class BindingFragment constructor(
+ @LayoutRes private val contentLayoutId: Int
+) : Fragment() {
+
+ /** This interface is generated during compilation to contain getters for all used instance `BindingAdapters`. */
+ protected var bindingComponent: DataBindingComponent? = DataBindingUtil.getDefaultComponent()
+
+ /** A backing field for providing an immutable [binding] property. */
+ private var _binding: T? = null
+
+ /**
+ * A data-binding property will be initialized in [onCreateView].
+ * And provide the inflated view which depends on [contentLayoutId].
+ */
+ @BindingOnly
+ protected val binding: T
+ get() = checkNotNull(_binding) {
+ "Fragment $this binding cannot be accessed before onCreateView() or after onDestroyView()"
+ }
+
+ /**
+ * An executable inline binding function that receives a binding receiver in lambda.
+ *
+ * @param block A lambda block will be executed with the binding receiver.
+ * @return T A generic class that extends [ViewDataBinding] and generated by DataBinding on compile time.
+ */
+ @BindingOnly
+ protected inline fun binding(block: T.() -> Unit): T {
+ return binding.apply(block)
+ }
+
+ /**
+ * Ensures the [binding] property should be executed and provide the inflated view which depends on [contentLayoutId].
+ */
+ @CallSuper
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = DataBindingUtil.inflate(inflater, contentLayoutId, container, false, bindingComponent)
+ return binding.root
+ }
+
+ /**
+ * Destroys the [_binding] backing property for preventing leaking the [ViewDataBinding] that references the Context.
+ */
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingManager.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingManager.kt
new file mode 100644
index 0000000..e651d02
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingManager.kt
@@ -0,0 +1,81 @@
+package com.lnmcode.galleryapp.bindables
+
+import androidx.databinding.Bindable
+import java.util.*
+import kotlin.reflect.KFunction
+import kotlin.reflect.KProperty
+import kotlin.reflect.full.hasAnnotation
+import androidx.databinding.library.baseAdapters.BR
+
+object BindingManager {
+
+ /** A map for holding information of the generated fields in the BR class. */
+ @PublishedApi
+ internal var bindingFieldsMap: Map = emptyMap()
+
+ /** Java Bean conventions for presenting a boolean. */
+ private const val JAVA_BEANS_BOOLEAN: String = "is"
+
+ /** Java Bean conventions for presenting a getter. */
+ private const val JAVA_BEANS_GETTER: String = "get"
+
+ /** Java Bean conventions for presenting a setter. */
+ private const val JAVA_BEANS_SETTER: String = "set"
+
+ /**
+ * Binds the `BR` class into the [BindingManager].
+ * This method only needs to be called once in the application.
+ * The `BR` class will be disassembled by the [BindingManager], binding fields will be used
+ * for finding the proper binding ID of properties.
+ *
+ * @param T The `BR` class that generated by the DataBinding processor.
+ * @return The size of the stored fields.
+ */
+ inline fun bind(): Int {
+ synchronized(this) {
+ if (bindingFieldsMap.isNotEmpty()) return@synchronized
+ bindingFieldsMap = BR::class.java.fields.asSequence()
+ .map { it.name to it.getInt(null) }.toMap()
+ }
+
+ return bindingFieldsMap.size
+ }
+
+ /**
+ * Returns proper binding ID by property.
+ *
+ * @param property A kotlin [androidx.databinding.Bindable] property for finding proper binding ID.
+ */
+ internal fun getBindingIdByProperty(property: KProperty<*>): Int {
+ val bindingProperty = property.takeIf {
+ it.getter.hasAnnotation()
+ }
+ ?: throw IllegalArgumentException("KProperty: ${property.name} must be annotated with the `@Bindable` annotation on the getter.")
+ val propertyName = bindingProperty.name.decapitalize(Locale.ENGLISH)
+ val bindingPropertyName = propertyName
+ .takeIf { it.startsWith(JAVA_BEANS_BOOLEAN) }
+ ?.replaceFirst(JAVA_BEANS_BOOLEAN, String())
+ ?.decapitalize(Locale.ENGLISH) ?: propertyName
+ return bindingFieldsMap[bindingPropertyName] ?: BR._all
+ }
+
+ /**
+ * Returns proper binding ID by function.
+ *
+ * @param function A kotlin [androidx.databinding.Bindable] function for finding proper binding ID.
+ */
+ internal fun getBindingIdByFunction(function: KFunction<*>): Int {
+ val bindingFunction = function.takeIf {
+ it.hasAnnotation()
+ }
+ ?: throw IllegalArgumentException("KFunction: ${function.name} must be annotated with the `@Bindable` annotation.")
+ val functionName = bindingFunction.name.decapitalize(Locale.ENGLISH)
+ val bindingFunctionName = when {
+ functionName.startsWith(JAVA_BEANS_GETTER) -> functionName.replaceFirst(JAVA_BEANS_GETTER, String())
+ functionName.startsWith(JAVA_BEANS_SETTER) -> functionName.replaceFirst(JAVA_BEANS_SETTER, String())
+ functionName.startsWith(JAVA_BEANS_BOOLEAN) -> functionName.replaceFirst(JAVA_BEANS_BOOLEAN, String())
+ else -> throw IllegalArgumentException("@Bindable associated with method must follow JavaBeans convention $functionName")
+ }.decapitalize(Locale.ENGLISH)
+ return bindingFieldsMap[bindingFunctionName] ?: BR._all
+ }
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingObservable.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingObservable.kt
new file mode 100644
index 0000000..ddc72e9
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingObservable.kt
@@ -0,0 +1,46 @@
+package com.lnmcode.galleryapp.bindables
+
+import androidx.databinding.Observable
+import androidx.databinding.PropertyChangeRegistry
+import kotlin.reflect.KFunction
+import kotlin.reflect.KProperty
+
+interface BindingObservable: Observable {
+
+ /**
+ * Notifies a specific property has changed that matches in [PropertyChangeRegistry].
+ * This function receives a [androidx.databinding.Bindable] property and if there is a change notification of any of the
+ * listed properties, this value will be refreshed.
+ *
+ * @param property A [androidx.databinding.Bindable] property that should be changed.
+ */
+ fun notifyPropertyChanged(property: KProperty<*>)
+
+ /**
+ * Notifies a specific property has changed that matches in [PropertyChangeRegistry].
+ * This function receives a [androidx.databinding.Bindable] function and if there is a change notification of any of the
+ * listed properties, this value will be refreshed.
+ *
+ * @param function A [androidx.databinding.Bindable] function that should be changed.
+ */
+ fun notifyPropertyChanged(property: KFunction<*>)
+
+ /**
+ * Notifies a specific property has changed that matches in [PropertyChangeRegistry].
+ * This function receives a data-binding id depending on its property name and if there is a change
+ * notification of any of the listed properties, this value will be refreshed.
+ *
+ * @param bindingId A specific data-binding id (generated BR id) that should be changed.
+ */
+ fun notifyPropertyChanged(bindingId: Int)
+
+ /**
+ * Notifies listeners that all properties of this instance have changed.
+ */
+ fun notifyAllPropertiesChanged()
+
+ /**
+ * Clears all binding properties from the callback registry.
+ */
+ fun clearAllProperties()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingProperty.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingProperty.kt
new file mode 100644
index 0000000..a49b312
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingProperty.kt
@@ -0,0 +1,72 @@
+package com.lnmcode.galleryapp.bindables
+
+import androidx.databinding.PropertyChangeRegistry
+import androidx.lifecycle.SavedStateHandle
+import kotlin.reflect.KProperty
+
+/**
+ *
+ * A property for notifying a specific has changed that matches in [PropertyChangeRegistry].
+ * The getter for the property that changes should be marked with [androidx.databinding.Bindable].
+ *
+ * @param defaultValue A default value should be initialized.
+ *
+ * @return A delegation property [BindingPropertyIdWithDefaultValue].
+ */
+@BindingPropertyDelegate
+fun bindingProperty(defaultValue: T) =
+ BindingPropertyIdWithDefaultValue(defaultValue)
+
+/**
+ *
+ * A delegate class for holding value and notifying changed value on a property.
+ *
+ * @param value A default value should be initialized.
+ */
+class BindingPropertyIdWithDefaultValue(
+ private var value: T
+) {
+ operator fun getValue(bindingObservable: BindingObservable, property: KProperty<*>): T = value
+
+ operator fun setValue(bindingObservable: BindingObservable, property: KProperty<*>, value: T) {
+ if (this.value != value) {
+ this.value = value
+ bindingObservable.notifyPropertyChanged(property.bindingId())
+ }
+ }
+}
+
+
+/**
+ *
+ * A [SavedStateHandle] property for notifying a specific has changed that matches in [PropertyChangeRegistry].
+ * We can set and get value that matches with [key] from the [SavedStateHandle].
+ * Android associate the given value with the key. The value must have a type that could be stored in [android.os.Build].
+ * The getter for the property that changes should be marked with [androidx.databinding.Bindable].
+ *
+ * @param key A key for finding saved value.
+ *
+ * @return A delegation property [SavedStateHandleBindingProperty].
+ */
+@BindingPropertyDelegate
+fun SavedStateHandle.asBindingProperty(key: String) =
+ SavedStateHandleBindingProperty(this, key)
+
+/**
+ *
+ * A delegate class for persisting key-value map and notifying changed value on a property.
+ *
+ * @param savedStateHandle A handle to saved state passed down to [androidx.lifecycle.ViewModel].
+ * @param key A key for finding saved value.
+ */
+class SavedStateHandleBindingProperty(
+ private val savedStateHandle: SavedStateHandle,
+ private var key: String
+) {
+ operator fun getValue(bindingObservable: BindingObservable, property: KProperty<*>): T? = savedStateHandle.get(key)
+
+ operator fun setValue(bindingObservable: BindingObservable, property: KProperty<*>, value: T?) {
+ savedStateHandle.set(key, value)
+ bindingObservable.notifyPropertyChanged(property.bindingId())
+ }
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingPropertyDelegate.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingPropertyDelegate.kt
new file mode 100644
index 0000000..755bba3
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingPropertyDelegate.kt
@@ -0,0 +1,23 @@
+package com.lnmcode.galleryapp.bindables
+
+/** Specifies that this annotation should be used to mark binding delegate properties and functions. */
+
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@DslMarker
+@Retention(AnnotationRetention.BINARY)
+internal annotation class BindingPropertyDelegate
+
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@DslMarker
+@Retention(AnnotationRetention.BINARY)
+internal annotation class BindingOnly
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingViewModel.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingViewModel.kt
new file mode 100644
index 0000000..0593e1c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/BindingViewModel.kt
@@ -0,0 +1,72 @@
+package com.lnmcode.galleryapp.bindables
+
+import androidx.databinding.Observable
+import androidx.databinding.PropertyChangeRegistry
+import androidx.lifecycle.ViewModel
+import kotlin.reflect.KFunction
+import kotlin.reflect.KProperty
+import androidx.databinding.library.baseAdapters.BR
+
+abstract class BindingViewModel : ViewModel(), BindingObservable {
+
+ private val lock: Any = Any()
+
+ private var propertyCallbacks: PropertyChangeRegistry? = null
+
+
+ override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
+ synchronized(lock) lock@{
+ val propertyCallbacks =
+ propertyCallbacks ?: PropertyChangeRegistry().also { propertyCallbacks = it }
+ propertyCallbacks.add(callback)
+ }
+ }
+
+ override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
+ synchronized(lock) lock@{
+ val propertyCallbacks = propertyCallbacks ?: return@lock
+ propertyCallbacks.remove(callback)
+ }
+ }
+
+ override fun notifyPropertyChanged(property: KProperty<*>) {
+ synchronized(lock) lock@{
+ val propertyCallbacks = propertyCallbacks ?: return@lock
+ propertyCallbacks.notifyCallbacks(this, property.bindingId(), null)
+ }
+ }
+
+ override fun notifyPropertyChanged(function: KFunction<*>) {
+ synchronized(lock) lock@{
+ val propertyCallbacks = propertyCallbacks ?: return@lock
+ propertyCallbacks.notifyCallbacks(this, function.bindingId(), null)
+ }
+ }
+
+ override fun notifyPropertyChanged(bindingId: Int) {
+ synchronized(lock) lock@{
+ val propertyCallbacks = propertyCallbacks ?: return@lock
+ propertyCallbacks.notifyCallbacks(this, bindingId, null)
+ }
+ }
+
+ override fun notifyAllPropertiesChanged() {
+ synchronized(lock) lock@{
+ val propertyCallbacks = propertyCallbacks ?: return@lock
+ propertyCallbacks.notifyCallbacks(this, BR._all, null)
+ }
+ }
+
+ override fun clearAllProperties() {
+ synchronized(lock) lock@{
+ val propertyCallbacks = propertyCallbacks ?: return@lock
+ propertyCallbacks.clear()
+ }
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ clearAllProperties()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/bindables/FlowBindingProperty.kt b/app/src/main/java/com/lnmcode/galleryapp/bindables/FlowBindingProperty.kt
new file mode 100644
index 0000000..34c6b24
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/bindables/FlowBindingProperty.kt
@@ -0,0 +1,212 @@
+package com.lnmcode.galleryapp.bindables
+
+import androidx.databinding.PropertyChangeRegistry
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
+import kotlin.reflect.KProperty
+
+
+/**
+ *
+ * A [Flow] property for notifying a specific has changed that matches in [PropertyChangeRegistry].
+ * This property only collect the flow data on the [androidx.lifecycle.viewModelScope] and notify them.
+ * So this property is read-only, we can't set a value directly.
+ *
+ * The getter for the property that changes should be marked with [androidx.databinding.Bindable].
+ *
+ * @param defaultValue A default value for initializing the property value before flow emitting.
+ *
+ * @return A flow delegation property [FlowBindingPropertyIdWithDefaultValue].
+ */
+@BindingPropertyDelegate
+fun Flow.asBindingProperty(defaultValue: T) =
+ FlowBindingPropertyIdWithDefaultValue(this, defaultValue)
+
+
+/**
+ *
+ * A flow delegate class for collecting value and notifying changed value on a property.
+ *
+ * @param flow A flow for providing data.
+ * @param defaultValue A default value for initializing the property value before flow emitting.
+ */
+class FlowBindingPropertyIdWithDefaultValue constructor(
+ private val flow: Flow,
+ private val defaultValue: T,
+) {
+
+ operator fun provideDelegate(bindingViewModel: BindingViewModel, property: KProperty<*>): Delegate {
+ val bindingId = BindingManager.getBindingIdByProperty(property)
+ val delegate = Delegate(defaultValue, bindingId)
+ delegate.collect(flow, bindingViewModel)
+ return delegate
+ }
+
+ class Delegate(private var value: T, private val bindingId: Int) {
+ fun collect(flow: Flow, bindingViewModel: BindingViewModel) {
+ bindingViewModel.viewModelScope.launch {
+ flow.distinctUntilChanged().collect {
+ value = it
+ bindingViewModel.notifyPropertyChanged(bindingId)
+ }
+ }
+ }
+
+ operator fun getValue(thisRef: Any, property: KProperty<*>): T = value
+ }
+
+}
+
+/**
+ *
+ * A [Flow] property for notifying a specific has changed that matches in [PropertyChangeRegistry].
+ * This property only collect the flow data on the [androidx.lifecycle.viewModelScope] and notify them.
+ * So this property is read-only, we can't set a value directly.
+ *
+ * The getter for the property that changes should be marked with [androidx.databinding.Bindable].
+ *
+ * @param coroutineScope A [CoroutineScope] where the collecting should be lunched.
+ * @param defaultValue A default value for initializing the property value before flow emitting.
+ *
+ * @return A flow delegation property [FlowBindingPropertyIdWithDefaultValue].
+ */
+@BindingPropertyDelegate
+fun Flow.asBindingProperty(coroutineScope: CoroutineScope, defaultValue: T) =
+ FlowBindingPropertyIdWithDefaultValueOnScope(this, coroutineScope, defaultValue)
+
+/**
+ *
+ * A flow delegate class for collecting value and notifying changed value on a property.
+ *
+ * @param flow A flow for providing data.
+ * @param coroutineScope A [CoroutineScope] where the collecting should be lunched.
+ * @param defaultValue A default value for initializing the property value before flow emitting.
+ */
+class FlowBindingPropertyIdWithDefaultValueOnScope constructor(
+ private val flow: Flow,
+ private val coroutineScope: CoroutineScope,
+ private val defaultValue: T
+) {
+ operator fun provideDelegate(bindingObservable: BindingObservable, property: KProperty<*>): Delegate {
+ val bindingId = BindingManager.getBindingIdByProperty(property)
+ val delegate = Delegate(defaultValue, coroutineScope, bindingId)
+ delegate.collect(flow, bindingObservable)
+ return delegate
+ }
+
+ class Delegate(
+ private var value: T,
+ private val coroutineScope: CoroutineScope,
+ private val bindingId: Int
+ ) {
+ fun collect(flow: Flow, bindingObservable: BindingObservable) {
+ coroutineScope.launch {
+ flow.distinctUntilChanged().collect {
+ value = it
+ bindingObservable.notifyPropertyChanged(bindingId)
+ }
+ }
+ }
+
+ operator fun getValue(thisRef: Any, property: KProperty<*>): T = value
+ }
+}
+
+/**
+ *
+ * A [StateFlow] property for notifying a specific has changed that matches in [PropertyChangeRegistry].
+ * This property only collect the flow data on the [androidx.lifecycle.viewModelScope] and notify them.
+ * So this property is read-only, we can't set a value directly.
+ *
+ * The getter for the property that changes should be marked with [androidx.databinding.Bindable].
+ *
+ * @return A flow delegation property [StateFlowBindingPropertyId].
+ */
+@BindingPropertyDelegate
+fun StateFlow.asBindingProperty() =
+ StateFlowBindingPropertyId(this)
+
+/**
+ *
+ * A state flow delegate class for collecting value and notifying changed value on a property.
+ *
+ * @param stateFlow A state flow for providing data.
+ */
+class StateFlowBindingPropertyId constructor(
+ private val stateFlow: StateFlow,
+) {
+
+ operator fun provideDelegate(bindingViewModel: BindingViewModel, property: KProperty<*>): Delegate {
+ val delegate = Delegate(stateFlow, property.bindingId())
+ delegate.collect(bindingViewModel)
+ return delegate
+ }
+
+ class Delegate(private val stateFlow: StateFlow, private val bindingId: Int) {
+ fun collect(bindingViewModel: BindingViewModel) {
+ bindingViewModel.viewModelScope.launch {
+ stateFlow.collect {
+ bindingViewModel.notifyPropertyChanged(bindingId)
+ }
+ }
+ }
+
+ operator fun getValue(thisRef: Any, property: KProperty<*>): T = stateFlow.value
+ }
+}
+
+/**
+ *
+ * A [StateFlow] property for notifying a specific has changed that matches in [PropertyChangeRegistry].
+ * This property only collect the flow data on the [androidx.lifecycle.viewModelScope] and notify them.
+ * So this property is read-only, we can't set a value directly.
+ *
+ * The getter for the property that changes should be marked with [androidx.databinding.Bindable].
+ *
+ * @param coroutineScope A [CoroutineScope] where the collecting should be lunched.
+ *
+ * @return A flow delegation property [StateFlowBindingPropertyId].
+ */
+@BindingPropertyDelegate
+fun StateFlow.asBindingProperty(coroutineScope: CoroutineScope) =
+ StateFlowBindingPropertyIdOnScope(coroutineScope, this)
+
+/**
+ *
+ * A state flow delegate class for collecting value and notifying changed value on a property.
+ *
+ * @param coroutineScope A [CoroutineScope] where the collecting should be lunched.
+ * @param stateFlow A state flow for providing data.
+ */
+class StateFlowBindingPropertyIdOnScope constructor(
+ private val coroutineScope: CoroutineScope,
+ private val stateFlow: StateFlow,
+) {
+
+ operator fun provideDelegate(bindingObservable: BindingObservable, property: KProperty<*>): Delegate {
+ val delegate = Delegate(stateFlow, coroutineScope, property.bindingId())
+ delegate.collect(bindingObservable)
+ return delegate
+ }
+
+ class Delegate(
+ private val stateFlow: StateFlow,
+ private val coroutineScope: CoroutineScope,
+ private val bindingId: Int
+ ) {
+ fun collect(bindingObservable: BindingObservable) {
+ coroutineScope.launch {
+ stateFlow.collect {
+ bindingObservable.notifyPropertyChanged(bindingId)
+ }
+ }
+ }
+
+ operator fun getValue(thisRef: Any, property: KProperty<*>): T = stateFlow.value
+ }
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/AppDatabase.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/AppDatabase.kt
new file mode 100644
index 0000000..a44d103
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/AppDatabase.kt
@@ -0,0 +1,13 @@
+package com.lnmcode.galleryapp.business.datasource.cache
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import com.lnmcode.galleryapp.business.datasource.cache.topics.TopicsDao
+import com.lnmcode.galleryapp.business.datasource.cache.topics.TopicsEntities
+
+@Database(entities = [TopicsEntities::class], version = 1, exportSchema = true)
+abstract class AppDatabase: RoomDatabase() {
+
+ abstract fun topicsDao(): TopicsDao
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/helper/TopicsCacheRepository.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/helper/TopicsCacheRepository.kt
new file mode 100644
index 0000000..92e66b2
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/helper/TopicsCacheRepository.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.business.datasource.cache.helper
+
+import com.lnmcode.galleryapp.business.datasource.cache.topics.TopicsEntities
+
+interface TopicsCacheRepository {
+ suspend fun getTopics(): List
+
+ suspend fun getTopicsFromId(
+ id: String,
+ ): TopicsEntities
+
+ suspend fun insertAndReplace(
+ topicsEntities: TopicsEntities
+ ): Long
+
+ suspend fun deleteTopics(
+ topicsEntities: TopicsEntities
+ ): Int
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/helper/TopicsCacheRepositoryImpl.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/helper/TopicsCacheRepositoryImpl.kt
new file mode 100644
index 0000000..44ba26e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/helper/TopicsCacheRepositoryImpl.kt
@@ -0,0 +1,24 @@
+package com.lnmcode.galleryapp.business.datasource.cache.helper
+
+import com.lnmcode.galleryapp.business.datasource.cache.topics.TopicsDao
+import com.lnmcode.galleryapp.business.datasource.cache.topics.TopicsEntities
+
+class TopicsCacheRepositoryImpl constructor(
+ private val topicsDao: TopicsDao,
+) : TopicsCacheRepository {
+ override suspend fun getTopics(): List {
+ return topicsDao.getTopics()
+ }
+
+ override suspend fun getTopicsFromId(id: String): TopicsEntities {
+ return topicsDao.getTopicsFromId(id)
+ }
+
+ override suspend fun insertAndReplace(topicsEntities: TopicsEntities): Long {
+ return topicsDao.insertAndReplace(topicsEntities)
+ }
+
+ override suspend fun deleteTopics(topicsEntities: TopicsEntities): Int {
+ return topicsDao.deleteTopics(topicsEntities)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/topics/TopicsDao.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/topics/TopicsDao.kt
new file mode 100644
index 0000000..02d5d5c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/topics/TopicsDao.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.business.datasource.cache.topics
+
+import androidx.room.*
+
+@Dao
+interface TopicsDao {
+
+ @Query("SELECT * FROM topics")
+ suspend fun getTopics(): List
+
+ @Query("SELECT * FROM topics WHERE id = :id")
+ suspend fun getTopicsFromId(id: String): TopicsEntities
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertAndReplace(topicsEntities: TopicsEntities): Long
+
+ @Delete
+ suspend fun deleteTopics(topicsEntities: TopicsEntities): Int
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/topics/TopicsEntities.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/topics/TopicsEntities.kt
new file mode 100644
index 0000000..71e4f8e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/cache/topics/TopicsEntities.kt
@@ -0,0 +1,33 @@
+package com.lnmcode.galleryapp.business.datasource.cache.topics
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+
+@Entity(tableName = "topics")
+class TopicsEntities(
+
+ @PrimaryKey(autoGenerate = false)
+ @ColumnInfo(name = "id")
+ val id: String,
+
+ @ColumnInfo(name = "slug")
+ val slug: String,
+
+ @ColumnInfo(name = "title")
+ val title: String,
+
+ @ColumnInfo(name = "description")
+ val description: String,
+
+)
+
+fun TopicsEntities.toTopicsCacheDomain(): TopicsCacheDomain{
+ return TopicsCacheDomain(
+ id = id,
+ slug = slug,
+ title = title,
+ description = description,
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/interceptor/RequestInterceptor.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/interceptor/RequestInterceptor.kt
new file mode 100644
index 0000000..25e04ba
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/interceptor/RequestInterceptor.kt
@@ -0,0 +1,14 @@
+package com.lnmcode.galleryapp.business.datasource.interceptor
+
+import okhttp3.Interceptor
+import okhttp3.Response
+import timber.log.Timber
+
+class RequestInterceptor: Interceptor {
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val originalRequest = chain.request()
+ val request = originalRequest.newBuilder().url(originalRequest.url).build()
+ Timber.d(request.toString())
+ return chain.proceed(request)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchApiResponsitoryImpl.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchApiResponsitoryImpl.kt
new file mode 100644
index 0000000..c01b1d6
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchApiResponsitoryImpl.kt
@@ -0,0 +1,5 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.search
+
+class SearchApiRepositoryImpl(private val apiService: SearchApiService) : SearchApiRepository {
+ override suspend fun search(key: String, query: String) = apiService.search(key = key, query = query)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchApiService.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchApiService.kt
new file mode 100644
index 0000000..7dfa806
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchApiService.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.search
+
+
+import com.lnmcode.galleryapp.business.datasource.network.search.response.Search
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface SearchApiService {
+ @GET("search/photos")
+ suspend fun search(
+ @Query("client_id") key :String,
+ @Query("") query :String
+ ) :Search
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchIApiResponsitory.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchIApiResponsitory.kt
new file mode 100644
index 0000000..bfe72e6
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/search/SearchIApiResponsitory.kt
@@ -0,0 +1,10 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.search
+
+import com.lnmcode.galleryapp.business.datasource.network.search.response.Search
+
+interface SearchApiRepository {
+ suspend fun search(
+ key :String,
+ query :String
+ ) : Search
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiRepository.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiRepository.kt
new file mode 100644
index 0000000..f419787
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiRepository.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topic
+
+import com.lnmcode.galleryapp.business.datasource.network.topic.reponse.TopicDto
+
+
+interface TopicApiRepository {
+ suspend fun topic(
+ id :String,
+ key :String
+ ) : TopicDto
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiRepositoryImpl.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiRepositoryImpl.kt
new file mode 100644
index 0000000..f60ed22
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiRepositoryImpl.kt
@@ -0,0 +1,5 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topic
+
+class TopicApiRepositoryImpl(private val topicApiService: TopicApiService) :TopicApiRepository {
+ override suspend fun topic(id: String, key: String) = topicApiService.topicPhoto(id =id, key =key)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiService.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiService.kt
new file mode 100644
index 0000000..d907991
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topic/TopicApiService.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topic
+
+import com.lnmcode.galleryapp.business.datasource.network.topic.reponse.TopicDto
+
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+interface TopicApiService {
+ @GET("topics/{id_or_slug}")
+ suspend fun topicPhoto(
+ @Path("id_or_slug") id :String,
+ @Query("client_id") key :String
+ ): TopicDto
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiRepository.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiRepository.kt
new file mode 100644
index 0000000..3a6874b
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiRepository.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topicphoto
+
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.reponse.TopicPhotoResponse
+
+
+interface TopicPhotoIApiRepository {
+ suspend fun topicPhoto(
+ id :String,
+ key :String
+ ) : List
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiRepositoryImpl.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiRepositoryImpl.kt
new file mode 100644
index 0000000..37a1d8c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiRepositoryImpl.kt
@@ -0,0 +1,7 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topicphoto
+
+
+
+class TopicPhotoApiRepositoryImpl(private val topicApiService: TopicPhotoApiService) : TopicPhotoIApiRepository {
+ override suspend fun topicPhoto(id :String,key: String) = topicApiService.topicPhoto(id =id,key = key)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiService.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiService.kt
new file mode 100644
index 0000000..f49ce47
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topicphoto/TopicPhotoApiService.kt
@@ -0,0 +1,14 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topicphoto
+
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.reponse.TopicPhotoResponse
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+interface TopicPhotoApiService {
+ @GET("topics/{id_or_slug}/photos")
+ suspend fun topicPhoto(
+ @Path("id_or_slug") id :String,
+ @Query("client_id") key :String,
+ ): List
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsApiRepositoryImpl.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsApiRepositoryImpl.kt
new file mode 100644
index 0000000..16c20d5
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsApiRepositoryImpl.kt
@@ -0,0 +1,5 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topics
+
+class TopicsApiRepositoryImpl(private val topicsApiService: TopicsApiService) :TopicsIApiRepository {
+ override suspend fun topics(key: String) = topicsApiService.topics(key = key)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsApiService.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsApiService.kt
new file mode 100644
index 0000000..811ce1b
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsApiService.kt
@@ -0,0 +1,12 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topics
+
+import com.lnmcode.galleryapp.business.datasource.network.topics.respose.TopicsDto
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface TopicsApiService {
+ @GET("topics")
+ suspend fun topics(
+ @Query("client_id") key: String
+ ): List
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsIApiRepository.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsIApiRepository.kt
new file mode 100644
index 0000000..d8185d3
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/helper/topics/TopicsIApiRepository.kt
@@ -0,0 +1,9 @@
+package com.lnmcode.galleryapp.business.datasource.network.helper.topics
+
+import com.lnmcode.galleryapp.business.datasource.network.topics.respose.TopicsDto
+
+interface TopicsIApiRepository {
+ suspend fun topics(
+ key :String
+ ) : List
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchLinksDto.kt
new file mode 100644
index 0000000..93f761a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchLinksDto.kt
@@ -0,0 +1,10 @@
+package com.lnmcode.galleryapp.business.datasource.network.search
+
+import com.google.gson.annotations.SerializedName
+
+data class SearchLinksDto(
+ @SerializedName("self" ) val self : String,
+ @SerializedName("html" ) val html : String,
+ @SerializedName("download" ) val download : String,
+ @SerializedName("download_location" ) val downloadLocation : String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchResultsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchResultsDto.kt
new file mode 100644
index 0000000..a6ab91e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchResultsDto.kt
@@ -0,0 +1,22 @@
+package com.lnmcode.galleryapp.business.datasource.network.search
+
+import com.google.gson.annotations.SerializedName
+
+data class SearchResultsDto(
+ @SerializedName("id" ) val id : String,
+ @SerializedName("created_at" ) val createdAt : String,
+ @SerializedName("updated_at" ) val updatedAt : String,
+ @SerializedName("promoted_at" ) val promotedAt : String,
+ @SerializedName("width" ) val width : Int,
+ @SerializedName("height" ) val height : Int,
+ @SerializedName("color" ) val color : String,
+ @SerializedName("blur_hash" ) val blurHash : String,
+ @SerializedName("description" ) val description : String,
+ @SerializedName("alt_description" ) val altDescription : String,
+ @SerializedName("urls" ) val searchUrls : SearchUrlsDto,
+ @SerializedName("links" ) val links : SearchLinksDto,
+ @SerializedName("likes" ) val likes : Int?,
+ @SerializedName("liked_by_user" ) val likedByUser : Boolean,
+ @SerializedName("sponsorship" ) val sponsorship : String,
+ @SerializedName("user" ) val searchUser : SearchUsersDto,
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUrlsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUrlsDto.kt
new file mode 100644
index 0000000..38069ae
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUrlsDto.kt
@@ -0,0 +1,12 @@
+package com.lnmcode.galleryapp.business.datasource.network.search
+
+import com.google.gson.annotations.SerializedName
+
+data class SearchUrlsDto(
+ @SerializedName("raw" ) val raw : String,
+ @SerializedName("full" ) val full : String,
+ @SerializedName("regular" ) val regular : String,
+ @SerializedName("small" ) val small : String,
+ @SerializedName("thumb" ) val thumb : String,
+ @SerializedName("small_s3" ) val smallS3 : String
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUserLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUserLinksDto.kt
new file mode 100644
index 0000000..4222e9e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUserLinksDto.kt
@@ -0,0 +1,13 @@
+package com.lnmcode.galleryapp.business.datasource.network.search
+
+import com.google.gson.annotations.SerializedName
+
+data class SearchUserLinksDto(
+ @SerializedName("self" ) val self : String,
+ @SerializedName("html" ) val html : String,
+ @SerializedName("photos" ) val photos : String,
+ @SerializedName("likes" ) val likes : String,
+ @SerializedName("portfolio" ) val portfolio : String,
+ @SerializedName("following" ) val following : String,
+ @SerializedName("followers" ) val followers : String
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUserProfileImageDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUserProfileImageDto.kt
new file mode 100644
index 0000000..4e6d7c0
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUserProfileImageDto.kt
@@ -0,0 +1,9 @@
+package com.lnmcode.galleryapp.business.datasource.network.search
+
+import com.google.gson.annotations.SerializedName
+
+data class SearchUserProfileImageDto(
+ @SerializedName("small" ) val small : String,
+ @SerializedName("medium" ) val medium : String,
+ @SerializedName("large" ) val large : String
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUsersDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUsersDto.kt
new file mode 100644
index 0000000..2d689de
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/SearchUsersDto.kt
@@ -0,0 +1,24 @@
+package com.lnmcode.galleryapp.business.datasource.network.search
+
+import com.google.gson.annotations.SerializedName
+
+data class SearchUsersDto (
+ @SerializedName("id" ) val id : String,
+ @SerializedName("updated_at" ) val updatedAt : String,
+ @SerializedName("username" ) val username : String,
+ @SerializedName("name" ) val name : String,
+ @SerializedName("first_name" ) val firstName : String,
+ @SerializedName("last_name" ) val lastName : String,
+ @SerializedName("twitter_username" ) val twitterUsername : String,
+ @SerializedName("portfolio_url" ) val portfolioUrl : String,
+ @SerializedName("bio" ) val bio : String,
+ @SerializedName("location" ) val location : String,
+ @SerializedName("links" ) val links : SearchUserLinksDto,
+ @SerializedName("profile_image" ) val profileImage : SearchUserProfileImageDto,
+ @SerializedName("instagram_username" ) val instagramUsername : String,
+ @SerializedName("total_collections" ) val totalCollections : Int,
+ @SerializedName("total_likes" ) val totalLikes : Int,
+ @SerializedName("total_photos" ) val totalPhotos : Int,
+ @SerializedName("accepted_tos" ) val acceptedTos : Boolean,
+ @SerializedName("for_hire" ) val forHire : Boolean,
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/response/Search.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/response/Search.kt
new file mode 100644
index 0000000..e798cd3
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/search/response/Search.kt
@@ -0,0 +1,10 @@
+package com.lnmcode.galleryapp.business.datasource.network.search.response
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.search.SearchResultsDto
+
+data class Search(
+ @SerializedName("total" ) val total : Int,
+ @SerializedName("total_pages" ) val totalPages : Int?,
+ @SerializedName("results" ) val results : ArrayList
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverLinksDto.kt
new file mode 100644
index 0000000..03e8793
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverLinksDto.kt
@@ -0,0 +1,20 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicCoverLinks
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicCoverPhoto
+
+data class TopicCoverLinksDto(
+ @SerializedName("self" ) val self : String?,
+ @SerializedName("html" ) val html : String?,
+ @SerializedName("download" ) val download : String?,
+ @SerializedName("download_location" ) val downloadLocation : String?
+)
+fun TopicCoverLinksDto.toTopicCoverLinks() : TopicCoverLinks {
+ return TopicCoverLinks(
+ self=self,
+ html= html,
+ download=download,
+ downloadLocation=downloadLocation
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverPhotoDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverPhotoDto.kt
new file mode 100644
index 0000000..88419cb
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverPhotoDto.kt
@@ -0,0 +1,43 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicCoverPhoto
+
+data class TopicCoverPhotoDto(
+ @SerializedName("id" ) val id : String?,
+ @SerializedName("created_at" ) val createdAt : String?,
+ @SerializedName("updated_at" ) val updatedAt : String?,
+ @SerializedName("promoted_at" ) val promotedAt : String?,
+ @SerializedName("width" ) val width : Int?,
+ @SerializedName("height" ) val height : Int?,
+ @SerializedName("color" ) val color : String?,
+ @SerializedName("blur_hash" ) val blurHash : String?,
+ @SerializedName("description" ) val description : String?,
+ @SerializedName("alt_description" ) val altDescription : String?,
+ @SerializedName("urls" ) val topicCoverPhotoDto : TopicCoverPhotoDto?,
+ @SerializedName("links" ) val topicCoverUrlsDto : TopicCoverUrlsDto?,
+ @SerializedName("likes" ) val likes : Int?,
+ @SerializedName("liked_by_user" ) val likedByUser : Boolean?,
+ @SerializedName("sponsorship" ) val sponsorship : String?,
+ @SerializedName("user" ) val topicUserDto : TopicUserDto?
+)
+fun TopicCoverPhotoDto.toTopicCoverPhoto() : TopicCoverPhoto{
+ return TopicCoverPhoto(
+ id=id,
+ createdAt=createdAt,
+ updatedAt= updatedAt,
+ promotedAt= promotedAt,
+ width=width,
+ height= height,
+ color= color,
+ blurHash= blurHash,
+ description= description,
+ altDescription= altDescription,
+ topicCoverPhoto =topicCoverPhotoDto?.toTopicCoverPhoto() ,
+ topicCoverUrls =topicCoverUrlsDto?.toTopicCoverUrls(),
+ likes= likes,
+ likedByUser= likedByUser,
+ sponsorship= sponsorship,
+ topicUser = topicUserDto?.toTopicUser()
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverUrlsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverUrlsDto.kt
new file mode 100644
index 0000000..fcc5063
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicCoverUrlsDto.kt
@@ -0,0 +1,25 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicCoverPhoto
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicCoverUrls
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUrls
+
+data class TopicCoverUrlsDto(
+ @SerializedName("raw" ) val raw : String?,
+ @SerializedName("full" ) val full : String?,
+ @SerializedName("regular" ) val regular : String?,
+ @SerializedName("small" ) val small : String?,
+ @SerializedName("thumb" ) val thumb : String?,
+ @SerializedName("small_s3" ) val smallS3 : String?
+)
+fun TopicCoverUrlsDto.toTopicCoverUrls() : TopicCoverUrls {
+ return TopicCoverUrls(
+ raw=raw,
+ full =full,
+ regular= regular,
+ small= small,
+ thumb =thumb,
+ smallS3= smallS3
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicLinksDto.kt
new file mode 100644
index 0000000..9fba1c0
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicLinksDto.kt
@@ -0,0 +1,17 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicLinks
+
+data class TopicLinksDto (
+ @SerializedName("self" ) val self : String?,
+ @SerializedName("html" ) val html : String?,
+ @SerializedName("photos" ) val photos : String?
+ )
+fun TopicLinksDto.toTopicLinks() :TopicLinks{
+ return TopicLinks(
+ self=self,
+ html =html,
+ photos =photos
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersDto.kt
new file mode 100644
index 0000000..1999cea
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersDto.kt
@@ -0,0 +1,47 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicOwners
+
+data class TopicOwnersDto(
+ @SerializedName("id" ) val id : String?,
+ @SerializedName("updated_at" ) val updatedAt : String?,
+ @SerializedName("username" ) val username : String?,
+ @SerializedName("name" ) val name : String?,
+ @SerializedName("first_name" ) val firstName : String?,
+ @SerializedName("last_name" ) val lastName : String?,
+ @SerializedName("twitter_username" ) val twitterUsername : String?,
+ @SerializedName("portfolio_url" ) val portfolioUrl : String?,
+ @SerializedName("bio" ) val bio : String?,
+ @SerializedName("location" ) val location : String?,
+ @SerializedName("links" ) val topicOwnersLinksDto : TopicOwnersLinksDto? ,
+ @SerializedName("profile_image" ) val topicOwnersProfileImageDto: TopicOwnersProfileImageDto?,
+ @SerializedName("instagram_username" ) val instagramUsername : String?,
+ @SerializedName("total_collections" ) val totalCollections : Int?,
+ @SerializedName("total_likes" ) val totalLikes : Int?,
+ @SerializedName("total_photos" ) val totalPhotos : Int?,
+ @SerializedName("accepted_tos" ) val acceptedTos : Boolean?,
+ @SerializedName("for_hire" ) val forHire : Boolean?,
+
+)
+fun TopicOwnersDto.toTopicOwners() :TopicOwners{
+ return TopicOwners(
+ id = id,
+ updatedAt= updatedAt,
+ username= username,
+ name = name,
+ firstName= firstName,
+ lastName= lastName,
+ twitterUsername= twitterUsername,
+ portfolioUrl= portfolioUrl,
+ bio =bio,
+ location=location,topicOwnersLinks= topicOwnersLinksDto?.toTopicOwnersLinks(),
+ topicOwnersProfileImage =topicOwnersProfileImageDto?.toTopicOwnersProfileImage(),
+ instagramUsername=instagramUsername,
+ totalCollections= totalCollections,
+ totalLikes= totalLikes,
+ totalPhotos= totalPhotos,
+ acceptedTos= acceptedTos,
+ forHire =forHire
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersLinksDto.kt
new file mode 100644
index 0000000..7c7464c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersLinksDto.kt
@@ -0,0 +1,26 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicOwnersLinks
+
+data class TopicOwnersLinksDto(
+ @SerializedName("self" ) val self : String?,
+ @SerializedName("html" ) val html : String?,
+ @SerializedName("photos" ) val photos : String?,
+ @SerializedName("likes" ) val likes : String?,
+ @SerializedName("portfolio" ) val portfolio : String?,
+ @SerializedName("following" ) val following : String?,
+ @SerializedName("followers" ) val followers : String?
+
+)
+fun TopicOwnersLinksDto.toTopicOwnersLinks() :TopicOwnersLinks{
+ return TopicOwnersLinks(
+ self=self,
+ html=html,
+ photos=photos,
+ likes=likes,
+ portfolio=portfolio,
+ following=following,
+ followers =followers
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersProfileImageDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersProfileImageDto.kt
new file mode 100644
index 0000000..e88c840
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicOwnersProfileImageDto.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicOwnersProfileImage
+
+data class TopicOwnersProfileImageDto(
+ @SerializedName("small" ) val small : String?,
+ @SerializedName("medium" ) val medium : String?,
+ @SerializedName("large" ) val large : String?
+)
+fun TopicOwnersProfileImageDto.toTopicOwnersProfileImage() : TopicOwnersProfileImage {
+ return TopicOwnersProfileImage(
+ small=small, medium=medium, large=large
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsDto.kt
new file mode 100644
index 0000000..d625f47
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsDto.kt
@@ -0,0 +1,51 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicTopContributors
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicTopContributorsImageProfile
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicTopContributorsLinks
+
+data class TopicTopContributorsDto(
+ @SerializedName("id" ) val id : String?,
+ @SerializedName("updated_at" ) val updatedAt : String?,
+ @SerializedName("username" ) val username : String?,
+ @SerializedName("name" ) val name : String?,
+ @SerializedName("first_name" ) val firstName : String?,
+ @SerializedName("last_name" ) val lastName : String?,
+ @SerializedName("twitter_username" ) val twitterUsername : String?,
+ @SerializedName("portfolio_url" ) val portfolioUrl : String?,
+ @SerializedName("bio" ) val bio : String?,
+ @SerializedName("location" ) val location : String?,
+ @SerializedName("links" ) val topicTopContributorsLinksDto: TopicTopContributorsLinksDto?,
+ @SerializedName("profile_image" ) val topicTopContributorsImageProfileDto: TopicTopContributorsImageProfileDto?,
+ @SerializedName("instagram_username" ) val instagramUsername : String?,
+ @SerializedName("total_collections" ) val totalCollections : Int?,
+ @SerializedName("total_likes" ) val totalLikes : Int?,
+ @SerializedName("total_photos" ) val totalPhotos : Int?,
+ @SerializedName("accepted_tos" ) val acceptedTos : Boolean?,
+ @SerializedName("for_hire" ) val forHire : Boolean?,
+
+ )
+fun TopicTopContributorsDto.toTopicTopContributors() :TopicTopContributors{
+ return TopicTopContributors(
+ id =id,
+ updatedAt =updatedAt,
+ username=username,
+ name =name,
+ firstName= firstName,
+ lastName = lastName,
+ twitterUsername =twitterUsername,
+ portfolioUrl=portfolioUrl,
+ bio=bio,
+ location=location,
+ topicTopContributorsImageProfile = topicTopContributorsImageProfileDto?.toTopicTopContributorsImageProfile(),
+ topicTopContributorsLinks = topicTopContributorsLinksDto?.toTopicTopContributorsLinks(),
+ instagramUsername = instagramUsername,
+ totalCollections =totalCollections,
+ totalLikes = totalLikes,
+ totalPhotos = totalPhotos,acceptedTos = acceptedTos,
+ forHire = forHire
+
+
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsImageProfileDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsImageProfileDto.kt
new file mode 100644
index 0000000..da3111e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsImageProfileDto.kt
@@ -0,0 +1,16 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicTopContributorsImageProfile
+
+data class TopicTopContributorsImageProfileDto(
+
+ @SerializedName("small" ) val small : String?,
+ @SerializedName("medium" ) val medium : String?,
+ @SerializedName("large" ) val large : String?
+)
+fun TopicTopContributorsImageProfileDto.toTopicTopContributorsImageProfile() : TopicTopContributorsImageProfile{
+ return TopicTopContributorsImageProfile(
+ small=small, medium=medium, large=large
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsLinksDto.kt
new file mode 100644
index 0000000..d13c5f0
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicTopContributorsLinksDto.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicTopContributorsLinks
+
+data class TopicTopContributorsLinksDto(
+ @SerializedName("self" ) val self : String?,
+ @SerializedName("html" ) val html : String?,
+ @SerializedName("photos" ) val photos : String?,
+ @SerializedName("likes" ) val likes : String?,
+ @SerializedName("portfolio" ) val portfolio : String?,
+ @SerializedName("following" ) val following : String?,
+ @SerializedName("followers" ) val followers : String?
+)
+fun TopicTopContributorsLinksDto.toTopicTopContributorsLinks() :TopicTopContributorsLinks{
+ return TopicTopContributorsLinks(
+ self=self, html=html, photos=photos, likes=likes, portfolio=portfolio, following=following, followers=followers
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserDto.kt
new file mode 100644
index 0000000..d1d7f98
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserDto.kt
@@ -0,0 +1,52 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicUser
+
+data class TopicUserDto(
+ @SerializedName("id" ) val id : String?,
+ @SerializedName("updated_at" ) val updatedAt : String?,
+ @SerializedName("username" ) val username : String?,
+ @SerializedName("name" ) val name : String?,
+ @SerializedName("first_name" ) val firstName : String?,
+ @SerializedName("last_name" ) val lastName : String?,
+ @SerializedName("twitter_username" ) val twitterUsername : String?,
+ @SerializedName("portfolio_url" ) val portfolioUrl : String?,
+ @SerializedName("bio" ) val bio : String?,
+ @SerializedName("location" ) val location : String?,
+ @SerializedName("links" ) val topicUserLinksDto : TopicUserLinksDto? ,
+ @SerializedName("profile_image" ) val topicUserProfileImageDto: TopicUserProfileImageDto?,
+ @SerializedName("instagram_username" ) val instagramUsername : String?,
+ @SerializedName("total_collections" ) val totalCollections : Int?,
+ @SerializedName("total_likes" ) val totalLikes : Int?,
+ @SerializedName("total_photos" ) val totalPhotos : Int?,
+ @SerializedName("accepted_tos" ) val acceptedTos : Boolean?,
+ @SerializedName("for_hire" ) val forHire : Boolean?,
+)
+fun TopicUserDto.toTopicUser() :TopicUser{
+ return TopicUser(
+ username=username,
+ id=id,
+ updatedAt=updatedAt,
+ name =name,
+ firstName = firstName,
+ lastName = lastName,
+ twitterUsername = twitterUsername,
+ topicUserProfileImage = topicUserProfileImageDto?.toTopicUserProfileImage(),
+ portfolioUrl = portfolioUrl,
+ bio = bio,
+ location = location,
+ topicUserLinks = topicUserLinksDto?.toTopicUserLinks(),
+ instagramUsername = instagramUsername,
+ totalPhotos = totalPhotos,
+ totalLikes = totalLikes,
+ totalCollections = totalCollections,
+ acceptedTos = acceptedTos,
+ forHire = forHire
+
+
+
+
+
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserLinksDto.kt
new file mode 100644
index 0000000..db391ed
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserLinksDto.kt
@@ -0,0 +1,20 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicUserLinks
+
+data class TopicUserLinksDto (
+ @SerializedName("self" ) val self : String?,
+ @SerializedName("html" ) val html : String?,
+ @SerializedName("photos" ) val photos : String?,
+ @SerializedName("likes" ) val likes : String?,
+ @SerializedName("portfolio" ) val portfolio : String?,
+ @SerializedName("following" ) val following : String?,
+ @SerializedName("followers" ) val followers : String?
+ )
+fun TopicUserLinksDto.toTopicUserLinks() :TopicUserLinks{
+ return TopicUserLinks(
+ self=self, html=html, photos=photos, likes=likes, portfolio=portfolio, following=following, followers=followers
+ )
+}
+
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserProfileImageDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserProfileImageDto.kt
new file mode 100644
index 0000000..958aa49
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/TopicUserProfileImageDto.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topic.TopicUserProfileImage
+
+data class TopicUserProfileImageDto (
+ @SerializedName("small" ) val small : String?,
+ @SerializedName("medium" ) val medium : String?,
+ @SerializedName("large" ) val large : String?
+)
+fun TopicUserProfileImageDto.toTopicUserProfileImage() :TopicUserProfileImage{
+ return TopicUserProfileImage(
+ small=small, medium=medium, large=large
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/reponse/TopicDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/reponse/TopicDto.kt
new file mode 100644
index 0000000..727fae6
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topic/reponse/TopicDto.kt
@@ -0,0 +1,46 @@
+package com.lnmcode.galleryapp.business.datasource.network.topic.reponse
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.topic.*
+import com.lnmcode.galleryapp.business.domain.models.topic.Topic
+
+data class TopicDto(
+ @SerializedName("id" ) val id : String?,
+ @SerializedName("slug" ) val slug : String?,
+ @SerializedName("title" ) val title : String?,
+ @SerializedName("description" ) val description : String?,
+ @SerializedName("published_at" ) val publishedAt : String?,
+ @SerializedName("updated_at" ) val updatedAt : String?,
+ @SerializedName("starts_at" ) val startsAt : String?,
+ @SerializedName("ends_at" ) val endsAt : String?,
+ @SerializedName("only_submissions_after" ) val onlySubmissionsAfter : String?,
+ @SerializedName("featured" ) val featured : Boolean?,
+ @SerializedName("total_photos" ) val totalPhotos : Int?,
+ @SerializedName("total_current_user_submissions" ) val totalCurrentUserSubmissions : String?,
+ @SerializedName("links" ) val topicLinksDto : TopicLinksDto?,
+ @SerializedName("status" ) val status : String?,
+ @SerializedName("owners" ) val topicOwnersDto : List?,
+ @SerializedName("top_contributors" ) val topContributorsDto : List?,
+ @SerializedName("cover_photo" ) val topicCoverPhotoDto : TopicCoverPhotoDto?,
+)
+fun TopicDto.toTopicDomain() : Topic {
+ return Topic(
+ id = id,
+ slug= slug,
+ title =title,
+ description =description ,
+ publishedAt =publishedAt,
+ updatedAt =updatedAt,
+ startsAt =startsAt,
+ endsAt =endsAt,
+ onlySubmissionsAfter =onlySubmissionsAfter,
+ featured=featured,
+ totalPhotos=totalPhotos,
+ totalCurrentUserSubmissions=totalCurrentUserSubmissions,
+ topicLinks=topicLinksDto?.toTopicLinks(),
+ status =status,
+ topicOwners= topicOwnersDto?.map { it.toTopicOwners() },
+ topContributors= topContributorsDto?.map { it.toTopicTopContributors() },
+ topicCoverPhoto= topicCoverPhotoDto?.toTopicCoverPhoto()
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoLinksDto.kt
new file mode 100644
index 0000000..a103097
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoLinksDto.kt
@@ -0,0 +1,20 @@
+package com.lnmcode.galleryapp.business.datasource.network.topicphoto
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhotoLinks
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhotoUserProfileImage
+
+data class TopicPhotoLinksDto (
+ @SerializedName("self") val self : String,
+ @SerializedName("html") val html : String,
+ @SerializedName("download") val download : String,
+ @SerializedName("download_location") val downloadLocation : String
+)
+fun TopicPhotoLinksDto.toTopicPhotoLinks() : TopicPhotoLinks{
+ return TopicPhotoLinks(
+ self =self,
+ html=html,
+ download=download,
+ downloadLocation=downloadLocation
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUrlsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUrlsDto.kt
new file mode 100644
index 0000000..c475d6a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUrlsDto.kt
@@ -0,0 +1,23 @@
+package com.lnmcode.galleryapp.business.datasource.network.topicphoto
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhotoUrls
+
+data class TopicPhotoUrlsDto(
+ @SerializedName("raw") val raw : String,
+ @SerializedName("full") val full : String,
+ @SerializedName("regular") val regular : String,
+ @SerializedName("small") val small : String,
+ @SerializedName("thumb") val thumb : String,
+ @SerializedName("small_s3") val smallS3 : String
+)
+fun TopicPhotoUrlsDto.toTopicPhotoUrls() :TopicPhotoUrls{
+ return TopicPhotoUrls(
+ raw=raw,
+ full=full,
+ regular=regular,
+ small=small,
+ thumb=thumb,
+ smallS3=smallS3
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserDto.kt
new file mode 100644
index 0000000..b602815
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserDto.kt
@@ -0,0 +1,49 @@
+package com.lnmcode.galleryapp.business.datasource.network.topicphoto
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhotoUser
+
+
+data class TopicPhotoUserDto(
+ @SerializedName("id") val id : String,
+ @SerializedName("updated_at") val updatedAt : String,
+ @SerializedName("username") val username : String,
+ @SerializedName("name") val name : String,
+ @SerializedName("first_name") val firstName : String,
+ @SerializedName("last_name") val lastName : String?,
+// @SerializedName("portfolio_url") val portfolioUrl: String,
+ @SerializedName("bio") val bio : String?,
+ @SerializedName("location") val location : String?,
+ @SerializedName("links") val topicPhotoUserLinksDto : TopicPhotoUserLinksDto,
+ @SerializedName("profile_image") val topicPhotoUserprofileImageDto : TopicPhotoUserProfileImageDto,
+ @SerializedName("instagram_username") val instagramUsername : String?,
+ @SerializedName("total_collections") val totalCollections : Int,
+ @SerializedName("total_likes") val totalLikes : Int,
+ @SerializedName("total_photos") val totalPhotos : Int,
+ @SerializedName("accepted_tos") val acceptedTos : Boolean,
+ @SerializedName("for_hire") val forHire : Boolean,
+
+ )
+fun TopicPhotoUserDto.toTopicPhotoUser() :TopicPhotoUser{
+ return TopicPhotoUser(
+ id=id,
+ updatedAt=updatedAt,
+ username=username,
+ name=name,
+ firstName=firstName,
+ lastName=lastName,
+// portfolioUrl= portfolioUrl,
+ bio=bio,
+ location=location,
+ topicPhotoUserLinks = topicPhotoUserLinksDto.toTopicPhotoUserLinks(),
+ topicPhotoUserprofileImage = topicPhotoUserprofileImageDto.toTopicPhotoUserProfileImage(),
+ instagramUsername = instagramUsername,
+ totalCollections = totalCollections,
+ totalLikes =totalLikes,
+ totalPhotos =totalPhotos,
+ acceptedTos =acceptedTos,
+ forHire =forHire
+
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserLinksDto.kt
new file mode 100644
index 0000000..f8098bc
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserLinksDto.kt
@@ -0,0 +1,25 @@
+package com.lnmcode.galleryapp.business.datasource.network.topicphoto
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhotoUserLinks
+
+data class TopicPhotoUserLinksDto (
+ @SerializedName("self" ) val self : String,
+ @SerializedName("html" ) val html : String,
+ @SerializedName("photos" ) val photos : String,
+ @SerializedName("likes" ) val likes : String?,
+ @SerializedName("portfolio" ) val portfolio : String,
+ @SerializedName("following" ) val following : String,
+ @SerializedName("followers" ) val followers : String
+ )
+fun TopicPhotoUserLinksDto.toTopicPhotoUserLinks() :TopicPhotoUserLinks{
+ return TopicPhotoUserLinks(
+ self=self,
+ html=html,
+ photos=photos,
+ likes=likes,
+ portfolio=portfolio,
+ following=following,
+ followers=followers
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserProfileImageDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserProfileImageDto.kt
new file mode 100644
index 0000000..a524775
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/TopicPhotoUserProfileImageDto.kt
@@ -0,0 +1,17 @@
+package com.lnmcode.galleryapp.business.datasource.network.topicphoto
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhotoUserProfileImage
+
+data class TopicPhotoUserProfileImageDto(
+ @SerializedName("small" ) val small : String,
+ @SerializedName("medium" ) val medium : String,
+ @SerializedName("large" ) val large : String
+)
+fun TopicPhotoUserProfileImageDto.toTopicPhotoUserProfileImage() :TopicPhotoUserProfileImage{
+ return TopicPhotoUserProfileImage(
+ small=small,
+ medium=medium,
+ large=large
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/reponse/TopicPhotoResponse.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/reponse/TopicPhotoResponse.kt
new file mode 100644
index 0000000..cf8eb85
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topicphoto/reponse/TopicPhotoResponse.kt
@@ -0,0 +1,44 @@
+package com.lnmcode.galleryapp.business.datasource.network.topicphoto.reponse
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.*
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+
+data class TopicPhotoResponse(
+ @SerializedName("id") val id: String,
+ @SerializedName("created_at") val createdAt : String?,
+ @SerializedName("updated_at") val updatedAt : String?,
+ @SerializedName("promoted_at")val promotedAt : String?,
+ @SerializedName("width") val width: Int,
+ @SerializedName("height") val height: Int,
+ @SerializedName("color") val color : String,
+ @SerializedName("blur_hash") val blurHash: String,
+ @SerializedName("description") val description: String?,
+ @SerializedName("alt_description") val altDescription: String?,
+ @SerializedName("urls") val topicPhotoUrlsDto : TopicPhotoUrlsDto,
+ @SerializedName("links") val topicPhotoLinksDto : TopicPhotoLinksDto,
+ @SerializedName("likes") val likes: Int,
+ @SerializedName("liked_by_user") val likedByUser: Boolean,
+ //@SerializedName("sponsorship") val sponsorship: String?,
+ @SerializedName("user") val topicPhotoUserDto: TopicPhotoUserDto
+)
+fun TopicPhotoResponse.toTopicPhoto() : TopicPhoto{
+ return TopicPhoto(
+ id=id,
+ createdAt=createdAt,
+ updatedAt=updatedAt,
+ promotedAt=promotedAt,
+ width=width,
+ height=height,
+ color=color,
+ blurHash=blurHash,
+ description=description,
+ altDescription=altDescription,
+ topicPhotoUrls =topicPhotoUrlsDto.toTopicPhotoUrls(),
+ topicPhotoLinks =topicPhotoLinksDto.toTopicPhotoLinks(),
+ likes= likes,
+ likedByUser=likedByUser,
+ //sponsorship=sponsorship,
+ topicPhotoUser=topicPhotoUserDto.toTopicPhotoUser()
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoDto.kt
new file mode 100644
index 0000000..add7161
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoDto.kt
@@ -0,0 +1,45 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsCoverPhoto
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUsers
+
+data class TopicsCoverPhotoDto(
+ @SerializedName("id" ) val id : String,
+ @SerializedName("created_at" ) val createdAt : String,
+ @SerializedName("updated_at" ) val updatedAt : String,
+ @SerializedName("promoted_at" ) val promotedAt : String?,
+ @SerializedName("width" ) val width : Int,
+ @SerializedName("height" ) val height : Int,
+ @SerializedName("color" ) val color : String,
+ @SerializedName("blur_hash" ) val blurHash : String,
+ @SerializedName("description" ) val description : String?,
+ @SerializedName("alt_description" ) val altDescription : String?,
+ @SerializedName("urls" ) val topicsCoverPhotoUrlsDto : TopicsCoverPhotoUrlsDto,
+ @SerializedName("links" ) val topicsCoverPhotoLinksDto : TopicsCoverPhotoLinksDto,
+ @SerializedName("likes" ) val likes : Int?,
+ @SerializedName("liked_by_user" ) val likedByUser : Boolean,
+ @SerializedName("sponsorship" ) val sponsorship : String?,
+ @SerializedName("user" ) val topicsUser : TopicsUsers
+)
+
+fun TopicsCoverPhotoDto.toTopicsCoverPhoto() : TopicsCoverPhoto {
+ return TopicsCoverPhoto(
+ id = id,
+ createdAt = createdAt,
+ updatedAt = updatedAt,
+ promotedAt = promotedAt,
+ width = width,
+ height = height,
+ color = color,
+ blurHash = blurHash,
+ description = description,
+ altDescription = altDescription,
+ topicsCoverPhotoLinks = topicsCoverPhotoLinksDto.toTopicsCoverPhotoLinks(),
+ topicsCoverPhotoUrls = topicsCoverPhotoUrlsDto.toTopicsCoverPhotoUrlsDto(),
+ likes = likes,
+ likedByUser = likedByUser,
+ sponsorship = sponsorship,
+ topicsUser = topicsUser,
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoLinksDto.kt
new file mode 100644
index 0000000..c67157d
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoLinksDto.kt
@@ -0,0 +1,16 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsCoverPhotoLinks
+
+data class TopicsCoverPhotoLinksDto (
+ @SerializedName("self" ) val self : String,
+ @SerializedName("html" ) val html : String,
+ @SerializedName("download" ) val download : String,
+ @SerializedName("download_location" ) val downloadLocation : String
+ )
+
+fun TopicsCoverPhotoLinksDto.toTopicsCoverPhotoLinks(): TopicsCoverPhotoLinks {
+ return TopicsCoverPhotoLinks(
+ self, html, download, downloadLocation
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoUrlsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoUrlsDto.kt
new file mode 100644
index 0000000..5d6256a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsCoverPhotoUrlsDto.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsCoverPhotoUrls
+
+data class TopicsCoverPhotoUrlsDto (
+ @SerializedName("raw" ) val raw : String,
+ @SerializedName("full" ) val full : String,
+ @SerializedName("regular" ) val regular : String,
+ @SerializedName("small" ) val small : String,
+ @SerializedName("thumb" ) val thumb : String,
+ @SerializedName("small_s3" ) val smallS3 : String
+ )
+
+fun TopicsCoverPhotoUrlsDto.toTopicsCoverPhotoUrlsDto(): TopicsCoverPhotoUrls {
+ return TopicsCoverPhotoUrls(
+ raw, full, regular, small, thumb, smallS3
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsLinksDto.kt
new file mode 100644
index 0000000..2cccdf1
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsLinksDto.kt
@@ -0,0 +1,16 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsLinks
+
+data class TopicsLinksDto(
+ @SerializedName("self" ) val self : String,
+ @SerializedName("html" ) val html : String,
+ @SerializedName("photos" ) val photos : String
+)
+
+fun TopicsLinksDto.toTopicsLink(): TopicsLinks {
+ return TopicsLinks(
+ self, html, photos
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersDto.kt
new file mode 100644
index 0000000..2682053
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersDto.kt
@@ -0,0 +1,51 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsOwners
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsOwnersLinks
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsOwnersProfileImage
+
+data class TopicsOwnersDto(
+ @SerializedName("id" ) val id : String,
+ @SerializedName("updated_at" ) val updatedAt : String,
+ @SerializedName("username" ) val username : String,
+ @SerializedName("name" ) val name : String,
+ @SerializedName("first_name" ) val firstName : String,
+ @SerializedName("last_name" ) val lastName : String?,
+ @SerializedName("twitter_username" ) val twitterUsername : String?,
+ @SerializedName("portfolio_url" ) val portfolioUrl : String?,
+ @SerializedName("bio" ) val bio : String,
+ @SerializedName("location" ) val location : String,
+ @SerializedName("links" ) val topicsOwnerLinksDto : TopicsOwnersLinksDto,
+ @SerializedName("profile_image" ) val topicsOwnerProfileImageDto :TopicsOwnersProfileImageDto,
+ @SerializedName("instagram_username" ) val instagramUsername : String,
+ @SerializedName("total_collections" ) val totalCollections : Int,
+ @SerializedName("total_likes" ) val totalLikes : Int,
+ @SerializedName("total_photos" ) val totalPhotos : Int,
+ @SerializedName("accepted_tos" ) val acceptedTos : Boolean,
+ @SerializedName("for_hire" ) val forHire : Boolean,
+
+)
+
+fun TopicsOwnersDto.toTopicsOwners(): TopicsOwners {
+ return TopicsOwners(
+ id = id,
+ updatedAt = updatedAt,
+ username = username,
+ name = name,
+ firstName = firstName,
+ lastName = lastName,
+ twitterUsername = twitterUsername,
+ portfolioUrl = portfolioUrl,
+ bio = bio,
+ location = location,
+ topicsOwnerLinks = topicsOwnerLinksDto.toTopicsOwnersLinks(),
+ topicsOwnerProfileImage = topicsOwnerProfileImageDto.toTopicsOwnersProfileImage(),
+ instagramUsername = instagramUsername,
+ totalCollections = totalCollections,
+ totalLikes = totalLikes,
+ totalPhotos = totalPhotos,
+ acceptedTos = acceptedTos,
+ forHire = forHire
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersLinksDto.kt
new file mode 100644
index 0000000..8be28a6
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersLinksDto.kt
@@ -0,0 +1,21 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsOwnersLinks
+
+data class TopicsOwnersLinksDto(
+ @SerializedName("self" ) val self : String,
+ @SerializedName("html" ) val html : String,
+ @SerializedName("photos" ) val photos : String,
+ @SerializedName("likes" ) val likes : String,
+ @SerializedName("portfolio" ) val portfolio : String,
+ @SerializedName("following" ) val following : String,
+ @SerializedName("followers" ) val followers : String
+)
+
+fun TopicsOwnersLinksDto.toTopicsOwnersLinks(): TopicsOwnersLinks {
+ return TopicsOwnersLinks(
+ self = self,
+ html, photos, likes, portfolio, following, followers
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersProfileImageDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersProfileImageDto.kt
new file mode 100644
index 0000000..d52e303
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsOwnersProfileImageDto.kt
@@ -0,0 +1,16 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsOwnersProfileImage
+
+data class TopicsOwnersProfileImageDto (
+ @SerializedName("small" ) val small : String,
+ @SerializedName("medium" ) val medium : String,
+ @SerializedName("large" ) val large : String
+ )
+
+fun TopicsOwnersProfileImageDto.toTopicsOwnersProfileImage(): TopicsOwnersProfileImage {
+ return TopicsOwnersProfileImage(
+ small, medium, large
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsPreviewPhotoUrlsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsPreviewPhotoUrlsDto.kt
new file mode 100644
index 0000000..285c88b
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsPreviewPhotoUrlsDto.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsPreviewPhotoUrls
+
+data class TopicsPreviewPhotoUrlsDto (
+ @SerializedName("raw" ) val raw : String,
+ @SerializedName("full" ) val full : String,
+ @SerializedName("regular" ) val regular : String,
+ @SerializedName("small" ) val small : String,
+ @SerializedName("thumb" ) val thumb : String,
+ @SerializedName("small_s3" ) val smallS3 : String
+ )
+
+fun TopicsPreviewPhotoUrlsDto.toTopicsPreviewPhotoUrls(): TopicsPreviewPhotoUrls {
+ return TopicsPreviewPhotoUrls(
+ raw, full, regular, small, thumb, smallS3
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsPreviewPhotosDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsPreviewPhotosDto.kt
new file mode 100644
index 0000000..3a7f92f
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsPreviewPhotosDto.kt
@@ -0,0 +1,20 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsPreviewPhotoUrls
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsPreviewPhotos
+
+data class TopicsPreviewPhotosDto(
+ @SerializedName("id" ) val id : String,
+ @SerializedName("created_at" ) val createdAt : String,
+ @SerializedName("updated_at" ) val updatedAt : String,
+ @SerializedName("blur_hash" ) val blurHash : String,
+ @SerializedName("urls" ) val TopicsPreviewPhotoUrls : TopicsPreviewPhotoUrls
+
+)
+
+fun TopicsPreviewPhotosDto.toTopicsPreviewPhotos(): TopicsPreviewPhotos {
+ return TopicsPreviewPhotos(
+ id, createdAt, updatedAt, blurHash, TopicsPreviewPhotoUrls
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUrlsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUrlsDto.kt
new file mode 100644
index 0000000..9937f1d
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUrlsDto.kt
@@ -0,0 +1,20 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUrls
+
+data class TopicsUrlsDto (
+ @SerializedName("raw" ) val raw : String,
+ @SerializedName("full" ) val full : String,
+ @SerializedName("regular" ) val regular : String,
+ @SerializedName("small" ) val small : String,
+ @SerializedName("thumb" ) val thumb : String,
+ @SerializedName("small_s3" ) val smallS3 : String
+
+)
+
+fun TopicsUrlsDto.toTopicsUrls(): TopicsUrls {
+ return TopicsUrls(
+ raw, full, regular, small, thumb, smallS3
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUserLinksDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUserLinksDto.kt
new file mode 100644
index 0000000..dc12efb
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUserLinksDto.kt
@@ -0,0 +1,21 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUserLinks
+
+data class TopicsUserLinksDto(
+ @SerializedName("self" ) val self : String,
+ @SerializedName("html" ) val html : String,
+ @SerializedName("photos" ) val photos : String,
+ @SerializedName("likes" ) val likes : String,
+ @SerializedName("portfolio" ) val portfolio : String,
+ @SerializedName("following" ) val following : String,
+ @SerializedName("followers" ) val followers : String,
+
+)
+
+fun TopicsUserLinksDto.toTopicsUserLinks(): TopicsUserLinks {
+ return TopicsUserLinks(
+ self, html, photos, likes, portfolio, following, followers
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUserProfileImageDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUserProfileImageDto.kt
new file mode 100644
index 0000000..9a640bf
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUserProfileImageDto.kt
@@ -0,0 +1,17 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUserProfileImage
+
+data class TopicsUserProfileImageDto(
+ @SerializedName("small" ) val small : String,
+ @SerializedName("medium" ) val medium : String,
+ @SerializedName("large" ) val large : String
+
+)
+
+fun TopicsUserProfileImageDto.toTopicsUserProfileImage(): TopicsUserProfileImage {
+ return TopicsUserProfileImage(
+ small, medium, large
+ )
+}
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUsersDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUsersDto.kt
new file mode 100644
index 0000000..799c61e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/TopicsUsersDto.kt
@@ -0,0 +1,46 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUserLinks
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUserProfileImage
+import com.lnmcode.galleryapp.business.domain.models.topics.TopicsUsers
+
+data class TopicsUsersDto(
+ @SerializedName("id" ) val id : String,
+ @SerializedName("updated_at" ) val updatedAt : String,
+ @SerializedName("username" ) val username : String,
+ @SerializedName("name" ) val name : String,
+ @SerializedName("first_name" ) val firstName : String,
+ @SerializedName("last_name" ) val lastName : String,
+ @SerializedName("twitter_username" ) val twitterUsername : String,
+ @SerializedName("portfolio_url" ) val portfolioUrl : String,
+ @SerializedName("bio" ) val bio : String,
+ @SerializedName("location" ) val location : String,
+ @SerializedName("links" ) val topicsUserlinksDto : TopicsUserLinksDto,
+ @SerializedName("profile_image" ) val topicsUserProfileImageDto : TopicsUserProfileImageDto,
+ @SerializedName("instagram_username" ) val instagramUsername : String,
+ @SerializedName("total_collections" ) val totalCollections : Int,
+ @SerializedName("total_likes" ) val totalLikes : Int,
+ @SerializedName("total_photos" ) val totalPhotos : Int,
+ @SerializedName("accepted_tos" ) val acceptedTos : Boolean,
+ @SerializedName("for_hire" ) val forHire : Boolean,
+
+ )
+
+fun TopicsUsersDto.toTopicsUsers(): TopicsUsers {
+ return TopicsUsers(
+ id = id,
+ updatedAt = updatedAt,
+ username = username,
+ name = name,
+ firstName = firstName,
+ lastName = lastName,
+ twitterUsername = twitterUsername,
+ portfolioUrl = portfolioUrl,
+ bio = bio,
+ location = location,
+ topicsUserLinks = topicsUserlinksDto.toTopicsUserLinks(),
+ topicsUserProfileImage = topicsUserProfileImageDto.toTopicsUserProfileImage(),
+ instagramUsername, totalCollections, totalLikes, totalPhotos, acceptedTos, forHire
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/respose/TopicsDto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/respose/TopicsDto.kt
new file mode 100644
index 0000000..b5a1b23
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/network/topics/respose/TopicsDto.kt
@@ -0,0 +1,47 @@
+package com.lnmcode.galleryapp.business.datasource.network.topics.respose
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.topics.*
+import com.lnmcode.galleryapp.business.domain.models.topics.Topics
+
+data class TopicsDto(
+ @SerializedName("id" ) val id : String,
+ @SerializedName("slug" ) val slug : String,
+ @SerializedName("title" ) val title : String,
+ @SerializedName("description" ) val description : String,
+ @SerializedName("published_at" ) val publishedAt : String,
+ @SerializedName("updated_at" ) val updatedAt : String,
+ @SerializedName("starts_at" ) val startsAt : String,
+ @SerializedName("ends_at" ) val endsAt : String?,
+ @SerializedName("only_submissions_after" ) val onlySubmissionsAfter : String?,
+ @SerializedName("featured" ) val featured : Boolean,
+ @SerializedName("total_photos" ) val totalPhotos : Int,
+ @SerializedName("total_current_user_submissions" ) val totalCurrentUserSubmissions : String?,
+ @SerializedName("links" ) val topicsLinkDto : TopicsLinksDto,
+ @SerializedName("status" ) val status : String,
+ @SerializedName("owners" ) val topicsOwnersDto : List,
+ @SerializedName("cover_photo" ) val topicsCoverPhotoDto : TopicsCoverPhotoDto?,
+ @SerializedName("preview_photos" ) val topicsPreviewPhotosDto : List
+)
+
+fun TopicsDto.toTopicsDomain(): Topics {
+ return Topics(
+ id = id,
+ slug = slug,
+ title = title,
+ description = description,
+ publishedAt = publishedAt,
+ updatedAt = updatedAt,
+ startsAt = startsAt,
+ endsAt = endsAt,
+ onlySubmissionsAfter = onlySubmissionsAfter,
+ featured = featured,
+ totalPhotos = totalPhotos,
+ totalCurrentUserSubmissions = totalCurrentUserSubmissions,
+ topicsLinkDto = topicsLinkDto.toTopicsLink(),
+ status = status,
+ topicsOwners = topicsOwnersDto.map { it.toTopicsOwners() },
+ topicsCoverPhoto = topicsCoverPhotoDto?.toTopicsCoverPhoto(),
+ topicsPreviewPhotos = topicsPreviewPhotosDto.map { it.toTopicsPreviewPhotos() },
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/SearchUseCase.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/SearchUseCase.kt
new file mode 100644
index 0000000..9667530
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/SearchUseCase.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.business.datasource.usecase
+
+import com.lnmcode.galleryapp.business.datasource.network.helper.search.SearchApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.search.SearchResultsDto
+
+class SearchUseCase(private val searchApiRepository: SearchApiRepository) {
+ suspend fun getSearch(query :String, key: String) : List{
+ val repository = searchApiRepository.search(query = query, key = key)
+ return repository.results
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicPhotoUseCase.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicPhotoUseCase.kt
new file mode 100644
index 0000000..d04246b
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicPhotoUseCase.kt
@@ -0,0 +1,38 @@
+package com.lnmcode.galleryapp.business.datasource.usecase
+import android.util.Log
+import androidx.annotation.WorkerThread
+import com.lnmcode.galleryapp.business.datasource.network.helper.topic.TopicApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.helper.topicphoto.TopicPhotoIApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.topic.reponse.toTopicDomain
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.reponse.TopicPhotoResponse
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.reponse.toTopicPhoto
+import com.lnmcode.galleryapp.business.domain.models.topic.Topic
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.*
+import timber.log.Timber
+
+class TopicPhotoUseCase(
+ private val keyApi :String,
+ private val topicPhotoApiRepository: TopicPhotoIApiRepository
+) {
+ init {
+ Timber.d("Injection TopicsPhotoUserCase")
+
+ }
+ @WorkerThread
+ fun getTopicPhoto(
+ topicsId: String,
+ onSuccess: () -> Unit,
+ ) : Flow> = flow{
+ val topicPhoto = topicPhotoApiRepository.topicPhoto(id= topicsId, key =keyApi).map { it.toTopicPhoto()
+ }
+ emit(topicPhoto)
+ }.onCompletion {
+ onSuccess()
+
+ }.flowOn(Dispatchers.IO).catch {e->
+ Timber.e(e.message)
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicUseCase.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicUseCase.kt
new file mode 100644
index 0000000..1c2770e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicUseCase.kt
@@ -0,0 +1,28 @@
+package com.lnmcode.galleryapp.business.datasource.usecase
+import androidx.annotation.WorkerThread
+import com.lnmcode.galleryapp.business.datasource.network.helper.topic.TopicApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.topic.reponse.toTopicDomain
+import com.lnmcode.galleryapp.business.domain.models.topic.Topic
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.*
+import timber.log.Timber
+
+
+class TopicUseCase(
+ private val keyApi :String,
+ private val topicApiRepository: TopicApiRepository) {
+ init {
+ Timber.d("Injection TopicsUserCase")
+
+ }
+ @WorkerThread
+ fun getTopic(
+ id: String,
+ onSuccess: () -> Unit,
+ ) : Flow = flow{
+ val topic = topicApiRepository.topic(id=id, key =keyApi).toTopicDomain()
+ emit(topic)
+ }.onCompletion { }.flowOn(Dispatchers.IO).catch {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicsCacheUseCase.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicsCacheUseCase.kt
new file mode 100644
index 0000000..8117909
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicsCacheUseCase.kt
@@ -0,0 +1,64 @@
+package com.lnmcode.galleryapp.business.datasource.usecase
+
+import androidx.annotation.WorkerThread
+import com.lnmcode.galleryapp.business.datasource.cache.helper.TopicsCacheRepository
+import com.lnmcode.galleryapp.business.datasource.cache.topics.toTopicsCacheDomain
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.business.domain.cache.toEntity
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.*
+import timber.log.Timber
+
+class TopicsCacheUseCase(
+ private val topicsCacheRepository: TopicsCacheRepository,
+) {
+
+ init {
+ Timber.d("Injection TopicsCacheUseCase")
+ }
+
+ @WorkerThread
+ fun getTopics(
+ onSuccess: () -> Unit,
+ ): Flow> = flow {
+ val topicsCache = topicsCacheRepository.getTopics().map { it.toTopicsCacheDomain() }
+ emit(topicsCache)
+ }.onCompletion { onSuccess() }.flowOn(Dispatchers.IO).catch { e ->
+ Timber.e(e.message)
+ }
+
+ @WorkerThread
+ fun getTopicsFromId(
+ id: String,
+ onSuccess: () -> Unit,
+ ): Flow = flow {
+ val topics = topicsCacheRepository.getTopicsFromId(id).toTopicsCacheDomain()
+ emit(topics)
+ }.onCompletion { onSuccess() }.flowOn(Dispatchers.IO).catch { e ->
+ Timber.e(e.message)
+ }
+
+ @WorkerThread
+ fun insertAndReplace(
+ topicsCacheDomain: TopicsCacheDomain?,
+ onSuccess: () -> Unit,
+ ) = flow {
+ val topicsEntities = topicsCacheDomain?.toEntity()
+ val longInsert = topicsEntities?.let { topicsCacheRepository.insertAndReplace(it) }
+ emit(longInsert)
+ }.onCompletion { onSuccess() }.flowOn(Dispatchers.IO).catch { e ->
+ Timber.e(e.message)
+ }
+
+ @WorkerThread
+ fun deleteTopics(
+ topicsCacheDomain: TopicsCacheDomain?,
+ onSuccess: () -> Unit,
+ ) = flow {
+ val topicsEntities = topicsCacheDomain?.toEntity()
+ val longInsert = topicsEntities?.let { topicsCacheRepository.deleteTopics(it) }
+ emit(longInsert)
+ }.onCompletion { onSuccess() }.flowOn(Dispatchers.IO).catch { e ->
+ Timber.e(e.message)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicsUseCase.kt b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicsUseCase.kt
new file mode 100644
index 0000000..92f672c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/datasource/usecase/TopicsUseCase.kt
@@ -0,0 +1,29 @@
+package com.lnmcode.galleryapp.business.datasource.usecase
+
+import androidx.annotation.WorkerThread
+import com.lnmcode.galleryapp.business.datasource.network.helper.topics.TopicsIApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.topics.respose.toTopicsDomain
+import com.lnmcode.galleryapp.business.domain.models.topics.Topics
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.*
+import timber.log.Timber
+
+class TopicsUseCase constructor(
+ private val keyAPI: String,
+ private val topicsIApiRepository: TopicsIApiRepository
+) {
+
+ init {
+ Timber.d("Injection TopicsUserCase")
+ }
+ @WorkerThread
+ fun getTopics(
+ onSuccess: () -> Unit,
+ ): Flow> = flow {
+ val topics = topicsIApiRepository.topics(key = keyAPI).map { it.toTopicsDomain() }
+ emit(topics)
+ }.onCompletion { onSuccess() }.flowOn(Dispatchers.IO).catch { e ->
+ Timber.e(e.message)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/cache/TopicsCacheDomain.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/cache/TopicsCacheDomain.kt
new file mode 100644
index 0000000..7789779
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/cache/TopicsCacheDomain.kt
@@ -0,0 +1,16 @@
+package com.lnmcode.galleryapp.business.domain.cache
+
+import com.lnmcode.galleryapp.business.datasource.cache.topics.TopicsEntities
+
+data class TopicsCacheDomain(
+ val id: String,
+ val slug: String,
+ val title: String,
+ val description: String,
+)
+
+fun TopicsCacheDomain.toEntity(): TopicsEntities {
+ return TopicsEntities(
+ id, slug, title, description
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/Topic.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/Topic.kt
new file mode 100644
index 0000000..58ea163
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/Topic.kt
@@ -0,0 +1,20 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+data class Topic(
+ val id : String?,
+ val slug : String?,
+ val title : String?,
+ val description : String?,
+ val publishedAt : String?,
+ val updatedAt : String?,
+ val startsAt : String?,
+ val endsAt : String?,
+ val onlySubmissionsAfter : String?,
+ val featured : Boolean?,
+ val totalPhotos : Int?,
+ val totalCurrentUserSubmissions : String?,
+ val topicLinks : TopicLinks?,
+ val status : String?,
+ val topicOwners : List?,
+ val topContributors : List?,
+ val topicCoverPhoto : TopicCoverPhoto?,
+ )
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverLinks.kt
new file mode 100644
index 0000000..c17e5ec
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverLinks.kt
@@ -0,0 +1,10 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicCoverLinks(
+ val self : String?,
+ val html : String?,
+ val download : String?,
+ val downloadLocation : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverPhoto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverPhoto.kt
new file mode 100644
index 0000000..9f9d6c5
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverPhoto.kt
@@ -0,0 +1,25 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicCoverPhotoDto
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicCoverUrlsDto
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicUserDto
+
+data class TopicCoverPhoto (
+ val id : String?,
+ val createdAt : String?,
+ val updatedAt : String?,
+ val promotedAt : String?,
+ val width : Int?,
+ val height : Int?,
+ val color : String?,
+ val blurHash : String?,
+ val description : String?,
+ val altDescription : String?,
+ val topicCoverPhoto : TopicCoverPhoto?,
+ val topicCoverUrls : TopicCoverUrls?,
+ val likes : Int?,
+ val likedByUser : Boolean?,
+ val sponsorship : String?,
+ val topicUser : TopicUser?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverUrls.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverUrls.kt
new file mode 100644
index 0000000..48d7218
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicCoverUrls.kt
@@ -0,0 +1,12 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicCoverUrls(
+ val raw : String?,
+ val full : String?,
+ val regular : String?,
+ val small : String?,
+ val thumb : String?,
+ val smallS3 : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicLinks.kt
new file mode 100644
index 0000000..b86f302
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicLinks.kt
@@ -0,0 +1,9 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicLinks(
+ val self : String?,
+ val html : String?,
+ val photos : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwners.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwners.kt
new file mode 100644
index 0000000..df8c7ba
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwners.kt
@@ -0,0 +1,26 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicOwnersLinksDto
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicOwnersProfileImageDto
+
+data class TopicOwners(
+ val id : String?,
+ val updatedAt : String?,
+ val username : String?,
+ val name : String?,
+ val firstName : String?,
+ val lastName : String?,
+ val twitterUsername : String?,
+ val portfolioUrl : String?,
+ val bio : String?,
+ val location : String?,
+ val topicOwnersLinks : TopicOwnersLinks?,
+ val topicOwnersProfileImage: TopicOwnersProfileImage?,
+ val instagramUsername : String?,
+ val totalCollections : Int?,
+ val totalLikes : Int?,
+ val totalPhotos : Int?,
+ val acceptedTos : Boolean?,
+ val forHire : Boolean?,
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwnersLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwnersLinks.kt
new file mode 100644
index 0000000..c9b5be0
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwnersLinks.kt
@@ -0,0 +1,13 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicOwnersLinks(
+ val self : String?,
+ val html : String?,
+ val photos : String?,
+ val likes : String?,
+ val portfolio : String?,
+ val following : String?,
+ val followers : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwnersProfileImage.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwnersProfileImage.kt
new file mode 100644
index 0000000..bfc2976
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicOwnersProfileImage.kt
@@ -0,0 +1,9 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicOwnersProfileImage(
+ val small : String?,
+ val medium : String?,
+ val large : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributors.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributors.kt
new file mode 100644
index 0000000..906531d
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributors.kt
@@ -0,0 +1,26 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicTopContributorsImageProfileDto
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicTopContributorsLinksDto
+
+data class TopicTopContributors(
+ val id : String?,
+ val updatedAt : String?,
+ val username : String?,
+ val name : String?,
+ val firstName : String?,
+ val lastName : String?,
+ val twitterUsername : String?,
+ val portfolioUrl : String?,
+ val bio : String?,
+ val location : String?,
+ val topicTopContributorsLinks: TopicTopContributorsLinks?,
+ val topicTopContributorsImageProfile: TopicTopContributorsImageProfile?,
+ val instagramUsername : String?,
+ val totalCollections : Int?,
+ val totalLikes : Int?,
+ val totalPhotos : Int?,
+ val acceptedTos : Boolean?,
+ val forHire : Boolean?,
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributorsImageProfile.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributorsImageProfile.kt
new file mode 100644
index 0000000..12e64a2
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributorsImageProfile.kt
@@ -0,0 +1,9 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicTopContributorsImageProfile(
+ val small : String?,
+ val medium : String?,
+ val large : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributorsLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributorsLinks.kt
new file mode 100644
index 0000000..541d8f8
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicTopContributorsLinks.kt
@@ -0,0 +1,13 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicTopContributorsLinks(
+ val self : String?,
+ val html : String?,
+ val photos : String?,
+ val likes : String?,
+ val portfolio : String?,
+ val following : String?,
+ val followers : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUser.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUser.kt
new file mode 100644
index 0000000..02c1b4a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUser.kt
@@ -0,0 +1,26 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicUserLinksDto
+import com.lnmcode.galleryapp.business.datasource.network.topic.TopicUserProfileImageDto
+
+data class TopicUser(
+ val id : String?,
+ val updatedAt : String?,
+ val username : String?,
+ val name : String?,
+ val firstName : String?,
+ val lastName : String?,
+ val twitterUsername : String?,
+ val portfolioUrl : String?,
+ val bio : String?,
+ val location : String?,
+ val topicUserLinks: TopicUserLinks?,
+ val topicUserProfileImage: TopicUserProfileImage?,
+ val instagramUsername : String?,
+ val totalCollections : Int?,
+ val totalLikes : Int?,
+ val totalPhotos : Int?,
+ val acceptedTos : Boolean?,
+ val forHire : Boolean?,
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUserLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUserLinks.kt
new file mode 100644
index 0000000..e0cd221
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUserLinks.kt
@@ -0,0 +1,13 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicUserLinks(
+ val self : String?,
+ val html : String?,
+ val photos : String?,
+ val likes : String?,
+ val portfolio : String?,
+ val following : String?,
+ val followers : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUserProfileImage.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUserProfileImage.kt
new file mode 100644
index 0000000..68c9f15
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topic/TopicUserProfileImage.kt
@@ -0,0 +1,9 @@
+package com.lnmcode.galleryapp.business.domain.models.topic
+
+import com.google.gson.annotations.SerializedName
+
+data class TopicUserProfileImage(
+ val small : String?,
+ val medium : String?,
+ val large : String?
+)
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhoto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhoto.kt
new file mode 100644
index 0000000..629694a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhoto.kt
@@ -0,0 +1,27 @@
+package com.lnmcode.galleryapp.business.domain.models.topicphoto
+import android.os.Parcelable
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.TopicPhotoLinksDto
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.TopicPhotoUrlsDto
+import com.lnmcode.galleryapp.business.datasource.network.topicphoto.TopicPhotoUserDto
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicPhoto(
+ val id: String,
+ val createdAt : String?,
+ val updatedAt : String?,
+ val promotedAt : String?,
+ val width: Int,
+ val height: Int,
+ val color : String,
+ val blurHash: String,
+ val description: String?,
+ val altDescription: String?,
+ val topicPhotoUrls : TopicPhotoUrls,
+ val topicPhotoLinks : TopicPhotoLinks,
+ val likes: Int,
+ val likedByUser: Boolean,
+ //val sponsorship: String?,
+ val topicPhotoUser: TopicPhotoUser
+): Parcelable
+
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoLinks.kt
new file mode 100644
index 0000000..2e72637
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoLinks.kt
@@ -0,0 +1,12 @@
+package com.lnmcode.galleryapp.business.domain.models.topicphoto
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicPhotoLinks(
+ val self : String,
+ val html : String,
+ val download : String,
+ val downloadLocation : String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUrls.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUrls.kt
new file mode 100644
index 0000000..8fda3d7
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUrls.kt
@@ -0,0 +1,14 @@
+package com.lnmcode.galleryapp.business.domain.models.topicphoto
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicPhotoUrls(
+ val raw : String,
+ val full : String,
+ val regular : String,
+ val small : String,
+ val thumb : String,
+ val smallS3 : String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUser.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUser.kt
new file mode 100644
index 0000000..1ccdb36
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUser.kt
@@ -0,0 +1,25 @@
+package com.lnmcode.galleryapp.business.domain.models.topicphoto
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicPhotoUser(
+ val id : String,
+ val updatedAt : String,
+ val username : String,
+ val name : String,
+ val firstName : String,
+ val lastName : String?,
+// val portfolioUrl: String,
+ val bio : String?,
+ val location : String?,
+ val topicPhotoUserLinks : TopicPhotoUserLinks,
+ val topicPhotoUserprofileImage : TopicPhotoUserProfileImage,
+ val instagramUsername : String?,
+ val totalCollections : Int,
+ val totalLikes : Int,
+ val totalPhotos : Int,
+ val acceptedTos : Boolean,
+ val forHire : Boolean,
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUserLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUserLinks.kt
new file mode 100644
index 0000000..acebe74
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUserLinks.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.business.domain.models.topicphoto
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicPhotoUserLinks(
+ val self : String,
+ val html : String,
+ val photos : String,
+ val likes : String?,
+ val portfolio : String?,
+ val following : String,
+ val followers : String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUserProfileImage.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUserProfileImage.kt
new file mode 100644
index 0000000..66e499f
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topicphoto/TopicPhotoUserProfileImage.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.business.domain.models.topicphoto
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicPhotoUserProfileImage(
+ val small : String,
+ val medium : String,
+ val large : String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/Topics.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/Topics.kt
new file mode 100644
index 0000000..dc35e40
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/Topics.kt
@@ -0,0 +1,25 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class Topics(
+ val id: String,
+ val slug: String,
+ val title: String,
+ val description: String,
+ val publishedAt: String,
+ val updatedAt: String,
+ val startsAt: String,
+ val endsAt: String?,
+ val onlySubmissionsAfter: String?,
+ val featured: Boolean,
+ val totalPhotos: Int,
+ val totalCurrentUserSubmissions: String?,
+ val topicsLinkDto: TopicsLinks,
+ val status: String,
+ val topicsOwners: List,
+ val topicsCoverPhoto: TopicsCoverPhoto?,
+ val topicsPreviewPhotos: List
+) :Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhoto.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhoto.kt
new file mode 100644
index 0000000..be01f49
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhoto.kt
@@ -0,0 +1,24 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsCoverPhoto(
+ val id: String,
+ val createdAt: String,
+ val updatedAt: String,
+ val promotedAt: String?,
+ val width: Int,
+ val height: Int,
+ val color: String,
+ val blurHash: String,
+ val description: String?,
+ val altDescription: String?,
+ val topicsCoverPhotoUrls: TopicsCoverPhotoUrls,
+ val topicsCoverPhotoLinks: TopicsCoverPhotoLinks,
+ val likes: Int?,
+ val likedByUser: Boolean,
+ val sponsorship: String?,
+ val topicsUser: TopicsUsers
+) :Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhotoLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhotoLinks.kt
new file mode 100644
index 0000000..b37869e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhotoLinks.kt
@@ -0,0 +1,12 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsCoverPhotoLinks(
+ val self: String,
+ val html: String,
+ val download: String,
+ val downloadLocation: String
+): Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhotoUrls.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhotoUrls.kt
new file mode 100644
index 0000000..ff03d29
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsCoverPhotoUrls.kt
@@ -0,0 +1,14 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsCoverPhotoUrls(
+ val raw: String,
+ val full: String,
+ val regular: String,
+ val small: String,
+ val thumb: String,
+ val smallS3: String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsLinks.kt
new file mode 100644
index 0000000..6b18467
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsLinks.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsLinks(
+ val self: String,
+ val html: String,
+ val photos: String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwners.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwners.kt
new file mode 100644
index 0000000..7e70ef2
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwners.kt
@@ -0,0 +1,26 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsOwners(
+ val id: String,
+ val updatedAt: String,
+ val username: String,
+ val name: String,
+ val firstName: String,
+ val lastName: String?,
+ val twitterUsername: String?,
+ val portfolioUrl: String?,
+ val bio: String,
+ val location: String?,
+ val topicsOwnerLinks: TopicsOwnersLinks,
+ val topicsOwnerProfileImage: TopicsOwnersProfileImage,
+ val instagramUsername: String,
+ val totalCollections: Int,
+ val totalLikes: Int,
+ val totalPhotos: Int,
+ val acceptedTos: Boolean,
+ val forHire: Boolean,
+) :Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwnersLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwnersLinks.kt
new file mode 100644
index 0000000..1f8db18
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwnersLinks.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsOwnersLinks(
+ val self: String,
+ val html: String,
+ val photos: String,
+ val likes: String,
+ val portfolio: String,
+ val following: String,
+ val followers: String
+):Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwnersProfileImage.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwnersProfileImage.kt
new file mode 100644
index 0000000..c7ce941
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsOwnersProfileImage.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsOwnersProfileImage(
+ val small: String,
+ val medium: String,
+ val large: String
+) :Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsPreviewPhotoUrls.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsPreviewPhotoUrls.kt
new file mode 100644
index 0000000..e0c2409
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsPreviewPhotoUrls.kt
@@ -0,0 +1,14 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsPreviewPhotoUrls(
+ val raw: String,
+ val full: String,
+ val regular: String,
+ val small: String,
+ val thumb: String,
+ val smallS3: String
+): Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsPreviewPhotos.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsPreviewPhotos.kt
new file mode 100644
index 0000000..2aded14
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsPreviewPhotos.kt
@@ -0,0 +1,13 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsPreviewPhotos(
+ val id: String,
+ val createdAt: String,
+ val updatedAt: String,
+ val blurHash: String,
+ val TopicsPreviewPhotoUrls: TopicsPreviewPhotoUrls
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUrls.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUrls.kt
new file mode 100644
index 0000000..e7361ea
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUrls.kt
@@ -0,0 +1,14 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsUrls(
+ val raw: String,
+ val full: String,
+ val regular: String,
+ val small: String,
+ val thumb: String,
+ val smallS3: String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUserLinks.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUserLinks.kt
new file mode 100644
index 0000000..f45a431
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUserLinks.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsUserLinks(
+ val self: String,
+ val html: String,
+ val photos: String,
+ val likes: String,
+ val portfolio: String,
+ val following: String,
+ val followers: String,
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUserProfileImage.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUserProfileImage.kt
new file mode 100644
index 0000000..fc6735c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUserProfileImage.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsUserProfileImage(
+ val small: String,
+ val medium: String,
+ val large: String
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUsers.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUsers.kt
new file mode 100644
index 0000000..9149c4b
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/models/topics/TopicsUsers.kt
@@ -0,0 +1,26 @@
+package com.lnmcode.galleryapp.business.domain.models.topics
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TopicsUsers(
+ val id: String,
+ val updatedAt: String,
+ val username: String,
+ val name: String,
+ val firstName: String,
+ val lastName: String,
+ val twitterUsername: String,
+ val portfolioUrl: String,
+ val bio: String,
+ val location: String?,
+ val topicsUserLinks: TopicsUserLinks,
+ val topicsUserProfileImage: TopicsUserProfileImage,
+ val instagramUsername: String,
+ val totalCollections: Int,
+ val totalLikes: Int,
+ val totalPhotos: Int,
+ val acceptedTos: Boolean,
+ val forHire: Boolean,
+): Parcelable
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/Constants.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/Constants.kt
new file mode 100644
index 0000000..3c8acb5
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/Constants.kt
@@ -0,0 +1,12 @@
+package com.lnmcode.galleryapp.business.domain.utils
+
+class Constants {
+
+ companion object{
+
+ const val BASE_URL_API_NAME = "baseUrlAPI"
+ const val KEY_API_URL_NAME = ""
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/DataState.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/DataState.kt
new file mode 100644
index 0000000..3c0c37e
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/DataState.kt
@@ -0,0 +1,39 @@
+package com.lnmcode.galleryapp.business.domain.utils
+
+
+class DataState(
+ val stateMessage: StateMessage? = null,
+ val data: T? = null,
+ val isLoading: Boolean = false
+) {
+
+ companion object {
+ fun error(
+ response: Response,
+ ): DataState {
+ return DataState(
+ stateMessage = StateMessage(
+ response
+ ),
+ data = null,
+ )
+ }
+
+ fun data(
+ response: Response?,
+ data: T? = null,
+ ): DataState {
+ return DataState(
+ stateMessage = response?.let {
+ StateMessage(
+ it
+ )
+ },
+ data = data,
+ )
+ }
+
+ fun loading(): DataState = DataState(isLoading = true)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/StateMessage.kt b/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/StateMessage.kt
new file mode 100644
index 0000000..b1c864c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/business/domain/utils/StateMessage.kt
@@ -0,0 +1,7 @@
+package com.lnmcode.galleryapp.business.domain.utils
+
+data class StateMessage(val response: Response)
+
+data class Response(
+ val message: String?,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/di/AppModule.kt b/app/src/main/java/com/lnmcode/galleryapp/di/AppModule.kt
new file mode 100644
index 0000000..1213d98
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/di/AppModule.kt
@@ -0,0 +1,4 @@
+package com.lnmcode.galleryapp.di
+
+val appModule =
+ networkModule + repositoryModel + useCaseModule + persistenceModule + viewModelModule
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/di/NetworkModule.kt b/app/src/main/java/com/lnmcode/galleryapp/di/NetworkModule.kt
new file mode 100644
index 0000000..483aadf
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/di/NetworkModule.kt
@@ -0,0 +1,63 @@
+package com.lnmcode.galleryapp.di
+
+import com.lnmcode.galleryapp.business.datasource.interceptor.RequestInterceptor
+import com.lnmcode.galleryapp.business.datasource.network.helper.search.SearchApiService
+import com.lnmcode.galleryapp.business.datasource.network.helper.topic.TopicApiService
+import com.lnmcode.galleryapp.business.datasource.network.helper.topicphoto.TopicPhotoApiService
+import com.lnmcode.galleryapp.business.datasource.network.helper.topics.TopicsApiService
+import com.lnmcode.galleryapp.business.domain.utils.Constants
+import okhttp3.OkHttpClient
+import org.koin.core.qualifier.named
+import org.koin.dsl.module
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+val networkModule = module {
+
+ single(named(Constants.BASE_URL_API_NAME)) { baseUrlString() }
+ single(named(Constants.KEY_API_URL_NAME)) { keyAPIUrl() }
+
+ single { RequestInterceptor() }
+
+ single { okHttpClientBuilder(get()) }
+
+ single { retrofitBuilder(get(), get(qualifier = named(Constants.BASE_URL_API_NAME))) }
+
+ // Search service
+ single { get().create(SearchApiService::class.java) }
+
+ // Topic service
+ single { get().create(TopicApiService::class.java) }
+
+ single { get().create(TopicPhotoApiService::class.java) }
+
+ single { get().create(TopicsApiService::class.java) }
+
+}
+
+internal fun okHttpClientBuilder(
+ interceptor: RequestInterceptor
+): OkHttpClient {
+ return OkHttpClient.Builder()
+ .addInterceptor(interceptor)
+ .build()
+}
+
+internal fun retrofitBuilder(
+ okHttpClient: OkHttpClient,
+ baseUrlAPI: String,
+): Retrofit {
+ return Retrofit.Builder()
+ .client(okHttpClient)
+ .baseUrl(baseUrlAPI)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+}
+
+internal fun baseUrlString(): String {
+ return "https://api.unsplash.com/"
+}
+
+internal fun keyAPIUrl(): String {
+ return "5IFGi9p0CqcZxupJ1ZV9ZUAoXddSBuRGYMW2cjKEvv8"
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/di/PersistenceModule.kt b/app/src/main/java/com/lnmcode/galleryapp/di/PersistenceModule.kt
new file mode 100644
index 0000000..6b01f2b
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/di/PersistenceModule.kt
@@ -0,0 +1,24 @@
+package com.lnmcode.galleryapp.di
+
+import android.content.Context
+import androidx.room.Room
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.business.datasource.cache.AppDatabase
+import org.koin.android.ext.koin.androidApplication
+import org.koin.dsl.module
+
+val persistenceModule = module {
+
+ single { roomDatabaseBuilder(androidApplication()) }
+
+ single { get().topicsDao() }
+
+}
+
+internal fun roomDatabaseBuilder(
+ context: Context
+): AppDatabase {
+ return Room.databaseBuilder(context, AppDatabase::class.java,
+ context.getString(R.string.database_name)
+ ).fallbackToDestructiveMigration().build()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/di/RepositoryModule.kt b/app/src/main/java/com/lnmcode/galleryapp/di/RepositoryModule.kt
new file mode 100644
index 0000000..9a9588a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/di/RepositoryModule.kt
@@ -0,0 +1,32 @@
+package com.lnmcode.galleryapp.di
+
+import com.lnmcode.galleryapp.business.datasource.cache.helper.TopicsCacheRepository
+import com.lnmcode.galleryapp.business.datasource.cache.helper.TopicsCacheRepositoryImpl
+import com.lnmcode.galleryapp.business.datasource.network.helper.search.SearchApiRepositoryImpl
+import com.lnmcode.galleryapp.business.datasource.network.helper.search.SearchApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.helper.topic.TopicApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.helper.topic.TopicApiRepositoryImpl
+import com.lnmcode.galleryapp.business.datasource.network.helper.topicphoto.TopicPhotoApiRepositoryImpl
+import com.lnmcode.galleryapp.business.datasource.network.helper.topicphoto.TopicPhotoIApiRepository
+import com.lnmcode.galleryapp.business.datasource.network.helper.topics.TopicsApiRepositoryImpl
+import com.lnmcode.galleryapp.business.datasource.network.helper.topics.TopicsIApiRepository
+import org.koin.dsl.bind
+import org.koin.dsl.module
+
+val repositoryModel = module {
+
+ // Repository network
+ // Search repository
+ single { SearchApiRepositoryImpl(get()) }
+
+ // Topic repository
+ single { TopicApiRepositoryImpl(get()) }
+
+
+ single { TopicPhotoApiRepositoryImpl(get()) }
+
+ single { TopicsApiRepositoryImpl(get()) }
+
+ // Repository cache
+ single { TopicsCacheRepositoryImpl(get()) }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/di/UseCaseModule.kt b/app/src/main/java/com/lnmcode/galleryapp/di/UseCaseModule.kt
new file mode 100644
index 0000000..279d9f1
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/di/UseCaseModule.kt
@@ -0,0 +1,21 @@
+package com.lnmcode.galleryapp.di
+
+import com.lnmcode.galleryapp.business.datasource.usecase.*
+import com.lnmcode.galleryapp.business.domain.utils.Constants
+import org.koin.core.qualifier.named
+import org.koin.dsl.module
+
+val useCaseModule = module {
+
+ // UseCase network
+ single { SearchUseCase(get()) }
+
+ single { TopicPhotoUseCase(get(qualifier = named(Constants.KEY_API_URL_NAME)),get()) }
+
+ factory { TopicsUseCase(get(qualifier = named(Constants.KEY_API_URL_NAME)), get()) }
+
+ single { TopicUseCase(get(qualifier = named(Constants.KEY_API_URL_NAME)),get()) }
+
+ // UseCase cache
+ single { TopicsCacheUseCase(get()) }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/di/ViewModelModule.kt b/app/src/main/java/com/lnmcode/galleryapp/di/ViewModelModule.kt
new file mode 100644
index 0000000..a031ef1
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/di/ViewModelModule.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.di
+
+import com.lnmcode.galleryapp.presentation.ui.MainViewModel
+import com.lnmcode.galleryapp.presentation.ui.boards.BoardsViewModel
+import com.lnmcode.galleryapp.presentation.ui.detail.DetailViewModel
+import com.lnmcode.galleryapp.presentation.ui.gallery.GalleryViewModel
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.dsl.module
+
+val viewModelModule = module {
+
+ viewModel { MainViewModel() }
+
+ viewModel { BoardsViewModel(get(), get()) }
+
+ viewModel { (topicsId: String) -> GalleryViewModel(topicsId, get()) }
+
+ viewModel { DetailViewModel() }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/BaseApplication.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/BaseApplication.kt
deleted file mode 100644
index 328c184..0000000
--- a/app/src/main/java/com/lnmcode/galleryapp/presentation/BaseApplication.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.lnmcode.galleryapp.presentation
-
-import android.app.Application
-import android.content.Context
-
-class BaseApplication: Application() {
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/MainActivity.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/MainActivity.kt
deleted file mode 100644
index 1a24fda..0000000
--- a/app/src/main/java/com/lnmcode/galleryapp/presentation/MainActivity.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.lnmcode.galleryapp.presentation
-
-import androidx.appcompat.app.AppCompatActivity
-import android.os.Bundle
-import com.lnmcode.galleryapp.R
-
-class MainActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/BaseAdapter.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/BaseAdapter.kt
new file mode 100644
index 0000000..55419b3
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/BaseAdapter.kt
@@ -0,0 +1,81 @@
+package com.lnmcode.galleryapp.presentation.adapter
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.*
+import androidx.recyclerview.widget.*
+
+abstract class BaseAdapter : RecyclerView.Adapter(), DefaultLifecycleObserver {
+
+ private val differ = AsyncListDiffer(
+ getAdapterCallBack(),
+ getAsyncDifferConfigBuilder()
+ )
+
+ fun addSubmit(list: List) {
+ differ.submitList(list.toMutableList())
+ }
+
+ private fun getDiffCallBack(): DiffUtil.ItemCallback {
+ return object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
+ return areItemsTheSameItem(oldItem, newItem)
+ }
+
+ override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
+ return areContentsTheSameItem(oldItem, newItem)
+ }
+ }
+ }
+
+ private fun getAsyncDifferConfigBuilder() = AsyncDifferConfig.Builder(getDiffCallBack()).build()
+
+ private fun getAdapterCallBack() = RecyclerAdapterCallBack(this)
+
+ /**
+ * return layout by resource id
+ */
+ abstract fun layout(): Int
+
+ /**
+ * return viewholder by resource id
+ */
+ abstract fun viewHolder(view: View): BaseViewHolder
+
+ /**
+ * Called to check whether two objects represent the same item.
+ */
+ abstract fun areItemsTheSameItem(oldItem: T, newItem: T): Boolean
+
+ /**
+ * Called to check whether two items have the same data.
+ */
+ abstract fun areContentsTheSameItem(oldItem: T, newItem: T): Boolean
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
+ return viewHolder(
+ LayoutInflater.from(parent.context).inflate(
+ layout(), parent, false,
+ )
+ )
+ }
+
+ override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
+ try {
+ holder.bindData(data = differ.currentList[position] as Any)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return differ.currentList.size
+ }
+
+ override fun onDestroy(owner: LifecycleOwner) {
+ super.onDestroy(owner)
+ differ.currentList.clear()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/BaseViewHolder.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/BaseViewHolder.kt
new file mode 100644
index 0000000..a711505
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/BaseViewHolder.kt
@@ -0,0 +1,21 @@
+package com.lnmcode.galleryapp.presentation.adapter
+
+import android.content.Context
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import kotlin.jvm.Throws
+
+abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
+
+ val context: Context = view.context
+
+ init {
+ view.setOnClickListener(this)
+ }
+
+ /**
+ * binds data to view holder class
+ * */
+ @Throws(Exception::class)
+ abstract fun bindData(data: Any)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/RecyclerAdapterCallBack.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/RecyclerAdapterCallBack.kt
new file mode 100644
index 0000000..e0adb37
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/RecyclerAdapterCallBack.kt
@@ -0,0 +1,23 @@
+package com.lnmcode.galleryapp.presentation.adapter
+
+import androidx.recyclerview.widget.ListUpdateCallback
+
+class RecyclerAdapterCallBack(
+ private val adapter: BaseAdapter
+) : ListUpdateCallback {
+ override fun onInserted(position: Int, count: Int) {
+ adapter.notifyItemChanged(position, count)
+ }
+
+ override fun onRemoved(position: Int, count: Int) {
+ adapter.notifyDataSetChanged()
+ }
+
+ override fun onMoved(fromPosition: Int, toPosition: Int) {
+ adapter.notifyDataSetChanged()
+ }
+
+ override fun onChanged(position: Int, count: Int, payload: Any?) {
+ adapter.notifyItemRangeChanged(position, count, payload)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/SectionRow.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/SectionRow.kt
new file mode 100644
index 0000000..329a754
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/adapter/SectionRow.kt
@@ -0,0 +1,15 @@
+package com.lnmcode.galleryapp.presentation.adapter
+
+data class SectionRow(
+ var section: Int = 0,
+ var row: Int = 0,
+) {
+
+ fun nextSection() {
+ this.section++
+ this.row = 0
+ }
+
+ fun nextRow() = row++
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/binding/RecyclerViewBinding.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/binding/RecyclerViewBinding.kt
new file mode 100644
index 0000000..bce86ca
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/binding/RecyclerViewBinding.kt
@@ -0,0 +1,81 @@
+package com.lnmcode.galleryapp.presentation.binding
+
+import androidx.databinding.BindingAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.business.domain.models.topics.Topics
+import com.lnmcode.galleryapp.presentation.adapter.BaseAdapter
+import com.lnmcode.galleryapp.presentation.ui.boards.BoardTopicsAdapter
+import com.lnmcode.galleryapp.presentation.ui.boards.BoardsAdapter
+import com.lnmcode.galleryapp.presentation.ui.gallery.GalleryAdapter
+import com.lnmcode.galleryapp.presentation.ui.gallery.GalleryHeadAdapter
+
+object RecyclerViewBinding {
+
+ @JvmStatic
+ @BindingAdapter("adapter", "adapterTopicsList")
+ fun bindAdapterPosterList(
+ view: RecyclerView,
+ adapter: BoardsAdapter,
+ topics: List?,
+ ) {
+ if (view.adapter != null) {
+ val adapterRecycler = view.adapter as BoardsAdapter
+ adapterRecycler.addTopics(topics ?: emptyList())
+ } else {
+ adapter.addTopics(topics ?: emptyList())
+ view.adapter = adapter
+ }
+
+ }
+
+ @JvmStatic
+ @BindingAdapter("adapter", "adapterTopicsCache")
+ fun bindAdapterTopicsCache(
+ view: RecyclerView,
+ adapter: BoardTopicsAdapter,
+ topics: List,
+ ) {
+ if (view.adapter != null) {
+ val adapterRecycler = view.adapter as BoardTopicsAdapter
+ adapterRecycler.addTopics(topics)
+ } else {
+ adapter.addTopics(topics)
+ view.adapter = adapter
+ }
+ }
+
+ @JvmStatic
+ @BindingAdapter("adapter", "adapterTopicPhoto")
+ fun bindAdapterTopicPhoto(
+ view: RecyclerView,
+ adapter: GalleryHeadAdapter,
+ topicPhoto: List,
+ ) {
+ if (view.adapter != null) {
+ val adapterRecycler = view.adapter as GalleryHeadAdapter
+ adapterRecycler.addTopics(topicPhoto)
+ } else {
+ adapter.addTopics(topicPhoto)
+ view.adapter = adapter
+ }
+ }
+
+ @JvmStatic
+ @BindingAdapter("adapter", "adapterGridTopicPhoto")
+ fun bindAdapterGridTopicPhoto(
+ view: RecyclerView,
+ adapter: GalleryAdapter,
+ topicPhoto: List,
+ ) {
+ if (view.adapter != null) {
+ val adapterRecycler = view.adapter as GalleryAdapter
+ adapterRecycler.addTopics(topicPhoto)
+ } else {
+ adapter.addTopics(topicPhoto)
+ view.adapter = adapter
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/binding/ViewBinding.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/binding/ViewBinding.kt
new file mode 100644
index 0000000..b4e8d24
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/binding/ViewBinding.kt
@@ -0,0 +1,29 @@
+package com.lnmcode.galleryapp.presentation.binding
+
+import android.view.View
+import android.widget.ImageView
+import androidx.databinding.BindingAdapter
+import com.lnmcode.galleryapp.presentation.extensions.gone
+import com.lnmcode.galleryapp.presentation.glide.GlideApp
+
+object ViewBinding {
+
+ @JvmStatic
+ @BindingAdapter("loadImage")
+ fun bindLoadImage(view: ImageView, url: String?) {
+ GlideApp.with(view.context)
+ .load(url)
+ .into(view)
+ }
+
+ @JvmStatic
+ @BindingAdapter("gone")
+ fun bindGone(view: View, shouldBeGone: Boolean?) {
+ if (shouldBeGone == true) {
+ view.gone(true)
+ } else {
+ view.gone(false)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/extensions/ViewExtensions.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/extensions/ViewExtensions.kt
new file mode 100644
index 0000000..f576186
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/extensions/ViewExtensions.kt
@@ -0,0 +1,12 @@
+package com.lnmcode.galleryapp.presentation.extensions
+
+import android.view.View
+
+fun View.visible() {
+ visibility = View.VISIBLE
+}
+
+fun View.gone(shouldBeGone: Boolean) {
+ if (shouldBeGone) visibility = View.GONE
+ else visible()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/glide/GlideApp.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/glide/GlideApp.kt
new file mode 100644
index 0000000..b66a49a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/glide/GlideApp.kt
@@ -0,0 +1,7 @@
+package com.lnmcode.galleryapp.presentation.glide
+
+import com.bumptech.glide.annotation.GlideModule
+import com.bumptech.glide.module.AppGlideModule
+
+@GlideModule
+class GlideAppModule: AppGlideModule()
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/BaseApplication.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/BaseApplication.kt
new file mode 100644
index 0000000..fdc08c2
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/BaseApplication.kt
@@ -0,0 +1,22 @@
+package com.lnmcode.galleryapp.presentation.ui
+
+import android.app.Application
+import android.content.Context
+import com.lnmcode.galleryapp.di.appModule
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.context.startKoin
+import timber.log.Timber
+
+class BaseApplication: Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+ startKoin {
+ androidContext(this@BaseApplication)
+ modules(appModule)
+ }
+
+ Timber.plant(Timber.DebugTree())
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/MainActivity.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/MainActivity.kt
new file mode 100644
index 0000000..e80d2f8
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/MainActivity.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.presentation.ui
+
+import android.os.Bundle
+import androidx.navigation.fragment.NavHostFragment
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.bindables.BindingActivity
+import com.lnmcode.galleryapp.databinding.ActivityMainBinding
+import org.koin.androidx.viewmodel.ext.android.getViewModel
+
+class MainActivity : BindingActivity(R.layout.activity_main) {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ //applyExitMaterialTransform()
+ super.onCreate(savedInstanceState)
+ binding {
+ vm = getViewModel()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/MainViewModel.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/MainViewModel.kt
new file mode 100644
index 0000000..b764652
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/MainViewModel.kt
@@ -0,0 +1,16 @@
+package com.lnmcode.galleryapp.presentation.ui
+
+import androidx.databinding.Bindable
+import com.lnmcode.galleryapp.bindables.BindingViewModel
+import com.lnmcode.galleryapp.bindables.bindingProperty
+
+class MainViewModel(): BindingViewModel() {
+
+ @get:Bindable
+ var isLoading: Boolean by bindingProperty(false)
+ private set
+
+ fun setShowLoading(boolean: Boolean) {
+ isLoading = boolean
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/OnChangeLayout.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/OnChangeLayout.kt
new file mode 100644
index 0000000..06c9c1d
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/OnChangeLayout.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.presentation.ui
+
+import android.view.View
+import android.widget.ImageView
+
+interface OnChangeLayout {
+
+ fun onChangeSharedWithParameters(data: Any, viewItemShare: ImageView)
+
+ fun onChangeWithParameters(data: Any)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardTopicsAdapter.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardTopicsAdapter.kt
new file mode 100644
index 0000000..28fe289
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardTopicsAdapter.kt
@@ -0,0 +1,40 @@
+package com.lnmcode.galleryapp.presentation.ui.boards
+
+import android.view.View
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.presentation.adapter.BaseAdapter
+import com.lnmcode.galleryapp.presentation.adapter.BaseViewHolder
+import com.lnmcode.galleryapp.presentation.adapter.SectionRow
+import com.lnmcode.galleryapp.presentation.viewholder.BoardTopicsViewHolder
+
+class BoardTopicsAdapter(
+ private val boardsActionCacheEvent: BoardsActionCacheEvent,
+) : BaseAdapter() {
+
+ init {
+ addSubmit(listOf())
+ }
+
+ fun addTopics(topics: List) {
+ addSubmit(topics)
+ }
+
+ override fun layout() = R.layout.layout_item_topics_boards
+
+ override fun viewHolder(view: View) = BoardTopicsViewHolder(view, boardsActionCacheEvent)
+
+ override fun areItemsTheSameItem(
+ oldItem: TopicsCacheDomain,
+ newItem: TopicsCacheDomain
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSameItem(
+ oldItem: TopicsCacheDomain,
+ newItem: TopicsCacheDomain
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsActionCacheEvent.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsActionCacheEvent.kt
new file mode 100644
index 0000000..06a92f7
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsActionCacheEvent.kt
@@ -0,0 +1,9 @@
+package com.lnmcode.galleryapp.presentation.ui.boards
+
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+
+interface BoardsActionCacheEvent {
+ fun insertTopicsCache(topicsCacheDomain: TopicsCacheDomain)
+
+ fun deleteTopicsCache(topicsCacheDomain: TopicsCacheDomain)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsAdapter.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsAdapter.kt
new file mode 100644
index 0000000..bccb71a
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsAdapter.kt
@@ -0,0 +1,42 @@
+package com.lnmcode.galleryapp.presentation.ui.boards
+
+import android.view.View
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.business.domain.models.topics.Topics
+import com.lnmcode.galleryapp.presentation.adapter.BaseAdapter
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import com.lnmcode.galleryapp.presentation.viewholder.BoardViewHolder
+
+class BoardsAdapter(
+ private val onChangeLayout: OnChangeLayout,
+ private val boardsActionCacheEvent: BoardsActionCacheEvent,
+) : BaseAdapter() {
+
+ init {
+ addSubmit(listOf())
+ }
+
+ fun addTopics(topics: List) {
+ addSubmit(topics)
+ }
+
+ override fun layout() = R.layout.layout_item_boards
+
+ override fun viewHolder(view: View) = BoardViewHolder(view, onChangeLayout, boardsActionCacheEvent)
+
+ override fun areItemsTheSameItem(
+ oldItem: Topics,
+ newItem: Topics
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSameItem(
+ oldItem: Topics,
+ newItem: Topics
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsFragment.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsFragment.kt
new file mode 100644
index 0000000..376e2db
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsFragment.kt
@@ -0,0 +1,55 @@
+package com.lnmcode.galleryapp.presentation.ui.boards
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.navigation.fragment.findNavController
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.bindables.BindingFragment
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.business.domain.models.topic.Topic
+import com.lnmcode.galleryapp.business.domain.models.topics.Topics
+import com.lnmcode.galleryapp.databinding.FragmentBoardsBinding
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import org.koin.androidx.viewmodel.ext.android.viewModel
+
+class BoardsFragment(
+) : BindingFragment(R.layout.fragment_boards), OnChangeLayout,
+ BoardsActionCacheEvent {
+
+ private val viewModel: BoardsViewModel by viewModel()
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ super.onCreateView(inflater, container, savedInstanceState)
+ return binding {
+ adapter = BoardsAdapter(this@BoardsFragment, this@BoardsFragment)
+ adapterTopics = BoardTopicsAdapter(this@BoardsFragment)
+ vm = viewModel
+ }.root
+ }
+
+ override fun onChangeSharedWithParameters(data: Any, viewItemShare: ImageView) {
+
+ }
+
+ override fun onChangeWithParameters(data: Any) {
+ findNavController().navigate(
+ BoardsFragmentDirections.actionBoardsFragmentToGalleryFragment(data as String)
+ )
+ }
+
+ override fun insertTopicsCache(topicsCacheDomain: TopicsCacheDomain) {
+ viewModel.insertTopicsCache(topicsCacheDomain)
+ }
+
+ override fun deleteTopicsCache(topicsCacheDomain: TopicsCacheDomain) {
+ viewModel.deleteTopicsCache(topicsCacheDomain)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsViewModel.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsViewModel.kt
new file mode 100644
index 0000000..2fbc9d1
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/boards/BoardsViewModel.kt
@@ -0,0 +1,106 @@
+package com.lnmcode.galleryapp.presentation.ui.boards
+
+import androidx.databinding.Bindable
+import androidx.lifecycle.viewModelScope
+import com.lnmcode.galleryapp.bindables.BindingViewModel
+import com.lnmcode.galleryapp.bindables.asBindingProperty
+import com.lnmcode.galleryapp.bindables.bindingProperty
+import com.lnmcode.galleryapp.business.datasource.usecase.TopicsCacheUseCase
+import com.lnmcode.galleryapp.business.domain.models.topics.Topics
+import com.lnmcode.galleryapp.business.datasource.usecase.TopicsUseCase
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import timber.log.Timber
+
+class BoardsViewModel(
+ private val topicsUseCase: TopicsUseCase,
+ private val topicsCacheUseCase: TopicsCacheUseCase,
+) : BindingViewModel() {
+ @get:Bindable
+ var isLoading: Boolean by bindingProperty(true)
+ private set
+ private val topicsFlow = topicsUseCase.getTopics(
+ onSuccess = { isLoading = true }
+ )
+
+ @get:Bindable
+ val topics: List by topicsFlow.asBindingProperty(viewModelScope, emptyList())
+ private val topicsCacheStateFlow = MutableStateFlow(0)
+ private val topicsCacheFlow = topicsCacheStateFlow.flatMapLatest {
+ topicsCacheUseCase.getTopics(
+ onSuccess = {
+ isLoading = true
+ }
+ )
+ }
+
+ @get:Bindable
+ val topicsCache: List by topicsCacheFlow.asBindingProperty(
+ viewModelScope,
+ emptyList()
+ )
+
+ private val insertTopicsDomainStateFlow = MutableStateFlow(null)
+ private val insertTopicsDomainFlow = insertTopicsDomainStateFlow.flatMapLatest {
+ topicsCacheUseCase.insertAndReplace(
+ it,
+ onSuccess = {
+ onDoneInsertTopicsCacheItem()
+ onRefreshTopicsFromCache()
+ isLoading = true
+ }
+ )
+ }
+
+ @get:Bindable
+ private val insertTopicsDomain by insertTopicsDomainFlow.asBindingProperty(viewModelScope)
+
+ private val deleteTopicsDomainStateFlow = MutableStateFlow(null)
+ private val deleteTopicsDomainFlow = deleteTopicsDomainStateFlow.flatMapLatest {
+ topicsCacheUseCase.deleteTopics(
+ it,
+ onSuccess = {
+ onDoneDeleteTopicsCacheItem()
+ onRefreshTopicsFromCache()
+ isLoading = true
+ }
+ )
+ }
+
+ @get:Bindable
+ private val deleteTopicsDomain by deleteTopicsDomainFlow.asBindingProperty(viewModelScope)
+
+ private var numberCountEvent: Int = -1
+
+ fun insertTopicsCache(topicsCacheDomain: TopicsCacheDomain) {
+ if (!topicsCache.contains(topicsCacheDomain)) {
+ Timber.d(topicsCacheDomain.toString())
+ insertTopicsDomainStateFlow.value = topicsCacheDomain
+ }
+ }
+
+ fun deleteTopicsCache(topicsCacheDomain: TopicsCacheDomain) {
+ Timber.d(topicsCacheDomain.toString())
+ deleteTopicsDomainStateFlow.value = topicsCacheDomain
+ }
+
+ private fun onRefreshTopicsFromCache() {
+ topicsCacheStateFlow.value = numberCountEvent++
+ Timber.d("onRefreshTopicsFromCache ${topicsCacheStateFlow.value}")
+ }
+
+ private fun onDoneDeleteTopicsCacheItem() {
+ deleteTopicsDomainStateFlow.value = null
+ }
+
+ private fun onDoneInsertTopicsCacheItem() {
+ insertTopicsDomainStateFlow.value = null
+ }
+
+ init {
+ Timber.d("Init Boards View model")
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailBottomSheet.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailBottomSheet.kt
new file mode 100644
index 0000000..a4b9ebb
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailBottomSheet.kt
@@ -0,0 +1,11 @@
+package com.lnmcode.galleryapp.presentation.ui.detail
+
+import android.view.View
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+
+interface DetailBottomSheet {
+
+ fun getBottomSheetBehavior(bottomSheet: View): BottomSheetBehavior
+
+ fun showAndHideBottomSheet()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailFragment.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailFragment.kt
new file mode 100644
index 0000000..4c82e89
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailFragment.kt
@@ -0,0 +1,81 @@
+package com.lnmcode.galleryapp.presentation.ui.detail
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.ViewCompat
+import androidx.fragment.app.viewModels
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
+import androidx.transition.Transition
+import androidx.transition.TransitionInflater
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.bindables.BindingFragment
+import com.lnmcode.galleryapp.databinding.FragmentDetailBinding
+import kotlinx.coroutines.delay
+import timber.log.Timber
+
+class DetailFragment : BindingFragment(R.layout.fragment_detail),
+ DetailBottomSheet {
+
+ private val viewModel: DetailViewModel by viewModels()
+ private lateinit var bottomSheetBehavior: BottomSheetBehavior
+ private val args by navArgs()
+ private val topicPhoto by lazy { args.topicPhoto }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setAnimationSharedElement()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ super.onCreateView(inflater, container, savedInstanceState)
+ return binding {
+ vm = viewModel
+ }.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ viewModel.setTopicPhotoArgs(topicPhoto)
+ binding {
+ bottomSheetBehavior = getBottomSheetBehavior(bottomSheet)
+ btnClose.setOnClickListener { backPopNav() }
+ textView2.setOnClickListener {
+ showAndHideBottomSheet()
+ }
+ }
+ }
+
+ private fun backPopNav() {
+ findNavController().popBackStack()
+ }
+
+ override fun getBottomSheetBehavior(bottomSheet: View): BottomSheetBehavior {
+ return BottomSheetBehavior.from(bottomSheet).apply {
+ state = BottomSheetBehavior.STATE_COLLAPSED
+ }
+ }
+
+ override fun showAndHideBottomSheet() {
+ Timber.d("${bottomSheetBehavior.state}")
+ if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED)
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+ else bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+ }
+
+ private fun setAnimationSharedElement() {
+ val animation = TransitionInflater.from(requireContext()).inflateTransition(
+ android.R.transition.move
+ )
+ sharedElementEnterTransition = animation
+ sharedElementReturnTransition = animation
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailViewModel.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailViewModel.kt
new file mode 100644
index 0000000..b0b99ad
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/detail/DetailViewModel.kt
@@ -0,0 +1,19 @@
+package com.lnmcode.galleryapp.presentation.ui.detail
+
+import androidx.databinding.Bindable
+import com.lnmcode.galleryapp.bindables.BindingViewModel
+import com.lnmcode.galleryapp.bindables.bindingProperty
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+
+class DetailViewModel : BindingViewModel() {
+
+ @get:Bindable
+ var topicPhoto: TopicPhoto? by bindingProperty(null)
+ private set
+
+
+ fun setTopicPhotoArgs(topicPhoto: TopicPhoto) {
+ this.topicPhoto = topicPhoto
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryAdapter.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryAdapter.kt
new file mode 100644
index 0000000..a600c13
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryAdapter.kt
@@ -0,0 +1,38 @@
+package com.lnmcode.galleryapp.presentation.ui.gallery
+
+import android.view.View
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.business.domain.models.topic.Topic
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.presentation.adapter.BaseAdapter
+import com.lnmcode.galleryapp.presentation.adapter.BaseViewHolder
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import com.lnmcode.galleryapp.presentation.viewholder.GalleryGridViewHolder
+import com.lnmcode.galleryapp.presentation.viewholder.GalleryViewHolder
+import timber.log.Timber
+
+
+class GalleryAdapter(
+ private val onChangeLayout: OnChangeLayout,
+) : BaseAdapter() {
+ fun addTopics(topicPhoto: List) {
+ addSubmit(topicPhoto)
+ }
+
+ override fun layout() = R.layout.layout_item_gridview_gallery
+
+ override fun viewHolder(view: View) = GalleryGridViewHolder(view, onChangeLayout)
+
+ override fun areItemsTheSameItem(oldItem: TopicPhoto, newItem: TopicPhoto): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSameItem(oldItem: TopicPhoto, newItem: TopicPhoto): Boolean {
+ return oldItem == newItem
+ }
+
+ init {
+ Timber.d("Init GalleryAdapter")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryFragment.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryFragment.kt
new file mode 100644
index 0000000..faba397
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryFragment.kt
@@ -0,0 +1,69 @@
+package com.lnmcode.galleryapp.presentation.ui.gallery
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.core.view.ViewCompat
+import androidx.core.view.doOnPreDraw
+import androidx.navigation.fragment.FragmentNavigator
+import androidx.navigation.fragment.FragmentNavigatorExtras
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
+import androidx.transition.TransitionInflater
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.bindables.BindingFragment
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.databinding.FragmentGalleryBinding
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import com.lnmcode.galleryapp.presentation.ui.detail.DetailFragmentArgs
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.koin.core.parameter.parametersOf
+import timber.log.Timber
+
+class GalleryFragment : BindingFragment(R.layout.fragment_gallery),
+ OnChangeLayout {
+
+ private val args by navArgs()
+ private val topicId by lazy { args.topicsId }
+
+ private val viewModel: GalleryViewModel by viewModel { parametersOf(topicId) }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ super.onCreateView(inflater, container, savedInstanceState)
+ return binding {
+ adapterHead = GalleryHeadAdapter(this@GalleryFragment)
+ adapterGrid = GalleryAdapter(this@GalleryFragment)
+ vm = viewModel
+ }.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ postponeEnterTransition()
+ binding.root.viewTreeObserver.addOnPreDrawListener {
+ startPostponedEnterTransition()
+ true
+ }
+ }
+
+ override fun onChangeSharedWithParameters(data: Any, viewItemShare: ImageView) {
+ val dataTopicPhoto = data as TopicPhoto
+ val actions = GalleryFragmentDirections.actionGalleryFragmentToDetailFragment(dataTopicPhoto)
+ val extras = FragmentNavigatorExtras(viewItemShare to "image_transition_detail")
+ findNavController().navigate(actions, extras)
+ }
+
+ override fun onChangeWithParameters(data: Any) {
+ TODO("Not yet implemented")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryHeadAdapter.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryHeadAdapter.kt
new file mode 100644
index 0000000..6e41894
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryHeadAdapter.kt
@@ -0,0 +1,34 @@
+package com.lnmcode.galleryapp.presentation.ui.gallery
+
+import android.view.View
+import com.lnmcode.galleryapp.R
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.presentation.adapter.BaseAdapter
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import com.lnmcode.galleryapp.presentation.viewholder.GalleryViewHolder
+import timber.log.Timber
+
+class GalleryHeadAdapter(
+ private val onChangeLayout: OnChangeLayout,
+) : BaseAdapter() {
+
+ fun addTopics(topicPhoto: List) {
+ addSubmit(topicPhoto)
+ }
+
+ override fun layout() = R.layout.layout_item_gallery
+
+ override fun viewHolder(view: View) = GalleryViewHolder(view, onChangeLayout)
+
+ override fun areItemsTheSameItem(oldItem: TopicPhoto, newItem: TopicPhoto): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSameItem(oldItem: TopicPhoto, newItem: TopicPhoto): Boolean {
+ return oldItem == newItem
+ }
+
+ init {
+ Timber.d("Init GalleryHeadAdapter")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryViewModel.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryViewModel.kt
new file mode 100644
index 0000000..534b939
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/ui/gallery/GalleryViewModel.kt
@@ -0,0 +1,31 @@
+package com.lnmcode.galleryapp.presentation.ui.gallery
+
+import androidx.databinding.Bindable
+import androidx.lifecycle.viewModelScope
+import com.lnmcode.galleryapp.bindables.BindingViewModel
+import com.lnmcode.galleryapp.bindables.asBindingProperty
+import com.lnmcode.galleryapp.bindables.bindingProperty
+import com.lnmcode.galleryapp.business.datasource.usecase.TopicPhotoUseCase
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+
+
+class GalleryViewModel(
+ private val topicsId: String,
+ private val topicPhotoUserCase: TopicPhotoUseCase
+) : BindingViewModel() {
+ @get:Bindable
+ var isLoading: Boolean by bindingProperty(true)
+ private set
+ private val topicPhotoFlow = topicPhotoUserCase.getTopicPhoto(
+ topicsId = topicsId,
+ onSuccess = { isLoading = true }
+ )
+
+ @get:Bindable
+ val topicPhoto: List by topicPhotoFlow.asBindingProperty(
+ viewModelScope,
+ emptyList()
+ )
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/utils/ResUtils.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/utils/ResUtils.kt
new file mode 100644
index 0000000..a370353
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/utils/ResUtils.kt
@@ -0,0 +1,40 @@
+package com.lnmcode.galleryapp.presentation.utils
+
+import android.content.Context
+import android.view.View
+import android.widget.Toast
+import androidx.annotation.ColorRes
+import androidx.annotation.IntDef
+import androidx.annotation.StringRes
+import androidx.core.content.res.ResourcesCompat
+import com.google.android.material.snackbar.Snackbar
+import com.lnmcode.galleryapp.R
+
+
+object ResUtils {
+
+ fun getResColor(context: Context, @ColorRes colorRes: Int) =
+ ResourcesCompat.getColor(context.resources, colorRes, null)
+
+ fun showToast(context: Context, message: String, @ToastDuration duration: Int = Toast.LENGTH_SHORT) =
+ Toast.makeText(context, message, duration).show()
+
+ fun showToast(context: Context, @StringRes messageRes: Int, @ToastDuration duration: Int = Toast.LENGTH_SHORT) =
+ showToast(context, context.getString(messageRes), duration)
+
+ fun showSnackBar(view: View, @StringRes messageRes: Int, @SnackBarDuration duration: Int = Snackbar.LENGTH_SHORT) =
+ showSnackBar(view, view.resources.getString(messageRes), duration)
+
+ fun showSnackBar(view: View, message: String, @SnackBarDuration duration: Int = Snackbar.LENGTH_SHORT) =
+ Snackbar.make(view, message, duration)
+ .setAnimationMode(Snackbar.ANIMATION_MODE_SLIDE)
+ .setTextColor(getResColor(view.context, android.R.color.white))
+ .setBackgroundTint(getResColor(view.context, R.color.black))
+ .show()
+}
+
+@IntDef(Snackbar.LENGTH_SHORT, Snackbar.LENGTH_LONG, Snackbar.LENGTH_INDEFINITE)
+annotation class SnackBarDuration
+
+@IntDef(Toast.LENGTH_SHORT, Toast.LENGTH_LONG)
+annotation class ToastDuration
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BindingLazy.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BindingLazy.kt
new file mode 100644
index 0000000..49d20f4
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BindingLazy.kt
@@ -0,0 +1,10 @@
+package com.lnmcode.galleryapp.presentation.viewholder
+
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ViewDataBinding
+import com.lnmcode.galleryapp.presentation.adapter.BaseViewHolder
+
+inline fun BaseViewHolder.bindings(): Lazy =
+ lazy(LazyThreadSafetyMode.NONE) {
+ requireNotNull(DataBindingUtil.bind(itemView)) { "cannot find the matched view to layout." }
+ }
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BoardTopicsViewHolder.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BoardTopicsViewHolder.kt
new file mode 100644
index 0000000..85a6abd
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BoardTopicsViewHolder.kt
@@ -0,0 +1,43 @@
+package com.lnmcode.galleryapp.presentation.viewholder
+
+import android.view.View
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.databinding.LayoutItemTopicsBoardsBinding
+import com.lnmcode.galleryapp.presentation.adapter.BaseViewHolder
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import com.lnmcode.galleryapp.presentation.ui.boards.BoardsActionCacheEvent
+import timber.log.Timber
+
+class BoardTopicsViewHolder(
+ view: View,
+ private val boardsActionCacheEvent: BoardsActionCacheEvent,
+) : BaseViewHolder(view) {
+ private lateinit var data: TopicsCacheDomain
+ private val binding: LayoutItemTopicsBoardsBinding by bindings()
+
+ override fun bindData(data: Any) {
+ if (data is TopicsCacheDomain) {
+ this.data = data
+ setUpLayout()
+ }
+ }
+
+ private fun setUpLayout() {
+ binding.apply {
+ topics = data
+ imgIconRemove.setOnClickListener {
+ val topicsData = TopicsCacheDomain(
+ id = data.id,
+ slug = data.slug,
+ title = data.title,
+ description = data.description
+ )
+ boardsActionCacheEvent.deleteTopicsCache(topicsData)
+ }
+ }
+ }
+
+ override fun onClick(v: View?) {
+ Timber.d("BoardTopicsViewHolder clicked: ${data.title}")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BoardViewHolder.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BoardViewHolder.kt
new file mode 100644
index 0000000..b1792d7
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/BoardViewHolder.kt
@@ -0,0 +1,46 @@
+package com.lnmcode.galleryapp.presentation.viewholder
+
+import android.view.View
+import com.lnmcode.galleryapp.business.domain.cache.TopicsCacheDomain
+import com.lnmcode.galleryapp.business.domain.models.topics.Topics
+import com.lnmcode.galleryapp.databinding.LayoutItemBoardsBinding
+import com.lnmcode.galleryapp.presentation.adapter.BaseViewHolder
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import com.lnmcode.galleryapp.presentation.ui.boards.BoardsActionCacheEvent
+import timber.log.Timber
+
+class BoardViewHolder(
+ view: View,
+ private val onChangeLayout: OnChangeLayout,
+ private val boardsActionCacheEvent: BoardsActionCacheEvent,
+) : BaseViewHolder(view) {
+
+ private lateinit var data: Topics
+ private val binding: LayoutItemBoardsBinding by bindings()
+
+ override fun bindData(data: Any) {
+ if (data is Topics) {
+ this.data = data
+ setUpLayout()
+ }
+ }
+ private fun setUpLayout() {
+ binding.apply {
+ topic = data
+ imgIconCard.setOnClickListener {
+ val topicsData = TopicsCacheDomain(
+ id = data.id,
+ slug = data.slug,
+ title = data.title,
+ description = data.description
+ )
+ boardsActionCacheEvent.insertTopicsCache(topicsData)
+ }
+ }
+ }
+
+ override fun onClick(v: View?) {
+ Timber.d("BoardViewItem clicked ${data.title}")
+ onChangeLayout.onChangeWithParameters(data.id)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/GalleryGridViewHolder.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/GalleryGridViewHolder.kt
new file mode 100644
index 0000000..cee3fd0
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/GalleryGridViewHolder.kt
@@ -0,0 +1,36 @@
+package com.lnmcode.galleryapp.presentation.viewholder
+
+import android.view.View
+import androidx.core.view.ViewCompat
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.databinding.LayoutItemGridviewGalleryBinding
+import com.lnmcode.galleryapp.presentation.adapter.BaseViewHolder
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import timber.log.Timber
+
+class GalleryGridViewHolder(
+ view: View,
+ private val onChangeLayout: OnChangeLayout,
+) : BaseViewHolder(view) {
+
+ private lateinit var data: TopicPhoto
+ private val binding: LayoutItemGridviewGalleryBinding by bindings()
+
+ override fun bindData(data: Any) {
+ if (data is TopicPhoto) {
+ this.data = data
+ setUpLayout()
+ }
+ }
+
+ private fun setUpLayout() {
+ binding.apply {
+ topicPhoto = data
+ }
+ }
+
+ override fun onClick(v: View?) {
+ Timber.d("GalleryGridViewHolder clicked item ${data.id}")
+ onChangeLayout.onChangeSharedWithParameters(data, binding.image)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/GalleryViewHolder.kt b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/GalleryViewHolder.kt
new file mode 100644
index 0000000..aeccc4c
--- /dev/null
+++ b/app/src/main/java/com/lnmcode/galleryapp/presentation/viewholder/GalleryViewHolder.kt
@@ -0,0 +1,35 @@
+package com.lnmcode.galleryapp.presentation.viewholder
+
+import android.view.View
+import com.lnmcode.galleryapp.business.domain.models.topicphoto.TopicPhoto
+import com.lnmcode.galleryapp.databinding.LayoutItemGalleryBinding
+import com.lnmcode.galleryapp.presentation.adapter.BaseViewHolder
+import com.lnmcode.galleryapp.presentation.ui.OnChangeLayout
+import timber.log.Timber
+
+class GalleryViewHolder(
+ view: View,
+ private val onChangeLayout: OnChangeLayout,
+) : BaseViewHolder(view) {
+
+ private lateinit var data: TopicPhoto
+ private val binding: LayoutItemGalleryBinding by bindings()
+ override fun bindData(data: Any) {
+ if (data is TopicPhoto) {
+ this.data = data
+ setUpLayout()
+ }
+ }
+
+ private fun setUpLayout() {
+ binding.apply {
+ topicPhoto = data
+ }
+ }
+
+ override fun onClick(v: View?) {
+ Timber.d("GalleryViewHolder clicked item ${data.id}")
+ onChangeLayout.onChangeSharedWithParameters(data, binding.imgItemTopGallery)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_from_left.xml b/app/src/main/res/anim/slide_from_left.xml
new file mode 100644
index 0000000..ada4f1a
--- /dev/null
+++ b/app/src/main/res/anim/slide_from_left.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_from_right.xml b/app/src/main/res/anim/slide_from_right.xml
new file mode 100644
index 0000000..2470170
--- /dev/null
+++ b/app/src/main/res/anim/slide_from_right.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_to_left.xml b/app/src/main/res/anim/slide_to_left.xml
new file mode 100644
index 0000000..64d6829
--- /dev/null
+++ b/app/src/main/res/anim/slide_to_left.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_to_right.xml b/app/src/main/res/anim/slide_to_right.xml
new file mode 100644
index 0000000..a73c633
--- /dev/null
+++ b/app/src/main/res/anim/slide_to_right.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_linear_gradient.xml b/app/src/main/res/drawable/bg_linear_gradient.xml
new file mode 100644
index 0000000..e367581
--- /dev/null
+++ b/app/src/main/res/drawable/bg_linear_gradient.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_radius_corner_white.xml b/app/src/main/res/drawable/bg_radius_corner_white.xml
new file mode 100644
index 0000000..01378a6
--- /dev/null
+++ b/app/src/main/res/drawable/bg_radius_corner_white.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/boder_image_itemgallery.xml b/app/src/main/res/drawable/boder_image_itemgallery.xml
new file mode 100644
index 0000000..344bd84
--- /dev/null
+++ b/app/src/main/res/drawable/boder_image_itemgallery.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml
new file mode 100644
index 0000000..b88b155
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_back.png b/app/src/main/res/drawable/ic_arrow_back.png
new file mode 100644
index 0000000..de3bc34
Binary files /dev/null and b/app/src/main/res/drawable/ic_arrow_back.png differ
diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml
new file mode 100644
index 0000000..1909c56
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml
new file mode 100644
index 0000000..70db409
--- /dev/null
+++ b/app/src/main/res/drawable/ic_close.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_pen_detail.xml b/app/src/main/res/drawable/ic_pen_detail.xml
new file mode 100644
index 0000000..6d52da0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pen_detail.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/font-v26/font_tt_norms.xml b/app/src/main/res/font-v26/font_tt_norms.xml
new file mode 100644
index 0000000..70f4549
--- /dev/null
+++ b/app/src/main/res/font-v26/font_tt_norms.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/font/europa_regular.ttf b/app/src/main/res/font/europa_regular.ttf
new file mode 100644
index 0000000..1d479fd
Binary files /dev/null and b/app/src/main/res/font/europa_regular.ttf differ
diff --git a/app/src/main/res/font/font_europa_regular.xml b/app/src/main/res/font/font_europa_regular.xml
new file mode 100644
index 0000000..f58c949
--- /dev/null
+++ b/app/src/main/res/font/font_europa_regular.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/font/font_tt_norms.xml b/app/src/main/res/font/font_tt_norms.xml
new file mode 100644
index 0000000..70f4549
--- /dev/null
+++ b/app/src/main/res/font/font_tt_norms.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/font/tt_norms_medium.otf b/app/src/main/res/font/tt_norms_medium.otf
new file mode 100644
index 0000000..54f5c4c
Binary files /dev/null and b/app/src/main/res/font/tt_norms_medium.otf differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 7a9080d..9c36099 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,18 +1,44 @@
-
+ xmlns:tools="http://schemas.android.com/tools">
-
+
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_boards.xml b/app/src/main/res/layout/fragment_boards.xml
new file mode 100644
index 0000000..bed3117
--- /dev/null
+++ b/app/src/main/res/layout/fragment_boards.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_detail.xml b/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..893531e
--- /dev/null
+++ b/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_gallery.xml b/app/src/main/res/layout/fragment_gallery.xml
new file mode 100644
index 0000000..cfd3100
--- /dev/null
+++ b/app/src/main/res/layout/fragment_gallery.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_bottom_sheet_detail.xml b/app/src/main/res/layout/layout_bottom_sheet_detail.xml
new file mode 100644
index 0000000..cae19f1
--- /dev/null
+++ b/app/src/main/res/layout/layout_bottom_sheet_detail.xml
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_item_boards.xml b/app/src/main/res/layout/layout_item_boards.xml
new file mode 100644
index 0000000..6be06ac
--- /dev/null
+++ b/app/src/main/res/layout/layout_item_boards.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_item_gallery.xml b/app/src/main/res/layout/layout_item_gallery.xml
new file mode 100644
index 0000000..d6030f7
--- /dev/null
+++ b/app/src/main/res/layout/layout_item_gallery.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_item_gridview_gallery.xml b/app/src/main/res/layout/layout_item_gridview_gallery.xml
new file mode 100644
index 0000000..96f98d2
--- /dev/null
+++ b/app/src/main/res/layout/layout_item_gridview_gallery.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_item_topics_boards.xml b/app/src/main/res/layout/layout_item_topics_boards.xml
new file mode 100644
index 0000000..e47ad5f
--- /dev/null
+++ b/app/src/main/res/layout/layout_item_topics_boards.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/nav_main.xml b/app/src/main/res/navigation/nav_main.xml
new file mode 100644
index 0000000..fdab28c
--- /dev/null
+++ b/app/src/main/res/navigation/nav_main.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f8c6127..3d891cc 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,5 +1,6 @@
+ #F0F4F4
#FFBB86FC
#FF6200EE
#FF3700B3
@@ -7,4 +8,8 @@
#FF018786
#FF000000
#FFFFFFFF
+ #F0F4F4
+ #FC5C4C
+ #FD814A
+ #E2E2E2
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..bc4d2b9
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,25 @@
+
+
+ 12dp
+ 8dp
+ 2dp
+ 260dp
+ 25dp
+ 10dp
+ 15dp
+ 34sp
+ 20dp
+ 34sp
+ 1dp
+ 10dp
+ 4dp
+ 15dp
+ 10dp
+ 8dp
+ 12dp
+ 15dp
+ 15sp
+ 17sp
+ 6dp
+ 20sp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0750463..0a86077 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,18 @@
GalleryApp
+ GalleryApp.db
+ Boards
+ Following galleries to power up your art care
+ Artist
+ Size
+ Location
+ Location
+ Overview
+ It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using Content here, content here, making it look like readable English.
+ %1$s x %2$s
+ No have any description
+ Gallery
+ Curated Galleries
+ %1$s_grid
+ %1$s_top
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index d71f818..2e8196a 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,6 +1,6 @@
-
\ No newline at end of file
diff --git a/preview/preview01.gif b/preview/preview01.gif
new file mode 100644
index 0000000..6c8b1ab
Binary files /dev/null and b/preview/preview01.gif differ
diff --git a/preview/preview02.gif b/preview/preview02.gif
new file mode 100644
index 0000000..16302de
Binary files /dev/null and b/preview/preview02.gif differ
diff --git a/preview/preview03.gif b/preview/preview03.gif
new file mode 100644
index 0000000..5b586ad
Binary files /dev/null and b/preview/preview03.gif differ