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
16 changes: 8 additions & 8 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

import {ApolloProvider} from '@apollo/client/react';
import {
DarkTheme,
DefaultTheme,
NavigationContainer,
useNavigationContainerRef,
} from '@react-navigation/native';
Expand All @@ -25,6 +23,7 @@ import {
usePendingShare,
useShareListener,
} from './src/share/shareImport';
import {useNavigationTheme} from './src/theme/navigationTheme';
import {useTheme} from './src/theme/useTheme';

function App() {
Expand All @@ -44,6 +43,7 @@ function App() {

function AppContent() {
const theme = useTheme();
const navigationTheme = useNavigationTheme();
const hydrated = useAuthHydrated();
const auth = useAuth();
const navigationRef = useNavigationContainerRef<RootStackParamList>();
Expand Down Expand Up @@ -76,9 +76,9 @@ function AppContent() {
style={[
styles.container,
styles.splash,
{backgroundColor: theme.background},
{backgroundColor: theme.canvas},
]}>
<ActivityIndicator />
<ActivityIndicator color={theme.muted} />
</View>
);
}
Expand All @@ -87,14 +87,14 @@ function AppContent() {
return <LoginScreen />;
}

// Match React Navigation's container background to the active scheme so the
// gap shown during the slide transition between screens isn't a white flash
// in dark mode.
// The navigator's own theme is built from our tokens, so the gap shown during
// the slide transition between screens is the canvas rather than React
// Navigation's default background.
return (
<NavigationContainer
ref={navigationRef}
onReady={routePendingShare}
theme={theme.scheme === 'dark' ? DarkTheme : DefaultTheme}>
theme={navigationTheme}>
<RootNavigator />
</NavigationContainer>
);
Expand Down
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions android/app/src/main/res/values-night/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
The dark half of the palette. Same names as values/colors.xml, so nothing
that reads them has to branch on the appearance — the resource system picks
the right one, which is the platform's version of one declaration per role.

ic_launcher_background isn't redeclared: a launcher icon is drawn by the
launcher, not the app, and has one appearance.
-->
<resources>
<color name="canvas">#1a1817</color>
<color name="ink">#ebe7e2</color>
<color name="accent">#c47a52</color>
<color name="control_highlight">#4d3a3532</color>
</resources>
23 changes: 22 additions & 1 deletion android/app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
The light half of the culpeo palette, mirroring src/theme/colors.ts.

Android supplies its own colors to things JS never touches — the text caret
and selection handles, switch and checkbox tints, ripples, and the window
behind the app before React Native has drawn anything. Left alone those come
from the Material theme and are blue. Only the roles the platform actually
reads are declared here; everything else lives in the TypeScript palette,
which stays the single source for the app's own UI.

values-night/colors.xml holds the dark values under the same names.
-->
<resources>
<color name="ic_launcher_background">#2A2A2E</color>
<color name="canvas">#faf8f5</color>
<color name="ink">#221f1d</color>
<color name="accent">#a8623d</color>
<!-- `line` at 30%, matching the lineFillStrong token used for pressed
states in JS, so a native ripple lands at the same weight. -->
<color name="control_highlight">#4d8c8478</color>

<!-- Warm near-black, so the fox reads against it and the icon doesn't sit
at the cool hue the rest of the palette moved away from. -->
<color name="ic_launcher_background">#1a1817</color>
</resources>
25 changes: 23 additions & 2 deletions android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
<resources>

<!-- Base application theme. -->
<!--
Base application theme.

The color items here exist because the platform draws several things the
JS palette can't reach: the text caret and selection handles, the tint on
a native switch or checkbox, touch ripples, and the window itself before
React Native has rendered a frame. Every one of those defaults to blue via
AppCompat, and no amount of grepping our own source would have turned them
up. DayNight resolves each against values-night/ in dark mode.
-->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>

<!-- Selection handles, the caret, and any control that draws itself as
"active" — the same sanctioned accent use as a focus ring. -->
<item name="colorAccent">@color/accent</item>
<item name="colorPrimary">@color/accent</item>
<item name="colorPrimaryDark">@color/canvas</item>
<item name="colorControlActivated">@color/accent</item>
<item name="colorControlNormal">@color/ink</item>
<item name="colorControlHighlight">@color/control_highlight</item>

<!-- Painted before the first JS frame, so a cold start opens on the
canvas rather than flashing the platform's white or black. -->
<item name="android:windowBackground">@color/canvas</item>
</style>

</resources>
15 changes: 10 additions & 5 deletions src/account/AccountMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,18 @@ export function AccountMenu() {

const makeStyles = (theme: Theme) =>
StyleSheet.create({
// Neutral: an avatar is an identity marker, not an action, and it sits over
// the map where an accent fill would pull against the pins. Matches the
// search button opposite it — both are map chrome.
avatarButton: {
position: 'absolute',
right: 16,
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: theme.accent,
backgroundColor: theme.surface,
borderWidth: 1,
borderColor: theme.lineControl,
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#000',
Expand All @@ -80,13 +85,13 @@ const makeStyles = (theme: Theme) =>
},
avatarPressed: {opacity: 0.8},
avatarText: {
color: '#ffffff',
color: theme.ink,
fontSize: 16,
fontWeight: '600',
},
sheetEmail: {
fontSize: 12,
color: theme.textSecondary,
color: theme.muted,
paddingHorizontal: 12,
paddingBottom: 8,
},
Expand All @@ -96,10 +101,10 @@ const makeStyles = (theme: Theme) =>
borderRadius: 8,
},
sheetItemPressed: {
backgroundColor: theme.surfaceMuted,
backgroundColor: theme.lineFill,
},
sheetItemText: {
fontSize: 16,
color: theme.textPrimary,
color: theme.ink,
},
});
36 changes: 13 additions & 23 deletions src/auth/LoginScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import {
Pressable,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import {LoginDocument} from '../graphql/__generated__/types';
import type {Theme} from '../theme/colors';
import {useTheme} from '../theme/useTheme';
import {TextField} from '../ui/TextField';
import {clearSecurityWarning, useSecurityWarning} from './authClient';
import {tokenStore} from './tokenStore';

Expand Down Expand Up @@ -85,10 +85,8 @@ export function LoginScreen() {
<View style={styles.container}>
<Text style={styles.title}>Sign in to Culpeos</Text>

<TextInput
style={styles.input}
<TextField
placeholder="Email"
placeholderTextColor={theme.textTertiary}
autoCapitalize="none"
autoCorrect={false}
autoComplete="email"
Expand All @@ -97,10 +95,8 @@ export function LoginScreen() {
onChangeText={setEmail}
editable={!loading}
/>
<TextInput
style={styles.input}
<TextField
placeholder="Password"
placeholderTextColor={theme.textTertiary}
autoCapitalize="none"
autoCorrect={false}
autoComplete="password"
Expand All @@ -126,7 +122,7 @@ export function LoginScreen() {
pressed && canSubmit && styles.buttonPressed,
]}>
{loading ? (
<ActivityIndicator color={theme.onAction} />
<ActivityIndicator color={theme.onAccent} />
) : (
<Text style={styles.buttonText}>Log in</Text>
)}
Expand All @@ -138,7 +134,7 @@ export function LoginScreen() {

const makeStyles = (theme: Theme) =>
StyleSheet.create({
flex: {flex: 1, backgroundColor: theme.background},
flex: {flex: 1, backgroundColor: theme.canvas},
container: {
flex: 1,
padding: 24,
Expand All @@ -150,30 +146,24 @@ const makeStyles = (theme: Theme) =>
fontWeight: '600',
marginBottom: 16,
textAlign: 'center',
color: theme.textPrimary,
},
input: {
borderWidth: 1,
borderColor: theme.border,
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 12,
fontSize: 16,
color: theme.textPrimary,
color: theme.ink,
},
error: {
color: theme.error,
color: theme.danger,
fontSize: 14,
textAlign: 'center',
},
warning: {
color: theme.error,
color: theme.danger,
fontSize: 13,
textAlign: 'center',
fontStyle: 'italic',
},
// The screen's one CTA, so this is where accent is spent. The pressed
// state is the fill at 85%, which is what the opacity below amounts to
// over the canvas.
button: {
backgroundColor: theme.action,
backgroundColor: theme.accent,
borderRadius: 8,
paddingVertical: 14,
alignItems: 'center',
Expand All @@ -182,7 +172,7 @@ const makeStyles = (theme: Theme) =>
buttonDisabled: {opacity: 0.5},
buttonPressed: {opacity: 0.85},
buttonText: {
color: theme.onAction,
color: theme.onAccent,
fontSize: 16,
fontWeight: '600',
},
Expand Down
Loading
Loading