Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.blackcandy.android.compose

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import org.blackcandy.android.R

@Composable
fun CustomErrorScreen(
errorDescription: String,
onRetry: () -> Unit,
onLogout: () -> Unit,
) {
Column(
modifier =
Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(dimensionResource(R.dimen.padding_medium)),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Icon(
painter = painterResource(R.drawable.baseline_warning_24),
contentDescription = null,
modifier =
Modifier
.size(dimensionResource(R.dimen.icon_size_large))
.padding(bottom = dimensionResource(R.dimen.padding_small)),
)

Text(
text = errorDescription,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
modifier =
Modifier
.padding(bottom = dimensionResource(R.dimen.padding_small)),
)

Row {
TextButton(onClick = onRetry) {
Text(stringResource(R.string.retry))
}

TextButton(onClick = onLogout) {
Text(
text = stringResource(R.string.logout),
color = MaterialTheme.colorScheme.error,
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.accompanist.themeadapter.material3.Mdc3Theme
import dev.hotwire.core.turbo.errors.HttpError
import dev.hotwire.core.turbo.errors.VisitError
import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink
import dev.hotwire.navigation.fragments.HotwireWebFragment
import kotlinx.coroutines.launch
import org.blackcandy.android.R
import org.blackcandy.android.compose.CustomErrorScreen
import org.blackcandy.android.utils.SnackbarUtil.Companion.showSnackbar
import org.blackcandy.shared.viewmodels.WebViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
Expand Down Expand Up @@ -53,4 +56,17 @@ open class WebFragment : HotwireWebFragment() {
super.onVisitErrorReceived(location, error)
}
}

override fun createErrorView(error: VisitError): View =
ComposeView(requireContext()).apply {
setContent {
Mdc3Theme {
CustomErrorScreen(
errorDescription = error.description().orEmpty(),
onRetry = { refresh() },
onLogout = { viewModel.logout() },
)
}
}
}
}
3 changes: 3 additions & 0 deletions androidApp/src/main/res/drawable/baseline_warning_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
</vector>
1 change: 1 addition & 0 deletions androidApp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<string name="delete">Delete</string>
<string name="empty_playlist_indication">Nothing to play here</string>
<string name="drag_handle">Drag Handle</string>
<string name="retry">Retry</string>
<string name="added_to_playlist">Added to playlist</string>
<plurals name="tracks_count">
<item quantity="one">%d track</item>
Expand Down
4 changes: 2 additions & 2 deletions iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nexport JAVA_HOME=\"/Applications/Android Studio.app/Contents/jbr/Contents/Home\"\ncd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n";
shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\n# Type a script or drag a script file from your workspace to insert its path.\nexport JAVA_HOME=\"/Applications/Android Studio.app/Contents/jbr/Contents/Home\"\ncd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode";
};
/* End PBXShellScriptBuildPhase section */

Expand Down Expand Up @@ -424,7 +424,7 @@
repositoryURL = "https://github.com/hotwired/hotwire-native-ios";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.2.2;
minimumVersion = "1.3.0-beta";
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions iosApp/iosApp/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
WebViewController(url: url)
}

Hotwire.config.makeCustomErrorView = { error, handler in
CustomErrorScreen(error: error, handler: handler)
}

Hotwire.registerBridgeComponents([
AccountComponent.self,
SearchComponent.self,
Expand Down
1 change: 0 additions & 1 deletion iosApp/iosApp/Bridge/AccountComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class AccountComponent: BridgeComponent {
default:
break
}

}

private func handleConnectEvent() {
Expand Down
13 changes: 12 additions & 1 deletion iosApp/iosApp/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,17 @@
}
}
},
"label.retry" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Retry"
}
}
}
},
"label.server_address" : {
"extractionState" : "manual",
"localizations" : {
Expand Down Expand Up @@ -300,4 +311,4 @@
}
},
"version" : "1.0"
}
}
41 changes: 41 additions & 0 deletions iosApp/iosApp/Views/CustomErrorScreen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import SwiftUI
import HotwireNative
import sharedKit

struct CustomErrorScreen: ErrorPresentableView {
private let viewModel: WebViewModel = KoinHelper().getWebViewModel()

let error: Error
let handler: ErrorPresenter.Handler?

var body: some View {
VStack(spacing: CustomStyle.spacing(.medium)) {
Image(systemName: "exclamationmark.triangle")
.customStyle(.largeSymbol)
.foregroundColor(.accentColor)

Text(error.localizedDescription)
.font(.body)
.multilineTextAlignment(.center)

HStack {
if let handler {
Button("label.retry") {
handler()
}
.customStyle(.mediumFont)
.buttonStyle(.bordered)
}

Button("label.logout", role: .destructive) {
viewModel.logout(onSuccess: {
changeRootViewController(viewController: LoginViewController())
})
}
.customStyle(.mediumFont)
.buttonStyle(.bordered)
}
}
.padding(CustomStyle.spacing(.extraWide))
}
}
Loading