diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 6025979..dfebd19 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -13,10 +13,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: set up JDK 1.8
+ - name: set up JDK 11
uses: actions/setup-java@v1
with:
- java-version: 1.8
+ java-version: 11
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
diff --git a/README.md b/README.md
index fd309d7..43daf5a 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@ The application for Android Academy made by Students
Design -- [Link](https://www.figma.com/file/qoC9xPS1X7GU9VcGK8SSpA/Main?node-id=0%3A1)
-Android Architecture Diagram -- [Link](https://drive.google.com/file/d/1svw28LBVJ2k2EIOyJmdzE41NW1Q-UQ3o/view?usp=sharing)
+Architecture diagram can be found [here](https://drive.google.com/file/d/1EgUr_bFkBrK0Cazm2GhbnaT5Y7i9nBkp).
-DataBase Entity Relation Diagram -- [Link](https://drive.google.com/file/d/1rtEUZz_3XEBnn2Ck9IwUpzrevrE9lzRU/view?usp=sharing)
+DataBase Entity Relation Diagram -- TBD.
+
+[Here](https://github.com/mik629/AndroidAcademyClient/wiki/Tech-stack) is the tech stack we agreed to use.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 2d8ca71..fc73162 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,20 +1,25 @@
+import Config.androidBuildTools
+import Config.androidCompileSdk
+import Config.androidMinSdk
+import Config.androidTargetSdk
+
plugins {
- id("com.android.application")
- kotlin("android")
- kotlin("plugin.serialization")
- kotlin("kapt")
- id("dagger.hilt.android.plugin")
- id("androidx.navigation.safeargs.kotlin")
+ id(Plugins.appPlugin)
+ kotlin(Plugins.androidPlugin)
+ kotlin(Plugins.serializationPlugin)
+ kotlin(Plugins.kapt)
+ id(Plugins.hiltPlugin)
+ id(Plugins.navigationSafeArgsPlugin)
}
android {
- compileSdkVersion(30)
- buildToolsVersion = "30.0.3"
+ compileSdk = androidCompileSdk
+ buildToolsVersion = androidBuildTools
defaultConfig {
applicationId = "com.academy.android"
- minSdkVersion(21)
- targetSdkVersion(30)
+ minSdk = androidMinSdk
+ targetSdk = androidTargetSdk
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
@@ -35,16 +40,24 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
+ composeOptions {
+ kotlinCompilerVersion = Versions.kotlin
+ kotlinCompilerExtensionVersion = Versions.compose
+ }
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
+ compose = true
}
}
dependencies {
+ // auth
+ implementation(Libs.playServicesAuth)
+
// Core
implementation("androidx.core:core-ktx:1.3.2")
@@ -62,6 +75,7 @@ dependencies {
// Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
+ implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0")
// Navigation
implementation("androidx.navigation:navigation-fragment-ktx:${rootProject.extra["navigationVersion"]}")
@@ -73,16 +87,21 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion")
implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion")
+ // Network
+ implementation("com.squareup.retrofit2:retrofit:2.9.0")
+ implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
+
// DI
- val daggerVersion = "2.33"
- implementation("com.google.dagger:dagger:$daggerVersion")
- kapt("com.google.dagger:dagger-compiler:$daggerVersion")
- implementation("com.google.dagger:hilt-android:${rootProject.extra["hiltVersion"]}")
- kapt("com.google.dagger:hilt-compiler:${rootProject.extra["hiltVersion"]}")
+ implementation(Libs.dagger)
+ kapt(Libs.daggerCompiler)
+
+ implementation(Libs.hilt)
+ kapt(Libs.hiltCompiler)
+ implementation(Libs.hiltNavigationCompose)
val androidxHilt = "1.0.0-alpha03"
implementation("androidx.hilt:hilt-lifecycle-viewmodel:$androidxHilt")
- kapt("androidx.hilt:hilt-compiler:$androidxHilt")
- implementation("androidx.hilt:hilt-work:$androidxHilt")
+ kapt("androidx.hilt:hilt-compiler:1.0.0")
+ implementation("androidx.hilt:hilt-work:1.0.0")
// Concurrency
val coroutinesVersion = "1.4.3"
@@ -96,7 +115,7 @@ dependencies {
implementation("com.jakewharton.timber:timber:4.7.1")
//SharedPreference
- implementation ("androidx.preference:preference-ktx:1.1.1")
+ implementation (Libs.dataStore)
// DB
val roomVersion = "2.2.6"
@@ -113,6 +132,17 @@ dependencies {
// ViewBindingPropertyDelegate
implementation("com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.4.4")
+ // compose
+ implementation(Libs.activityCompose)
+ implementation(Libs.vmCompose)
+ implementation(Libs.composeCompiler)
+ implementation(Libs.composeFoundation)
+ implementation(Libs.composeMaterial)
+ implementation(Libs.composeUI)
+ implementation(Libs.composeTooling)
+ implementation(Libs.coil)
+ implementation(Libs.glideComposeVersion)
+
// Testing
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.2")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d276c77..b419955 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,7 +4,7 @@
-
+
+ android:theme="@style/Theme.AndroidAcademy"
+ android:usesCleartextTraffic="true"
+ android:windowSoftInputMode="adjustResize">
+ android:exported="true">
diff --git a/app/src/main/java/com/academy/android/MainActivity.kt b/app/src/main/java/com/academy/android/MainActivity.kt
index 62692e6..c17119b 100644
--- a/app/src/main/java/com/academy/android/MainActivity.kt
+++ b/app/src/main/java/com/academy/android/MainActivity.kt
@@ -1,39 +1,24 @@
package com.academy.android
import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.navigation.NavController
-import androidx.navigation.findNavController
-import androidx.navigation.ui.AppBarConfiguration
-import androidx.navigation.ui.NavigationUI
-import androidx.navigation.ui.setupActionBarWithNavController
-import androidx.navigation.ui.setupWithNavController
-import com.google.android.material.bottomnavigation.BottomNavigationView
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.navigation.compose.rememberNavController
+import com.academy.android.ui.AcademyTheme
+import com.academy.android.ui.StartNavigation
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
-class MainActivity : AppCompatActivity() {
-
- private val navController: NavController by lazy {
- findNavController(R.id.nav_host_fragment)
- }
+class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- val navView: BottomNavigationView = findViewById(R.id.nav_view)
- // Passing each menu ID as a set of Ids because each
- // menu should be considered as top level destinations.
- val appBarConfiguration = AppBarConfiguration(
- setOf(
- R.id.navigation_videos, R.id.navigation_news, R.id.navigation_profile
- )
- )
- setupActionBarWithNavController(navController, appBarConfiguration)
- navView.setupWithNavController(navController)
- }
+ setContent {
+ val navController = rememberNavController()
- override fun onSupportNavigateUp(): Boolean {
- return NavigationUI.navigateUp(navController, null)
+ AcademyTheme {
+ StartNavigation(navController = navController)
+ }
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/PrefsStorage.kt b/app/src/main/java/com/academy/android/data/PrefsStorage.kt
new file mode 100644
index 0000000..6d591a8
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/PrefsStorage.kt
@@ -0,0 +1,59 @@
+package com.academy.android.data
+
+import android.content.Context
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import com.academy.android.domain.OperationResult
+import com.academy.android.domain.models.UserProfile
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.serialization.json.Json
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class PrefsStorage @Inject constructor(
+ @ApplicationContext private val appContext: Context
+) {
+ private val Context.dataStore by preferencesDataStore("settings")
+
+ suspend fun writeString(key: Preferences.Key, value: String) {
+ appContext.dataStore.edit { prefs ->
+ prefs[key] = value
+ }
+ }
+
+ fun readString(key: Preferences.Key, default: String): Flow =
+ appContext.dataStore.data
+ .map { prefs ->
+ prefs[key] ?: default
+ }
+
+ suspend fun saveProfile(profile: UserProfile) {
+ val serializedProfile = Json.encodeToString(UserProfile.serializer(), profile)
+ appContext.dataStore.edit { prefs ->
+ prefs[PROFILE_KEY] = serializedProfile
+ }
+ }
+
+ fun loadProfile(): Flow> =
+ appContext.dataStore
+ .data
+ .map { prefs ->
+ val profile = prefs[PROFILE_KEY]
+ OperationResult.Success(
+ data = if (profile != null) {
+ Json.decodeFromString(UserProfile.serializer(), profile)
+ } else {
+ UserProfile.UNKNOWN
+ }
+ )
+ }
+
+ companion object {
+ private val PROFILE_KEY = stringPreferencesKey("user_profile")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/ServerApi.kt b/app/src/main/java/com/academy/android/data/network/ServerApi.kt
new file mode 100644
index 0000000..d8862f3
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/ServerApi.kt
@@ -0,0 +1,56 @@
+package com.academy.android.data.network
+
+import com.academy.android.data.network.models.CourseDTO
+import com.academy.android.data.network.models.LectureDTO
+import com.academy.android.data.network.models.LoginRequestDTO
+import com.academy.android.data.network.models.LoginResponseDTO
+import com.academy.android.data.network.models.RegisterRequestDTO
+import com.academy.android.data.network.models.UpdateCourseRequestDTO
+import com.academy.android.data.network.models.UpdateLectureRequestDTO
+import retrofit2.http.Body
+import retrofit2.http.GET
+import retrofit2.http.POST
+import retrofit2.http.Query
+
+interface ServerApi {
+ @POST("login")
+ suspend fun login(
+ @Body loginRequest: LoginRequestDTO
+ ): LoginResponseDTO
+
+ @POST("register")
+ suspend fun register(
+ @Body registerRequestDTO: RegisterRequestDTO
+ ): LoginResponseDTO
+
+ @GET("courses/favorite")
+ suspend fun getFavouriteCourses(): List
+
+ @GET("courses/all")
+ suspend fun getAllCourses(): List
+
+ @POST("courses/update")
+ suspend fun updateCourse(
+ @Body updateCourseRequestDTO: UpdateCourseRequestDTO
+ )
+
+ @POST("lectures/update")
+ suspend fun updateLecture(
+ @Body updateLectureRequestDTO: UpdateLectureRequestDTO
+ )
+
+ @GET("lectures/all")
+ suspend fun getAllLectures(
+ @Query(value = "courseId") courseId: Long
+ ): List
+
+ @GET("lectures/by-id")
+ suspend fun getLectureById(
+ @Query(value = "lectureId") lectureId: Long
+ ): LectureDTO
+
+ @POST("user/update-fcm-token")
+ suspend fun updateFcmToken(
+ @Body fcmToken: String
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/TokenAuthenticator.kt b/app/src/main/java/com/academy/android/data/network/TokenAuthenticator.kt
new file mode 100644
index 0000000..4592308
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/TokenAuthenticator.kt
@@ -0,0 +1,36 @@
+package com.academy.android.data.network
+
+import okhttp3.Authenticator
+import okhttp3.Request
+import okhttp3.Response
+import okhttp3.Route
+import javax.inject.Inject
+
+// fixme: fix user auth
+class TokenAuthenticator @Inject constructor(
+// private val prefsStorage: PrefsStorage
+) : Authenticator {
+ override fun authenticate(route: Route?, response: Response): Request? =
+ if (responseCount(response) < MAX_ATTEMPTS) {
+ response.request
+ .newBuilder()
+// .header(AUTH_HEADER, prefsStorage.loadUser()?.token.orEmpty())
+ .build()
+ } else {
+ null
+ }
+
+ private fun responseCount(response: Response): Int {
+ var result = 1
+ var r: Response? = response
+ while (true) {
+ r = r?.priorResponse ?: return result
+ result++
+ }
+ }
+
+ companion object {
+ const val MAX_ATTEMPTS = 3
+ const val AUTH_HEADER = "Token"
+ }
+}
diff --git a/app/src/main/java/com/academy/android/data/network/models/AdditionalMaterialDTO.kt b/app/src/main/java/com/academy/android/data/network/models/AdditionalMaterialDTO.kt
new file mode 100644
index 0000000..72f5a51
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/AdditionalMaterialDTO.kt
@@ -0,0 +1,13 @@
+package com.academy.android.data.network.models
+
+import com.academy.android.domain.models.AdditionalMaterial
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class AdditionalMaterialDTO(
+ val topicName: String,
+ val url: String
+)
+
+fun AdditionalMaterialDTO.toAdditionalMaterial(): AdditionalMaterial =
+ AdditionalMaterial(topicName = topicName, url = url)
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/models/CourseDTO.kt b/app/src/main/java/com/academy/android/data/network/models/CourseDTO.kt
new file mode 100644
index 0000000..ee82200
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/CourseDTO.kt
@@ -0,0 +1,27 @@
+package com.academy.android.data.network.models
+
+import com.academy.android.domain.models.Course
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class CourseDTO(
+ val id: Long,
+ val title: String,
+ val shortDescription: String? = null,
+ val fullDescription: String? = null,
+ val imgUrl: String? = null,
+ val tags: List,
+ @SerialName("subscribed") val isSubscribed: Boolean = false
+)
+
+fun CourseDTO.toCourse(): Course =
+ Course(
+ id = id,
+ title = title,
+ shortDescription = shortDescription,
+ fullDescription = fullDescription,
+ imgUrl = imgUrl,
+ tags = tags,
+ isSubscribed = isSubscribed
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/models/LectureDTO.kt b/app/src/main/java/com/academy/android/data/network/models/LectureDTO.kt
new file mode 100644
index 0000000..dd71af9
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/LectureDTO.kt
@@ -0,0 +1,32 @@
+package com.academy.android.data.network.models
+
+import com.academy.android.domain.models.Lecture
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LectureDTO(
+ val id: Long,
+ val title: String,
+ val youtubeUrl: String = "",
+ val githubRepoUrl: String = "",
+ val telegramChannel: String = "",
+ val additionalMaterials: List,
+ val imgUrl: String? = null,
+ val tags: List,
+ val courseId: Long,
+ val startTimestamp : Long
+)
+
+fun LectureDTO.toLecture(): Lecture =
+ Lecture(
+ id = id,
+ title = title,
+ youtubeUrl = youtubeUrl,
+ githubRepoUrl = githubRepoUrl,
+ telegramChannel = telegramChannel,
+ additionalMaterials = additionalMaterials.map(AdditionalMaterialDTO::toAdditionalMaterial),
+ imgUrl = imgUrl,
+ tags = tags,
+ courseId = courseId,
+ startTimestamp = startTimestamp
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/models/LoginRequestDTO.kt b/app/src/main/java/com/academy/android/data/network/models/LoginRequestDTO.kt
new file mode 100644
index 0000000..05ab248
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/LoginRequestDTO.kt
@@ -0,0 +1,9 @@
+package com.academy.android.data.network.models
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LoginRequestDTO(
+ val username: String,
+ val pwd: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/models/LoginResponseDTO.kt b/app/src/main/java/com/academy/android/data/network/models/LoginResponseDTO.kt
new file mode 100644
index 0000000..8a2130e
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/LoginResponseDTO.kt
@@ -0,0 +1,12 @@
+package com.academy.android.data.network.models
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LoginResponseDTO(
+ @SerialName("user_profile")
+ val userProfile: UserProfileDTO,
+ @SerialName("token")
+ val token: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/models/RegisterRequestDTO.kt b/app/src/main/java/com/academy/android/data/network/models/RegisterRequestDTO.kt
new file mode 100644
index 0000000..34a95d2
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/RegisterRequestDTO.kt
@@ -0,0 +1,12 @@
+package com.academy.android.data.network.models
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class RegisterRequestDTO(
+ val username: String,
+ val pwd: String,
+ val name: String,
+ @SerialName("mentor") val isMentor: Boolean
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/models/UpdateCourseRequestDTO.kt b/app/src/main/java/com/academy/android/data/network/models/UpdateCourseRequestDTO.kt
new file mode 100644
index 0000000..406fa25
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/UpdateCourseRequestDTO.kt
@@ -0,0 +1,14 @@
+package com.academy.android.data.network.models
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class UpdateCourseRequestDTO(
+ val id: Long? = null,
+ val title: String,
+ val shortDescription: String? = null,
+ val fullDescription: String? = null,
+ val imgUrl: String? = null,
+ val tags: List,
+ val subscribed: Boolean
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/network/models/UpdateLectureRequestDTO.kt b/app/src/main/java/com/academy/android/data/network/models/UpdateLectureRequestDTO.kt
new file mode 100644
index 0000000..2f82254
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/UpdateLectureRequestDTO.kt
@@ -0,0 +1,17 @@
+package com.academy.android.data.network.models
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class UpdateLectureRequestDTO(
+ val id: Long? = null,
+ val title: String,
+ val youtubeUrl: String = "",
+ val githubRepoUrl: String = "",
+ val telegramChannel: String = "",
+ val additionalMaterials: List,
+ val imgUrl: String? = null,
+ val tags: List,
+ val courseId: Long,
+ val startTimestamp : Long
+)
diff --git a/app/src/main/java/com/academy/android/data/network/models/UserProfileDTO.kt b/app/src/main/java/com/academy/android/data/network/models/UserProfileDTO.kt
new file mode 100644
index 0000000..3dd35f9
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/network/models/UserProfileDTO.kt
@@ -0,0 +1,36 @@
+package com.academy.android.data.network.models
+
+import com.academy.android.domain.models.UserProfile
+import com.academy.android.domain.models.UserTitle
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class UserProfileDTO(
+ @SerialName("username")
+ val username: String,
+ @SerialName("mentor")
+ val mentor: Boolean,
+ @SerialName("prof_pic")
+ var profPic: String? = null,
+ @SerialName("first_name")
+ var firstName: String? = null,
+ @SerialName("last_name")
+ var lastName: String? = null,
+ @SerialName("phone_number")
+ var phoneNumber: String? = null,
+ @SerialName("email")
+ var email: String? = null,
+ @SerialName("title")
+ var title: String? = null
+)
+
+fun UserProfileDTO.toUserProfile(): UserProfile =
+ UserProfile(
+ profPic = profPic,
+ firstName = firstName,
+ lastName = lastName,
+ phoneNumber = phoneNumber,
+ email = email,
+ title = title?.let { userTitle -> UserTitle.valueOf(userTitle) } ?: UserTitle.UNKNOWN
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/repositories/AuthRepositoryImpl.kt b/app/src/main/java/com/academy/android/data/repositories/AuthRepositoryImpl.kt
new file mode 100644
index 0000000..218ae40
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/repositories/AuthRepositoryImpl.kt
@@ -0,0 +1,96 @@
+package com.academy.android.data.repositories
+
+import com.academy.android.data.PrefsStorage
+import com.academy.android.data.network.ServerApi
+import com.academy.android.data.network.models.LoginRequestDTO
+import com.academy.android.data.network.models.RegisterRequestDTO
+import com.academy.android.data.network.models.UserProfileDTO
+import com.academy.android.data.network.models.toUserProfile
+import com.academy.android.domain.OperationResult
+import com.academy.android.domain.models.UserProfile
+import com.academy.android.domain.repositories.AuthRepository
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+class AuthRepositoryImpl @Inject constructor(
+ private val prefsStorage: PrefsStorage,
+ private val serverApi: ServerApi
+) : AuthRepository {
+
+ override val userProfile: Flow> =
+ prefsStorage.loadProfile()
+
+ override suspend fun login(
+ username: String,
+ password: String
+ ): OperationResult =
+ try {
+ serverApi.login(
+ LoginRequestDTO(
+ username = username,
+ pwd = password
+ )
+ ).userProfile
+ .also { profile ->
+ saveProfile(profile)
+ }
+
+ sendFcmTokenToBackend()
+
+ OperationResult.Success(Unit)
+
+ } catch (e: Throwable) {
+ OperationResult.Error(e)
+ }
+
+ override suspend fun register(
+ username: String,
+ password: String,
+ name: String,
+ isMentor: Boolean
+ ): OperationResult =
+ try {
+ serverApi.register(
+ RegisterRequestDTO(
+ username = username,
+ pwd = password,
+ name = name,
+ isMentor = isMentor
+ )
+ ).userProfile
+ .also { profile ->
+ saveProfile(profile)
+ }
+
+ sendFcmTokenToBackend()
+
+ OperationResult.Success(Unit)
+
+ } catch (e: Throwable) {
+ OperationResult.Error(e)
+ }
+
+ private suspend fun saveProfile(profileDto: UserProfileDTO) {
+ profileDto.toUserProfile()
+ .also { profile ->
+ prefsStorage.saveProfile(profile = profile)
+ }
+ }
+
+ private fun sendFcmTokenToBackend() {
+// FirebaseMessaging.getInstance().token
+// .addOnSuccessListener { fcmToken ->
+// GlobalScope.launch {
+// println("fcm_token:$fcmToken")
+// serverApi.updateFcmToken(fcmToken)
+// }
+// }
+// .addOnFailureListener {
+// Timber.e(it, "Cannot get FCM token")
+// }
+ }
+
+ override suspend fun enterGuestMode() {
+ prefsStorage.saveProfile(UserProfile.GUEST)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/repositories/CourseRepositoryImpl.kt b/app/src/main/java/com/academy/android/data/repositories/CourseRepositoryImpl.kt
new file mode 100644
index 0000000..766616d
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/repositories/CourseRepositoryImpl.kt
@@ -0,0 +1,108 @@
+package com.academy.android.data.repositories
+
+import com.academy.android.data.network.ServerApi
+import com.academy.android.data.network.models.AdditionalMaterialDTO
+import com.academy.android.data.network.models.UpdateCourseRequestDTO
+import com.academy.android.data.network.models.UpdateLectureRequestDTO
+import com.academy.android.data.network.models.toLecture
+import com.academy.android.domain.OperationResult
+import com.academy.android.domain.models.Course
+import com.academy.android.domain.models.Lecture
+import com.academy.android.domain.repositories.CourseRepository
+import javax.inject.Inject
+
+class CourseRepositoryImpl @Inject constructor(
+ private val serverApi: ServerApi
+) : CourseRepository {
+
+ override suspend fun getAllCourses(): OperationResult, String?> =
+ try {
+ val courses = serverApi.getAllCourses().map {
+ Course(
+ id = it.id,
+ title = it.title,
+ shortDescription = it.shortDescription,
+ fullDescription = it.fullDescription,
+ imgUrl = it.imgUrl,
+ isSubscribed = it.isSubscribed,
+ tags = it.tags
+ )
+ }
+ OperationResult.Success(courses)
+ } catch (e: Throwable) {
+ OperationResult.Error(e.message)
+ }
+
+ override suspend fun getFavouriteCourses(username: String): OperationResult, String?> =
+ try {
+ val courses = serverApi.getFavouriteCourses().map {
+ Course(
+ id = it.id,
+ title = it.title,
+ shortDescription = it.shortDescription,
+ fullDescription = it.fullDescription,
+ imgUrl = it.imgUrl,
+ isSubscribed = it.isSubscribed,
+ tags = it.tags
+ )
+ }.filter { it.isSubscribed } // TODO: remove filter
+ OperationResult.Success(courses)
+ } catch (e: Throwable) {
+ OperationResult.Error(e.message)
+ }
+
+ override suspend fun updateCourse(course: Course): OperationResult =
+ try {
+ serverApi.updateCourse(
+ UpdateCourseRequestDTO(
+ id = course.id,
+ title = course.title,
+ shortDescription = course.shortDescription,
+ fullDescription = course.fullDescription,
+ imgUrl = course.imgUrl,
+ subscribed = course.isSubscribed,
+ tags = course.tags
+ )
+ )
+ OperationResult.Success(Unit)
+ } catch (e: Throwable) {
+ OperationResult.Error(e.message)
+ }
+
+
+ override suspend fun getAllLectures(courseId: Long): OperationResult, String?> =
+ try {
+ val lectures = serverApi.getAllLectures(courseId).map {
+ it.toLecture()
+ }
+ OperationResult.Success(lectures)
+ } catch (e: Throwable) {
+ OperationResult.Error(e.message)
+ }
+
+ override suspend fun updateLecture(lecture: Lecture): OperationResult =
+ try {
+ serverApi.updateLecture(
+ UpdateLectureRequestDTO(
+ id = lecture.id,
+ title = lecture.title,
+ youtubeUrl = lecture.youtubeUrl,
+ githubRepoUrl = lecture.githubRepoUrl,
+ telegramChannel = lecture.telegramChannel,
+ additionalMaterials = lecture.additionalMaterials.map {
+ AdditionalMaterialDTO(
+ topicName = it.topicName,
+ url = it.url
+ )
+ },
+ imgUrl = lecture.imgUrl,
+ tags = lecture.tags,
+ courseId = lecture.courseId,
+ startTimestamp = lecture.startTimestamp
+ )
+ )
+ OperationResult.Success(Unit)
+ } catch (e: Throwable) {
+ OperationResult.Error(e.message)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/repositories/LecturesRepositoryImpl.kt b/app/src/main/java/com/academy/android/data/repositories/LecturesRepositoryImpl.kt
new file mode 100644
index 0000000..ff290e9
--- /dev/null
+++ b/app/src/main/java/com/academy/android/data/repositories/LecturesRepositoryImpl.kt
@@ -0,0 +1,16 @@
+package com.academy.android.data.repositories
+
+import com.academy.android.data.network.ServerApi
+import com.academy.android.data.network.models.toLecture
+import com.academy.android.domain.models.Lecture
+import com.academy.android.domain.repositories.LecturesRepository
+import javax.inject.Inject
+
+class LecturesRepositoryImpl @Inject constructor(
+ private val serverApi: ServerApi
+) : LecturesRepository {
+
+ override suspend fun getLecture(id: Long): Lecture =
+ serverApi.getLectureById(lectureId = id)
+ .toLecture()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/repositories/ProfileRepository.kt b/app/src/main/java/com/academy/android/data/repositories/ProfileRepository.kt
deleted file mode 100644
index 26ad1fe..0000000
--- a/app/src/main/java/com/academy/android/data/repositories/ProfileRepository.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.academy.android.data.repositories
-
-import com.academy.android.model.Profile
-import com.academy.android.util.SharedPreferenceHelper
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flow
-import javax.inject.Inject
-
-
-interface ProfileRepositorySource{
-
- fun saveProfileData(
- profile: Profile
- )
-
- fun getProfileData(): Profile
-}
-
-class ProfileRepository @Inject constructor(
- private val prefsHelper: SharedPreferenceHelper
-): ProfileRepositorySource {
-
- val profileData: Flow = flow {
- val profile = getProfileData()
- emit(profile)
- }
-
- override fun saveProfileData(profile: Profile) {
- prefsHelper.saveProfData(profile)
- }
-
- override fun getProfileData(): Profile {
- return prefsHelper.getProfData()
- }
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/academy/android/data/repositories/VideosRepository.kt b/app/src/main/java/com/academy/android/data/repositories/VideosRepository.kt
deleted file mode 100644
index 27c6f48..0000000
--- a/app/src/main/java/com/academy/android/data/repositories/VideosRepository.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.academy.android.data.repositories
-
-import com.academy.android.model.FilterParameters
-import com.academy.android.model.Video
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import javax.inject.Inject
-
-interface VideosRepositorySource {
- val videosList: StateFlow>
- fun getFilterParameters() : FilterParameters
-}
-
-class VideosRepository @Inject constructor() : VideosRepositorySource {
- private val _videosList = MutableStateFlow(provideMokkVideos())
- override val videosList: StateFlow> = _videosList
-
- private val filterParameters = FilterParameters(listOf("Moscow", "Minsk", "Tel-Aviv"),
- listOf("Fundamentals", "Advanced"),
- listOf("2019-2020", "2020-2021"))
-
- override fun getFilterParameters() : FilterParameters = filterParameters
-
- private fun provideMokkVideos(): List