From 84f405d464f3d84f78239a62762d23e75b985f9a Mon Sep 17 00:00:00 2001 From: Marty Ulrich Date: Tue, 5 Nov 2024 04:26:12 -0500 Subject: [PATCH 1/2] Fixes (#339) * fixes * cleanup * Cleanup * MOBILE-275 Tapping search bar moves mini player view up * fix cache --- .../Modules/AudioPlayer/PlayQueue/PlayQueue.swift | 1 - .../AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift | 9 +++------ iosApp/Modules/Helpers/Files/DownloadManager.swift | 2 -- .../Modules/Helpers/Files/FileManagerService.swift | 13 +++++++++++-- .../Helpers/Logging/SentryErrorReporter.swift | 4 ++-- iosApp/Modules/Screens/Main/UI/MainView.swift | 4 +++- 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift b/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift index b0fc4c01..28b55785 100644 --- a/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift +++ b/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift @@ -182,7 +182,6 @@ struct PlayQueue { if let prevItem { currentIndex = currentQueue.firstIndex(of: prevItem) } - print("sorted: \(currentQueue.map(\.title))") } mutating diff --git a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift index bfca07ef..9ea205c3 100644 --- a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift +++ b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift @@ -95,16 +95,14 @@ public class VLCAudioPlayer: ObservableObject { } private func setUpDelegateHandling() { - Task { + Task { @MainActor in for await _ in delegate.stream { if mediaPlayer.state == .ended, let _ = playQueue.nextTrack() { playCurrentTrackInQueue() } else if mediaPlayer.state == .error { _errors.send("Unable to load \(currentTrack?.title ?? "song")") - } else if mediaPlayer.time != currentTime { - Task { @MainActor in - update() - } + } else { + update() } } } @@ -177,7 +175,6 @@ public class VLCAudioPlayer: ObservableObject { try await fileManager.download(track: track) { [weak self] progress in guard let self else { return } - print("progress for [\(track.title)]: \(progress)") Task { @MainActor in self.loadingProgress[track] = progress } diff --git a/iosApp/Modules/Helpers/Files/DownloadManager.swift b/iosApp/Modules/Helpers/Files/DownloadManager.swift index 23a00c96..6e992dd6 100644 --- a/iosApp/Modules/Helpers/Files/DownloadManager.swift +++ b/iosApp/Modules/Helpers/Files/DownloadManager.swift @@ -43,7 +43,6 @@ class DownloadManager: NSObject, ObservableObject { } func cancelDownload(url: URL) { - print(#function) guard let download = downloads[url] else { logger.logError(DownloadManagerError.canceledUnknownURL(url)) return @@ -100,7 +99,6 @@ extension DownloadManager: URLSessionDownloadDelegate { func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { guard let url = downloadTask.originalRequest?.url else { return } let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite) - print("BYTES WRITTEN: \t\(totalBytesWritten) out of \(totalBytesExpectedToWrite)\n") progressHandlers[url]?(progress) } diff --git a/iosApp/Modules/Helpers/Files/FileManagerService.swift b/iosApp/Modules/Helpers/Files/FileManagerService.swift index b7c531d4..5bcf1b6c 100644 --- a/iosApp/Modules/Helpers/Files/FileManagerService.swift +++ b/iosApp/Modules/Helpers/Files/FileManagerService.swift @@ -8,7 +8,8 @@ public class FileManagerService: ObservableObject { public typealias ProgressHandler = (Double) -> Void let downloadManager = DownloadManager() - + private var downloadedFilesCache: [URL: Bool] = [:] + @Injected private var errorLogger: ErrorReporting private var cancels: Set = [] @@ -27,8 +28,13 @@ public class FileManagerService: ObservableObject { public func fileExists(for url: URL) -> Bool { do { + if let cached = downloadedFilesCache[url] { + return cached + } let url = try fileURL(forDownloadURL: url) - return FileManager.default.fileExists(atPath: url.path) + let fileExists = FileManager.default.fileExists(atPath: url.path) + downloadedFilesCache[url] = fileExists + return fileExists } catch { errorLogger.logError(error) return false @@ -36,6 +42,7 @@ public class FileManagerService: ObservableObject { } public func clearFiles() { + downloadedFilesCache.removeAll() let fileManager = FileManager.default guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return } @@ -51,6 +58,7 @@ public class FileManagerService: ObservableObject { } public func clearFile(at url: URL) async { + downloadedFilesCache[url] = nil await Task { do { try FileManager.default.removeItem(at: try fileURL(forDownloadURL: url)) @@ -64,6 +72,7 @@ public class FileManagerService: ObservableObject { public func download(url: URL, progressHandler: @escaping ProgressHandler) async throws { progressHandler(0) try await downloadManager.download(url: url, progressHandler: progressHandler) + downloadedFilesCache[url] = true } public func cancelDownload(url: URL) { diff --git a/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift b/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift index 1e6daada..39b62ad5 100644 --- a/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift +++ b/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift @@ -5,15 +5,15 @@ import shared class SentryErrorReporter: ErrorReporting { func logError(_ error: String) { - print("ERROR: \(error)") #if !DEBUG + print("ERROR: \(error)") SentrySDK.capture(error: error) #endif } func logError(_ error: Error) { - print("ERROR: \(error.kmmException?.description() ?? error)") #if !DEBUG + print("ERROR: \(error.kmmException?.description() ?? error)") SentrySDK.capture(error: error.kmmException ?? error) #endif } diff --git a/iosApp/Modules/Screens/Main/UI/MainView.swift b/iosApp/Modules/Screens/Main/UI/MainView.swift index 45e1fd34..b9ad0892 100644 --- a/iosApp/Modules/Screens/Main/UI/MainView.swift +++ b/iosApp/Modules/Screens/Main/UI/MainView.swift @@ -21,6 +21,8 @@ public struct MainView: View { @State private var showDebugView: Bool = false @State private var tab: MainViewModelTab = .library + private let keyboardObserver = KeyboardObserver() + public var body: some View { GeometryReader { geometry in if viewModel.shouldShowLogin { @@ -34,7 +36,7 @@ public struct MainView: View { .overlay { Spacer() miniPlayerView - .offset(x: 0, y: -geometry.safeAreaInsets.bottom+1) + .offset(x: 0, y: keyboardObserver.isKeyboardShown ? 0 : -geometry.safeAreaInsets.bottom+1) .transition(.move(edge: .bottom)) } .transition(.move(edge: .bottom)) From ead3a6666de47b49770fea1a2a6463df4eaff010 Mon Sep 17 00:00:00 2001 From: Gohan Date: Tue, 12 Nov 2024 13:25:57 -0600 Subject: [PATCH 2/2] Record Store Integration (#335) * Record Store Integration * Added feature flag * update record store feature flag * PR Feedback --- .../src/main/java/io/newm/HomeActivity.kt | 26 +++++++-- .../main/java/io/newm/NewmAppComposable.kt | 19 ++++++- .../java/io/newm/di/android/Dependencies.kt | 7 +++ .../src/main/java/io/newm/screens/Screen.kt | 2 + .../screens/library/NFTLibraryScreenUi.kt | 4 +- .../library/screens/EmptyWalletScreen.kt | 1 - .../io/newm/screens/profile/view/ProfileUi.kt | 2 +- .../recordstore/RecordStorePresenter.kt | 46 ++++++++++++++++ .../recordstore/RecordStoreScreenUi.kt | 55 +++++++++++++++++++ .../screens/recordstore/RecordStoreState.kt | 12 ++++ .../screens/recordstore/RecordStoreUiEvent.kt | 5 ++ .../newm/utils/AndroidFeatureFlagManager.kt | 1 - .../res/drawable/ic_recordstore_active.xml | 25 +++++++++ .../resources/src/main/res/values/strings.xml | 1 + android/core/ui-utils/build.gradle.kts | 3 +- .../java/io/newm/core/ui/utils/ErrorScreen.kt | 14 +++-- .../newm/core/ui/webview/FullScreenWebView.kt | 47 ++++++++++++++++ gradle/libs.versions.toml | 2 + .../public/analytics/events/AppScreens.kt | 1 + .../public/featureflags/FeatureFlags.kt | 4 +- 20 files changed, 259 insertions(+), 18 deletions(-) create mode 100644 android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStorePresenter.kt create mode 100644 android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreScreenUi.kt create mode 100644 android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreState.kt create mode 100644 android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreUiEvent.kt create mode 100644 android/core/resources/src/main/res/drawable/ic_recordstore_active.xml create mode 100644 android/core/ui-utils/src/main/java/io/newm/core/ui/webview/FullScreenWebView.kt diff --git a/android/app-newm/src/main/java/io/newm/HomeActivity.kt b/android/app-newm/src/main/java/io/newm/HomeActivity.kt index c5b6df62..9cf50183 100644 --- a/android/app-newm/src/main/java/io/newm/HomeActivity.kt +++ b/android/app-newm/src/main/java/io/newm/HomeActivity.kt @@ -27,6 +27,9 @@ import io.newm.screens.library.NFTLibraryState import io.newm.screens.profile.edit.ProfileEditPresenter import io.newm.screens.profile.edit.ProfileEditUi import io.newm.screens.profile.edit.ProfileEditUiState +import io.newm.screens.recordstore.RecordStorePresenter +import io.newm.screens.recordstore.RecordStoreScreenUi +import io.newm.screens.recordstore.RecordStoreState import io.newm.screens.profile.view.ProfilePresenter import io.newm.screens.profile.view.ProfileUi import io.newm.screens.profile.view.ProfileUiState @@ -50,10 +53,6 @@ class HomeActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { WindowCompat.setDecorFitsSystemWindows(window, false) - if(featureFlagManager.isEnabled(FeatureFlags.MarketPlace)) { - // TODO show/hide marketplace - logger.info("HomeActivity", "MarketPlace feature is enabled") - } super.onCreate(savedInstanceState) setContent { NewmTheme(darkTheme = true) { @@ -68,7 +67,10 @@ class HomeActivity : ComponentActivity() { eventLogger ) } else { - NewmApp(logger, eventLogger) + NewmApp( + logger = logger, + eventLogger = eventLogger, + showRecordStore = featureFlagManager.isEnabled(FeatureFlags.ShowRecordStore)) } } } @@ -93,6 +95,14 @@ class HomeActivity : ComponentActivity() { ) } + is Screen.RecordStore -> ui { state, modifier -> + RecordStoreScreenUi( + state = state, + modifier = modifier, + eventLogger = eventLogger + ) + } + is NFTLibrary -> ui { state, modifier -> NFTLibraryScreenUi( state = state, @@ -138,6 +148,12 @@ class HomeActivity : ComponentActivity() { ) }.value + is Screen.RecordStore -> inject { + parametersOf( + navigator + ) + }.value + is Screen.EditProfile -> inject { parametersOf( navigator diff --git a/android/app-newm/src/main/java/io/newm/NewmAppComposable.kt b/android/app-newm/src/main/java/io/newm/NewmAppComposable.kt index bffa2ab0..b0336695 100644 --- a/android/app-newm/src/main/java/io/newm/NewmAppComposable.kt +++ b/android/app-newm/src/main/java/io/newm/NewmAppComposable.kt @@ -89,6 +89,7 @@ val initialScreen = Screen.NFTLibrary internal fun NewmApp( logger: NewmAppLogger, eventLogger: NewmAppEventLogger, + showRecordStore: Boolean ) { val context = LocalContext.current val backstack = rememberSaveableBackStack { @@ -166,6 +167,7 @@ internal fun NewmApp( NewmBottomNavigation( currentRootScreen = currentRootScreen, eventLogger = eventLogger, + showRecordStore = showRecordStore, onNavigationSelected = { circuitNavigator.resetRoot(it) } @@ -188,6 +190,7 @@ internal fun NewmApp( internal fun NewmBottomNavigation( currentRootScreen: CircuitScreen?, eventLogger: NewmAppEventLogger, + showRecordStore: Boolean, onNavigationSelected: (Screen) -> Unit ) { Column(Modifier.height(76.dp)) { @@ -209,6 +212,20 @@ internal fun NewmBottomNavigation( onNavigationSelected(Screen.NFTLibrary) }, ) + if (showRecordStore) { + HomeBottomNavigationItem( + selected = currentRootScreen == Screen.RecordStore, + iconResId = R.drawable.ic_recordstore_active, + labelResId = R.string.record_store, + selectedIconBrush = AccountIconGradient, + selectedLabelColor = DarkPink, + onClick = { + eventLogger.logClickEvent(AppScreens.RecordStoreScreen.RECORD_STORE_BUTTON) + eventLogger.logPageLoad(AppScreens.RecordStoreScreen.name) + onNavigationSelected(Screen.RecordStore) + }, + ) + } HomeBottomNavigationItem( selected = currentRootScreen == Screen.UserAccount, iconResId = R.drawable.ic_profile, @@ -228,7 +245,7 @@ internal fun NewmBottomNavigation( @Preview(showBackground = true) @Composable fun BottomNavigationBarPreview() { - NewmBottomNavigation(Screen.NFTLibrary, NewmAppEventLogger()) {} + NewmBottomNavigation(Screen.NFTLibrary, NewmAppEventLogger(), showRecordStore = false) {} } // Based on content from: https://github.com/wlara/android-next-gen/blob/main/app/src/main/java/com/github/wlara/nextgen/ui/home/HomeScreen.kt diff --git a/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt b/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt index d864c5b4..d3472bef 100644 --- a/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt +++ b/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt @@ -18,6 +18,7 @@ import io.newm.screens.forceupdate.ForceAppUpdatePresenter import io.newm.screens.library.NFTLibraryPresenter import io.newm.screens.profile.edit.ProfileEditPresenter import io.newm.screens.profile.view.ProfilePresenter +import io.newm.screens.recordstore.RecordStorePresenter import io.newm.shared.config.NewmSharedBuildConfig import io.newm.shared.public.featureflags.FeatureFlagManager import io.newm.utils.AndroidFeatureFlagManager @@ -88,6 +89,12 @@ val viewModule = module { get() ) } + factory { params -> + RecordStorePresenter( + params.get(), + get(), + ) + } factory { params -> ProfileEditPresenter( params.get(), diff --git a/android/app-newm/src/main/java/io/newm/screens/Screen.kt b/android/app-newm/src/main/java/io/newm/screens/Screen.kt index f9a9cb89..7277c576 100644 --- a/android/app-newm/src/main/java/io/newm/screens/Screen.kt +++ b/android/app-newm/src/main/java/io/newm/screens/Screen.kt @@ -10,6 +10,8 @@ sealed class Screen(val screenName: String) : CircuitScreen { data object UserAccount : Screen(screenName = AppScreens.AccountScreen.name) + data object RecordStore : Screen(screenName = AppScreens.RecordStoreScreen.name) + data object NFTLibrary : Screen(screenName = AppScreens.NFTLibraryScreen.name) data object Welcome : Screen(screenName = AppScreens.WelcomeScreen.name) diff --git a/android/app-newm/src/main/java/io/newm/screens/library/NFTLibraryScreenUi.kt b/android/app-newm/src/main/java/io/newm/screens/library/NFTLibraryScreenUi.kt index 41d8804a..c7007e18 100644 --- a/android/app-newm/src/main/java/io/newm/screens/library/NFTLibraryScreenUi.kt +++ b/android/app-newm/src/main/java/io/newm/screens/library/NFTLibraryScreenUi.kt @@ -127,7 +127,9 @@ fun NFTLibraryScreenUi( LaunchedEffect(Unit) { eventLogger.logPageLoad(AppScreens.ErrorScreen.name) } - ErrorScreen(state.message) + ErrorScreen( + title = "Oops, something went wrong!", + message =state.message) } is NFTLibraryState.Content -> { diff --git a/android/app-newm/src/main/java/io/newm/screens/library/screens/EmptyWalletScreen.kt b/android/app-newm/src/main/java/io/newm/screens/library/screens/EmptyWalletScreen.kt index 98418428..592c3ae7 100644 --- a/android/app-newm/src/main/java/io/newm/screens/library/screens/EmptyWalletScreen.kt +++ b/android/app-newm/src/main/java/io/newm/screens/library/screens/EmptyWalletScreen.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext diff --git a/android/app-newm/src/main/java/io/newm/screens/profile/view/ProfileUi.kt b/android/app-newm/src/main/java/io/newm/screens/profile/view/ProfileUi.kt index a6f17592..d2e19319 100644 --- a/android/app-newm/src/main/java/io/newm/screens/profile/view/ProfileUi.kt +++ b/android/app-newm/src/main/java/io/newm/screens/profile/view/ProfileUi.kt @@ -113,7 +113,7 @@ private val disconnectWalletButtonTextGradient = fun ProfileUi( state: ProfileUiState, modifier: Modifier = Modifier, - eventLogger : NewmAppEventLogger + eventLogger: NewmAppEventLogger ) { when (state) { ProfileUiState.Loading -> LoadingScreen() diff --git a/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStorePresenter.kt b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStorePresenter.kt new file mode 100644 index 00000000..e2e2caad --- /dev/null +++ b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStorePresenter.kt @@ -0,0 +1,46 @@ +package io.newm.screens.recordstore + +import android.content.Context +import android.net.ConnectivityManager +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.runtime.snapshotFlow +import androidx.compose.ui.platform.LocalContext +import com.slack.circuit.runtime.Navigator +import com.slack.circuit.runtime.presenter.Presenter +import io.newm.screens.profile.view.ProfileUiState +import io.newm.shared.public.analytics.NewmAppEventLogger +import io.newm.shared.public.analytics.events.AppScreens +import io.newm.shared.public.usecases.HasWalletConnectionsUseCase +import io.newm.shared.public.usecases.UserDetailsUseCase +import kotlinx.coroutines.flow.distinctUntilChanged + +class RecordStorePresenter( + private val navigator: Navigator, + private val eventLogger: NewmAppEventLogger +) : Presenter { + @Composable + override fun present(): RecordStoreState { + val context = LocalContext.current + val connectivityManager = remember { + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + } + val isNetworkAvailable by remember { + mutableStateOf(connectivityManager.activeNetwork != null) + } + return when { + !isNetworkAvailable -> RecordStoreState.Error + else -> { + eventLogger.logPageLoad(AppScreens.RecordStoreScreen.name) + RecordStoreState.Content( + eventSink = {} + ) + } + } + } +} diff --git a/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreScreenUi.kt b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreScreenUi.kt new file mode 100644 index 00000000..b0b4d3bc --- /dev/null +++ b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreScreenUi.kt @@ -0,0 +1,55 @@ +package io.newm.screens.recordstore + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag +import io.newm.core.ui.LoadingScreen +import io.newm.core.ui.utils.ErrorScreen +import io.newm.core.ui.webview.FullScreenWebView +import io.newm.screens.library.TAG_NFT_LIBRARY_SCREEN +import io.newm.shared.public.analytics.NewmAppEventLogger +import io.newm.shared.public.analytics.events.AppScreens + +private const val RECORD_STORE_URL = "https://recordstore.newm.io/" + +@Composable +fun RecordStoreScreenUi( + modifier: Modifier = Modifier, + state: RecordStoreState, + eventLogger: NewmAppEventLogger +) { + val context = LocalContext.current + Column( + modifier = modifier + .fillMaxSize() + .statusBarsPadding() + .testTag(TAG_NFT_LIBRARY_SCREEN), + ) { + when (state) { + is RecordStoreState.Content -> { + FullScreenWebView(context, RECORD_STORE_URL) + } + + RecordStoreState.Loading -> { + LaunchedEffect(Unit) { + eventLogger.logPageLoad(AppScreens.LoadingScreen.name) + } + LoadingScreen() + } + + RecordStoreState.Error -> { + ErrorScreen( + title = "Connection Lost", + message = "It looks like you're not connected to the internet. Please check your connection and try again." + ) + } + } + } +} + diff --git a/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreState.kt b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreState.kt new file mode 100644 index 00000000..411035e4 --- /dev/null +++ b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreState.kt @@ -0,0 +1,12 @@ +package io.newm.screens.recordstore + +import com.slack.circuit.runtime.CircuitUiState + + +sealed class RecordStoreState : CircuitUiState { + data object Loading : RecordStoreState() + data class Content( + val eventSink: (RecordStoreUiEvent) -> Unit, + ) : RecordStoreState() + data object Error : RecordStoreState() +} diff --git a/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreUiEvent.kt b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreUiEvent.kt new file mode 100644 index 00000000..74a279f9 --- /dev/null +++ b/android/app-newm/src/main/java/io/newm/screens/recordstore/RecordStoreUiEvent.kt @@ -0,0 +1,5 @@ +package io.newm.screens.recordstore + +import com.slack.circuit.runtime.CircuitUiEvent + +sealed interface RecordStoreUiEvent : CircuitUiEvent diff --git a/android/app-newm/src/main/java/io/newm/utils/AndroidFeatureFlagManager.kt b/android/app-newm/src/main/java/io/newm/utils/AndroidFeatureFlagManager.kt index dba60a0b..ecbd4dc0 100644 --- a/android/app-newm/src/main/java/io/newm/utils/AndroidFeatureFlagManager.kt +++ b/android/app-newm/src/main/java/io/newm/utils/AndroidFeatureFlagManager.kt @@ -42,7 +42,6 @@ class AndroidFeatureFlagManager( override suspend fun setUser(user: User) { val ldContext = LDContext.builder(ContextKind.DEFAULT, user.id) .set("email", user.email) - .name(user.nickname ?: user.email) .build() client.identify(ldContext) diff --git a/android/core/resources/src/main/res/drawable/ic_recordstore_active.xml b/android/core/resources/src/main/res/drawable/ic_recordstore_active.xml new file mode 100644 index 00000000..a4d48f3d --- /dev/null +++ b/android/core/resources/src/main/res/drawable/ic_recordstore_active.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/core/resources/src/main/res/values/strings.xml b/android/core/resources/src/main/res/values/strings.xml index aa599bcd..9ea05218 100644 --- a/android/core/resources/src/main/res/values/strings.xml +++ b/android/core/resources/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ Account + RecordStore NEWM %s Songs How can I connect a wallet? diff --git a/android/core/ui-utils/build.gradle.kts b/android/core/ui-utils/build.gradle.kts index 3f506ddf..9da1217f 100644 --- a/android/core/ui-utils/build.gradle.kts +++ b/android/core/ui-utils/build.gradle.kts @@ -28,9 +28,10 @@ android { dependencies { api(project(Modules.coreResources)) + implementation(libs.androidx.browser) implementation(libs.androidx.core.ktx) implementation(libs.androidx.material) implementation(libs.androidx.material.icons.extended) - implementation(project(Modules.coreTheme)) implementation(project(":shared")) + implementation(project(Modules.coreTheme)) } diff --git a/android/core/ui-utils/src/main/java/io/newm/core/ui/utils/ErrorScreen.kt b/android/core/ui-utils/src/main/java/io/newm/core/ui/utils/ErrorScreen.kt index d420e5e8..9671dd20 100644 --- a/android/core/ui-utils/src/main/java/io/newm/core/ui/utils/ErrorScreen.kt +++ b/android/core/ui-utils/src/main/java/io/newm/core/ui/utils/ErrorScreen.kt @@ -6,28 +6,32 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.newm.shared.public.analytics.NewmAppEventLogger @Composable fun ErrorScreen( - errorMessage: String + title: String, + message: String ) { Box( contentAlignment = Alignment.Center, - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxSize() + .padding(16.dp) ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( - text = "Oops, something went wrong!", - style = MaterialTheme.typography.h6, + text = title, + style = MaterialTheme.typography.h4, textAlign = TextAlign.Center, modifier = Modifier.padding(16.dp) ) Spacer(modifier = Modifier.height(8.dp)) Text( - text = errorMessage, + text = message, style = MaterialTheme.typography.body1, textAlign = TextAlign.Center, modifier = Modifier.padding(horizontal = 16.dp) diff --git a/android/core/ui-utils/src/main/java/io/newm/core/ui/webview/FullScreenWebView.kt b/android/core/ui-utils/src/main/java/io/newm/core/ui/webview/FullScreenWebView.kt new file mode 100644 index 00000000..c9507513 --- /dev/null +++ b/android/core/ui-utils/src/main/java/io/newm/core/ui/webview/FullScreenWebView.kt @@ -0,0 +1,47 @@ +package io.newm.core.ui.webview + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView + +@SuppressLint("SetJavaScriptEnabled") +@Composable +fun FullScreenWebView(context: Context, url: String) { + AndroidView( + factory = { ctx -> + WebView(ctx).apply { + webViewClient = object : WebViewClient() { + override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { + val currentUrl = request?.url.toString() + return if (currentUrl.startsWith("https://newm.io/")) { + // Load the URL in the current WebView + false + } else { + // Open URLs that don't start with "https://newm.io/" in an external browser or another WebView + launchExternalUrl(context, currentUrl) + true + } + } + } + + settings.javaScriptEnabled = true + + loadUrl(url) + } + }, + modifier = Modifier.fillMaxSize() + ) +} + +fun launchExternalUrl(context: Context, url: String) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + context.startActivity(intent) +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 800ed823..c7823147 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ androidx-espresso-core = "3.6.1" androidx-material = "1.12.0" androidx-test-junit = "1.2.1" barcodeScanning = "17.2.0" +browser = "1.8.0" cameraView = "1.4.0-beta02" circuitFoundation = "0.17.0" coil = "2.6.0" @@ -50,6 +51,7 @@ uiUtil = "1.6.8" [libraries] android-driver = { module = "com.squareup.sqldelight:android-driver", version.ref = "coroutinesExtensions" } +androidx-browser = { module = "androidx.browser:browser", version.ref = "browser" } androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraView" } androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraView" } androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraView" } diff --git a/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/events/AppScreens.kt b/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/events/AppScreens.kt index 91df0aa2..d3c97a8b 100644 --- a/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/events/AppScreens.kt +++ b/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/events/AppScreens.kt @@ -99,6 +99,7 @@ object AppScreens { object RecordStoreScreen : ScreenEvents { override val name = "Record Store" + const val RECORD_STORE_BUTTON: String = "RecordStore" } object ResetPasswordEnterCodeScreen : ScreenEvents { diff --git a/shared/src/commonMain/kotlin/io.newm.shared/public/featureflags/FeatureFlags.kt b/shared/src/commonMain/kotlin/io.newm.shared/public/featureflags/FeatureFlags.kt index e09070db..a31fdb56 100644 --- a/shared/src/commonMain/kotlin/io.newm.shared/public/featureflags/FeatureFlags.kt +++ b/shared/src/commonMain/kotlin/io.newm.shared/public/featureflags/FeatureFlags.kt @@ -5,7 +5,7 @@ interface FeatureFlag { } object FeatureFlags { - object MarketPlace : FeatureFlag { - override val key = "streams-marketplace" + object ShowRecordStore : FeatureFlag { + override val key = "mobile-app-show-recordstore" } } \ No newline at end of file