-
Notifications
You must be signed in to change notification settings - Fork 178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/compose cleanup #1543
Feature/compose cleanup #1543
Conversation
Updated the typography in an composition local so it can be accessed from anyehere in compose and matching the figma declarations.
Unifying spacing values and reusing common ones Identified spacing issues and inconsistencies in design and figma
app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt
Show resolved
Hide resolved
Discussed with QA to make sure the 'new message' screen should indeed behave as the other screens and use disabled state instead of disappearing
Re-using our new radio buttons in disappearing messages Tweaking UI to match designs
onView(withId(R.id.displayNameEditText)).perform(ViewActions.typeText("test-user123")) | ||
onView(withId(R.id.registerButton)).perform(ViewActions.click()) | ||
objectFromDesc(R.string.displayNameEnter).click() | ||
device.pressKeyCode(65) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment like "Enter the dummy name 'ABC'" (is it sad that I remember some ASCII codes? Yes it is, haha)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is code from the onboarding PR. I suggest looking at the commits instead to see the work from this PR.
...java/org/thoughtcrime/securesms/conversation/disappearingmessages/ui/DisappearingMessages.kt
Show resolved
Hide resolved
@@ -5,7 +5,7 @@ import network.loki.messenger.libsession_util.util.ExpiryMode | |||
import org.thoughtcrime.securesms.ui.GetString | |||
import org.thoughtcrime.securesms.ui.RadioOption | |||
|
|||
typealias ExpiryOptionsCard = OptionsCard<ExpiryMode> | |||
typealias ExpiryOptionsCard = OptionsCardData<ExpiryMode> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, maybe suffix the typealias too?
* Some behaviour is hardcoded to cater for legacy usage of people with themes already set | ||
* But future themes will be picked and set directly from the "Appearance" screen | ||
*/ | ||
@Composable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is only @Composable
because of the call to isSystemInDarkTheme()
.
Perhaps extract everything else to a non-composable? or use val lightDarkColors: StateFlow<LightDarkColors>
from this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is always called from Compose so there is no issue with it being Composable. You do need to know what the system is currently set to isSystemInDarkTheme
in addition to the preferences chosen by the user.
It can be cleaned up once we have the sharedPref code if that manages to help.
val LocalColors = compositionLocalOf <ThemeColors> { ClassicDark() } | ||
val LocalType = compositionLocalOf { sessionTypography } | ||
|
||
var selectedTheme: ThemeColors? = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
top-level var is probably a code smell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I don't love this but wanted something temporary while we get a root composable.
Maybe I can tweak the sharedPrefs flow once they're added in. Though I wouldn't move the compose data outside the app module where it belongs.
} | ||
|
||
fun setNewFollowSystemSettings(followSystemSettings: Boolean) { | ||
prefs.setFollowSystemSettings(followSystemSettings) | ||
_uiState.value = prefs.themeState() | ||
|
||
// force compose to refresh its style reference | ||
selectedTheme = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't force currently composed layouts to refresh, it'll just refresh on a subsequent call.
So this Activity (and all open activities in the back stack) won't update.
Maybe this PR will help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a fully compose setup the value will come from a stateflow, directly passed to the root composable.
For now it works, even with the back stack, as going back to a previous fragment/activity calls the composable again which will check the value.
I'm a bit wary of moving all our composable theming in a module where it doesn't belong...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah... EmptyView
is recomposed when you return to HomeActivity
, I don't know why, I don't know if it's reliable.
Yeah, we don't have to move compose components. We can keep compose in this module, We'd just probably need to make another object... or just @Provides
this StateFlow<Theme>
with Dagger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The currently open activity won't update though. It doesn't have any compose now...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once we move to a fully composed screen the theme will be passed via a Flow so this won't be an issue anymore.
If we change this screen first before changing the full app we can make that flow available and collected at the root composable here to be applied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All looking fair and reasonable from my perspective - have added a couple of comments / suggestions for you to address as you see fit.
if (mnemonic.isEmpty()) throw DecodingError.InputTooShort | ||
if (words.isEmpty()) throw DecodingError.InputTooShort | ||
// Check preconditions | ||
if (words.size < 13) throw DecodingError.InputTooShort |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also add a check for if the mneumonic words is > 13 (i.e., additional superflous words)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an onboarding comment > @bemusementpark
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ummm... It's technically possibly valid
@@ -138,6 +138,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), | |||
// region Lifecycle | |||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { | |||
super.onCreate(savedInstanceState, isReady) | |||
|
|||
if (!isTaskRoot) { finish(); return } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially add a comment like "Prevent multiple instances of the app running at once"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an onboarding comment > @bemusementpark
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's worth a comment, as yeah, it's a lot more subtle than your suggestion ^^ :/
app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingViewModel.kt
Outdated
Show resolved
Hide resolved
state.cards.forEachIndexed { index, option -> | ||
OptionsCard(option, callbacks) | ||
|
||
// add spacing if not the last item | ||
if(index != state.cards.lastIndex){ | ||
Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
state.cards.forEachIndexed { index, option -> | |
OptionsCard(option, callbacks) | |
// add spacing if not the last item | |
if(index != state.cards.lastIndex){ | |
Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) | |
} | |
state.cards.forEachIndexed { index, option -> | |
if (index != 0) { | |
Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) | |
} | |
OptionsCard(option, callbacks) |
Column(modifier = Modifier.background(LocalColors.current.backgroundSecondary)) { | ||
Column(modifier = Modifier.background( | ||
LocalColors.current.backgroundSecondary, | ||
shape = MaterialTheme.shapes.small |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a small shape, it's a bottom sheet, it's large.
We shouldn't just use this because it has the same value.
We could do our own Shapes
object, but probably overkill atm, so just some extra val BottomSheetShape
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't large, it is the rounded corner value that we want, which in this case matches the small value that we have defined in the Theme
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm just saying it's a coincidence, that our theme at the moment uses the same shape for small and bottom sheets.
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.tooling.preview.PreviewParameter | ||
|
||
interface ThemeColors { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the inspiration for the name change?
We still have LocalColors
, which is longer than I like 😂 and the idiom is to name that the same as the class it provides.
If it's just because of the clash in this file, we could use a typealias MatColors = material.Colors
or whatever.
And then yeah, we can add those courtesies...
Colors {
companion object {
@Composable val primary get() = LocalColors.current.primary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I didn't think it was worth having yet another clashing class with the name Color
. There are already enough of these around...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it actually clashes, anywhere except the one function in this file.
} | ||
|
||
fun setNewFollowSystemSettings(followSystemSettings: Boolean) { | ||
prefs.setFollowSystemSettings(followSystemSettings) | ||
_uiState.value = prefs.themeState() | ||
|
||
// force compose to refresh its style reference | ||
selectedTheme = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The currently open activity won't update though. It doesn't have any compose now...
Button(onClick, ButtonType.Outline(LocalColors.current.primaryButtonFill), modifier, enabled, content = content) | ||
} | ||
|
||
@Composable fun SlimOutlineButton(onClick: () -> Unit, modifier: Modifier = Modifier, color: Color = LocalColors.current.text, enabled: Boolean = true, content: @Composable RowScope.() -> Unit) { | ||
@Composable fun SlimOutlineButton(modifier: Modifier = Modifier, color: Color = LocalColors.current.text, enabled: Boolean = true, onClick: () -> Unit, content: @Composable RowScope.() -> Unit) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably makes sense to follow the order of the base component unless there's a good reason, no?
@Composable
fun Button(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = ButtonDefaults.shape,
colors: ButtonColors = ButtonDefaults.buttonColors(),
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
border: BorderStroke? = null,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit
) {
I think I had the other method with onClick
at the end so you could use kotlin-trailing-lambda syntax, because there was no content
but we could change that one, as it's probably confusing to have a trailing-lambda that is a click listener when people expect it to be @Compasable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would have been great indeed but you matched all the other signature a different way so this was the only one that wasn't like the others.
Ideally we'd change all of them to match the original Button but that was quicker to at least have all our composable be the same...
fun TextSecurePreferences.getComposeTheme(): ThemeColors { | ||
val selectedTheme = getThemeStyle() | ||
|
||
// get the chosen primary color from the preferences | ||
val selectedPrimary = primaryColor() | ||
|
||
// create a theme set with the appropriate primary | ||
val colorSet = when(selectedTheme){ | ||
TextSecurePreferences.OCEAN_DARK, | ||
TextSecurePreferences.OCEAN_LIGHT -> ThemeColorSet( | ||
light = OceanLight(selectedPrimary), | ||
dark = OceanDark(selectedPrimary) | ||
) | ||
|
||
else -> ThemeColorSet( | ||
light = ClassicLight(selectedPrimary), | ||
dark = ClassicDark(selectedPrimary) | ||
) | ||
} | ||
|
||
// deliver the right set from the light/dark mode chosen | ||
val theme = when{ | ||
getFollowSystemSettings() -> if(isSystemInDarkTheme()) colorSet.dark else colorSet.light | ||
|
||
selectedTheme == TextSecurePreferences.CLASSIC_LIGHT || | ||
selectedTheme == TextSecurePreferences.OCEAN_LIGHT -> colorSet.light | ||
|
||
else -> colorSet.dark | ||
} | ||
|
||
return theme | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps simpler if we either delete ColorSet
, or defer some responsibility to it.
fun TextSecurePreferences.getComposeTheme(): ThemeColors { | |
val selectedTheme = getThemeStyle() | |
// get the chosen primary color from the preferences | |
val selectedPrimary = primaryColor() | |
// create a theme set with the appropriate primary | |
val colorSet = when(selectedTheme){ | |
TextSecurePreferences.OCEAN_DARK, | |
TextSecurePreferences.OCEAN_LIGHT -> ThemeColorSet( | |
light = OceanLight(selectedPrimary), | |
dark = OceanDark(selectedPrimary) | |
) | |
else -> ThemeColorSet( | |
light = ClassicLight(selectedPrimary), | |
dark = ClassicDark(selectedPrimary) | |
) | |
} | |
// deliver the right set from the light/dark mode chosen | |
val theme = when{ | |
getFollowSystemSettings() -> if(isSystemInDarkTheme()) colorSet.dark else colorSet.light | |
selectedTheme == TextSecurePreferences.CLASSIC_LIGHT || | |
selectedTheme == TextSecurePreferences.OCEAN_LIGHT -> colorSet.light | |
else -> colorSet.dark | |
} | |
return theme | |
} | |
@Composable | |
fun TextSecurePreferences.getComposeTheme(): ThemeColors { | |
val selectedTheme = getThemeStyle() | |
// get the chosen primary color from the preferences | |
val selectedPrimary = primaryColor() | |
// create a theme set with the appropriate primary | |
return (if (getFollowSystemSettings()) when (selectedTheme) { | |
OCEAN_DARK -> ::OceanDark | |
OCEAN_LIGHT -> ::OceanLight | |
CLASSIC_LIGHT -> ::ClassicLight | |
else -> ::ClassicDark | |
} else when (selectedTheme) { | |
OCEAN_DARK, OCEAN_LIGHT -> if (isSystemInDarkTheme()) ::OceanDark else ::OceanLight | |
else -> if (isSystemInDarkTheme()) ::ClassicDark else ::ClassicLight | |
})(selectedPrimary) | |
} |
Clean up work on our Compose classes.
Working on theming and overall standardising of UI elements.
Applying UI fixes where found.