diff --git a/.editorConfig b/.editorConfig new file mode 100644 index 00000000..03e6d151 --- /dev/null +++ b/.editorConfig @@ -0,0 +1,4 @@ +root = true + +[*.{kt,kts}] +ktlint_function_naming_ignore_when_annotated_with=Composable \ No newline at end of file diff --git a/.gitignore b/.gitignore index aef2159a..b1984842 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Compiled class file *.class +*.DS_STORE # Log file *.log @@ -34,7 +35,6 @@ ios/FotoPresenter.xcworkspace/xcuserdata/kevinschildhorn.xcuserdatad/xcdebugger/ ios/FotoPresenter.xcworkspace/xcuserdata/kevinschildhorn.xcuserdatad/UserInterfaceState.xcuserstate *.hprof ios/FotoPresenter.xcworkspace/xcuserdata/kevinschildhorn.xcuserdatad/UserInterfaceState.xcuserstate -ios/FotoPresenter.xcworkspace/xcuserdata/kevinschildhorn.xcuserdatad/UserInterfaceState.xcuserstate common/src/commonMain/kotlin/me/kevinschildhorn/common/network/ftps/TestingLoginInfo.kt androidApp/build/kotlin/compileDebugKotlinAndroid/cacheable/caches-jvm/lookups/lookups.tab_i.len diff --git a/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt b/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt index 9a0a6233..4e9ea81a 100644 --- a/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt +++ b/androidApp/src/androidMain/kotlin/com/kevinschildhorn/MainActivity.kt @@ -6,8 +6,10 @@ import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import coil3.ImageLoader import coil3.compose.setSingletonImageLoaderFactory -import com.kevinschildhorn.fotopresenter.SMBJFetcher +import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource +import com.kevinschildhorn.fotopresenter.ui.SMBJFetcher import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler +import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository import com.kevinschildhorn.fotopresenter.startKoin import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryViewModel import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel @@ -23,7 +25,7 @@ class MainActivity : AppCompatActivity(), KoinComponent { private val directoryViewModel by viewModel() private val slideshowViewModel by viewModel() private val playlistViewModel by viewModel() - private val networkHandler: NetworkHandler by inject() + private val imageRepository: ImageRepository by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -33,7 +35,7 @@ class MainActivity : AppCompatActivity(), KoinComponent { setSingletonImageLoaderFactory { context -> ImageLoader.Builder(context) .components { - add(SMBJFetcher.Factory(networkHandler)) + add(SMBJFetcher.Factory(imageRepository)) } .build() } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 10ec4f1a..b6280bc4 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -10,7 +10,7 @@ plugins { alias(libs.plugins.jetbrains.compose) alias(libs.plugins.ktlint) alias(libs.plugins.sqldelight) - //alias(libs.plugins.crashlytics) + // alias(libs.plugins.crashlytics) alias(libs.plugins.serialization) } @@ -54,7 +54,6 @@ kotlin { implementation(libs.kotlinx.datetime) implementation(libs.kim) implementation(libs.coil) - } } val commonTest by getting { diff --git a/shared/src/androidMain/kotlin/Main.android.kt b/shared/src/androidMain/kotlin/Main.android.kt index fd25cda6..193c4f9a 100644 --- a/shared/src/androidMain/kotlin/Main.android.kt +++ b/shared/src/androidMain/kotlin/Main.android.kt @@ -12,4 +12,4 @@ fun MainView( directoryViewModel: DirectoryViewModel, slideshowViewModel: SlideshowViewModel, playlistViewModel: PlaylistViewModel, - ) = App(loginViewModel, directoryViewModel, slideshowViewModel, playlistViewModel) +) = App(loginViewModel, directoryViewModel, slideshowViewModel, playlistViewModel) diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt index b4b5b554..f79c4035 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/KoinAndroid.kt @@ -26,26 +26,28 @@ fun startKoin(context: Context) { } } -internal actual val platformModule: Module = module { - single { - SharedPreferencesSettings( - delegate = EncryptedSharedPreferences.create( - get(), - CredentialsDataSource.DATABASE_NAME, - MasterKey.Builder(get()) - .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) - .build(), - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, - ), - commit = false, - ) +internal actual val platformModule: Module = + module { + single { + SharedPreferencesSettings( + delegate = + EncryptedSharedPreferences.create( + get(), + CredentialsDataSource.DATABASE_NAME, + MasterKey.Builder(get()) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build(), + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, + ), + commit = false, + ) + } + single { + SMBJHandler + } + single { DriverFactory(context = get()).createDriver() } } - single { - SMBJHandler - } - single { DriverFactory(context = get()).createDriver() } -} @OptIn(KoinInternalApi::class) fun KoinApplication.androidContext(androidContext: Context): KoinApplication { diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/SMBJFetcher.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/SMBJFetcher.kt deleted file mode 100644 index 30e7299c..00000000 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/SMBJFetcher.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.kevinschildhorn.fotopresenter - -import android.content.Context -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import coil3.ImageLoader -import coil3.asImage -import coil3.decode.DataSource -import coil3.fetch.FetchResult -import coil3.fetch.Fetcher -import coil3.fetch.ImageFetchResult -import coil3.request.Options -import com.hierynomus.smbj.share.File -import com.kevinschildhorn.fotopresenter.data.datasources.ImageCacheDataSource -import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler -import com.kevinschildhorn.fotopresenter.ui.shared.DriverFactory -import com.kevinschildhorn.fotopresenter.ui.shared.SharedCache -import com.kevinschildhorn.fotopresenter.ui.shared.getScaledDimensions -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlin.math.roundToInt - -class SMBJFetcher( - private val directoryDetails: NetworkDirectoryDetails, - private val networkHandler: NetworkHandler, - context: Context, -) : Fetcher { - - val imageCacheDataSource = ImageCacheDataSource( - cache = SharedCache, - driver = DriverFactory(context).createDriver(), - logger = baseLogger.withTag("ImageCacheDataSource") - ) - - override suspend fun fetch(): FetchResult? { - return withContext(Dispatchers.IO) { - imageCacheDataSource.getImage(directoryDetails) - if (networkHandler.isConnected) { - val image = networkHandler.openImage(path = directoryDetails.fullPath) - val file = image?.file - if (file != null) { - val bitmap = getBitmapFromFile(file, 64) - if (bitmap != null) { - file.close() - ImageFetchResult( - image = bitmap.asImage(), - isSampled = true, - dataSource = DataSource.NETWORK, - ) - } else { - throw Exception("Failed to fetch image from FTP") - } - } else { - throw Exception("Failed to fetch image from FTP") - } - } else { - throw Exception("Failed to fetch image from FTP") - } - } - } - - private fun getBitmapFromFile(file: File, size: Int): Bitmap? { - - val options = BitmapFactory.Options() - options.inJustDecodeBounds = true - BitmapFactory.decodeStream(file.inputStream, null, options) - - val height: Int = options.outHeight - val width: Int = options.outWidth - val dimensions = getScaledDimensions(width, height, size) - val heightRatio: Int = (height.toFloat() / dimensions.second.toFloat()).roundToInt() - val widthRatio: Int = (width.toFloat() / dimensions.first.toFloat()).roundToInt() - options.inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio - - options.inJustDecodeBounds = false - return BitmapFactory.decodeStream(file.inputStream, null, options) - } - - class Factory(private val networkHandler: NetworkHandler) : Fetcher.Factory { - override fun create( - data: NetworkDirectoryDetails, - options: Options, - imageLoader: ImageLoader - ): Fetcher? = SMBJFetcher(data, networkHandler, options.context) - } -} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryAndroid.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryAndroid.kt index e59af8d6..54367661 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryAndroid.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryAndroid.kt @@ -1,3 +1,5 @@ +@file:Suppress("ktlint:standard:filename") + package com.kevinschildhorn.fotopresenter import com.kevinschildhorn.fotopresenter.domain.RetrieveDirectoryContentsUseCase @@ -14,7 +16,6 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject actual object UseCaseFactory : KoinComponent { - actual val connectToServerUseCase: ConnectToServerUseCase get() { val useCase: ConnectToServerUseCase by inject() @@ -65,4 +66,4 @@ actual object UseCaseFactory : KoinComponent { val useCase: SaveMetadataForPathUseCase by inject() return useCase } -} \ No newline at end of file +} diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/CommonPreviews.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/CommonPreviews.kt index 9f7c3569..f639a4fd 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/CommonPreviews.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/CommonPreviews.kt @@ -1,3 +1,5 @@ +@file:Suppress("ktlint:standard:class-naming") + package com.kevinschildhorn.fotopresenter.ui.compose import androidx.compose.foundation.layout.Column @@ -28,10 +30,11 @@ fun ActionSheetPreview() { ActionSheet( visible = true, offset = 200, - values = listOf( - ActionSheetContext(ActionSheetAction.START_SLIDESHOW, 1), - ActionSheetContext(ActionSheetAction.NONE, 2), - ), + values = + listOf( + ActionSheetContext(ActionSheetAction.START_SLIDESHOW, 1), + ActionSheetContext(ActionSheetAction.NONE, 2), + ), onClick = {}, onDismiss = {}, ) @@ -44,10 +47,8 @@ fun ConfirmationDialogPreview() { "Hello", "World", { - }, { - }, ) } @@ -59,10 +60,8 @@ fun TextConfirmationDialogPreview() { title = "", initialValue = "", { - }, { - }, ) } @@ -73,11 +72,8 @@ fun FilterDialogPreview() { FilterDialog( "Hello", { - }, { - }, ) } - diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/DirectoryPreviews.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/DirectoryPreviews.kt index d6fddee5..19c0cc04 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/DirectoryPreviews.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/DirectoryPreviews.kt @@ -3,14 +3,12 @@ package com.kevinschildhorn.fotopresenter.ui.compose import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview -import com.kevinschildhorn.fotopresenter.data.network.DefaultNetworkDirectoryDetails import com.kevinschildhorn.fotopresenter.data.network.MockNetworkDirectoryDetails -import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryGridCellState -import com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.grid.DirectoryGridCell +import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryGridState import com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.grid.DirectoryGrid +import com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.grid.DirectoryGridCell import com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.grid.FolderDirectoryGridCell -import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryGridState import com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.navbar.DirectoryNavigationBar import com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.navbar.DirectoryNavigationItem @@ -18,7 +16,7 @@ import com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.navbar @Composable fun BaseDirectoryPreview() { Column { - DirectoryGridCell() {} + DirectoryGridCell {} } } @@ -26,7 +24,7 @@ fun BaseDirectoryPreview() { @Composable fun FolderDirectoryEmptyPreview() { Column { - FolderDirectoryGridCell(DirectoryGridCellState.Folder("Hello",0)) + FolderDirectoryGridCell(DirectoryGridCellState.Folder("Hello", 0)) } } @@ -34,14 +32,17 @@ fun FolderDirectoryEmptyPreview() { @Composable fun DirectoryGridPreview() { DirectoryGrid( - directoryContent = DirectoryGridState( - folderStates = listOf( - DirectoryGridCellState.Folder("Hello",0), + directoryContent = + DirectoryGridState( + folderStates = + listOf( + DirectoryGridCellState.Folder("Hello", 0), + ), + imageStates = + mutableListOf( + DirectoryGridCellState.Image(MockNetworkDirectoryDetails(), "Hello", 1), + ), ), - imageStates = mutableListOf( - DirectoryGridCellState.Image(MockNetworkDirectoryDetails(),"Hello", 1) - ) - ), onFolderPressed = {}, onImageDirectoryPressed = {}, onActionSheet = {}, @@ -50,18 +51,17 @@ fun DirectoryGridPreview() { @Preview @Composable -fun DirectoryNavigationItemPreview(){ - DirectoryNavigationItem("Photos"){ - +fun DirectoryNavigationItemPreview() { + DirectoryNavigationItem("Photos") { } } @Preview @Composable -fun DirectoryNavigationBarPreview(){ +fun DirectoryNavigationBarPreview() { DirectoryNavigationBar( - listOf("Photos1", "Subfolder1","Photos2", "Subfolder2","Photos3", "Subfolder3"), + listOf("Photos1", "Subfolder1", "Photos2", "Subfolder2", "Photos3", "Subfolder3"), + {}, {}, - {} ) } diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/LoginPreviews.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/LoginPreviews.kt index 18aad095..1bb20524 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/LoginPreviews.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/LoginPreviews.kt @@ -3,13 +3,13 @@ package com.kevinschildhorn.fotopresenter.ui.compose import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview +import com.kevinschildhorn.fotopresenter.ui.UiState import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ErrorView import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.TitleView +import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginScreenState import com.kevinschildhorn.fotopresenter.ui.screens.login.composables.LoginPasswordTextField -import com.kevinschildhorn.fotopresenter.ui.screens.login.composables.LoginTextField import com.kevinschildhorn.fotopresenter.ui.screens.login.composables.LoginScreenForm -import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginScreenState -import com.kevinschildhorn.fotopresenter.ui.UiState +import com.kevinschildhorn.fotopresenter.ui.screens.login.composables.LoginTextField @Preview @Composable diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/PlaylistPreviews.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/PlaylistPreviews.kt index 6908f64f..44ccf330 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/PlaylistPreviews.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/compose/PlaylistPreviews.kt @@ -1,8 +1,6 @@ package com.kevinschildhorn.fotopresenter.ui.compose -import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Button @@ -15,19 +13,17 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview -import com.kevinschildhorn.fotopresenter.Playlist import com.kevinschildhorn.fotopresenter.data.PlaylistDetails import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.PlaylistColumn import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.PlaylistOverlay import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.PlaylistScreenCreateRow import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.PlaylistScreenPlaylistRow - @Composable @Preview fun PlaylistRowsPreview() { Column(modifier = Modifier.fillMaxSize()) { - PlaylistScreenPlaylistRow("Playlist 1", {},{}, {}, {}) + PlaylistScreenPlaylistRow("Playlist 1", {}, {}, {}, {}) PlaylistScreenPlaylistRow("Playlist 2", {}, {}, {}, {}) PlaylistScreenCreateRow(onClick = {}) } @@ -37,12 +33,12 @@ fun PlaylistRowsPreview() { @Preview fun PlaylistColumnPreview() { PlaylistColumn( - listOf(PlaylistDetails(1,"Playlist 1"), PlaylistDetails(2, "Playlist 2")), + listOf(PlaylistDetails(1, "Playlist 1"), PlaylistDetails(2, "Playlist 2")), + {}, {}, {}, {}, {}, - {} ) } @@ -59,7 +55,7 @@ fun PlaylistOverlayPreview() { Text("Toggle") } PlaylistOverlay( - listOf(PlaylistDetails(1,"Playlist 1"), PlaylistDetails(2, "Playlist 2")), + listOf(PlaylistDetails(1, "Playlist 1"), PlaylistDetails(2, "Playlist 2")), overlaid = false, {}, {}, @@ -68,6 +64,5 @@ fun PlaylistOverlayPreview() { {}, {}, ) - } } diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt index 4392a3c1..aef1010e 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt @@ -9,4 +9,4 @@ actual class DriverFactory(private val context: Context) { actual fun createDriver(): SqlDriver { return AndroidSqliteDriver(PlaylistDatabase.Schema, context, "playlist.db") } -} \ No newline at end of file +} diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt index 0452f5b1..17add46a 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageAndroid.kt @@ -1,24 +1,31 @@ +@file:Suppress("ktlint:standard:filename") + package com.kevinschildhorn.fotopresenter.ui.shared import android.graphics.Bitmap import android.graphics.BitmapFactory -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.asImageBitmap -import com.hierynomus.smbj.share.File +import coil3.Image +import coil3.asImage -actual fun getBitmapFromFile(file: File, size: Int): ImageBitmap? { +actual open class SharedImage actual constructor(actual val byteArray: ByteArray) { + actual fun getCoilImage(size: Int): Image? = getAndroidBitmap(byteArray, size)?.asImage() - val options = BitmapFactory.Options() - options.inJustDecodeBounds = true - BitmapFactory.decodeStream(file.inputStream, null, options) + private fun getAndroidBitmap( + byteArray: ByteArray, + size: Int, + ): Bitmap? { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options) - val height: Int = options.outHeight - val width: Int = options.outWidth - val dimensions = getScaledDimensions(width, height, size) - val heightRatio: Int = Math.round(height.toFloat() / dimensions.second.toFloat()) - val widthRatio: Int = Math.round(width.toFloat() / dimensions.first.toFloat()) - options.inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio + val height: Int = options.outHeight + val width: Int = options.outWidth + val dimensions = getScaledDimensions(width, height, size) + val heightRatio: Int = Math.round(height.toFloat() / dimensions.second.toFloat()) + val widthRatio: Int = Math.round(width.toFloat() / dimensions.first.toFloat()) + options.inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio - options.inJustDecodeBounds = false - return BitmapFactory.decodeStream(file.inputStream, null, options)?.asImageBitmap() -} \ No newline at end of file + options.inJustDecodeBounds = false + return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options) + } +} diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt deleted file mode 100644 index 11070236..00000000 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.kevinschildhorn.fotopresenter.ui.shared - -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.asAndroidBitmap -import androidx.compose.ui.graphics.asImageBitmap -import java.io.ByteArrayOutputStream - - -actual object SharedImageConverter { - actual fun convertBytes(byteArray: ByteArray): ImageBitmap { - val bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size) - return bmp.asImageBitmap() - } - - actual fun convertImage(imageBitmap: ImageBitmap): ByteArray { - val bitmap = imageBitmap.asAndroidBitmap() - val stream = ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) - val byteArray = stream.toByteArray() - return byteArray - } - - private fun Bitmap.convertToByteArray(): ByteArray { - val stream = ByteArrayOutputStream() - this.compress(Bitmap.CompressFormat.PNG, 100, stream) - val byteArray = stream.toByteArray() - if (!this.isRecycled) { - this.recycle(); - } - return byteArray - } -} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt index 810ca1b3..45a70229 100644 --- a/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt +++ b/shared/src/androidMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt @@ -6,7 +6,8 @@ import androidx.lifecycle.viewModelScope as androidXViewModelScope actual abstract class ViewModel actual constructor() : ViewModel() { actual val viewModelScope: CoroutineScope = androidXViewModelScope + actual override fun onCleared() { super.onCleared() } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/App.kt b/shared/src/commonMain/kotlin/App.kt index aed9a377..c6114c6d 100644 --- a/shared/src/commonMain/kotlin/App.kt +++ b/shared/src/commonMain/kotlin/App.kt @@ -28,7 +28,7 @@ fun App( directoryViewModel: DirectoryViewModel, slideshowViewModel: SlideshowViewModel, playlistViewModel: PlaylistViewModel, - navController: NavHostController = rememberNavController() + navController: NavHostController = rememberNavController(), ) { MaterialTheme( colors = fotoColors, @@ -39,7 +39,7 @@ fun App( NavHost( navController = navController, startDestination = Screen.LOGIN.name, - modifier = Modifier.fillMaxSize().padding(innerPadding) + modifier = Modifier.fillMaxSize().padding(innerPadding), ) { composable(route = Screen.LOGIN.name) { LoginScreen(loginViewModel) { @@ -60,7 +60,7 @@ fun App( }, onShowPlaylists = { navController.navigate(Screen.PLAYLIST.name) - } + }, ) } composable(route = Screen.SLIDESHOW.name) { diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt index 27543619..bbc09e74 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/Koin.kt @@ -1,14 +1,13 @@ package com.kevinschildhorn.fotopresenter import co.touchlab.kermit.Logger -import co.touchlab.kermit.LoggerConfig import com.kevinschildhorn.fotopresenter.data.datasources.CredentialsDataSource import com.kevinschildhorn.fotopresenter.data.datasources.DirectoryDataSource -import com.kevinschildhorn.fotopresenter.data.datasources.ImageCacheDataSource import com.kevinschildhorn.fotopresenter.data.datasources.ImageMetadataDataSource -import com.kevinschildhorn.fotopresenter.data.datasources.ImageRemoteDataSource import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistFileDataSource import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistSQLDataSource +import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource +import com.kevinschildhorn.fotopresenter.data.datasources.image.NetworkImageDataSource import com.kevinschildhorn.fotopresenter.data.repositories.CredentialsRepository import com.kevinschildhorn.fotopresenter.data.repositories.DirectoryRepository import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository @@ -28,30 +27,28 @@ import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel import com.kevinschildhorn.fotopresenter.ui.screens.playlist.PlaylistViewModel import com.kevinschildhorn.fotopresenter.ui.screens.slideshow.SlideshowViewModel import com.kevinschildhorn.fotopresenter.ui.shared.CacheInterface -import com.kevinschildhorn.fotopresenter.ui.shared.SharedCache +import com.kevinschildhorn.fotopresenter.ui.shared.SharedInMemoryCache import org.koin.core.module.Module import org.koin.dsl.module - val baseLogger = Logger.withTag("") - val commonModule = module { // Data - single { SharedCache } + single { SharedInMemoryCache } + single { NetworkImageDataSource(get()) } single { CredentialsDataSource(get()) } single { CredentialsRepository(get()) } single { DirectoryDataSource(get(), baseLogger.withTag("DirectoryDataSource")) } single { DirectoryRepository(get(), get()) } - single { ImageRemoteDataSource(get()) } - single { ImageRepository(get()) } - single { ImageCacheDataSource(get(), get(), baseLogger.withTag("ImageCacheDataSource")) } + single { CachedImageDataSource(get(), get(), baseLogger.withTag("ImageCacheDataSource")) } single { PlaylistFileDataSource(baseLogger.withTag("PlaylistDataSource"), get()) } single { PlaylistSQLDataSource(get(), baseLogger.withTag("PlaylistDataSource")) } single { PlaylistRepository(get(), get()) } factory { ImageMetadataDataSource(baseLogger.withTag("ImageMetadataDataSource"), get()) } + single { ImageRepository(get(), get(), baseLogger.withTag("ImageRepository")) } // Domain factory { ConnectToServerUseCase(get(), baseLogger.withTag("ConnectToServerUseCase")) } @@ -62,19 +59,18 @@ val commonModule = DisconnectFromServerUseCase( get(), get(), - baseLogger.withTag("DisconnectFromServerUseCase") + baseLogger.withTag("DisconnectFromServerUseCase"), ) } factory { RetrieveImageDirectoriesUseCase(baseLogger.withTag("RetrieveImageDirectoriesUseCase")) } factory { RetrieveSlideshowFromPlaylistUseCase( baseLogger.withTag("RetrieveSlideshowFromPlaylistUseCase"), - get() + get(), ) } factory { RetrieveDirectoryContentsUseCase( - get(), get(), baseLogger.withTag("RetrieveDirectoryContentsUseCase"), ) diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactory.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactory.kt index d43e875b..82ac56fe 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactory.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactory.kt @@ -22,4 +22,4 @@ expect object UseCaseFactory { val retrieveDirectoryContentsUseCase: RetrieveDirectoryContentsUseCase val retrieveImageUseCase: RetrieveImageUseCase val saveMetadataForPathUseCase: SaveMetadataForPathUseCase -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/Directory.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/Directory.kt index f663b314..967a71e2 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/Directory.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/Directory.kt @@ -20,7 +20,8 @@ data class FolderDirectory( override fun toString(): String = "(F:${details.fullPath}:${details.id})" val isValid: Boolean - get() = name != ".." && + get() = + name != ".." && name.isNotEmpty() && name.isNotBlank() } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/ImageUtils.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/ImageUtils.kt index 08b825a8..a2d2e46d 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/ImageUtils.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/ImageUtils.kt @@ -1,8 +1,9 @@ package com.kevinschildhorn.fotopresenter.data -val supportedImageTypes = listOf( - "png", - "jpg", - "jpeg", - "bmp" -) \ No newline at end of file +val supportedImageTypes = + listOf( + "png", + "jpg", + "jpeg", + "bmp", + ) diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/LoginCredentials.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/LoginCredentials.kt index 3a450a43..efcd9960 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/LoginCredentials.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/LoginCredentials.kt @@ -19,13 +19,13 @@ data class LoginCredentials( override fun toString(): String { return """ - LoginCredentials( - hostname: $hostname - username: $username - password: $password - sharedFolder: $sharedFolder - shouldAutoConnect: $shouldAutoConnect - ) - """.trimIndent() + LoginCredentials( + hostname: $hostname + username: $username + password: $password + sharedFolder: $sharedFolder + shouldAutoConnect: $shouldAutoConnect + ) + """.trimIndent() } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/MetadataDetails.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/MetadataDetails.kt index e3faf36a..d33d6e8f 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/MetadataDetails.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/MetadataDetails.kt @@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable @Serializable data class MetadataDetails( - val files: MutableList + val files: MutableList, ) @Serializable @@ -14,4 +14,4 @@ data class MetadataFileDetails( ) { val tagsString: String get() = tags.joinToString(", ") -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt index 98972ffe..af0d37ed 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/PlaylistDetails.kt @@ -30,6 +30,6 @@ data class PlaylistItem( id = item.id, playlistId = item.playlist_id, directoryPath = item.directory_path, - directoryId = item.directory_id + directoryId = item.directory_id, ) -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/State.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/State.kt index 5f43142b..0a3a7b6d 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/State.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/State.kt @@ -51,10 +51,9 @@ sealed class State { when (this) { is IDLE -> "Idle" is LOADING -> "Loading" - is ERROR -> "Error: ${message}" - is SUCCESS -> "Success:${data}" + is ERROR -> "Error: $message" + is SUCCESS -> "Success:$data" } } """.trimIndent() - } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/DirectoryDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/DirectoryDataSource.kt index 18bd4800..3a356cca 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/DirectoryDataSource.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/DirectoryDataSource.kt @@ -21,8 +21,8 @@ class DirectoryDataSource( if (!networkHandler.isConnected) throw NetworkHandlerException(NetworkHandlerError.NOT_CONNECTED) logger?.i { "Does the directory exist?" } - //val exists = networkHandler.folderExists(directoryName) - //logger?.i { "Does the directory exist? $exists" } + // val exists = networkHandler.folderExists(directoryName) + // logger?.i { "Does the directory exist? $exists" } logger?.i { "Opening the directory..." } networkHandler.openDirectory(directoryName)?.let { return it } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSource.kt index 739deb85..5813bb32 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSource.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSource.kt @@ -14,7 +14,6 @@ class ImageMetadataDataSource( private val logger: Logger?, private val networkHandler: NetworkHandler, ) { - suspend fun importMetaData(): MetadataDetails { logger?.i { "Importing Metadata" } networkHandler.getMetadata()?.let { @@ -56,9 +55,9 @@ class ImageMetadataDataSource( return MetadataFileDetails( filePath = filePath, - tags = keywords.toSet() + tags = keywords.toSet(), ) } return null } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageRemoteDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageRemoteDataSource.kt deleted file mode 100644 index e132203d..00000000 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageRemoteDataSource.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.kevinschildhorn.fotopresenter.data.datasources - -import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerError -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerException -import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage -import kotlin.coroutines.cancellation.CancellationException - -/** -Fetches Photos from a NAS - **/ -class ImageRemoteDataSource(private val networkHandler: NetworkHandler) { - @Throws(NetworkHandlerException::class, CancellationException::class) - suspend fun getImage(directory: NetworkDirectoryDetails): SharedImage? { - if (!networkHandler.isConnected) throw NetworkHandlerException(NetworkHandlerError.NOT_CONNECTED) - return networkHandler.openImage(directory.fullPath) - } -} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSource.kt index 38074427..0dc16c80 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSource.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSource.kt @@ -11,7 +11,6 @@ class PlaylistFileDataSource( private val logger: Logger?, private val networkHandler: NetworkHandler, ) : KoinComponent { - suspend fun importPlaylists(): List = networkHandler.getPlaylists().mapNotNull { try { @@ -41,4 +40,4 @@ class PlaylistFileDataSource( } return true } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSource.kt index 270739f8..82d28485 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSource.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSource.kt @@ -15,10 +15,12 @@ class PlaylistSQLDataSource( driver: SqlDriver, private val logger: Logger? = null, ) : KoinComponent { - private val database = PlaylistDatabase(driver) - fun createPlaylist(name: String, directories: List = emptyList()): Playlist? { + fun createPlaylist( + name: String, + directories: List = emptyList(), + ): Playlist? { return try { logger?.i { "Creating Playlist $name with images: ${directories.count()}" } database.playlistQueries.insertPlaylist(name) @@ -74,7 +76,10 @@ class PlaylistSQLDataSource( } } - fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItem? { + fun insertPlaylistImage( + playlistId: Long, + directory: Directory, + ): PlaylistItem? { logger?.i { "Inserting Playlist Image ${directory.name}" } database.playlistItemsQueries.insertPlaylistImage( playlist_id = playlistId, @@ -84,7 +89,10 @@ class PlaylistSQLDataSource( return getPlaylistImage(playlistId, directory.details.fullPath) } - fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistItem? { + fun getPlaylistImage( + playlistId: Long, + directoryPath: String, + ): PlaylistItem? { return try { logger?.i { "Selecting Playlist Image $playlistId" } val image: PlaylistItems = @@ -107,4 +115,4 @@ class PlaylistSQLDataSource( false } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageCacheDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/CachedImageDataSource.kt similarity index 59% rename from shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageCacheDataSource.kt rename to shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/CachedImageDataSource.kt index ad99df89..ad51c562 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageCacheDataSource.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/CachedImageDataSource.kt @@ -1,46 +1,42 @@ -package com.kevinschildhorn.fotopresenter.data.datasources +package com.kevinschildhorn.fotopresenter.data.datasources.image -import androidx.compose.ui.graphics.ImageBitmap import app.cash.sqldelight.db.SqlDriver import co.touchlab.kermit.Logger import com.kevinschildhorn.fotopresenter.PlaylistDatabase import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails import com.kevinschildhorn.fotopresenter.ui.shared.CacheInterface -import com.kevinschildhorn.fotopresenter.ui.shared.SharedImageConverter +import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage +import com.kevinschildhorn.fotopresenter.Image as SQLImage -class ImageCacheDataSource( +class CachedImageDataSource( private val cache: CacheInterface, driver: SqlDriver, - private val logger: Logger + private val logger: Logger, ) { private val database = PlaylistDatabase(driver) - - fun getImage(directory: NetworkDirectoryDetails): ImageBitmap? { + fun getImage(directory: NetworkDirectoryDetails): SharedImage? { logger.i { "Getting Image from Cache ${directory.cacheId}" } return try { - val image = database.imageQueries.selectImageByName(directory.cacheId).executeAsOne() - SharedImageConverter.convertBytes(image.image) + val image: SQLImage = database.imageQueries.selectImageByName(directory.cacheId).executeAsOne() + SharedImage(image.image) } catch (e: Exception) { logger.e(e) { "Image NOT found" } - null } - //return cache.getImage(directory.cacheId) } fun saveImage( directory: NetworkDirectoryDetails, - bitmap: ImageBitmap, + image: SharedImage, ) { logger.i { "Saving Image To Cache ${directory.cacheId}" } database.imageQueries.insertImage( directory.cacheId, - SharedImageConverter.convertImage(bitmap) + image.byteArray, ) logger.i { "Image Saved" } - - //DiscCache.storeFile(directory.cacheId, bitmap) + // cache.cacheImage(directory.cacheId, image) TODO } private val NetworkDirectoryDetails.cacheId: String diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/NetworkImageDataSource.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/NetworkImageDataSource.kt new file mode 100644 index 00000000..4fa81afa --- /dev/null +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/image/NetworkImageDataSource.kt @@ -0,0 +1,14 @@ +package com.kevinschildhorn.fotopresenter.data.datasources.image + +import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails +import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler +import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage + +class NetworkImageDataSource(private val networkHandler: NetworkHandler) { + suspend fun getImage(directory: NetworkDirectoryDetails): SharedImage? = + if (networkHandler.isConnected) { + networkHandler.openImage(path = directory.fullPath) + } else { + null + } +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt index 5a9fd98f..53410a92 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkDirectoryDetails.kt @@ -5,7 +5,7 @@ import kotlinx.datetime.Clock interface NetworkDirectoryDetails { val fullPath: String - val dateMillis:Long + val dateMillis: Long val id: Int val fileName: String @@ -26,11 +26,11 @@ interface NetworkDirectoryDetails { class DefaultNetworkDirectoryDetails( override val fullPath: String, override val id: Int, - override val dateMillis: Long = Clock.System.now().toEpochMilliseconds() + override val dateMillis: Long = Clock.System.now().toEpochMilliseconds(), ) : NetworkDirectoryDetails class MockNetworkDirectoryDetails( override val fullPath: String = "", override val id: Int = 0, - override val dateMillis: Long = Clock.System.now().toEpochMilliseconds() + override val dateMillis: Long = Clock.System.now().toEpochMilliseconds(), ) : NetworkDirectoryDetails diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkHandler.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkHandler.kt index 84d31ba4..46c87dec 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkHandler.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/NetworkHandler.kt @@ -20,10 +20,15 @@ interface NetworkHandler { suspend fun folderExists(path: String): Boolean? - suspend fun savePlaylist(playlistName:String, json:String): Boolean + suspend fun savePlaylist( + playlistName: String, + json: String, + ): Boolean + suspend fun getPlaylists(): List - suspend fun setMetadata(json:String): Boolean + suspend fun setMetadata(json: String): Boolean + suspend fun getMetadata(): String? suspend fun deletePlaylist(playlistName: String) diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/DirectoryRepository.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/DirectoryRepository.kt index e4aea211..a7799d2c 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/DirectoryRepository.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/DirectoryRepository.kt @@ -3,7 +3,6 @@ package com.kevinschildhorn.fotopresenter.data.repositories import com.kevinschildhorn.fotopresenter.data.DirectoryContents import com.kevinschildhorn.fotopresenter.data.FolderDirectory import com.kevinschildhorn.fotopresenter.data.ImageDirectory -import com.kevinschildhorn.fotopresenter.data.MetadataFileDetails import com.kevinschildhorn.fotopresenter.data.datasources.DirectoryDataSource import com.kevinschildhorn.fotopresenter.data.datasources.ImageMetadataDataSource import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails @@ -18,17 +17,18 @@ class DirectoryRepository( val imageDirectories: List = directoryDataSource.getImageDirectories(path) - //val metaData = metadataDataSource.importMetaData() - + // val metaData = metadataDataSource.importMetaData() return DirectoryContents( folders = folderDirectories.map { FolderDirectory(it) }, - images = imageDirectories.map { networkDetails -> - ImageDirectory( - networkDetails, - metaData = null,// metaData.files.find { networkDetails.fullPath == it.filePath } - ) - }, + images = + imageDirectories.map { networkDetails -> + ImageDirectory( + networkDetails, + // metaData.files.find { networkDetails.fullPath == it.filePath } + metaData = null, + ) + }, ) } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt index ddbc81b9..d8bb4141 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepository.kt @@ -1,11 +1,30 @@ package com.kevinschildhorn.fotopresenter.data.repositories -import com.kevinschildhorn.fotopresenter.data.datasources.ImageRemoteDataSource +import co.touchlab.kermit.Logger +import coil3.Image +import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource +import com.kevinschildhorn.fotopresenter.data.datasources.image.NetworkImageDataSource import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails -import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage class ImageRepository( - private val remoteDataSource: ImageRemoteDataSource, + private val remoteImageDataSource: NetworkImageDataSource, + private val localImageDataSource: CachedImageDataSource, + private val logger: Logger?, ) { - suspend fun getImage(directory: NetworkDirectoryDetails): SharedImage? = remoteDataSource.getImage(directory) + suspend fun getCoilImage( + directoryDetails: NetworkDirectoryDetails, + size: Int, + ): Image? { + logger?.i { "Getting Image from Cache" } + val cachedImage = localImageDataSource.getImage(directoryDetails) + if (cachedImage != null) return cachedImage.getCoilImage(size) + + logger?.i { "No cached image found, getting image from directory" } + val image = remoteImageDataSource.getImage(directoryDetails) + if (image != null) { + logger?.i { "Storing image in cache" } + localImageDataSource.saveImage(directoryDetails, image) + } + return image?.getCoilImage(size) + } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt index 60dcb40d..5e65b86e 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/PlaylistRepository.kt @@ -12,8 +12,10 @@ class PlaylistRepository( private val playlistSQLDataSource: PlaylistSQLDataSource, private val playlistFileDataSource: PlaylistFileDataSource, ) { - - suspend fun createPlaylist(name: String, directories: List = emptyList()): Playlist? { + suspend fun createPlaylist( + name: String, + directories: List = emptyList(), + ): Playlist? { val playlist = playlistSQLDataSource.createPlaylist(name, directories) playlistSQLDataSource.getPlaylistByName(name)?.let { playlistFileDataSource.exportPlaylist(it) @@ -26,7 +28,10 @@ class PlaylistRepository( return playlistSQLDataSource.getAllPlaylists() } - suspend fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItem? { + suspend fun insertPlaylistImage( + playlistId: Long, + directory: Directory, + ): PlaylistItem? { val item = playlistSQLDataSource.insertPlaylistImage(playlistId, directory) playlistSQLDataSource.getPlaylistById(playlistId)?.let { playlistFileDataSource.exportPlaylist(it) @@ -40,5 +45,4 @@ class PlaylistRepository( } return playlistSQLDataSource.deletePlaylist(id) } - -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/RetrieveDirectoryContentsUseCase.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/RetrieveDirectoryContentsUseCase.kt index 8f88a152..869f0697 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/RetrieveDirectoryContentsUseCase.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/RetrieveDirectoryContentsUseCase.kt @@ -2,18 +2,13 @@ package com.kevinschildhorn.fotopresenter.domain import co.touchlab.kermit.Logger import com.kevinschildhorn.fotopresenter.data.DirectoryContents -import com.kevinschildhorn.fotopresenter.data.ImageDirectory -import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails import com.kevinschildhorn.fotopresenter.data.repositories.DirectoryRepository -import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository -import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage /** Retrieving Directory Contents from Path **/ class RetrieveDirectoryContentsUseCase( private val directoryRepository: DirectoryRepository, - private val imageRepository: ImageRepository, private val logger: Logger, ) { suspend operator fun invoke(path: String): DirectoryContents { diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageDirectoriesUseCase.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageDirectoriesUseCase.kt index c8de06a8..98c2872f 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageDirectoriesUseCase.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageDirectoriesUseCase.kt @@ -21,10 +21,11 @@ class RetrieveImageDirectoriesUseCase( val contents = retrieveContentsUseCase(directoryDetails.fullPath) logger.i { "Retrieved Contents: $contents" } - val folders = contents.folders.filter { - logger.i { "Filtering (${it.name})" } - it.isValid - } + val folders = + contents.folders.filter { + logger.i { "Filtering (${it.name})" } + it.isValid + } val images = contents.images.toMutableList() if (recursively) { diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt index cc2efaa8..d55057ca 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveImageUseCase.kt @@ -1,36 +1,34 @@ package com.kevinschildhorn.fotopresenter.domain.image -import androidx.compose.ui.graphics.ImageBitmap import co.touchlab.kermit.Logger import com.kevinschildhorn.fotopresenter.data.ImageDirectory -import com.kevinschildhorn.fotopresenter.data.State -import com.kevinschildhorn.fotopresenter.data.datasources.ImageCacheDataSource +import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource +import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage /** Retrieving Image Bitmap **/ class RetrieveImageUseCase( - private val imageCacheDataSource: ImageCacheDataSource, + private val cachedImageDataSource: CachedImageDataSource, private val logger: Logger, ) { - suspend operator fun invoke( directory: ImageDirectory, imageSize: Int, - ): ImageBitmap? { + ): SharedImage? { val imageName = "\"${directory.details.fullPath}\"" logger.i { "Starting to get Image $imageName" } - imageCacheDataSource.getImage(directory.details)?.let { + cachedImageDataSource.getImage(directory.details)?.let { logger.i { "$imageName found in cache, using that" } return it } logger.i { "Getting Image Bitmap from File ${directory.name}" } - val imageBitmap: ImageBitmap? = directory.image?.getImageBitmap(imageSize) + val imageBitmap: SharedImage? = directory.image imageBitmap?.let { logger.i { "Caching new Image ${directory.name}" } - imageCacheDataSource.saveImage(directory.details, it) + cachedImageDataSource.saveImage(directory.details, it) } return imageBitmap } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveSlideshowFromPlaylistUseCase.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveSlideshowFromPlaylistUseCase.kt index b91c57c4..162788e5 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveSlideshowFromPlaylistUseCase.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/RetrieveSlideshowFromPlaylistUseCase.kt @@ -7,7 +7,6 @@ import com.kevinschildhorn.fotopresenter.data.PlaylistDetails import com.kevinschildhorn.fotopresenter.data.network.DefaultNetworkDirectoryDetails import com.kevinschildhorn.fotopresenter.extension.isImagePath import org.koin.core.component.KoinComponent -import org.koin.core.component.inject /** Retrieving Slideshow Details From Playlist Details @@ -16,30 +15,30 @@ class RetrieveSlideshowFromPlaylistUseCase( private val logger: Logger, private val retrieveDirectoryUseCase: RetrieveImageDirectoriesUseCase, ) : KoinComponent { - suspend operator fun invoke( - playlistDetails: PlaylistDetails, - ): ImageSlideshowDetails { + suspend operator fun invoke(playlistDetails: PlaylistDetails): ImageSlideshowDetails { logger.i { "Starting to get details from playlist ${playlistDetails.name}" } - val directories: List = playlistDetails.items.map { item -> - val directoryDetails = DefaultNetworkDirectoryDetails( - id = item.directoryId.toInt(), - fullPath = item.directoryPath - ) - if (item.directoryPath.isImagePath) { - listOf( - ImageDirectory( - directoryDetails, - null + val directories: List = + playlistDetails.items.map { item -> + val directoryDetails = + DefaultNetworkDirectoryDetails( + id = item.directoryId.toInt(), + fullPath = item.directoryPath, ) - ) - } else { - retrieveDirectoryUseCase( - directoryDetails = directoryDetails, - recursively = true, - ) - } - }.flatten() + if (item.directoryPath.isImagePath) { + listOf( + ImageDirectory( + directoryDetails, + null, + ), + ) + } else { + retrieveDirectoryUseCase( + directoryDetails = directoryDetails, + recursively = true, + ) + } + }.flatten() return ImageSlideshowDetails(directories) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/SaveMetadataForPathUseCase.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/SaveMetadataForPathUseCase.kt index d078f2c1..f4682537 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/SaveMetadataForPathUseCase.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/domain/image/SaveMetadataForPathUseCase.kt @@ -1,6 +1,5 @@ package com.kevinschildhorn.fotopresenter.domain.image -import com.kevinschildhorn.fotopresenter.data.ImageSlideshowDetails import com.kevinschildhorn.fotopresenter.data.MetadataFileDetails import com.kevinschildhorn.fotopresenter.data.datasources.ImageMetadataDataSource import org.koin.core.component.KoinComponent @@ -8,7 +7,6 @@ import org.koin.core.component.KoinComponent class SaveMetadataForPathUseCase( private val dataSource: ImageMetadataDataSource, ) : KoinComponent { - suspend operator fun invoke( path: String, tags: String, @@ -16,14 +14,15 @@ class SaveMetadataForPathUseCase( val tagList: List = tags.split(",").map { it.trim() } val metaData = dataSource.importMetaData() - val fileMetadata = MetadataFileDetails( - filePath = path, - tags = tagList.toSet(), - ) + val fileMetadata = + MetadataFileDetails( + filePath = path, + tags = tagList.toSet(), + ) metaData.files.removeIf { it.filePath == path } if (fileMetadata.tags.isNotEmpty()) metaData.files.add(fileMetadata) return dataSource.exportMetadata(metaData) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt index 4ca6a561..66143255 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/extension/StringExtensions.kt @@ -33,4 +33,3 @@ val String.isImagePath: Boolean val extension = this.split(".").last() return supportedImageTypes.contains(extension) } - diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/ByteArrayFetcher.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/ByteArrayFetcher.kt new file mode 100644 index 00000000..3f88d766 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/ByteArrayFetcher.kt @@ -0,0 +1,41 @@ +package com.kevinschildhorn.fotopresenter.ui + +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.ImageFetchResult +import coil3.request.Options +import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler +import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class ByteArrayFetcher( + private val byteArray: ByteArray, + private val networkHandler: NetworkHandler, +) : Fetcher { + override suspend fun fetch(): FetchResult? { + return withContext(Dispatchers.IO) { + val image = SharedImage(byteArray) + val coilImage = image.getCoilImage(64) + if (coilImage != null) { + ImageFetchResult( + image = coilImage, + isSampled = true, + dataSource = DataSource.NETWORK, + ) + } else { + throw Exception("Failed to fetch image from FTP") + } + } + } + + class Factory(private val networkHandler: NetworkHandler) : Fetcher.Factory { + override fun create( + data: ByteArray, + options: Options, + imageLoader: ImageLoader, + ): Fetcher = ByteArrayFetcher(data, networkHandler) + } +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SortingType.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SortingType.kt index ea0fe2dc..86cc01c3 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SortingType.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SortingType.kt @@ -5,4 +5,4 @@ enum class SortingType { NAME_DESC, TIME_ASC, TIME_DESC, -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/UiState.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/UiState.kt index b93771fc..02bc23f4 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/UiState.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/UiState.kt @@ -1,10 +1,5 @@ package com.kevinschildhorn.fotopresenter.ui -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ErrorView -import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.LoadingOverlay - sealed class UiState { data object IDLE : UiState() diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoColors.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoColors.kt index 14f3c527..bdd66ece 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoColors.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoColors.kt @@ -1,33 +1,34 @@ package com.kevinschildhorn.fotopresenter.ui.atoms -import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Colors import androidx.compose.material.Shapes import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -val fotoColors = Colors( - primary = Color(0xFFFFA500), - primaryVariant = Color(0xFFFFA500), - secondary = Color(0xFFFFD383), - secondaryVariant = Color(0xFFFFD383), - background = Color(0xFFFFFFFF), - surface = Color(0xFFFFFFFF), - error = Color(0xFFFF0000), - onPrimary = Color(0xFF402900), - onSecondary = Color(0xFFC1872E), - onBackground = Color(0xFF25231F), - onSurface = Color(0xFFC2882E), - onError = Color(0xFF9E1F1F), - isLight = true -) +val fotoColors = + Colors( + primary = Color(0xFFFFA500), + primaryVariant = Color(0xFFFFA500), + secondary = Color(0xFFFFD383), + secondaryVariant = Color(0xFFFFD383), + background = Color(0xFFFFFFFF), + surface = Color(0xFFFFFFFF), + error = Color(0xFFFF0000), + onPrimary = Color(0xFF402900), + onSecondary = Color(0xFFC1872E), + onBackground = Color(0xFF25231F), + onSurface = Color(0xFFC2882E), + onError = Color(0xFF9E1F1F), + isLight = true, + ) val disabled = Color(0xE0E0E0) val shadow = Color(red = 0f, green = 0f, blue = 0f, alpha = 0.25f) -val fotoShapes = Shapes( - small = RoundedCornerShape(5.dp), - medium = RoundedCornerShape(10.dp), - large = RoundedCornerShape(20.dp), -) \ No newline at end of file +val fotoShapes = + Shapes( + small = RoundedCornerShape(5.dp), + medium = RoundedCornerShape(10.dp), + large = RoundedCornerShape(20.dp), + ) diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoTypography.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoTypography.kt index 05aeca68..4ee641b8 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoTypography.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/atoms/FotoTypography.kt @@ -1,3 +1,5 @@ +@file:Suppress("ktlint:standard:class-naming") + package com.kevinschildhorn.fotopresenter.ui.atoms import androidx.compose.material.Typography @@ -17,27 +19,29 @@ import org.jetbrains.compose.resources.Font @OptIn(ExperimentalResourceApi::class) @Composable -fun QuicksandFontFamily() = FontFamily( - Font(Res.font.quicksand_light, weight = FontWeight.Light), - Font(Res.font.quicksand_regular, weight = FontWeight.Normal), - Font(Res.font.quicksand_medium, weight = FontWeight.Medium), - Font(Res.font.quicksand_semibold, weight = FontWeight.SemiBold), - Font(Res.font.quicksand_bold, weight = FontWeight.Bold) -) +fun QuicksandFontFamily() = + FontFamily( + Font(Res.font.quicksand_light, weight = FontWeight.Light), + Font(Res.font.quicksand_regular, weight = FontWeight.Normal), + Font(Res.font.quicksand_medium, weight = FontWeight.Medium), + Font(Res.font.quicksand_semibold, weight = FontWeight.SemiBold), + Font(Res.font.quicksand_bold, weight = FontWeight.Bold), + ) @Composable -fun FotoTypography() = Typography().run { - val fontFamily = QuicksandFontFamily() - this.copy( - h1 = TextStyle(fontSize = 96.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - h2 = TextStyle(fontSize = 48.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - h3 = TextStyle(fontSize = 36.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - h4 = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - subtitle1 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - subtitle2 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - button = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold, fontFamily = fontFamily), - body1 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - body2 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), - caption = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold, fontFamily = fontFamily), - ) -} \ No newline at end of file +fun FotoTypography() = + Typography().run { + val fontFamily = QuicksandFontFamily() + this.copy( + h1 = TextStyle(fontSize = 96.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + h2 = TextStyle(fontSize = 48.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + h3 = TextStyle(fontSize = 36.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + h4 = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + subtitle1 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + subtitle2 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + button = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold, fontFamily = fontFamily), + body1 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + body2 = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = fontFamily), + caption = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold, fontFamily = fontFamily), + ) + } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageScreenState.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageScreenState.kt index fbe2814d..b8a6297f 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageScreenState.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageScreenState.kt @@ -1,12 +1,12 @@ package com.kevinschildhorn.fotopresenter.ui.screens.common -import androidx.compose.ui.graphics.ImageBitmap import com.kevinschildhorn.fotopresenter.data.ImageDirectory +import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage data class ImageScreenState( val imageDirectories: List = emptyList(), val selectedImageIndex: Int? = null, - val selectedImage: ImageBitmap? = null, + val selectedImage: SharedImage? = null, ) { val selectedImageDirectory: ImageDirectory? get() = diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageViewModel.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageViewModel.kt index 37f3400d..993ff079 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageViewModel.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/ImageViewModel.kt @@ -1,22 +1,19 @@ package com.kevinschildhorn.fotopresenter.ui.screens.common -import androidx.compose.ui.graphics.ImageBitmap import co.touchlab.kermit.Logger +import com.kevinschildhorn.fotopresenter.UseCaseFactory import com.kevinschildhorn.fotopresenter.data.ImageDirectory -import com.kevinschildhorn.fotopresenter.data.State import com.kevinschildhorn.fotopresenter.extension.getNextIndex import com.kevinschildhorn.fotopresenter.extension.getPreviousIndex import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job +import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent -import com.kevinschildhorn.fotopresenter.UseCaseFactory -import kotlinx.coroutines.cancelChildren interface ImageViewModel { var scope: CoroutineScope? @@ -35,10 +32,12 @@ interface ImageViewModel { } fun clearPresentedImage() + fun cancelImageJobs() } class DefaultImageViewModel(private val logger: Logger? = null) : ImageViewModel, KoinComponent { + @Suppress("ktlint:standard:property-naming") private val _uiState = MutableStateFlow(ImageScreenState()) override var scope: CoroutineScope? = null override val imageUiState: StateFlow = _uiState.asStateFlow() diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ActionSheet.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ActionSheet.kt index d741e9eb..f7806eec 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ActionSheet.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ActionSheet.kt @@ -1,13 +1,8 @@ package com.kevinschildhorn.fotopresenter.ui.screens.common.composables -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -42,39 +37,39 @@ fun ActionSheet( visible = visible, onDismiss = onDismiss, enter = - slideInVertically( - initialOffsetY = { offset }, - ), + slideInVertically( + initialOffsetY = { offset }, + ), exit = - slideOutVertically( - targetOffsetY = { offset }, - ), + slideOutVertically( + targetOffsetY = { offset }, + ), ) { Column( modifier = - Modifier - .fillMaxSize(), + Modifier + .fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween, ) { Spacer(modifier = Modifier.fillMaxWidth()) Column( modifier = - Modifier - .clip(shape = RoundedCornerShape(15.dp)) - .fillMaxWidth() - .padding(10.dp) - .weight(1f, false) - .background(fotoColors.secondary), + Modifier + .clip(shape = RoundedCornerShape(15.dp)) + .fillMaxWidth() + .padding(10.dp) + .weight(1f, false) + .background(fotoColors.secondary), horizontalAlignment = Alignment.CenterHorizontally, ) { LazyColumn { items(values) { TextButton( modifier = - Modifier - .fillMaxWidth() - .wrapContentHeight() - .padding(start = 10.dp), + Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(start = 10.dp), onClick = { onClick(it) }, diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/DialogButtonText.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/DialogButtonText.kt index 1131ba1e..96d335c2 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/DialogButtonText.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/DialogButtonText.kt @@ -7,41 +7,53 @@ import com.kevinschildhorn.fotopresenter.ui.atoms.FotoTypography import com.kevinschildhorn.fotopresenter.ui.atoms.fotoColors @Composable -fun DialogButtonText(text: String, modifier: Modifier = Modifier) { +fun DialogButtonText( + text: String, + modifier: Modifier = Modifier, +) { Text( text, style = FotoTypography().button, color = fotoColors.onSurface, - modifier = modifier + modifier = modifier, ) } @Composable -fun DialogTitle(text: String, modifier: Modifier = Modifier) { +fun DialogTitle( + text: String, + modifier: Modifier = Modifier, +) { Text( text, style = FotoTypography().h4, color = fotoColors.onSecondary, - modifier = modifier + modifier = modifier, ) } @Composable -fun DialogMessage(text: String, modifier: Modifier = Modifier) { +fun DialogMessage( + text: String, + modifier: Modifier = Modifier, +) { Text( text, style = FotoTypography().body1, color = fotoColors.onSurface, - modifier = modifier + modifier = modifier, ) } @Composable -fun ToastMessage(text: String, modifier: Modifier = Modifier) { +fun ToastMessage( + text: String, + modifier: Modifier = Modifier, +) { Text( text, style = FotoTypography().body1, color = fotoColors.onPrimary, - modifier = modifier + modifier = modifier, ) -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ErrorView.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ErrorView.kt index 634cff5e..892cec2e 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ErrorView.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ErrorView.kt @@ -1,6 +1,5 @@ package com.kevinschildhorn.fotopresenter.ui.screens.common.composables -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FilterDialog.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FilterDialog.kt index 81bdb3db..ae057b87 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FilterDialog.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FilterDialog.kt @@ -29,46 +29,46 @@ fun FilterDialog( Row(verticalAlignment = Alignment.CenterVertically) { RadioButton( selected = selectedOption.value == SortingType.NAME_ASC, - onClick = { selectedOption.value = SortingType.NAME_ASC } + onClick = { selectedOption.value = SortingType.NAME_ASC }, ) Text( text = "File Name A-Z", style = FotoTypography().button, - color = fotoColors.onSurface + color = fotoColors.onSurface, ) } Row(verticalAlignment = Alignment.CenterVertically) { RadioButton( selected = selectedOption.value == SortingType.NAME_DESC, - onClick = { selectedOption.value = SortingType.NAME_DESC } + onClick = { selectedOption.value = SortingType.NAME_DESC }, ) Text( text = "File Name Z-A", style = FotoTypography().button, - color = fotoColors.onSurface + color = fotoColors.onSurface, ) } Row(verticalAlignment = Alignment.CenterVertically) { RadioButton( selected = selectedOption.value == SortingType.TIME_ASC, - onClick = { selectedOption.value = SortingType.TIME_ASC } + onClick = { selectedOption.value = SortingType.TIME_ASC }, ) Text( text = "Time Created Ascending", style = FotoTypography().button, - color = fotoColors.onSurface + color = fotoColors.onSurface, ) } Row(verticalAlignment = Alignment.CenterVertically) { RadioButton( selected = selectedOption.value == SortingType.TIME_DESC, - onClick = { selectedOption.value = SortingType.TIME_DESC } + onClick = { selectedOption.value = SortingType.TIME_DESC }, ) Text( text = "Time Created Descending", style = FotoTypography().button, - color = fotoColors.onSurface + color = fotoColors.onSurface, ) } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FotoDIalog.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FotoDIalog.kt index 0abad4f8..ca094d51 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FotoDIalog.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/FotoDIalog.kt @@ -28,14 +28,16 @@ fun FotoDialog( Dialog(onDismissRequest = { onDismissRequest() }) { // Draw a rectangle shape with rounded corners inside the dialog Card( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), + modifier = + Modifier + .fillMaxWidth() + .padding(16.dp), shape = RoundedCornerShape(16.dp), ) { Column( - modifier = Modifier - .fillMaxWidth().padding(Padding.STANDARD.dp), + modifier = + Modifier + .fillMaxWidth().padding(Padding.STANDARD.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.Start, ) { @@ -43,17 +45,18 @@ fun FotoDialog( Spacer(Modifier.size(Padding.MEDIUM.dp)) content() Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = Padding.SMALL.dp), + modifier = + Modifier + .fillMaxWidth() + .padding(top = Padding.SMALL.dp), horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { TextButton( onClick = { onDismissRequest() }, - modifier = Modifier.padding(Padding.SMALL.dp) + modifier = Modifier.padding(Padding.SMALL.dp), ) { DialogButtonText("Dismiss") } @@ -66,4 +69,4 @@ fun FotoDialog( } } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ImagePreviewOverlay.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ImagePreviewOverlay.kt index 36921b5f..38a98125 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ImagePreviewOverlay.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ImagePreviewOverlay.kt @@ -1,9 +1,5 @@ package com.kevinschildhorn.fotopresenter.ui.screens.common.composables -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -12,15 +8,12 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material.Button -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.Icon import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage import com.kevinschildhorn.fotopresenter.ui.atoms.Padding +import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage import compose.icons.EvaIcons import compose.icons.evaicons.Fill import compose.icons.evaicons.fill.ArrowLeft @@ -28,13 +21,12 @@ import compose.icons.evaicons.fill.ArrowRight @Composable fun ImagePreviewOverlay( - image: ImageBitmap, + image: SharedImage, visible: Boolean, onDismiss: () -> Unit, onBack: () -> Unit, onForward: () -> Unit, ) { - Overlay( 5f, visible = visible, @@ -45,21 +37,21 @@ fun ImagePreviewOverlay( verticalArrangement = Arrangement.SpaceBetween, ) { // imageState.onSuccess { - Image( - bitmap = image, + AsyncImage( + model = image, contentDescription = null, modifier = - Modifier - .fillMaxWidth() - .fillMaxHeight(.9f) - .padding(Padding.IMAGE.dp), + Modifier + .fillMaxWidth() + .fillMaxHeight(.9f) + .padding(Padding.IMAGE.dp), ) Row( modifier = - Modifier - .fillMaxWidth() - .padding(Padding.STANDARD.dp) - .height(44.dp), + Modifier + .fillMaxWidth() + .padding(Padding.STANDARD.dp) + .height(44.dp), horizontalArrangement = Arrangement.SpaceBetween, ) { PrimaryIconButton( diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/Overlay.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/Overlay.kt index 49d42895..6700ba51 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/Overlay.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/Overlay.kt @@ -3,10 +3,8 @@ package com.kevinschildhorn.fotopresenter.ui.screens.common.composables import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.EnterTransition import androidx.compose.animation.ExitTransition -import androidx.compose.animation.expandIn import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkOut import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -17,14 +15,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.zIndex -import com.kevinschildhorn.fotopresenter.ui.atoms.fotoColors import com.kevinschildhorn.fotopresenter.ui.atoms.shadow @Composable fun Overlay( z: Float, visible: Boolean, - shadow:Boolean = true, + shadow: Boolean = true, modifier: Modifier = Modifier, onDismiss: () -> Unit, enter: EnterTransition = fadeIn(), @@ -34,7 +31,7 @@ fun Overlay( OverlayShadow( z - 1, visible && shadow, - onDismiss = onDismiss + onDismiss = onDismiss, ) AnimatedVisibility( visible = visible, @@ -42,9 +39,10 @@ fun Overlay( exit = exit, ) { Box( - modifier = modifier - .fillMaxSize() - .zIndex(z), + modifier = + modifier + .fillMaxSize() + .zIndex(z), content = content, ) } @@ -64,15 +62,16 @@ fun OverlayShadow( ) { val interactionSource = remember { MutableInteractionSource() } Box( - modifier = modifier - .zIndex(z) - .fillMaxSize() - .background(shadow) - .clickable( - interactionSource = interactionSource, - indication = null, - onClick = onDismiss, - ), + modifier = + modifier + .zIndex(z) + .fillMaxSize() + .background(shadow) + .clickable( + interactionSource = interactionSource, + indication = null, + onClick = onDismiss, + ), ) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryButton.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryButton.kt index e815efb4..7ddcc91e 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryButton.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryButton.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.unit.dp import com.kevinschildhorn.fotopresenter.ui.atoms.disabled import com.kevinschildhorn.fotopresenter.ui.atoms.fotoColors - @Composable fun PrimaryButton( modifier: Modifier = Modifier, @@ -25,10 +24,10 @@ fun PrimaryButton( modifier = modifier.height(44.dp), enabled = buttonState.enabled, colors = - ButtonDefaults.buttonColors( - backgroundColor = fotoColors.primary, - disabledBackgroundColor = disabled, - ), + ButtonDefaults.buttonColors( + backgroundColor = fotoColors.primary, + disabledBackgroundColor = disabled, + ), ) { if (buttonState.loading) { CircularProgressIndicator( @@ -52,4 +51,4 @@ enum class ButtonState { val loading: Boolean get() = this == LOADING -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryIconButton.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryIconButton.kt index d0df19e6..9c0471bd 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryIconButton.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryIconButton.kt @@ -5,7 +5,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector - @Composable fun PrimaryIconButton( imageVector: ImageVector, @@ -16,4 +15,4 @@ fun PrimaryIconButton( PrimaryButton(modifier = modifier, buttonState = buttonState, onClick = onClick) { Icon(imageVector, contentDescription = null) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryTextButton.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryTextButton.kt index 7c236463..2d22c753 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryTextButton.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/PrimaryTextButton.kt @@ -1,6 +1,5 @@ package com.kevinschildhorn.fotopresenter.ui.screens.common.composables - import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -14,7 +13,6 @@ fun PrimaryTextButton( buttonState: ButtonState = ButtonState.ENABLED, onClick: () -> Unit, ) { - PrimaryButton(modifier = modifier, buttonState = buttonState, onClick = onClick) { Text( title, diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ToastOverlay.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ToastOverlay.kt index b47fe337..fff9cad4 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ToastOverlay.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/common/composables/ToastOverlay.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment @@ -39,7 +38,7 @@ fun ToastOverlay( AnimatedVisibility( visible = visible, enter = slideInVertically { -25 } + fadeIn(initialAlpha = 0.3f), - exit = slideOutVertically { -25 } + fadeOut() + exit = slideOutVertically { -25 } + fadeOut(), ) { Overlay( z = 8f, @@ -50,22 +49,24 @@ fun ToastOverlay( Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween, - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(Modifier) Column(modifier = Modifier.padding(horizontal = Padding.STANDARD.dp)) { Box( - modifier = Modifier - .clip(RoundedCornerShape(25.dp)) - .background(fotoColors.secondary), + modifier = + Modifier + .clip(RoundedCornerShape(25.dp)) + .background(fotoColors.secondary), ) { ToastMessage( text, - modifier = Modifier - .padding( - horizontal = Padding.STANDARD.dp, - vertical = Padding.MEDIUM.dp - ), + modifier = + Modifier + .padding( + horizontal = Padding.STANDARD.dp, + vertical = Padding.MEDIUM.dp, + ), ) } Spacer(Modifier.height(25.dp)) @@ -73,4 +74,4 @@ fun ToastOverlay( } } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreen.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreen.kt index 18def33b..7427b01f 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreen.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreen.kt @@ -3,12 +3,7 @@ package com.kevinschildhorn.fotopresenter.ui.screens.directory import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.LinearProgressIndicator -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -17,14 +12,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp import com.kevinschildhorn.fotopresenter.data.ImageSlideshowDetails import com.kevinschildhorn.fotopresenter.ui.UiState -import com.kevinschildhorn.fotopresenter.ui.atoms.FotoTypography import com.kevinschildhorn.fotopresenter.ui.atoms.Padding -import com.kevinschildhorn.fotopresenter.ui.atoms.fotoColors import com.kevinschildhorn.fotopresenter.ui.screens.common.ActionSheetAction import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ActionSheet import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ConfirmationDialog @@ -82,10 +72,11 @@ fun DirectoryScreen( (uiState.state as? UiState.ERROR)?.let { ErrorView( it.message, - modifier = Modifier.padding( - horizontal = Padding.STANDARD.dp, - vertical = Padding.SMALL.dp, - ) + modifier = + Modifier.padding( + horizontal = Padding.STANDARD.dp, + vertical = Padding.SMALL.dp, + ), ) } DirectoryNavigationBar( @@ -96,7 +87,7 @@ fun DirectoryScreen( onItem = { viewModel.navigateToFolder(it) }, - modifier = Modifier.padding(Padding.SMALL.dp) + modifier = Modifier.padding(Padding.SMALL.dp), ) DirectoryGrid( uiState.directoryGridState, @@ -216,7 +207,7 @@ fun DirectoryScreen( }, onConfirmation = { viewModel.setFilterType(it) - } + }, ) } @@ -227,7 +218,7 @@ fun DirectoryScreen( overlaid = true, onDismiss = { overlayVisible = DirectoryOverlay.NONE - } + }, ) { playlist -> viewModel.addSelectedDirectoryToPlaylist(playlist) overlayVisible = DirectoryOverlay.NONE @@ -242,10 +233,12 @@ fun DirectoryScreen( initialValue = viewModel.selectedMetadata?.tagsString ?: "", { overlayVisible = DirectoryOverlay.NONE - }, { + }, + { viewModel.saveMetadata(it) viewModel.setSelectedDirectory(null) overlayVisible = DirectoryOverlay.NONE - }) + }, + ) } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreenState.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreenState.kt index 9d717074..9d84c0b4 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreenState.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryScreenState.kt @@ -16,9 +16,7 @@ data class DirectoryScreenState( val sortingType: SortingType = SortingType.NAME_ASC, override val state: UiState = UiState.IDLE, ) : ScreenState { - - fun getImageIndexFromId(id: Int): Int = - directoryGridState.imageStates.indexOfFirst { it.id == id } + fun getImageIndexFromId(id: Int): Int = directoryGridState.imageStates.indexOfFirst { it.id == id } val currentPathList: List get() = currentPath.split("\\").filter { it.isNotEmpty() } @@ -44,21 +42,25 @@ data class DirectoryGridState( sealed class DirectoryGridCellState( val name: String, val id: Int, - val actionSheetContexts: List - + val actionSheetContexts: List, ) { class Folder(name: String, id: Int) : DirectoryGridCellState( - name, id, listOf( + name, + id, + listOf( ActionSheetContext(ActionSheetAction.START_SLIDESHOW, 1), ActionSheetContext(ActionSheetAction.ADD_DYNAMIC_LOCATION, 2), - ) + ), ) class Image(val directoryDetails: NetworkDirectoryDetails, name: String, id: Int) : DirectoryGridCellState( - name, id, listOf( + name, + id, + listOf( ActionSheetContext(ActionSheetAction.ADD_STATIC_LOCATION, 1), ActionSheetContext(ActionSheetAction.ADD_METADATA, 2), - ) + ), ) + override fun toString(): String = "(I:$name:$id)" } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt index 62a6cd77..cadf2b58 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/DirectoryViewModel.kt @@ -35,11 +35,13 @@ class DirectoryViewModel( ) : PlaylistViewModel(playlistRepository, logger), ImageViewModel by DefaultImageViewModel(logger), KoinComponent { - private val slideshowScope: CoroutineScope = viewModelScope + Dispatchers.IO private val imageScope: CoroutineScope = viewModelScope + Dispatchers.IO + @Suppress("ktlint:standard:property-naming") private val _directoryContentsState = MutableStateFlow(DirectoryContents()) + + @Suppress("ktlint:standard:property-naming") private val _uiState = MutableStateFlow(DirectoryScreenState()) val uiState: StateFlow = _uiState.asStateFlow() @@ -87,7 +89,7 @@ class DirectoryViewModel( val retrieveImagesUseCase = UseCaseFactory.retrieveImageDirectoriesUseCase val images = retrieveImagesUseCase(it.details) logger.v { "Retrieved images, copying them to state" } - //_uiState.update { it.copy(slideshowDetails = ImageSlideshowDetails(images)) } + // _uiState.update { it.copy(slideshowDetails = ImageSlideshowDetails(images)) } } } } ?: run { @@ -174,36 +176,39 @@ class DirectoryViewModel( } } - private fun updateGrid() = with(_directoryContentsState.value) { - logger.i { "Updating State to Success" } - logger.i { "Setting Directories: $this" } - setImageDirectories(this.images) - val gridState = this.asDirectoryGridState - logger.i { "New Grid State $gridState" } - _uiState.update { - it.copy( - directoryGridState = gridState, - state = UiState.SUCCESS, - ) + private fun updateGrid() = + with(_directoryContentsState.value) { + logger.i { "Updating State to Success" } + logger.i { "Setting Directories: $this" } + setImageDirectories(this.images) + val gridState = this.asDirectoryGridState + logger.i { "New Grid State $gridState" } + _uiState.update { + it.copy( + directoryGridState = gridState, + state = UiState.SUCCESS, + ) + } } - } private val DirectoryContents.asDirectoryGridState: DirectoryGridState get() = DirectoryGridState( - folderStates = folders.map { - DirectoryGridCellState.Folder( - it.name, - it.id - ) - }, - imageStates = images.map { - DirectoryGridCellState.Image( - it.details, - it.name, - it.id, - ) - }.toMutableList(), + folderStates = + folders.map { + DirectoryGridCellState.Folder( + it.name, + it.id, + ) + }, + imageStates = + images.map { + DirectoryGridCellState.Image( + it.details, + it.name, + it.id, + ) + }.toMutableList(), ) //endregion @@ -216,8 +221,11 @@ class DirectoryViewModel( logger.i { "Inserting Playlist Image ${playlist.id} as ${uiState.value.selectedDirectory}" } val states: List = - if (selectedDirectory is DirectoryGridCellState.Image) this.images - else this.folders + if (selectedDirectory is DirectoryGridCellState.Image) { + this.images + } else { + this.folders + } states.find { it.id == selectedDirectory.id } ?.let { directory -> diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/grid/DirectoryGrid.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/grid/DirectoryGrid.kt index 1cf3745e..a08dbe40 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/grid/DirectoryGrid.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/grid/DirectoryGrid.kt @@ -63,14 +63,16 @@ fun DirectoryGrid( onLongClickLabel = "Action Sheet", ) when (state) { - is DirectoryGridCellState.Folder -> FolderDirectoryGridCell( - state, - modifier = directoryItemModifier, - ) - is DirectoryGridCellState.Image -> ImageDirectoryGridCell( - state, - modifier = directoryItemModifier, - ) + is DirectoryGridCellState.Folder -> + FolderDirectoryGridCell( + state, + modifier = directoryItemModifier, + ) + is DirectoryGridCellState.Image -> + ImageDirectoryGridCell( + state, + modifier = directoryItemModifier, + ) } } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationBar.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationBar.kt index c76e4190..fa10de10 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationBar.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationBar.kt @@ -24,26 +24,25 @@ fun DirectoryNavigationBar( ) { Row( modifier = modifier, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { DirectoryNavigationHome { onHome() } - LazyRow() { - itemsIndexed(directories){ index, item -> + LazyRow { + itemsIndexed(directories) { index, item -> Icon( EvaIcons.Fill.ChevronRight, tint = fotoColors.onSecondary, contentDescription = null, - modifier = Modifier.height(44.dp) + modifier = Modifier.height(44.dp), ) DirectoryNavigationItem(item) { onItem(index) } } items(directories) { - } } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationHome.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationHome.kt index ce1f514e..a2d808e2 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationHome.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationHome.kt @@ -23,16 +23,16 @@ fun DirectoryNavigationHome(onClick: () -> Unit) { onClick = onClick, modifier = Modifier.height(44.dp).width(44.dp).clip(RoundedCornerShape(10.dp)), colors = - ButtonDefaults.buttonColors( - backgroundColor = fotoColors.secondary, - disabledBackgroundColor = disabled, - ), + ButtonDefaults.buttonColors( + backgroundColor = fotoColors.secondary, + disabledBackgroundColor = disabled, + ), ) { Icon( imageVector = EvaIcons.Fill.Home, tint = fotoColors.onSecondary, contentDescription = "Home", - modifier = Modifier.size(44.dp) + modifier = Modifier.size(44.dp), ) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationItem.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationItem.kt index f60bb366..9c491b82 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationItem.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navbar/DirectoryNavigationItem.kt @@ -12,16 +12,19 @@ import com.kevinschildhorn.fotopresenter.ui.atoms.fotoColors import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.DialogButtonText @Composable -fun DirectoryNavigationItem(title: String, onClick: () -> Unit) { +fun DirectoryNavigationItem( + title: String, + onClick: () -> Unit, +) { Button( onClick = onClick, modifier = Modifier.height(44.dp).clip(RoundedCornerShape(10.dp)), colors = - ButtonDefaults.buttonColors( - backgroundColor = fotoColors.secondary, - disabledBackgroundColor = fotoColors.onSecondary, - ), + ButtonDefaults.buttonColors( + backgroundColor = fotoColors.secondary, + disabledBackgroundColor = fotoColors.onSecondary, + ), ) { DialogButtonText(title) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/DirectoryTitleBarButton.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/DirectoryTitleBarButton.kt index 42400840..79cd1e86 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/DirectoryTitleBarButton.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/DirectoryTitleBarButton.kt @@ -1,27 +1,27 @@ package com.kevinschildhorn.fotopresenter.ui.screens.directory.composables.navrail +import androidx.compose.foundation.layout.size import androidx.compose.material.Icon import androidx.compose.material.TextButton import androidx.compose.runtime.Composable -import androidx.compose.ui.unit.dp import androidx.compose.ui.Modifier -import androidx.compose.foundation.layout.size import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp import com.kevinschildhorn.fotopresenter.ui.atoms.fotoColors @Composable fun DirectoryTitleBarButton( imageVector: ImageVector, - onClick: () -> Unit + onClick: () -> Unit, ) { TextButton( modifier = Modifier.size(55.dp), - onClick = onClick + onClick = onClick, ) { Icon( imageVector, contentDescription = "Menu", - tint = fotoColors.onBackground + tint = fotoColors.onBackground, ) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/NavigationRailOverlay.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/NavigationRailOverlay.kt index 698d2fd0..be3e9eb8 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/NavigationRailOverlay.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/directory/composables/navrail/NavigationRailOverlay.kt @@ -32,7 +32,7 @@ fun NavigationRailOverlay( ) { Row { NavigationRail( - backgroundColor = fotoColors.surface + backgroundColor = fotoColors.surface, ) { NavigationRailItem( label = { @@ -42,11 +42,11 @@ fun NavigationRailOverlay( Icon( EvaIcons.Outline.Monitor, contentDescription = "Playlists", - tint = fotoColors.onSurface + tint = fotoColors.onSurface, ) }, selected = true, - onClick = onPlaylists + onClick = onPlaylists, ) NavigationRailItem( label = { @@ -56,13 +56,13 @@ fun NavigationRailOverlay( Icon( EvaIcons.Fill.LogOut, contentDescription = "Logout", - tint = fotoColors.onSurface + tint = fotoColors.onSurface, ) }, selected = true, - onClick = onLogout + onClick = onLogout, ) } } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/LoginScreen.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/LoginScreen.kt index ebb7f0b9..a8255cde 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/LoginScreen.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/LoginScreen.kt @@ -7,8 +7,6 @@ import androidx.compose.foundation.text.ClickableText import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler @@ -18,7 +16,6 @@ import androidx.compose.ui.text.style.TextAlign import com.kevinschildhorn.fotopresenter.ui.UiState import com.kevinschildhorn.fotopresenter.ui.atoms.Padding import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.TitleView -import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ToastOverlay import com.kevinschildhorn.fotopresenter.ui.screens.login.composables.LoginScreenForm @Composable @@ -36,11 +33,11 @@ fun LoginScreen( TitleView( "Foto", modifier = - Modifier.padding( - top = Padding.SMALL.dp, - start = Padding.STANDARD.dp, - bottom = Padding.LARGE.dp, - ), + Modifier.padding( + top = Padding.SMALL.dp, + start = Padding.STANDARD.dp, + bottom = Padding.LARGE.dp, + ), ) LoginScreenForm( uiState = uiState, diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginScreenForm.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginScreenForm.kt index a964d852..74e37e9f 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginScreenForm.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginScreenForm.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.kevinschildhorn.fotopresenter.data.State import com.kevinschildhorn.fotopresenter.extension.required import com.kevinschildhorn.fotopresenter.ui.UiState import com.kevinschildhorn.fotopresenter.ui.atoms.Padding diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginTextField.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginTextField.kt index ef4e703f..9f409bef 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginTextField.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/login/composables/LoginTextField.kt @@ -1,7 +1,6 @@ package com.kevinschildhorn.fotopresenter.ui.screens.login.composables import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.text.KeyboardOptions.Companion import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.material.TextFieldDefaults @@ -34,10 +33,11 @@ fun LoginTextField( style = FotoTypography().subtitle1, ) }, - colors = TextFieldDefaults.outlinedTextFieldColors( - textColor = fotoColors.onPrimary, - disabledTextColor = disabled, - ), + colors = + TextFieldDefaults.outlinedTextFieldColors( + textColor = fotoColors.onPrimary, + disabledTextColor = disabled, + ), shape = fotoShapes.small, textStyle = FotoTypography().subtitle1, keyboardOptions = keyboardOptions, diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt index a8df055b..21b8b67e 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreen.kt @@ -1,6 +1,5 @@ package com.kevinschildhorn.fotopresenter.ui.screens.playlist - import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -8,10 +7,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import com.kevinschildhorn.fotopresenter.Playlist import com.kevinschildhorn.fotopresenter.data.PlaylistDetails import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.ConfirmationDialog -import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryOverlay import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.PlaylistOverlay import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.TextEntryDialog import com.kevinschildhorn.fotopresenter.ui.screens.playlist.composables.TextListDialog @@ -21,7 +18,7 @@ enum class PlaylistDialog { CREATE, DELETE, DETAILS, - EDIT + EDIT, } @Composable @@ -45,22 +42,27 @@ fun PlaylistScreen( viewModel.getPlaylist(id)?.let { onPlaylistSelected(it) } - }, onDetails = { + }, + onDetails = { dialogOpen = PlaylistDialog.DETAILS viewModel.setSelectedPlaylist(it) - }, onDelete = { + }, + onDelete = { dialogOpen = PlaylistDialog.DELETE viewModel.setSelectedPlaylist(it) - }, onEdit = { + }, + onEdit = { dialogOpen = PlaylistDialog.EDIT viewModel.setSelectedPlaylist(it) - }, onCreate = { + }, + onCreate = { dialogOpen = PlaylistDialog.CREATE - }, onDismiss = { + }, + onDismiss = { if (onDismiss != null) { onDismiss() } - } + }, ) when (dialogOpen) { @@ -70,10 +72,12 @@ fun PlaylistScreen( initialValue = "", { dialogOpen = PlaylistDialog.NONE - }, { + }, + { viewModel.createPlaylist(it) dialogOpen = PlaylistDialog.NONE - }) + }, + ) } PlaylistDialog.DELETE -> { @@ -101,7 +105,6 @@ fun PlaylistScreen( } else -> { - } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreenState.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreenState.kt index 4359864c..4a4157d7 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreenState.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistScreenState.kt @@ -1,6 +1,5 @@ package com.kevinschildhorn.fotopresenter.ui.screens.playlist -import com.kevinschildhorn.fotopresenter.Playlist import com.kevinschildhorn.fotopresenter.data.PlaylistDetails import com.kevinschildhorn.fotopresenter.ui.UiState import com.kevinschildhorn.fotopresenter.ui.screens.common.ScreenState @@ -10,10 +9,9 @@ data class PlaylistScreenState( val selectedId: Long? = null, override val state: UiState = UiState.IDLE, ) : ScreenState { - val selectedPlaylist: PlaylistDetails? get() = selectedId?.let { id -> playlists.find { it.id == id } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt index bfaf1f2e..6cc6ef88 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/PlaylistViewModel.kt @@ -1,9 +1,7 @@ package com.kevinschildhorn.fotopresenter.ui.screens.playlist import co.touchlab.kermit.Logger -import com.kevinschildhorn.fotopresenter.Playlist import com.kevinschildhorn.fotopresenter.data.Directory -import com.kevinschildhorn.fotopresenter.data.ImageDirectory import com.kevinschildhorn.fotopresenter.data.PlaylistDetails import com.kevinschildhorn.fotopresenter.data.repositories.PlaylistRepository import com.kevinschildhorn.fotopresenter.ui.shared.ViewModel @@ -19,7 +17,7 @@ open class PlaylistViewModel( private val playlistRepository: PlaylistRepository, private val logger: Logger, ) : ViewModel(), KoinComponent { - + @Suppress("ktlint:standard:property-naming") private val _uiState = MutableStateFlow(PlaylistScreenState()) val playlistState: StateFlow = _uiState.asStateFlow() @@ -38,8 +36,7 @@ open class PlaylistViewModel( _uiState.update { it.copy(selectedId = id) } } - fun getPlaylist(id: Long): PlaylistDetails? = - _uiState.value.playlists.find { it.id == id } + fun getPlaylist(id: Long): PlaylistDetails? = _uiState.value.playlists.find { it.id == id } fun clearSelectedPlaylist() { _uiState.update { it.copy(selectedId = null) } @@ -52,12 +49,15 @@ open class PlaylistViewModel( } } - fun addToPlaylist(directory: Directory, playlist: PlaylistDetails) { + fun addToPlaylist( + directory: Directory, + playlist: PlaylistDetails, + ) { logger.i { "Inserting Playlist Image ${playlist.id} as ${directory.name}" } viewModelScope.launch(Dispatchers.Default) { playlistRepository.insertPlaylistImage( playlistId = playlist.id, - directory = directory + directory = directory, )?.let { logger.i { "Successfully inserted playlist image" } } ?: run { @@ -75,4 +75,4 @@ open class PlaylistViewModel( clearSelectedPlaylist() } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistColumn.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistColumn.kt index caf49a1c..45989141 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistColumn.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistColumn.kt @@ -32,7 +32,7 @@ fun PlaylistColumn( .fillMaxWidth() .padding(Padding.STANDARD.dp) .clip(RoundedCornerShape(10.dp)) - .background(fotoColors.secondary) + .background(fotoColors.secondary), ) { Column(Modifier.fillMaxWidth().padding(Padding.MEDIUM.dp)) { DialogButtonText("Playlists") @@ -51,16 +51,16 @@ fun PlaylistColumn( }, onDelete = { onDelete(it.id) - } + }, ) Divider( startIndent = 0.dp, thickness = 1.dp, - color = fotoColors.onSecondary + color = fotoColors.onSecondary, ) } } PlaylistScreenCreateRow(onClick = onCreate) } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistOverlay.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistOverlay.kt index 7faa22b9..c496fd24 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistOverlay.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistOverlay.kt @@ -26,7 +26,7 @@ fun PlaylistOverlay( onDismiss = onDismiss, ) { Box( - modifier = Modifier.align(Alignment.Center) + modifier = Modifier.align(Alignment.Center), ) { PlaylistColumn( options, @@ -49,4 +49,4 @@ fun PlaylistOverlay( onDelete = onDelete, ) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenCreateRow.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenCreateRow.kt index 70cbeee7..c22ac764 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenCreateRow.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenCreateRow.kt @@ -29,7 +29,7 @@ fun PlaylistScreenCreateRow( ) { TextButton( modifier = Modifier.fillMaxWidth(), - onClick = onClick + onClick = onClick, ) { Icon( EvaIcons.Outline.PlusCircle, @@ -41,4 +41,4 @@ fun PlaylistScreenCreateRow( Spacer(Modifier.fillMaxWidth()) } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenPlaylistRow.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenPlaylistRow.kt index cd4d7fd4..0fed2e89 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenPlaylistRow.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/PlaylistScreenPlaylistRow.kt @@ -33,11 +33,11 @@ fun PlaylistScreenPlaylistRow( Row( modifier = Modifier.fillMaxWidth().height(55.dp), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { TextButton( onClick = onClick, - modifier = Modifier.fillMaxSize(0.7f) + modifier = Modifier.fillMaxSize(0.7f), ) { DialogButtonText(title) Spacer(Modifier.fillMaxWidth()) @@ -45,7 +45,7 @@ fun PlaylistScreenPlaylistRow( Row(modifier = Modifier.fillMaxHeight()) { TextButton( modifier = Modifier.width(44.dp), - onClick = onDetails + onClick = onDetails, ) { Icon( EvaIcons.Outline.Info, @@ -55,7 +55,7 @@ fun PlaylistScreenPlaylistRow( } TextButton( modifier = Modifier.width(44.dp), - onClick = onEdit + onClick = onEdit, ) { Icon( EvaIcons.Outline.Edit, @@ -70,9 +70,9 @@ fun PlaylistScreenPlaylistRow( Icon( EvaIcons.Outline.Trash, tint = fotoColors.onSecondary, - contentDescription = "Trash" + contentDescription = "Trash", ) } } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextEntryDialog.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextEntryDialog.kt index 4266b0b3..5030e266 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextEntryDialog.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextEntryDialog.kt @@ -13,15 +13,15 @@ import com.kevinschildhorn.fotopresenter.ui.screens.login.composables.LoginTextF @Composable fun TextEntryDialog( - title:String, + title: String, initialValue: String, onDismissRequest: () -> Unit, onConfirmation: (String) -> Unit, ) { - var enteredValue:String by remember { mutableStateOf(initialValue) } + var enteredValue: String by remember { mutableStateOf(initialValue) } FotoDialog( - dialogTitle = title,//"Playlist Name", + dialogTitle = title, onDismissRequest = onDismissRequest, onConfirmation = { onConfirmation(enteredValue) @@ -33,7 +33,7 @@ fun TextEntryDialog( enteredValue = it }, placeholder = "Name", - modifier = Modifier.padding(top = Padding.STANDARD.dp) + modifier = Modifier.padding(top = Padding.STANDARD.dp), ) } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextListDialog.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextListDialog.kt index 104a9eab..ac58dea8 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextListDialog.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/playlist/composables/TextListDialog.kt @@ -16,9 +16,9 @@ fun TextListDialog( onDismissRequest = onDismissRequest, ) { LazyColumn { - items(list){ + items(list) { Text(it) } } } -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreen.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreen.kt index 3fafd7bc..ab0dd61d 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreen.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreen.kt @@ -6,7 +6,6 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement @@ -31,6 +30,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.LoadingOverlay import com.kevinschildhorn.fotopresenter.ui.screens.common.composables.Overlay import compose.icons.EvaIcons @@ -59,10 +59,10 @@ fun SlideshowScreen( } Box(modifier = Modifier.fillMaxSize().background(Color.Black)) { - imageState.selectedImage?.let { bitmap -> - Crossfade(imageState.selectedImageIndex, animationSpec = tween(500)){ - Image( - bitmap = bitmap, + imageState.selectedImage?.let { sharedImage -> + Crossfade(imageState.selectedImageIndex, animationSpec = tween(500)) { + AsyncImage( + model = sharedImage, contentDescription = null, modifier = Modifier.fillMaxSize(), ) @@ -76,8 +76,7 @@ fun SlideshowScreen( 5f, visible = true, onDismiss = { - - } + }, ) { Column( modifier = Modifier.fillMaxSize(), @@ -89,7 +88,7 @@ fun SlideshowScreen( AnimatedVisibility( visible = show, enter = fadeIn(animationSpec = tween(500)), - exit = fadeOut(animationSpec = tween(500)) + exit = fadeOut(animationSpec = tween(500)), ) { TextButton(onClick = { viewModel.stopSlideshow() @@ -99,7 +98,7 @@ fun SlideshowScreen( EvaIcons.Fill.Close, tint = Color.White, contentDescription = "Close", - modifier = Modifier.size(55.dp) + modifier = Modifier.size(55.dp), ) } } @@ -110,57 +109,59 @@ fun SlideshowScreen( horizontalArrangement = Arrangement.SpaceBetween, ) { Box( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth(0.5f) - .combinedClickable( - onClick = { - show = true - }, - onDoubleClick = { - viewModel.skipBackwards() - }, - ) + modifier = + Modifier + .fillMaxHeight() + .fillMaxWidth(0.5f) + .combinedClickable( + onClick = { + show = true + }, + onDoubleClick = { + viewModel.skipBackwards() + }, + ), ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { if (show) { Icon( EvaIcons.Fill.ArrowLeft, tint = Color.White, contentDescription = "Left", - modifier = Modifier.size(55.dp) + modifier = Modifier.size(55.dp), ) } } } Box( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - .combinedClickable( - onClick = { - show = true - }, - onDoubleClick = { - viewModel.skipForward() - }, - ) + modifier = + Modifier + .fillMaxHeight() + .fillMaxWidth() + .combinedClickable( + onClick = { + show = true + }, + onDoubleClick = { + viewModel.skipForward() + }, + ), ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { if (show) { Icon( EvaIcons.Fill.ArrowRight, tint = Color.White, contentDescription = "Right", - modifier = Modifier.size(55.dp) + modifier = Modifier.size(55.dp), ) } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreenState.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreenState.kt index 7d6bd309..c1883d43 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreenState.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowScreenState.kt @@ -1,7 +1,6 @@ package com.kevinschildhorn.fotopresenter.ui.screens.slideshow import com.kevinschildhorn.fotopresenter.data.ImageSlideshowDetails -import com.kevinschildhorn.fotopresenter.ui.screens.common.ImageScreenState data class SlideshowScreenState( val slideshowDetails: ImageSlideshowDetails = ImageSlideshowDetails(), diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt index db690920..13de2d93 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/screens/slideshow/SlideshowViewModel.kt @@ -1,6 +1,7 @@ package com.kevinschildhorn.fotopresenter.ui.screens.slideshow import co.touchlab.kermit.Logger +import com.kevinschildhorn.fotopresenter.UseCaseFactory import com.kevinschildhorn.fotopresenter.data.ImageSlideshowDetails import com.kevinschildhorn.fotopresenter.data.PlaylistDetails import com.kevinschildhorn.fotopresenter.ui.screens.common.DefaultImageViewModel @@ -14,16 +15,14 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent -import java.util.* +import java.util.Timer import kotlin.concurrent.fixedRateTimer -import com.kevinschildhorn.fotopresenter.UseCaseFactory class SlideshowViewModel( private val logger: Logger, ) : ViewModel(), ImageViewModel by DefaultImageViewModel(logger), KoinComponent { - private val _uiState = MutableStateFlow(SlideshowScreenState()) val uiState: StateFlow = _uiState.asStateFlow() private var timer: Timer? = null @@ -74,9 +73,10 @@ class SlideshowViewModel( private fun startImageTimer(seconds: Long = 5L) { val time = seconds * 1000 - timer = fixedRateTimer(period = time, initialDelay = time) { - showNextImage() - } + timer = + fixedRateTimer(period = time, initialDelay = time) { + showNextImage() + } } private fun stopImageTimer() { diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt index 7bb208a0..67f384c4 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt @@ -4,4 +4,4 @@ import app.cash.sqldelight.db.SqlDriver expect class DriverFactory { fun createDriver(): SqlDriver -} \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt index 697f19e8..0e535d9f 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedCache.kt @@ -1,39 +1,44 @@ package com.kevinschildhorn.fotopresenter.ui.shared -import androidx.compose.ui.graphics.ImageBitmap import io.github.reactivecircus.cache4k.Cache interface CacheInterface { - fun getImage(id: String): ImageBitmap? + fun getImage(id: String): SharedImage? fun cacheImage( id: String, - imageBitmap: ImageBitmap, + image: SharedImage, ) } -object SharedCache : CacheInterface { - private val imageCache = Cache.Builder().build() +object SharedInMemoryCache : CacheInterface { + private val imageCache = Cache.Builder().build() - override fun getImage(id: String): ImageBitmap? = imageCache.get(id) + override fun getImage(id: String): SharedImage? = + imageCache.get(id)?.let { + SharedImage(it) + } override fun cacheImage( id: String, - imageBitmap: ImageBitmap, + image: SharedImage, ) { - imageCache.put(id, imageBitmap) + imageCache.put(id, image.byteArray) } } class MockSharedCache : CacheInterface { - val contents = mutableMapOf() + private val contents = mutableMapOf() override fun cacheImage( id: String, - imageBitmap: ImageBitmap, + image: SharedImage, ) { - contents[id] = imageBitmap + contents[id] = image.byteArray } - override fun getImage(id: String): ImageBitmap? = contents[id] + override fun getImage(id: String): SharedImage? = + contents[id]?.let { + SharedImage(it) + } } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt index 14dd76ef..831d34d6 100644 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt +++ b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt @@ -1,8 +1,28 @@ package com.kevinschildhorn.fotopresenter.ui.shared -import androidx.compose.ui.graphics.ImageBitmap +import coil3.Image -expect class SharedImage { - fun getImageBitmap(size: Int): ImageBitmap? +expect class SharedImage(byteArray: ByteArray) { val byteArray: ByteArray + + fun getCoilImage(size: Int): Image? +} + +fun getScaledDimensions( + width: Int, + height: Int, + minSize: Int, +): Pair { + val newWidth: Float + val newHeight: Float + if (height < width) { + newHeight = minSize.toFloat() + val ratio: Float = (newHeight / height) + newWidth = width * ratio + } else { + newWidth = minSize.toFloat() + val ratio: Float = (newWidth / width) + newHeight = height * ratio + } + return Pair(newWidth.toInt(), newHeight.toInt()) } diff --git a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt b/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt deleted file mode 100644 index 6eae73a1..00000000 --- a/shared/src/commonMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.kevinschildhorn.fotopresenter.ui.shared - -import androidx.compose.ui.graphics.ImageBitmap - -expect object SharedImageConverter { - fun convertBytes(byteArray: ByteArray): ImageBitmap - - fun convertImage(imageBitmap: ImageBitmap): ByteArray -} - -fun getScaledDimensions( - width: Int, - height: Int, - minSize: Int, -): Pair { - val newWidth: Float - val newHeight: Float - if (height < width) { - newHeight = minSize.toFloat() - val ratio: Float = (newHeight / height) - newWidth = width * ratio - } else { - newWidth = minSize.toFloat() - val ratio: Float = (newWidth / width) - newHeight = height * ratio - } - return Pair(newWidth.toInt(), newHeight.toInt()) -} diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/KoinTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/KoinTest.kt index 6f12fc5f..b6d586e3 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/KoinTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/KoinTest.kt @@ -3,7 +3,6 @@ package com.kevinschildhorn.fotopresenter import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver import co.touchlab.kermit.Logger -import co.touchlab.kermit.LoggerConfig import com.kevinschildhorn.fotopresenter.data.network.MockNetworkHandler import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler import com.russhwolf.settings.MapSettings @@ -22,6 +21,4 @@ fun testingModule(settings: MapSettings = MapSettings()) = PlaylistDatabase.Schema.create(driver) driver } - - } + commonModule diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/StringTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/StringTest.kt index dea7a504..47fafdd1 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/StringTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/StringTest.kt @@ -47,7 +47,5 @@ class StringTest { path = path.navigateBackToPathAtIndex(-1) assertEquals("", path) - - } } diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageCacheDataSourceTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageCacheDataSourceTest.kt deleted file mode 100644 index 57ef6dc3..00000000 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageCacheDataSourceTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.kevinschildhorn.fotopresenter.data.datasources - -/* TODO: Requires mocking -/** -Testing [ImageCacheDataSource] - **/ -class ImageCacheDataSourceTest { - private val cache = MockSharedCache() - private val dataSource = ImageCacheDataSource(cache) - - @Test - fun `save Image`() = - runBlocking { - val directoryDetails = MockNetworkDirectoryDetails( - fullPath = "Photos/sample.png", - id = 1, - ) - val bitmap = mockk() - assertTrue(cache.contents.isEmpty()) - dataSource.saveImage(directoryDetails, bitmap) - assertFalse(cache.contents.isEmpty()) - - val result = dataSource.getImage(directoryDetails) - assertEquals(bitmap, result) - } -}*/ diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSourceTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSourceTest.kt index 15589d1a..430b373d 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSourceTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageMetadataDataSourceTest.kt @@ -4,11 +4,11 @@ import com.kevinschildhorn.fotopresenter.data.MetadataFileDetails import com.kevinschildhorn.fotopresenter.data.network.MockNetworkHandler import kotlinx.coroutines.runBlocking import org.junit.Test +import kotlin.test.AfterTest +import kotlin.test.BeforeTest import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue -import kotlin.test.AfterTest -import kotlin.test.BeforeTest /** Testing [ImageMetadataDataSource] @@ -16,7 +16,6 @@ Testing [ImageMetadataDataSource] class ImageMetadataDataSourceTest { private val networkHandler: MockNetworkHandler = MockNetworkHandler - @BeforeTest fun startTest() = runBlocking { @@ -30,34 +29,37 @@ class ImageMetadataDataSourceTest { } @Test - fun `Import Metadata`() = runBlocking { - val dataSource = ImageMetadataDataSource(null, networkHandler) - val metadata = dataSource.importMetaData() - assertNotNull(metadata) - assertEquals(1, metadata.files.count()) - } + fun `Import Metadata`() = + runBlocking { + val dataSource = ImageMetadataDataSource(null, networkHandler) + val metadata = dataSource.importMetaData() + assertNotNull(metadata) + assertEquals(1, metadata.files.count()) + } @Test - fun `Export Playlist`() = runBlocking { - val dataSource = ImageMetadataDataSource(null, networkHandler) - var metadata = dataSource.importMetaData() - assertNotNull(metadata) - assertEquals(0, metadata.files.count()) - - val newMetadataFileDetails = MetadataFileDetails( - "MyPath.png", - setOf("Tag1", "Wallpaper"), - ) - val mutableFiles = metadata.files.toMutableList() - mutableFiles.add(newMetadataFileDetails) - metadata = metadata.copy(files = mutableFiles) - - val result = dataSource.exportMetadata(metadata) - assertTrue(result) - - metadata = dataSource.importMetaData() - - assertNotNull(metadata) - assertTrue(metadata.files.contains(newMetadataFileDetails)) - } -} \ No newline at end of file + fun `Export Playlist`() = + runBlocking { + val dataSource = ImageMetadataDataSource(null, networkHandler) + var metadata = dataSource.importMetaData() + assertNotNull(metadata) + assertEquals(0, metadata.files.count()) + + val newMetadataFileDetails = + MetadataFileDetails( + "MyPath.png", + setOf("Tag1", "Wallpaper"), + ) + val mutableFiles = metadata.files.toMutableList() + mutableFiles.add(newMetadataFileDetails) + metadata = metadata.copy(files = mutableFiles) + + val result = dataSource.exportMetadata(metadata) + assertTrue(result) + + metadata = dataSource.importMetaData() + + assertNotNull(metadata) + assertTrue(metadata.files.contains(newMetadataFileDetails)) + } +} diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageRemoteDataSourceTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageRemoteDataSourceTest.kt deleted file mode 100644 index 4f7acf8f..00000000 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/ImageRemoteDataSourceTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.kevinschildhorn.fotopresenter.data.datasources - -import com.kevinschildhorn.fotopresenter.data.network.DefaultNetworkDirectoryDetails -import com.kevinschildhorn.fotopresenter.data.network.MockNetworkHandler -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerError -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerException -import junit.framework.TestCase.assertEquals -import kotlinx.coroutines.runBlocking -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertNull -import kotlin.test.fail - -/** -Testing [ImageRemoteDataSource] - **/ -class ImageRemoteDataSourceTest { - private val networkHandler: MockNetworkHandler = MockNetworkHandler - private val dataSource = ImageRemoteDataSource(networkHandler) - - @BeforeTest - fun startTest() = - runBlocking { - networkHandler.connectSuccessfully() - } - - @AfterTest - fun tearDown() = - runBlocking { - networkHandler.disconnect() - } - - @Test - fun `get Image Success`() = - runBlocking { - val networkDirectory = - DefaultNetworkDirectoryDetails( - fullPath = "Photos/Success.png", - id = 1, - ) - try { - val image = dataSource.getImage(networkDirectory) - } catch (e: Exception) { - assertEquals("Success", e.message) - } - } - - @Test - fun `get Image Failure`() = - runBlocking { - val networkDirectory = - DefaultNetworkDirectoryDetails( - fullPath = "Photos/nonExistant.png", - id = 1, - ) - val image = dataSource.getImage(networkDirectory) - assertNull(image) - } - - @Test - fun `get Image Disconnected`() = - runBlocking { - networkHandler.disconnect() - try { - val image = dataSource.getImage(DefaultNetworkDirectoryDetails("", 1)) - fail("Should have thrown exception") - } catch (e: NetworkHandlerException) { - assertEquals(e.message, NetworkHandlerError.NOT_CONNECTED.message) - } - } -} diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSourceTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSourceTest.kt index ae1edf9e..49985a74 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSourceTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistFileDataSourceTest.kt @@ -17,24 +17,26 @@ Testing [PlaylistFileDataSource] class PlaylistFileDataSourceTest { private val networkHandler: MockNetworkHandler = MockNetworkHandler - private val newPlaylist = PlaylistDetails( - id = 2, - name = "NewPlaylist", - items = listOf( - PlaylistItem( - id = 1, - playlistId = 2, - directoryPath = "Photos/SubPhotos/Peeng3.png", - directoryId = 2, - ), - PlaylistItem( - id = 2, - playlistId = 2, - directoryPath = "Photos/SubPhotos/Jaypeg3.jpg", - directoryId = 3, - ) - ), - ) + private val newPlaylist = + PlaylistDetails( + id = 2, + name = "NewPlaylist", + items = + listOf( + PlaylistItem( + id = 1, + playlistId = 2, + directoryPath = "Photos/SubPhotos/Peeng3.png", + directoryId = 2, + ), + PlaylistItem( + id = 2, + playlistId = 2, + directoryPath = "Photos/SubPhotos/Jaypeg3.jpg", + directoryId = 3, + ), + ), + ) @BeforeTest fun startTest() = @@ -49,42 +51,45 @@ class PlaylistFileDataSourceTest { } @Test - fun `Import Playlist`() = runBlocking { - val dataSource = PlaylistFileDataSource(null, networkHandler) - val playlists = dataSource.importPlaylists() + fun `Import Playlist`() = + runBlocking { + val dataSource = PlaylistFileDataSource(null, networkHandler) + val playlists = dataSource.importPlaylists() - val existingPlaylist = playlists.firstOrNull() - assertNotNull(existingPlaylist) - assertEquals("Existing", existingPlaylist.name) - assertEquals(2, existingPlaylist.items.count()) - } + val existingPlaylist = playlists.firstOrNull() + assertNotNull(existingPlaylist) + assertEquals("Existing", existingPlaylist.name) + assertEquals(2, existingPlaylist.items.count()) + } @Test - fun `Export Playlist`() = runBlocking { - val dataSource = PlaylistFileDataSource(null, networkHandler) - var playlists = dataSource.importPlaylists().toMutableList() - assertEquals(1, playlists.count()) + fun `Export Playlist`() = + runBlocking { + val dataSource = PlaylistFileDataSource(null, networkHandler) + var playlists = dataSource.importPlaylists().toMutableList() + assertEquals(1, playlists.count()) - val result = dataSource.exportPlaylist(newPlaylist) - assertTrue(result) + val result = dataSource.exportPlaylist(newPlaylist) + assertTrue(result) - playlists = dataSource.importPlaylists().toMutableList() + playlists = dataSource.importPlaylists().toMutableList() - val searchedPlaylist = playlists.find { it.name == "NewPlaylist" } - assertNotNull(searchedPlaylist) - print("finished") - } + val searchedPlaylist = playlists.find { it.name == "NewPlaylist" } + assertNotNull(searchedPlaylist) + print("finished") + } @Test - fun `Delete Playlist`() = runBlocking { - val dataSource = PlaylistFileDataSource(null, networkHandler) - dataSource.exportPlaylist(newPlaylist) - var playlists = dataSource.importPlaylists() - val searchedPlaylist = playlists.find { it.name == "NewPlaylist" } - assertNotNull(searchedPlaylist) - dataSource.deletePlaylist(searchedPlaylist) + fun `Delete Playlist`() = + runBlocking { + val dataSource = PlaylistFileDataSource(null, networkHandler) + dataSource.exportPlaylist(newPlaylist) + var playlists = dataSource.importPlaylists() + val searchedPlaylist = playlists.find { it.name == "NewPlaylist" } + assertNotNull(searchedPlaylist) + dataSource.deletePlaylist(searchedPlaylist) - playlists = dataSource.importPlaylists() - assertEquals(1, playlists.count()) - } -} \ No newline at end of file + playlists = dataSource.importPlaylists() + assertEquals(1, playlists.count()) + } +} diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSourceTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSourceTest.kt index 43fbb520..3c121e3b 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSourceTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/datasources/PlaylistSQLDataSourceTest.kt @@ -16,32 +16,35 @@ import kotlin.test.fail Testing [PlaylistSQLDataSource] **/ class PlaylistSQLDataSourceTest { - - private val imageDirectory = ImageDirectory( - details = DefaultNetworkDirectoryDetails( - fullPath = "Image1.png", - id = 1, - ), - metaData = null, - - ) - private val imageDirectoryList: List = listOf( - imageDirectory, + private val imageDirectory = ImageDirectory( - details = DefaultNetworkDirectoryDetails( - fullPath = "Photos/Image2.png", - id = 2, - ), + details = + DefaultNetworkDirectoryDetails( + fullPath = "Image1.png", + id = 1, + ), metaData = null, - ), - ImageDirectory( - details = DefaultNetworkDirectoryDetails( - fullPath = "Image3.png", - id = 3, + ) + private val imageDirectoryList: List = + listOf( + imageDirectory, + ImageDirectory( + details = + DefaultNetworkDirectoryDetails( + fullPath = "Photos/Image2.png", + id = 2, + ), + metaData = null, ), - metaData = null, - ), - ) + ImageDirectory( + details = + DefaultNetworkDirectoryDetails( + fullPath = "Image3.png", + id = 3, + ), + metaData = null, + ), + ) @Test fun `Create Playlist Success`() { @@ -196,7 +199,6 @@ class PlaylistSQLDataSourceTest { val dataSource = PlaylistSQLDataSource(createInMemorySqlDriver()) val playlists = dataSource.getAllPlaylists() assertEquals(0, playlists.count()) - } private fun createInMemorySqlDriver(): SqlDriver { @@ -204,4 +206,4 @@ class PlaylistSQLDataSourceTest { PlaylistDatabase.Schema.create(driver) return driver } -} \ No newline at end of file +} diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/network/MockNetworkHandler.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/network/MockNetworkHandler.kt index 591d2699..f1167154 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/network/MockNetworkHandler.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/network/MockNetworkHandler.kt @@ -15,72 +15,74 @@ object MockNetworkHandler : NetworkHandler { val photoDirectoryId = 5 - private val playlists = mutableMapOf( - "Existing" to """ - { - "id" : 1, - "name" : "Existing", - "items": [ + private val playlists = + mutableMapOf( + "Existing" to + """ { "id" : 1, - "playlistId" : 1, - "directoryPath" : "Photos/SubPhotos/Peeng3.png", - "directoryId" : 2 - }, - { - "id" : 2, - "playlistId" : 1, - "directoryPath" : "Photos/Jaypeg2.jpg", - "directoryId" : 3 + "name" : "Existing", + "items": [ + { + "id" : 1, + "playlistId" : 1, + "directoryPath" : "Photos/SubPhotos/Peeng3.png", + "directoryId" : 2 + }, + { + "id" : 2, + "playlistId" : 1, + "directoryPath" : "Photos/Jaypeg2.jpg", + "directoryId" : 3 + } + ] } - ] - } - """.trimIndent() - ) + """.trimIndent(), + ) private var metadata: String? = null private val networkContents = mapOf( "" to - listOf( - DefaultNetworkDirectoryDetails(fullPath = "Photos", id = photoDirectoryId), - DefaultNetworkDirectoryDetails(fullPath = "NewDirectory", id = 1), - DefaultNetworkDirectoryDetails(fullPath = "Peeng.png", id = 75), - DefaultNetworkDirectoryDetails(fullPath = "Jaypeg.jpg", id = 3), - DefaultNetworkDirectoryDetails(fullPath = "textFile.txt", id = 4), - ), + listOf( + DefaultNetworkDirectoryDetails(fullPath = "Photos", id = photoDirectoryId), + DefaultNetworkDirectoryDetails(fullPath = "NewDirectory", id = 1), + DefaultNetworkDirectoryDetails(fullPath = "Peeng.png", id = 75), + DefaultNetworkDirectoryDetails(fullPath = "Jaypeg.jpg", id = 3), + DefaultNetworkDirectoryDetails(fullPath = "textFile.txt", id = 4), + ), "Directories" to - listOf( - DefaultNetworkDirectoryDetails( - fullPath = "Directories/NewDirectory", - id = 1 - ), - DefaultNetworkDirectoryDetails( - fullPath = "Directories/NewDirectory2", - id = 2 - ), + listOf( + DefaultNetworkDirectoryDetails( + fullPath = "Directories/NewDirectory", + id = 1, ), - "Photos" to - listOf( - DefaultNetworkDirectoryDetails(fullPath = "Photos/Peeng2.png", id = 2), - DefaultNetworkDirectoryDetails(fullPath = "Photos/Jaypeg2.jpg", id = 3), - DefaultNetworkDirectoryDetails(fullPath = "Photos/textFile2.txt", id = 4), - DefaultNetworkDirectoryDetails(fullPath = "Photos/SubPhotos", id = 5), + DefaultNetworkDirectoryDetails( + fullPath = "Directories/NewDirectory2", + id = 2, ), + ), + "Photos" to + listOf( + DefaultNetworkDirectoryDetails(fullPath = "Photos/Peeng2.png", id = 2), + DefaultNetworkDirectoryDetails(fullPath = "Photos/Jaypeg2.jpg", id = 3), + DefaultNetworkDirectoryDetails(fullPath = "Photos/textFile2.txt", id = 4), + DefaultNetworkDirectoryDetails(fullPath = "Photos/SubPhotos", id = 5), + ), "Photos/SubPhotos" to - listOf( - DefaultNetworkDirectoryDetails( - fullPath = "Photos/SubPhotos/Peeng3.png", - id = 2, - ), - DefaultNetworkDirectoryDetails( - fullPath = "Photos/SubPhotos/Jaypeg3.jpg", - id = 3, - ), - DefaultNetworkDirectoryDetails( - fullPath = "Photos/SubPhotos/textFile3.txt", - id = 4, - ), + listOf( + DefaultNetworkDirectoryDetails( + fullPath = "Photos/SubPhotos/Peeng3.png", + id = 2, + ), + DefaultNetworkDirectoryDetails( + fullPath = "Photos/SubPhotos/Jaypeg3.jpg", + id = 3, ), + DefaultNetworkDirectoryDetails( + fullPath = "Photos/SubPhotos/textFile3.txt", + id = 4, + ), + ), ) private val successImageName: String = "Photos/Success.png" @@ -101,7 +103,7 @@ object MockNetworkHandler : NetworkHandler { } connected = credentials == successLoginCredentials print("Is Connected $connected\n") - if(!connected) { + if (!connected) { print("Success Credentials: $successLoginCredentials\n") print("Actual Credentials $credentials\n") } @@ -148,10 +150,16 @@ object MockNetworkHandler : NetworkHandler { } override suspend fun folderExists(path: String): Boolean? = - if (path == "") null - else networkContents.keys.contains(path) + if (path == "") { + null + } else { + networkContents.keys.contains(path) + } - override suspend fun savePlaylist(playlistName: String, json: String): Boolean { + override suspend fun savePlaylist( + playlistName: String, + json: String, + ): Boolean { playlists[playlistName] = json return true } diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepositoryTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepositoryTest.kt deleted file mode 100644 index 2babcf5b..00000000 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/data/repositories/ImageRepositoryTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.kevinschildhorn.fotopresenter.data.repositories - -import com.kevinschildhorn.fotopresenter.data.network.DefaultNetworkDirectoryDetails -import com.kevinschildhorn.fotopresenter.data.network.MockNetworkHandler -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerError -import com.kevinschildhorn.fotopresenter.data.network.NetworkHandlerException -import com.kevinschildhorn.fotopresenter.testingModule -import kotlinx.coroutines.runBlocking -import org.koin.core.context.startKoin -import org.koin.core.context.stopKoin -import org.koin.test.KoinTest -import org.koin.test.inject -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull -import kotlin.test.fail - -/** -Testing [ImageRepository] - **/ -class ImageRepositoryTest : KoinTest { - private val networkHandler: MockNetworkHandler = MockNetworkHandler - private val repository: ImageRepository by inject() - - @BeforeTest - fun startTest() = - runBlocking { - startKoin { - modules(testingModule()) - } - networkHandler.connectSuccessfully() - } - - @AfterTest - fun tearDown() = - runBlocking { - stopKoin() - networkHandler.disconnect() - } - - @Test - fun `get Image Success`() = - runBlocking { - try { - val result = - repository.getImage( - DefaultNetworkDirectoryDetails( - fullPath = "Photos/Success.png", - id = 1, - ), - ) - } catch (e: Exception) { - assertEquals("Success", e.message) - } - } - - @Test - fun `get Image Failure`() = - runBlocking { - val result = - repository.getImage( - DefaultNetworkDirectoryDetails( - fullPath = "Photos/nonExistant.png", - id = 1, - ), - ) - assertNull(result) - } - - @Test - fun `get Image Disconnected`() = - runBlocking { - networkHandler.disconnect() - try { - val result = - repository.getImage( - DefaultNetworkDirectoryDetails( - fullPath = "Photos/nonExistant.png", - id = 1, - ), - ) - fail("Should Throw Exception") - } catch (e: NetworkHandlerException) { - assertEquals(e.message, NetworkHandlerError.NOT_CONNECTED.message) - } - } -} diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/domain/SaveCredentialsUseCaseTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/domain/SaveCredentialsUseCaseTest.kt index 3c7e0411..50c6574f 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/domain/SaveCredentialsUseCaseTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/domain/SaveCredentialsUseCaseTest.kt @@ -1,7 +1,6 @@ package com.kevinschildhorn.fotopresenter.domain import co.touchlab.kermit.Logger -import co.touchlab.kermit.LoggerConfig import com.kevinschildhorn.fotopresenter.data.LoginCredentials import com.kevinschildhorn.fotopresenter.domain.connection.SaveCredentialsUseCase import com.kevinschildhorn.fotopresenter.testingModule diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/DirectoryViewModelTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/DirectoryViewModelTest.kt index 4c08a34b..9d462b34 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/DirectoryViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/DirectoryViewModelTest.kt @@ -4,8 +4,8 @@ import app.cash.turbine.test import com.kevinschildhorn.fotopresenter.data.network.MockNetworkHandler import com.kevinschildhorn.fotopresenter.testingModule import com.kevinschildhorn.fotopresenter.ui.UiState +import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryGridCellState import com.kevinschildhorn.fotopresenter.ui.screens.directory.DirectoryViewModel -import com.kevinschildhorn.fotopresenter.ui.screens.directory.FolderDirectoryGridCellState import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking @@ -21,9 +21,7 @@ import org.koin.test.inject import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.assertEquals -import kotlin.test.assertFalse import kotlin.test.assertNotNull -import kotlin.test.assertTrue /** Testing [DirectoryViewModel] @@ -130,10 +128,11 @@ class DirectoryViewModelTest : KoinTest { state = awaitItem() assertEquals(UiState.SUCCESS, state.state) - val directory = FolderDirectoryGridCellState( - name = "", - id = MockNetworkHandler.photoDirectoryId, - ) + val directory = + DirectoryGridCellState.Folder( + name = "", + id = MockNetworkHandler.photoDirectoryId, + ) viewModel.setSelectedDirectory(directory) viewModel.startSlideshow() while (state.slideshowDetails == null) { diff --git a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/LoginViewModelTest.kt b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/LoginViewModelTest.kt index a02dd170..49641140 100644 --- a/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/LoginViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/kevinschildhorn/fotopresenter/ui/viewmodel/LoginViewModelTest.kt @@ -6,8 +6,6 @@ import com.kevinschildhorn.fotopresenter.ui.UiState import com.kevinschildhorn.fotopresenter.ui.screens.login.LoginViewModel import com.russhwolf.settings.MapSettings import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -159,7 +157,6 @@ class LoginViewModelTest : KoinTest { } } - @Test fun `Login Success`() = runTest { diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/KoinDesktop.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/KoinDesktop.kt index 4157ea77..5674f68b 100644 --- a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/KoinDesktop.kt +++ b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/KoinDesktop.kt @@ -16,12 +16,13 @@ fun startKoin() { } } -internal actual val platformModule: Module = module { - single { - PreferencesSettings(get()) +internal actual val platformModule: Module = + module { + single { + PreferencesSettings(get()) + } + single { + SMBJHandler + } + single { DriverFactory().createDriver() } } - single { - SMBJHandler - } - single { DriverFactory().createDriver() } -} diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryDesktop.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryDesktop.kt index 460b0abf..81e9e649 100644 --- a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryDesktop.kt +++ b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/UseCaseFactoryDesktop.kt @@ -1,19 +1,17 @@ +@file:Suppress("ktlint:standard:filename") + package com.kevinschildhorn.fotopresenter -import co.touchlab.kermit.Logger -import co.touchlab.kermit.LoggerConfig import com.kevinschildhorn.fotopresenter.data.datasources.CredentialsDataSource import com.kevinschildhorn.fotopresenter.data.datasources.DirectoryDataSource -import com.kevinschildhorn.fotopresenter.data.datasources.ImageCacheDataSource import com.kevinschildhorn.fotopresenter.data.datasources.ImageMetadataDataSource -import com.kevinschildhorn.fotopresenter.data.datasources.ImageRemoteDataSource import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistFileDataSource import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistSQLDataSource +import com.kevinschildhorn.fotopresenter.data.datasources.image.CachedImageDataSource import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler import com.kevinschildhorn.fotopresenter.data.network.SMBJHandler import com.kevinschildhorn.fotopresenter.data.repositories.CredentialsRepository import com.kevinschildhorn.fotopresenter.data.repositories.DirectoryRepository -import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository import com.kevinschildhorn.fotopresenter.data.repositories.PlaylistRepository import com.kevinschildhorn.fotopresenter.domain.RetrieveDirectoryContentsUseCase import com.kevinschildhorn.fotopresenter.domain.connection.AutoConnectUseCase @@ -26,32 +24,33 @@ import com.kevinschildhorn.fotopresenter.domain.image.RetrieveImageUseCase import com.kevinschildhorn.fotopresenter.domain.image.RetrieveSlideshowFromPlaylistUseCase import com.kevinschildhorn.fotopresenter.domain.image.SaveMetadataForPathUseCase import com.kevinschildhorn.fotopresenter.ui.shared.DriverFactory -import com.kevinschildhorn.fotopresenter.ui.shared.SharedCache +import com.kevinschildhorn.fotopresenter.ui.shared.SharedInMemoryCache import com.russhwolf.settings.PreferencesSettings import java.util.prefs.Preferences actual object UseCaseFactory { - private val preferences: Preferences = Preferences.userRoot() private val settings = PreferencesSettings(preferences) private val networkHandler: NetworkHandler = SMBJHandler - private val directoryDataSource = DirectoryDataSource( - networkHandler, - baseLogger.withTag("DirectoryDataSource") - ) + private val directoryDataSource = + DirectoryDataSource( + networkHandler, + baseLogger.withTag("DirectoryDataSource"), + ) private val sqlDriver = DriverFactory().createDriver() private val credentialDataSource = CredentialsDataSource(settings) val credentialsRepository = CredentialsRepository(credentialDataSource) - private val imageMetadataDataSource = ImageMetadataDataSource( - networkHandler = networkHandler, - logger = baseLogger.withTag("imageMetadataDataSource") - ) + private val imageMetadataDataSource = + ImageMetadataDataSource( + networkHandler = networkHandler, + logger = baseLogger.withTag("imageMetadataDataSource"), + ) private val directoryRepository = DirectoryRepository(directoryDataSource, imageMetadataDataSource) - private val imageRepository = ImageRepository(ImageRemoteDataSource(networkHandler)) - private val playlistSQLDataSource = PlaylistSQLDataSource( - sqlDriver, - com.kevinschildhorn.fotopresenter.baseLogger - ) + private val playlistSQLDataSource = + PlaylistSQLDataSource( + sqlDriver, + com.kevinschildhorn.fotopresenter.baseLogger, + ) private val playlistFileDataSource = PlaylistFileDataSource( baseLogger.withTag("playlistFileDataSource"), @@ -60,59 +59,69 @@ actual object UseCaseFactory { val playlistRepository = PlaylistRepository(playlistSQLDataSource, playlistFileDataSource) actual val connectToServerUseCase: ConnectToServerUseCase - get() = ConnectToServerUseCase( - client = networkHandler, - logger = baseLogger.withTag("ConnectToServerUseCase") - ) + get() = + ConnectToServerUseCase( + client = networkHandler, + logger = baseLogger.withTag("ConnectToServerUseCase"), + ) actual val changeDirectoryUseCase: ChangeDirectoryUseCase - get() = ChangeDirectoryUseCase( - dataSource = DirectoryDataSource( - networkHandler, - baseLogger.withTag("DirectoryDataSource") - ), - logger = baseLogger.withTag("ChangeDirectoryUseCase") - ) + get() = + ChangeDirectoryUseCase( + dataSource = + DirectoryDataSource( + networkHandler, + baseLogger.withTag("DirectoryDataSource"), + ), + logger = baseLogger.withTag("ChangeDirectoryUseCase"), + ) actual val autoConnectUseCase: AutoConnectUseCase - get() = AutoConnectUseCase( - client = networkHandler, - repository = credentialsRepository, - logger = baseLogger.withTag("AutoConnectUseCase") - ) + get() = + AutoConnectUseCase( + client = networkHandler, + repository = credentialsRepository, + logger = baseLogger.withTag("AutoConnectUseCase"), + ) actual val saveCredentialsUseCase: SaveCredentialsUseCase - get() = SaveCredentialsUseCase( - repository = credentialsRepository, - logger = baseLogger.withTag("SaveCredentialsUseCase") - ) + get() = + SaveCredentialsUseCase( + repository = credentialsRepository, + logger = baseLogger.withTag("SaveCredentialsUseCase"), + ) actual val disconnectFromServerUseCase: DisconnectFromServerUseCase - get() = DisconnectFromServerUseCase( - credentialsRepository, - networkHandler, - baseLogger.withTag("DisconnectFromServerUseCase") - ) + get() = + DisconnectFromServerUseCase( + credentialsRepository, + networkHandler, + baseLogger.withTag("DisconnectFromServerUseCase"), + ) actual val retrieveImageDirectoriesUseCase: RetrieveImageDirectoriesUseCase - get() = RetrieveImageDirectoriesUseCase( - logger = baseLogger.withTag("RetrieveImageDirectoriesUseCase") - ) + get() = + RetrieveImageDirectoriesUseCase( + logger = baseLogger.withTag("RetrieveImageDirectoriesUseCase"), + ) actual val retrieveSlideshowFromPlaylistUseCase: RetrieveSlideshowFromPlaylistUseCase - get() = RetrieveSlideshowFromPlaylistUseCase( - logger = baseLogger.withTag("RetrieveSlideshowFromPlaylistUseCase"), - retrieveDirectoryUseCase = this.retrieveImageDirectoriesUseCase, - ) + get() = + RetrieveSlideshowFromPlaylistUseCase( + logger = baseLogger.withTag("RetrieveSlideshowFromPlaylistUseCase"), + retrieveDirectoryUseCase = this.retrieveImageDirectoriesUseCase, + ) actual val retrieveDirectoryContentsUseCase: RetrieveDirectoryContentsUseCase - get() = RetrieveDirectoryContentsUseCase( - directoryRepository = directoryRepository, - imageRepository = imageRepository, - logger = baseLogger.withTag("RetrieveDirectoryContentsUseCase") - ) + get() = + RetrieveDirectoryContentsUseCase( + directoryRepository = directoryRepository, + logger = baseLogger.withTag("RetrieveDirectoryContentsUseCase"), + ) actual val retrieveImageUseCase: RetrieveImageUseCase - get() = RetrieveImageUseCase( - imageCacheDataSource = ImageCacheDataSource( - cache = SharedCache, - driver = sqlDriver, - logger = baseLogger.withTag("ImageCacheDataSource") - ), - logger = baseLogger.withTag("RetrieveImageUseCase") - ) + get() = + RetrieveImageUseCase( + cachedImageDataSource = + CachedImageDataSource( + cache = SharedInMemoryCache, + driver = sqlDriver, + logger = baseLogger.withTag("ImageCacheDataSource"), + ), + logger = baseLogger.withTag("RetrieveImageUseCase"), + ) actual val saveMetadataForPathUseCase: SaveMetadataForPathUseCase get() = SaveMetadataForPathUseCase(dataSource = imageMetadataDataSource) -} \ No newline at end of file +} diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt index 93fb9c9c..dc0e3ca7 100644 --- a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt +++ b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/DriverFactory.kt @@ -10,4 +10,4 @@ actual class DriverFactory { PlaylistDatabase.Schema.create(driver) return driver } -} \ No newline at end of file +} diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt deleted file mode 100644 index feda2259..00000000 --- a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.kevinschildhorn.fotopresenter.ui.shared - -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.res.loadImageBitmap -import com.hierynomus.smbj.share.File - -actual fun getBitmapFromFile(file: File, size:Int): ImageBitmap? = - file.inputStream.buffered().use(::loadImageBitmap) - diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt deleted file mode 100644 index eac3ecad..00000000 --- a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageConverter.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.kevinschildhorn.fotopresenter.ui.shared - -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.asSkiaBitmap -import androidx.compose.ui.graphics.toComposeImageBitmap -import org.jetbrains.skia.Image -import kotlin.jvm.Throws - -actual object SharedImageConverter { - actual fun convertBytes(byteArray: ByteArray): ImageBitmap { - return Image.makeFromEncoded(byteArray).toComposeImageBitmap() - } - - @Throws(Exception::class) - actual fun convertImage(imageBitmap: ImageBitmap): ByteArray { - return Image.makeFromBitmap(imageBitmap.asSkiaBitmap()).encodeToData()?.bytes!! - } -} \ No newline at end of file diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageDesktop.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageDesktop.kt new file mode 100644 index 00000000..29e52185 --- /dev/null +++ b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageDesktop.kt @@ -0,0 +1,10 @@ +@file:Suppress("ktlint:standard:filename") + +package com.kevinschildhorn.fotopresenter.ui.shared + +import coil3.Image + +actual open class SharedImage actual constructor(actual val byteArray: ByteArray) { + // TODO + actual fun getCoilImage(size: Int): Image? = null +} diff --git a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt index dc6d93d0..60eb688b 100644 --- a/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt +++ b/shared/src/desktopMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/ViewModel.kt @@ -25,4 +25,4 @@ actual abstract class ViewModel { onCleared() // viewModelScope.cancel() TODO } -} \ No newline at end of file +} diff --git a/shared/src/iosMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt b/shared/src/iosMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageIos.kt similarity index 100% rename from shared/src/iosMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt rename to shared/src/iosMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImageIos.kt diff --git a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJHandler.kt b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJHandler.kt index 9f7aa7c0..69b4ed81 100644 --- a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJHandler.kt +++ b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJHandler.kt @@ -15,19 +15,14 @@ import com.hierynomus.smbj.share.File import com.kevinschildhorn.fotopresenter.data.LoginCredentials import com.kevinschildhorn.fotopresenter.extension.addPath import com.kevinschildhorn.fotopresenter.ui.shared.SharedImage -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json import java.io.OutputStream -import java.util.* -import kotlin.math.log - object SMBJHandler : NetworkHandler { private val client = SMBClient() private var connection: Connection? = null private var session: Session? = null private var share: DiskShare? = null - private const val metaDataName: String = "FotoMetaData.json" + private const val META_DATA_NAME: String = "FotoMetaData.json" private val logger = Logger.withTag("SMBJHandler") private val accessMask: Set = @@ -93,20 +88,23 @@ object SMBJHandler : NetworkHandler { override suspend fun openDirectory(path: String): String? { logger.i { "Opening Directory $path" } - val result = share?.openDirectory( - path, - accessMask, - attributes, - shareAccesses, - createDisposition, - createOptions, - ) + val result = + share?.openDirectory( + path, + accessMask, + attributes, + shareAccesses, + createDisposition, + createOptions, + ) return result?.path } override suspend fun openImage(path: String): SharedImage? = - getFile(path)?.let { - val sharedImage = SharedImage(it) + getFile(path)?.let { file -> + val byteArray = file.inputStream.readAllBytes() + file.close() + val sharedImage = SharedImage(byteArray) sharedImage } ?: run { null } @@ -123,13 +121,15 @@ object SMBJHandler : NetworkHandler { connection = null } - override suspend fun savePlaylist(playlistName: String, json: String): Boolean = - writeFile(fileName = "$playlistName.json", contents = json) + override suspend fun savePlaylist( + playlistName: String, + json: String, + ): Boolean = writeFile(fileName = "$playlistName.json", contents = json) override suspend fun getPlaylists(): List = getDirectoryContents("") .filter { it.fileExtension == "json" } - .filter { !it.fileName.contains(metaDataName) } + .filter { !it.fileName.contains(META_DATA_NAME) } .mapNotNull { getFile(it.fullPath) } .map { val str = it.inputStream.readAllBytes().decodeToString() @@ -139,24 +139,22 @@ object SMBJHandler : NetworkHandler { override suspend fun setMetadata(json: String): Boolean { logger.i { "Setting Metadata" } - return writeFile(fileName = metaDataName, contents = json) + return writeFile(fileName = META_DATA_NAME, contents = json) } - override suspend fun getMetadata(): String? = getFile(metaDataName)?.let { - logger.i { "Importing Metadata" } - val str = it.inputStream.readAllBytes().decodeToString() - it.close() - str - } + override suspend fun getMetadata(): String? = + getFile(META_DATA_NAME)?.let { + logger.i { "Importing Metadata" } + val str = it.inputStream.readAllBytes().decodeToString() + it.close() + str + } override suspend fun deletePlaylist(playlistName: String) { share?.rm("$playlistName.json") } - - private fun getFile( - path: String, - ): File? = + private fun getFile(path: String): File? = try { logger.v { "Getting File at path $path" } share?.openFile( @@ -172,8 +170,10 @@ object SMBJHandler : NetworkHandler { null } - - private suspend fun writeFile(fileName: String, contents: String): Boolean { + private suspend fun writeFile( + fileName: String, + contents: String, + ): Boolean { logger.i { "Writing File" } val fileAttributes: MutableSet = HashSet() fileAttributes.add(FileAttributes.FILE_ATTRIBUTE_NORMAL) @@ -181,16 +181,16 @@ object SMBJHandler : NetworkHandler { createOptions.add(SMB2CreateOptions.FILE_RANDOM_ACCESS) try { - logger.i { "Trying to open a file $fileName" } - val file = share?.openFile( - fileName, - setOf(AccessMask.GENERIC_ALL), - fileAttributes, - SMB2ShareAccess.ALL, - SMB2CreateDisposition.FILE_OVERWRITE_IF, - createOptions - ) + val file = + share?.openFile( + fileName, + setOf(AccessMask.GENERIC_ALL), + fileAttributes, + SMB2ShareAccess.ALL, + SMB2CreateDisposition.FILE_OVERWRITE_IF, + createOptions, + ) file?.let { logger.i { "Got the file, writing contents" } val oStream: OutputStream = it.outputStream diff --git a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJNetworkDirectoryDetails.kt b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJNetworkDirectoryDetails.kt index b93a74be..32642672 100644 --- a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJNetworkDirectoryDetails.kt +++ b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/data/network/SMBJNetworkDirectoryDetails.kt @@ -9,5 +9,4 @@ class SMBJNetworkDirectoryDetails( override val id: Int = information.fileId.toInt() override val dateMillis: Long = information.changeTime.toEpochMillis() - } diff --git a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SMBJFetcher.kt b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SMBJFetcher.kt new file mode 100644 index 00000000..651ef073 --- /dev/null +++ b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/SMBJFetcher.kt @@ -0,0 +1,41 @@ +package com.kevinschildhorn.fotopresenter.ui + +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.ImageFetchResult +import coil3.request.Options +import com.kevinschildhorn.fotopresenter.data.network.NetworkDirectoryDetails +import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class SMBJFetcher( + private val directoryDetails: NetworkDirectoryDetails, + private val imageRepository: ImageRepository, +) : Fetcher { + override suspend fun fetch(): FetchResult? { + return withContext(Dispatchers.IO) { + val image = imageRepository.getCoilImage(directoryDetails, 64) + if (image != null) { + ImageFetchResult( + image = image, + isSampled = true, + dataSource = DataSource.NETWORK, + ) + } else { + null + } + } + } + + class Factory(private val imageRepository: ImageRepository) : + Fetcher.Factory { + override fun create( + data: NetworkDirectoryDetails, + options: Options, + imageLoader: ImageLoader, + ): Fetcher = SMBJFetcher(data, imageRepository) + } +} diff --git a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt b/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt deleted file mode 100644 index 2a95e3f1..00000000 --- a/shared/src/jvmMain/kotlin/com/kevinschildhorn/fotopresenter/ui/shared/SharedImage.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.kevinschildhorn.fotopresenter.ui.shared - -import androidx.compose.ui.graphics.ImageBitmap -import com.hierynomus.smbj.share.File - -actual open class SharedImage(val file: File) { - actual fun getImageBitmap(size: Int): ImageBitmap? = getBitmapFromFile(file, size) - actual val byteArray: ByteArray = file.inputStream.readAllBytes() -} - -expect fun getBitmapFromFile(file: File, size: Int): ImageBitmap? -