Fixes: https://github.com/lesspass/lesspass/issues/677tags/mobile-v9.7.3
@@ -147,8 +147,8 @@ android { | |||||
applicationId "com.lesspass.android" | applicationId "com.lesspass.android" | ||||
minSdkVersion rootProject.ext.minSdkVersion | minSdkVersion rootProject.ext.minSdkVersion | ||||
targetSdkVersion rootProject.ext.targetSdkVersion | targetSdkVersion rootProject.ext.targetSdkVersion | ||||
versionCode 9007002 | |||||
versionName "9.7.2" | |||||
versionCode 9007003 | |||||
versionName "9.7.3" | |||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() | ||||
if (isNewArchitectureEnabled()) { | if (isNewArchitectureEnabled()) { | ||||
@@ -257,9 +257,8 @@ android { | |||||
def abi = output.getFilter(OutputFile.ABI) | def abi = output.getFilter(OutputFile.ABI) | ||||
if (abi != null) { // null for the universal-debug, universal-release variants | if (abi != null) { // null for the universal-debug, universal-release variants | ||||
output.versionCodeOverride = | output.versionCodeOverride = | ||||
defaultConfig.versionCode * 1000 + versionCodes.get(abi) | |||||
defaultConfig.versionCode * 100 + versionCodes.get(abi) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1,7 +1,7 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
<resources> | <resources> | ||||
<color name="statusBarColor">#2E2E2E</color> | |||||
<color name="colorPrimary">#F3F3F3</color> | |||||
<color name="backgroundColor">#4D4D4D</color> | |||||
<color name="navigationBarColor">#2E2E2E</color> | |||||
<color name="statusBarColor">#1b1b1f</color> | |||||
<color name="colorPrimary">#bfc2ff</color> | |||||
<color name="backgroundColor">#46464f</color> | |||||
<color name="navigationBarColor">#1b1b1f</color> | |||||
</resources> | </resources> |
@@ -1,7 +1,7 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
<resources> | <resources> | ||||
<color name="statusBarColor">#FFFFFF</color> | |||||
<color name="colorPrimary">#2E2E2E</color> | |||||
<color name="backgroundColor">#F3F3F3</color> | |||||
<color name="navigationBarColor">#FFFFFF</color> | |||||
<color name="statusBarColor">#fffbff</color> | |||||
<color name="colorPrimary">#4951c3</color> | |||||
<color name="backgroundColor">#e4e1ec</color> | |||||
<color name="navigationBarColor">#fffbff</color> | |||||
</resources> | </resources> |
@@ -529,7 +529,7 @@ | |||||
"$(inherited)", | "$(inherited)", | ||||
"@executable_path/Frameworks", | "@executable_path/Frameworks", | ||||
); | ); | ||||
MARKETING_VERSION = 9.7.2; | |||||
MARKETING_VERSION = 9.7.3; | |||||
OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
"$(inherited)", | "$(inherited)", | ||||
"-ObjC", | "-ObjC", | ||||
@@ -557,7 +557,7 @@ | |||||
"$(inherited)", | "$(inherited)", | ||||
"@executable_path/Frameworks", | "@executable_path/Frameworks", | ||||
); | ); | ||||
MARKETING_VERSION = 9.7.2; | |||||
MARKETING_VERSION = 9.7.3; | |||||
OTHER_LDFLAGS = ( | OTHER_LDFLAGS = ( | ||||
"$(inherited)", | "$(inherited)", | ||||
"-ObjC", | "-ObjC", | ||||
@@ -1,6 +1,6 @@ | |||||
{ | { | ||||
"name": "lesspass-mobile", | "name": "lesspass-mobile", | ||||
"version": "9.7.2", | |||||
"version": "9.7.3", | |||||
"description": "LessPass mobile application", | "description": "LessPass mobile application", | ||||
"license": "(MPL-2.0 OR GPL-3.0)", | "license": "(MPL-2.0 OR GPL-3.0)", | ||||
"author": "Guillaume Vincent <guillaume@oslab.fr>", | "author": "Guillaume Vincent <guillaume@oslab.fr>", | ||||
@@ -21,47 +21,47 @@ | |||||
}, | }, | ||||
"dependencies": { | "dependencies": { | ||||
"@react-native-community/async-storage": "^1.12.1", | "@react-native-community/async-storage": "^1.12.1", | ||||
"@react-navigation/bottom-tabs": "^6.4.0", | |||||
"@react-navigation/native": "^6.0.13", | |||||
"@react-navigation/native-stack": "^6.9.1", | |||||
"@react-navigation/stack": "^6.3.2", | |||||
"axios": "^0.27.2", | |||||
"fuzzysort": "^2.0.1", | |||||
"@react-navigation/bottom-tabs": "^6.5.2", | |||||
"@react-navigation/native": "^6.1.1", | |||||
"@react-navigation/native-stack": "^6.9.7", | |||||
"@react-navigation/stack": "^6.3.10", | |||||
"axios": "^1.2.1", | |||||
"fuzzysort": "^2.0.4", | |||||
"lesspass-fingerprint": "^9.1.9", | "lesspass-fingerprint": "^9.1.9", | ||||
"lesspass-render-password": "^9.1.9", | "lesspass-render-password": "^9.1.9", | ||||
"lodash": "^4.17.21", | "lodash": "^4.17.21", | ||||
"react": "18.2.0", | "react": "18.2.0", | ||||
"react-native": "0.70.6", | "react-native": "0.70.6", | ||||
"react-native-gesture-handler": "^2.7.1", | |||||
"react-native-gesture-handler": "^2.8.0", | |||||
"react-native-keychain": "^8.1.1", | "react-native-keychain": "^8.1.1", | ||||
"react-native-paper": "^4.12.5", | |||||
"react-native-paper": "^5.1.0", | |||||
"react-native-safe-area-context": "^4.4.1", | "react-native-safe-area-context": "^4.4.1", | ||||
"react-native-screens": "^3.18.2", | "react-native-screens": "^3.18.2", | ||||
"react-native-touch-id": "^4.4.1", | |||||
"react-native-touch-id": "https://github.com/lesspass/react-native-touch-id", | |||||
"react-native-vector-icons": "^9.2.0", | "react-native-vector-icons": "^9.2.0", | ||||
"react-redux": "^8.0.4", | |||||
"react-redux": "^8.0.5", | |||||
"redux": "^4.2.0", | "redux": "^4.2.0", | ||||
"redux-persist": "^6.0.0", | "redux-persist": "^6.0.0", | ||||
"redux-thunk": "^2.4.1" | |||||
"redux-thunk": "^2.4.2" | |||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
"@babel/core": "^7.19.6", | |||||
"@babel/runtime": "^7.12.5", | |||||
"@react-native-community/eslint-config": "^3.1.0", | |||||
"@tsconfig/react-native": "^2.0.2", | |||||
"@types/jest": "^29.2.0", | |||||
"@types/react-native": "^0.70.6", | |||||
"@babel/core": "^7.20.7", | |||||
"@babel/runtime": "^7.20.7", | |||||
"@react-native-community/eslint-config": "^3.2.0", | |||||
"@tsconfig/react-native": "^2.0.3", | |||||
"@types/jest": "^29.2.4", | |||||
"@types/react-native": "^0.70.8", | |||||
"@types/react-test-renderer": "^18.0.0", | "@types/react-test-renderer": "^18.0.0", | ||||
"@typescript-eslint/eslint-plugin": "^5.40.1", | |||||
"@typescript-eslint/parser": "^5.40.1", | |||||
"babel-jest": "^29.2.1", | |||||
"eslint": "^8.25.0", | |||||
"jest": "^29.2.1", | |||||
"metro-react-native-babel-preset": "0.73.2", | |||||
"prettier": "^2.7.1", | |||||
"@typescript-eslint/eslint-plugin": "^5.47.0", | |||||
"@typescript-eslint/parser": "^5.47.0", | |||||
"babel-jest": "^29.3.1", | |||||
"eslint": "^8.30.0", | |||||
"jest": "^29.3.1", | |||||
"metro-react-native-babel-preset": "0.73.6", | |||||
"prettier": "^2.8.1", | |||||
"react-native-version": "^4.0.0", | "react-native-version": "^4.0.0", | ||||
"react-test-renderer": "18.2.0", | "react-test-renderer": "18.2.0", | ||||
"typescript": "^4.8.3" | |||||
"typescript": "^4.9.4" | |||||
}, | }, | ||||
"jest": { | "jest": { | ||||
"preset": "react-native", | "preset": "react-native", | ||||
@@ -12,18 +12,20 @@ import routes from "./routes"; | |||||
import { getPasswordProfiles } from "./password/profilesActions"; | import { getPasswordProfiles } from "./password/profilesActions"; | ||||
import { refreshTokens, signOut } from "./auth/authActions"; | import { refreshTokens, signOut } from "./auth/authActions"; | ||||
import ProfilesScreen from "./profiles/ProfilesScreen"; | import ProfilesScreen from "./profiles/ProfilesScreen"; | ||||
import { SafeAreaView, useColorScheme } from "react-native"; | |||||
import { SafeAreaView } from "react-native"; | |||||
import Errors from "./errors/Errors"; | import Errors from "./errors/Errors"; | ||||
import Header from "./header/Header"; | import Header from "./header/Header"; | ||||
import Styles from "./ui/Styles"; | import Styles from "./ui/Styles"; | ||||
import { useTheme } from "react-native-paper"; | |||||
import { getReactNavigationTheme } from "./ui/Theme"; | import { getReactNavigationTheme } from "./ui/Theme"; | ||||
const Tab = createBottomTabNavigator(); | const Tab = createBottomTabNavigator(); | ||||
export default function App() { | export default function App() { | ||||
const colorScheme = useColorScheme(); | |||||
const { isAuthenticated } = useSelector((state) => state.auth); | const { isAuthenticated } = useSelector((state) => state.auth); | ||||
const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
const theme = useTheme(); | |||||
useEffect(() => { | useEffect(() => { | ||||
if (isAuthenticated) { | if (isAuthenticated) { | ||||
dispatch(refreshTokens()) | dispatch(refreshTokens()) | ||||
@@ -37,12 +39,12 @@ export default function App() { | |||||
}); | }); | ||||
} | } | ||||
}, [isAuthenticated, dispatch]); | }, [isAuthenticated, dispatch]); | ||||
const reactNavigationTheme = getReactNavigationTheme(colorScheme); | |||||
const reactNavigationTheme = getReactNavigationTheme(theme); | |||||
return ( | return ( | ||||
<SafeAreaView | <SafeAreaView | ||||
style={{ | style={{ | ||||
...Styles.container, | ...Styles.container, | ||||
backgroundColor: reactNavigationTheme.colors.card, | |||||
backgroundColor: theme.colors.background, | |||||
}} | }} | ||||
> | > | ||||
<Header /> | <Header /> | ||||
@@ -11,8 +11,14 @@ export default function DeleteMyAccountModal({ onDeleteConfirmed }) { | |||||
return ( | return ( | ||||
<View> | <View> | ||||
<Portal> | <Portal> | ||||
<Dialog onDismiss={() => setShowModal(false)} visible={showModal}> | |||||
<Dialog.Title style={{ color: theme.colors.red }}> | |||||
<Dialog | |||||
onDismiss={() => { | |||||
setMasterPassword(""); | |||||
setShowModal(false); | |||||
}} | |||||
visible={showModal} | |||||
> | |||||
<Dialog.Title style={{ color: theme.colors.error }}> | |||||
Delete my account | Delete my account | ||||
</Dialog.Title> | </Dialog.Title> | ||||
<Dialog.ScrollArea style={{ maxHeight: 170, paddingHorizontal: 0 }}> | <Dialog.ScrollArea style={{ maxHeight: 170, paddingHorizontal: 0 }}> | ||||
@@ -22,38 +28,50 @@ export default function DeleteMyAccountModal({ onDeleteConfirmed }) { | |||||
masterPassword={masterPassword} | masterPassword={masterPassword} | ||||
onChangeText={setMasterPassword} | onChangeText={setMasterPassword} | ||||
/> | /> | ||||
<Text style={{ color: theme.colors.red }}> | |||||
<Text style={{ color: theme.colors.error }}> | |||||
Enter your master password to delete your account. Warning! this | Enter your master password to delete your account. Warning! this | ||||
action is irreversible. | action is irreversible. | ||||
</Text> | </Text> | ||||
</View> | </View> | ||||
</Dialog.ScrollArea> | </Dialog.ScrollArea> | ||||
<Dialog.Actions> | <Dialog.Actions> | ||||
<Button primary onPress={() => setShowModal(false)}> | |||||
<Button | |||||
primary | |||||
onPress={() => { | |||||
setMasterPassword(""); | |||||
setShowModal(false); | |||||
}} | |||||
> | |||||
Cancel | Cancel | ||||
</Button> | </Button> | ||||
<Button | <Button | ||||
disabled={!masterPassword} | |||||
primary | |||||
disabled={!masterPassword} | |||||
mode="contained" | |||||
style={{ | |||||
borderColor: theme.colors.error, | |||||
}} | |||||
buttonColor={theme.colors.error} | |||||
onPress={() => { | onPress={() => { | ||||
setShowModal(false); | setShowModal(false); | ||||
onDeleteConfirmed(masterPassword); | onDeleteConfirmed(masterPassword); | ||||
}} | }} | ||||
> | > | ||||
<Text style={{ color: theme.colors.red }}>Delete my account</Text> | |||||
Delete my account | |||||
</Button> | </Button> | ||||
</Dialog.Actions> | </Dialog.Actions> | ||||
</Dialog> | </Dialog> | ||||
</Portal> | </Portal> | ||||
<Button | <Button | ||||
compact | |||||
mode="outlined" | |||||
style={{ marginTop: 10, color: theme.colors.red }} | |||||
mode="contained" | |||||
style={{ | |||||
borderColor: theme.colors.error, | |||||
}} | |||||
buttonColor={theme.colors.error} | |||||
onPress={() => { | onPress={() => { | ||||
setShowModal(true); | setShowModal(true); | ||||
}} | }} | ||||
> | > | ||||
<Text style={{ color: theme.colors.red }}>Delete my account</Text> | |||||
Delete my account | |||||
</Button> | </Button> | ||||
</View> | </View> | ||||
); | ); | ||||
@@ -8,7 +8,7 @@ import { | |||||
TouchableWithoutFeedback, | TouchableWithoutFeedback, | ||||
Keyboard, | Keyboard, | ||||
} from "react-native"; | } from "react-native"; | ||||
import { Text, Button, Title } from "react-native-paper"; | |||||
import { Text, Button, Title, useTheme } from "react-native-paper"; | |||||
import MasterPassword from "../password/MasterPassword"; | import MasterPassword from "../password/MasterPassword"; | ||||
import TextInput from "../ui/TextInput"; | import TextInput from "../ui/TextInput"; | ||||
import Styles from "../ui/Styles"; | import Styles from "../ui/Styles"; | ||||
@@ -23,9 +23,10 @@ export default function SignInScreen() { | |||||
const [isLoading, setIsLoading] = useState(false); | const [isLoading, setIsLoading] = useState(false); | ||||
const navigation = useNavigation(); | const navigation = useNavigation(); | ||||
const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
const settings = useSelector((state) => state.settings); | |||||
const { encryptMasterPassword } = settings; | |||||
const theme = useTheme(); | |||||
const encryptMasterPassword = useSelector( | |||||
(state) => state.settings.encryptMasterPassword | |||||
); | |||||
return ( | return ( | ||||
<KeyboardAvoidingView | <KeyboardAvoidingView | ||||
behavior={Platform.OS === "ios" ? "padding" : "height"} | behavior={Platform.OS === "ios" ? "padding" : "height"} | ||||
@@ -33,7 +34,7 @@ export default function SignInScreen() { | |||||
> | > | ||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}> | <TouchableWithoutFeedback onPress={Keyboard.dismiss}> | ||||
<ScrollView contentContainerStyle={Styles.innerContainer}> | <ScrollView contentContainerStyle={Styles.innerContainer}> | ||||
<Title>Connect to Lesspass Database</Title> | |||||
<Title style={Styles.title}>Connect to Lesspass Database</Title> | |||||
<TextInput | <TextInput | ||||
mode="outlined" | mode="outlined" | ||||
label="Email" | label="Email" | ||||
@@ -47,10 +48,12 @@ export default function SignInScreen() { | |||||
onChangeText={(password) => setPassword(password)} | onChangeText={(password) => setPassword(password)} | ||||
/> | /> | ||||
<Button | <Button | ||||
compact | |||||
icon={"account-circle"} | icon={"account-circle"} | ||||
mode="contained" | mode="contained" | ||||
style={Styles.loginSignInButton} | |||||
style={{ | |||||
marginTop: 10, | |||||
marginBottom: 30, | |||||
}} | |||||
disabled={isEmpty(email) || isEmpty(password) || isLoading} | disabled={isEmpty(email) || isEmpty(password) || isLoading} | ||||
onPress={() => { | onPress={() => { | ||||
setIsLoading(true); | setIsLoading(true); | ||||
@@ -78,15 +81,11 @@ export default function SignInScreen() { | |||||
> | > | ||||
Sign In | Sign In | ||||
</Button> | </Button> | ||||
<Text>Don't have an account?</Text> | |||||
<Button | <Button | ||||
compact | |||||
icon="account-circle" | |||||
mode="outlined" | |||||
style={Styles.loginSignUpButton} | |||||
mode="text" | |||||
onPress={() => navigation.navigate(routes.SIGN_UP)} | onPress={() => navigation.navigate(routes.SIGN_UP)} | ||||
> | > | ||||
Sign Up | |||||
Don't have an account? Sign Up | |||||
</Button> | </Button> | ||||
</ScrollView> | </ScrollView> | ||||
</TouchableWithoutFeedback> | </TouchableWithoutFeedback> | ||||
@@ -1,5 +1,5 @@ | |||||
import React, { useEffect, useState } from "react"; | import React, { useEffect, useState } from "react"; | ||||
import { ScrollView, Text } from "react-native"; | |||||
import { ScrollView } from "react-native"; | |||||
import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
import { deleteMyAccount, getCurrentUser, signOut } from "./authActions"; | import { deleteMyAccount, getCurrentUser, signOut } from "./authActions"; | ||||
import Styles from "../ui/Styles"; | import Styles from "../ui/Styles"; | ||||
@@ -24,10 +24,11 @@ const SignOutScreen = ({ navigation }) => { | |||||
<Title style={{ marginBottom: 10 }}>My Account</Title> | <Title style={{ marginBottom: 10 }}>My Account</Title> | ||||
<Paragraph>Email: {email}</Paragraph> | <Paragraph>Email: {email}</Paragraph> | ||||
<Button | <Button | ||||
compact | |||||
icon="account-circle" | icon="account-circle" | ||||
mode="outlined" | |||||
style={Styles.loginSignUpButton} | |||||
mode="contained" | |||||
style={{ | |||||
marginTop: 10 | |||||
}} | |||||
onPress={() => { | onPress={() => { | ||||
dispatch(signOut()); | dispatch(signOut()); | ||||
navigation.navigate(routes.PASSWORD_GENERATOR); | navigation.navigate(routes.PASSWORD_GENERATOR); | ||||
@@ -36,7 +37,7 @@ const SignOutScreen = ({ navigation }) => { | |||||
Sign out | Sign out | ||||
</Button> | </Button> | ||||
<Title | <Title | ||||
style={{ marginTop: 50, marginBottom: 10, color: theme.colors.red }} | |||||
style={{ marginTop: 60, marginBottom: 10, color: theme.colors.error }} | |||||
> | > | ||||
Danger zone | Danger zone | ||||
</Title> | </Title> | ||||
@@ -1,5 +1,4 @@ | |||||
import React, { Component } from "react"; | |||||
import { connect } from "react-redux"; | |||||
import React, { useState } from "react"; | |||||
import { | import { | ||||
KeyboardAvoidingView, | KeyboardAvoidingView, | ||||
ScrollView, | ScrollView, | ||||
@@ -7,7 +6,7 @@ import { | |||||
TouchableWithoutFeedback, | TouchableWithoutFeedback, | ||||
Keyboard, | Keyboard, | ||||
} from "react-native"; | } from "react-native"; | ||||
import { Text, Button, Title } from "react-native-paper"; | |||||
import { Button, Title, useTheme } from "react-native-paper"; | |||||
import MasterPassword from "../password/MasterPassword"; | import MasterPassword from "../password/MasterPassword"; | ||||
import TextInput from "../ui/TextInput"; | import TextInput from "../ui/TextInput"; | ||||
import Styles from "../ui/Styles"; | import Styles from "../ui/Styles"; | ||||
@@ -15,97 +14,82 @@ import { addError } from "../errors/errorsActions"; | |||||
import { signUp } from "./authActions"; | import { signUp } from "./authActions"; | ||||
import { isEmpty } from "lodash"; | import { isEmpty } from "lodash"; | ||||
import routes from "../routes"; | import routes from "../routes"; | ||||
import { useNavigation } from "@react-navigation/native"; | |||||
import { useDispatch, useSelector } from "react-redux"; | |||||
export class SignUpScreen extends Component { | |||||
constructor(props) { | |||||
super(props); | |||||
this.state = { | |||||
email: "", | |||||
password: "", | |||||
isLoading: false, | |||||
}; | |||||
} | |||||
export default function SignUpScreen() { | |||||
const [email, setEmail] = useState(""); | |||||
const [password, setPassword] = useState(""); | |||||
const [isLoading, setIsLoading] = useState(false); | |||||
const navigation = useNavigation(); | |||||
const dispatch = useDispatch(); | |||||
const theme = useTheme(); | |||||
const encryptMasterPassword = useSelector( | |||||
(state) => state.settings.encryptMasterPassword | |||||
); | |||||
render() { | |||||
const { email, password, isLoading } = this.state; | |||||
const { navigation, settings, addError, signUp } = this.props; | |||||
const { encryptMasterPassword } = settings; | |||||
return ( | |||||
<KeyboardAvoidingView | |||||
behavior={Platform.OS === "ios" ? "padding" : "height"} | |||||
style={Styles.container} | |||||
> | |||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}> | |||||
<ScrollView contentContainerStyle={Styles.innerContainer}> | |||||
<Title style={Styles.title}>Create an account</Title> | |||||
<TextInput | |||||
mode="outlined" | |||||
label="Email" | |||||
value={email} | |||||
onChangeText={(text) => this.setState({ email: text.trim() })} | |||||
/> | |||||
<MasterPassword | |||||
label={encryptMasterPassword ? "Master Password" : "Password"} | |||||
masterPassword={password} | |||||
hideFingerprint={!encryptMasterPassword} | |||||
onChangeText={(password) => this.setState({ password })} | |||||
/> | |||||
<Button | |||||
compact | |||||
icon="account-circle" | |||||
mode="contained" | |||||
style={Styles.loginSignInButton} | |||||
disabled={isEmpty(email) || isEmpty(password) || isLoading} | |||||
onPress={() => { | |||||
this.setState({ isLoading: true }); | |||||
return ( | |||||
<KeyboardAvoidingView | |||||
behavior={Platform.OS === "ios" ? "padding" : "height"} | |||||
style={Styles.container} | |||||
> | |||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}> | |||||
<ScrollView contentContainerStyle={Styles.innerContainer}> | |||||
<Title style={Styles.title}>Create an account</Title> | |||||
<TextInput | |||||
mode="outlined" | |||||
label="Email" | |||||
value={email} | |||||
onChangeText={setEmail} | |||||
/> | |||||
<MasterPassword | |||||
label={encryptMasterPassword ? "Master Password" : "Password"} | |||||
masterPassword={password} | |||||
hideFingerprint={!encryptMasterPassword} | |||||
onChangeText={setPassword} | |||||
/> | |||||
<Button | |||||
icon="account-circle" | |||||
mode="contained" | |||||
style={{ | |||||
marginTop: 10, | |||||
marginBottom: 30, | |||||
}} | |||||
disabled={isEmpty(email) || isEmpty(password) || isLoading} | |||||
onPress={() => { | |||||
setIsLoading(true); | |||||
dispatch( | |||||
signUp( | signUp( | ||||
{ | { | ||||
email, | |||||
email: email.trim(), | |||||
password, | password, | ||||
}, | }, | ||||
encryptMasterPassword | encryptMasterPassword | ||||
) | ) | ||||
.then(() => navigation.navigate(routes.PASSWORD_GENERATOR)) | |||||
.catch(() => { | |||||
this.setState({ isLoading: false }); | |||||
) | |||||
.then(() => navigation.navigate(routes.PASSWORD_GENERATOR)) | |||||
.catch(() => { | |||||
dispatch( | |||||
addError( | addError( | ||||
"Unable to sign up. Try in a few minutes or contact an administrator." | "Unable to sign up. Try in a few minutes or contact an administrator." | ||||
); | |||||
}); | |||||
}} | |||||
> | |||||
Sign Up | |||||
</Button> | |||||
<Text>Already have an account?</Text> | |||||
<Button | |||||
compact | |||||
icon="account-circle" | |||||
mode="outlined" | |||||
style={Styles.loginSignUpButton} | |||||
onPress={() => navigation.navigate(routes.SIGN_IN)} | |||||
> | |||||
Sign In | |||||
</Button> | |||||
</ScrollView> | |||||
</TouchableWithoutFeedback> | |||||
</KeyboardAvoidingView> | |||||
); | |||||
} | |||||
} | |||||
function mapStateToProps(state) { | |||||
return { | |||||
settings: state.settings, | |||||
}; | |||||
) | |||||
); | |||||
}) | |||||
.finally(() => { | |||||
setIsLoading(false); | |||||
}); | |||||
}} | |||||
> | |||||
Sign Up | |||||
</Button> | |||||
<Button | |||||
mode="text" | |||||
onPress={() => navigation.navigate(routes.SIGN_IN)} | |||||
> | |||||
Already have an account? Sign In | |||||
</Button> | |||||
</ScrollView> | |||||
</TouchableWithoutFeedback> | |||||
</KeyboardAvoidingView> | |||||
); | |||||
} | } | ||||
function mapDispatchToProps(dispatch) { | |||||
return { | |||||
addError: (message) => dispatch(addError(message)), | |||||
signUp: (credentials, encryptMasterPassword) => | |||||
dispatch(signUp(credentials, encryptMasterPassword)), | |||||
}; | |||||
} | |||||
export default connect(mapStateToProps, mapDispatchToProps)(SignUpScreen); |
@@ -79,7 +79,8 @@ export function refreshTokens() { | |||||
.post(`${settings.baseURL}/auth/jwt/refresh/`, { | .post(`${settings.baseURL}/auth/jwt/refresh/`, { | ||||
refresh: auth.refreshToken, | refresh: auth.refreshToken, | ||||
}) | }) | ||||
.then((response) => dispatch(setJWT(response.data))); | |||||
.then((response) => dispatch(setJWT(response.data))) | |||||
.catch(console.log); | |||||
}; | }; | ||||
} | } | ||||
@@ -13,7 +13,7 @@ export default function Errors() { | |||||
<Snackbar | <Snackbar | ||||
key={error.id} | key={error.id} | ||||
visible={true} | visible={true} | ||||
style={{ backgroundColor: theme.colors.red }} | |||||
style={{ backgroundColor: theme.colors.error }} | |||||
onDismiss={() => dispatch(deleteError(error))} | onDismiss={() => dispatch(deleteError(error))} | ||||
action={{ | action={{ | ||||
label: "x", | label: "x", | ||||
@@ -1,9 +1,10 @@ | |||||
import React from "react"; | import React from "react"; | ||||
import { ScrollView, Image, Linking, View } from "react-native"; | import { ScrollView, Image, Linking, View } from "react-native"; | ||||
import { Title, Subheading, Paragraph, Button } from "react-native-paper"; | |||||
import { Title, Subheading, Paragraph, Button, useTheme } from "react-native-paper"; | |||||
import Styles from "../ui/Styles"; | import Styles from "../ui/Styles"; | ||||
export default function HelpScreen() { | export default function HelpScreen() { | ||||
const theme = useTheme() | |||||
return ( | return ( | ||||
<ScrollView contentContainerStyle={Styles.innerContainer}> | <ScrollView contentContainerStyle={Styles.innerContainer}> | ||||
<Title style={Styles.title}>Help</Title> | <Title style={Styles.title}>Help</Title> | ||||
@@ -82,7 +83,7 @@ export default function HelpScreen() { | |||||
marginTop: 10, | marginTop: 10, | ||||
}} | }} | ||||
> | > | ||||
send us an email | |||||
Send us an email | |||||
</Button> | </Button> | ||||
</ScrollView> | </ScrollView> | ||||
); | ); | ||||
@@ -19,8 +19,8 @@ export default function Counter({ | |||||
setIsValid(false); | setIsValid(false); | ||||
} | } | ||||
}, [value]); | }, [value]); | ||||
const color = isValid ? theme.colors.placeholder : theme.colors.red; | |||||
const colorAccent = isValid ? theme.colors.accent : theme.colors.red; | |||||
const color = isValid ? theme.colors.primary : theme.colors.error; | |||||
const borderColor = isValid ? theme.colors.outline : theme.colors.error; | |||||
return ( | return ( | ||||
<View {...props}> | <View {...props}> | ||||
<Text | <Text | ||||
@@ -37,10 +37,6 @@ export default function Counter({ | |||||
justifyContent: "center", | justifyContent: "center", | ||||
alignItems: "center", | alignItems: "center", | ||||
height: 26, | height: 26, | ||||
borderWidth: 1, | |||||
borderColor: colorAccent, | |||||
borderRadius: theme.roundness, | |||||
backgroundColor: colorAccent, | |||||
}} | }} | ||||
> | > | ||||
<TouchableOpacity | <TouchableOpacity | ||||
@@ -50,6 +46,11 @@ export default function Counter({ | |||||
alignSelf: "stretch", | alignSelf: "stretch", | ||||
paddingVertical: 6, | paddingVertical: 6, | ||||
paddingHorizontal: 16, | paddingHorizontal: 16, | ||||
backgroundColor: isValid | |||||
? theme.colors.primary | |||||
: theme.colors.error, | |||||
borderTopLeftRadius: 5 * theme.roundness, | |||||
borderBottomLeftRadius: 5 * theme.roundness, | |||||
}} | }} | ||||
onPress={() => setValue(value - 1)} | onPress={() => setValue(value - 1)} | ||||
> | > | ||||
@@ -57,7 +58,7 @@ export default function Counter({ | |||||
size={12} | size={12} | ||||
name="minus" | name="minus" | ||||
style={{ | style={{ | ||||
color: theme.colors.background, | |||||
color: isValid ? theme.colors.onPrimary : theme.colors.onError, | |||||
}} | }} | ||||
/> | /> | ||||
</TouchableOpacity> | </TouchableOpacity> | ||||
@@ -65,7 +66,11 @@ export default function Counter({ | |||||
style={{ | style={{ | ||||
justifyContent: "center", | justifyContent: "center", | ||||
alignItems: "center", | alignItems: "center", | ||||
backgroundColor: theme.colors.background, | |||||
borderWidth: 0, | |||||
borderTopWidth: 1, | |||||
borderTopColor: borderColor, | |||||
borderBottomWidth: 1, | |||||
borderBottomColor: borderColor, | |||||
}} | }} | ||||
> | > | ||||
<TextInput | <TextInput | ||||
@@ -104,6 +109,11 @@ export default function Counter({ | |||||
alignSelf: "stretch", | alignSelf: "stretch", | ||||
paddingVertical: 6, | paddingVertical: 6, | ||||
paddingHorizontal: 16, | paddingHorizontal: 16, | ||||
backgroundColor: isValid | |||||
? theme.colors.primary | |||||
: theme.colors.error, | |||||
borderTopRightRadius: 5 * theme.roundness, | |||||
borderBottomRightRadius: 5 * theme.roundness, | |||||
}} | }} | ||||
onPress={() => setValue(value + 1)} | onPress={() => setValue(value + 1)} | ||||
> | > | ||||
@@ -111,7 +121,7 @@ export default function Counter({ | |||||
size={12} | size={12} | ||||
name="plus" | name="plus" | ||||
style={{ | style={{ | ||||
color: theme.colors.background, | |||||
color: isValid ? theme.colors.onPrimary : theme.colors.onError, | |||||
}} | }} | ||||
/> | /> | ||||
</TouchableOpacity> | </TouchableOpacity> | ||||
@@ -6,7 +6,7 @@ import { areOptionsValid } from "./validations"; | |||||
export default function Options({ options, onOptionsChange, style }) { | export default function Options({ options, onOptionsChange, style }) { | ||||
const [isValid, setIsvalid] = useState(true); | const [isValid, setIsvalid] = useState(true); | ||||
const theme = useTheme(); | const theme = useTheme(); | ||||
const color = isValid ? theme.colors.placeholder : theme.colors.red; | |||||
const color = isValid ? theme.colors.secondary : theme.colors.error; | |||||
useEffect(() => { | useEffect(() => { | ||||
if (areOptionsValid(options)) { | if (areOptionsValid(options)) { | ||||
@@ -96,13 +96,11 @@ export default function PasswordGeneratorScreen() { | |||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}> | <TouchableWithoutFeedback onPress={Keyboard.dismiss}> | ||||
<ScrollView contentContainerStyle={Styles.innerContainer}> | <ScrollView contentContainerStyle={Styles.innerContainer}> | ||||
<TextInput | <TextInput | ||||
mode="outlined" | |||||
label="Site" | label="Site" | ||||
value={state.site} | value={state.site} | ||||
onChangeText={(site) => setState((state) => ({ ...state, site }))} | onChangeText={(site) => setState((state) => ({ ...state, site }))} | ||||
/> | /> | ||||
<TextInput | <TextInput | ||||
mode="outlined" | |||||
label="Login" | label="Login" | ||||
value={state.login} | value={state.login} | ||||
onChangeText={(login) => setState((state) => ({ ...state, login }))} | onChangeText={(login) => setState((state) => ({ ...state, login }))} | ||||
@@ -199,7 +197,7 @@ export default function PasswordGeneratorScreen() { | |||||
style={{ marginRight: 10, marginBottom: 10 }} | style={{ marginRight: 10, marginBottom: 10 }} | ||||
icon="refresh" | icon="refresh" | ||||
> | > | ||||
clear | |||||
CLEAR | |||||
</Button> | </Button> | ||||
{state.password && state.copyPasswordAfterGeneration === false && ( | {state.password && state.copyPasswordAfterGeneration === false && ( | ||||
<Button | <Button | ||||
@@ -223,7 +221,7 @@ export default function PasswordGeneratorScreen() { | |||||
icon="eye" | icon="eye" | ||||
style={{ marginRight: 10, marginBottom: 10 }} | style={{ marginRight: 10, marginBottom: 10 }} | ||||
> | > | ||||
{seePassword ? "hide" : "show"} | |||||
{seePassword ? "HIDE" : "SHOW"} | |||||
</Button> | </Button> | ||||
)} | )} | ||||
{state.password && auth.isAuthenticated ? ( | {state.password && auth.isAuthenticated ? ( | ||||
@@ -242,7 +240,7 @@ export default function PasswordGeneratorScreen() { | |||||
icon="content-save" | icon="content-save" | ||||
style={{ marginRight: 10, marginBottom: 10 }} | style={{ marginRight: 10, marginBottom: 10 }} | ||||
> | > | ||||
Save | |||||
SAVE | |||||
</Button> | </Button> | ||||
) : ( | ) : ( | ||||
<Button | <Button | ||||
@@ -259,7 +257,7 @@ export default function PasswordGeneratorScreen() { | |||||
icon="content-save" | icon="content-save" | ||||
style={{ marginRight: 10, marginBottom: 10 }} | style={{ marginRight: 10, marginBottom: 10 }} | ||||
> | > | ||||
Update | |||||
UPDATE | |||||
</Button> | </Button> | ||||
) | ) | ||||
) : null} | ) : null} | ||||
@@ -1,44 +1,38 @@ | |||||
import React, { Component } from "react"; | |||||
import { connect } from "react-redux"; | |||||
import React from "react"; | |||||
import { useSelector } from "react-redux"; | |||||
import TouchID from "react-native-touch-id"; | import TouchID from "react-native-touch-id"; | ||||
import { View } from "react-native"; | import { View } from "react-native"; | ||||
import { IconButton } from "react-native-paper"; | |||||
import { IconButton, useTheme } from "react-native-paper"; | |||||
import Styles from "../ui/Styles"; | import Styles from "../ui/Styles"; | ||||
import { getGenericPassword } from "react-native-keychain"; | import { getGenericPassword } from "react-native-keychain"; | ||||
export class TouchId extends Component { | |||||
getMasterPasswordSavedLocally = () => { | |||||
const { onChangeText } = this.props; | |||||
TouchID.authenticate() | |||||
.then(() => { | |||||
return getGenericPassword().then((credentials) => { | |||||
if (credentials) { | |||||
onChangeText(credentials.password); | |||||
} | |||||
}); | |||||
}) | |||||
.catch(console.log); | |||||
}; | |||||
render() { | |||||
const { settings } = this.props; | |||||
const { keepMasterPasswordLocally = false } = settings; | |||||
if (!keepMasterPasswordLocally) return null; | |||||
return ( | |||||
<View style={Styles.fingerprint}> | |||||
<IconButton | |||||
icon="fingerprint" | |||||
onPress={() => this.getMasterPasswordSavedLocally()} | |||||
/> | |||||
</View> | |||||
); | |||||
} | |||||
} | |||||
function mapStateToProps(state) { | |||||
return { | |||||
settings: state.settings, | |||||
}; | |||||
export default function TouchId({ onChangeText }) { | |||||
const theme = useTheme(); | |||||
const keepMasterPasswordLocally = useSelector( | |||||
(state) => state.settings.keepMasterPasswordLocally | |||||
); | |||||
if (!keepMasterPasswordLocally) return null; | |||||
return ( | |||||
<View style={Styles.fingerprint}> | |||||
<IconButton | |||||
icon="fingerprint" | |||||
onPress={() => { | |||||
TouchID.authenticate("Get master password saved locally", { | |||||
imageColor: theme.colors.primary, | |||||
imageErrorColor: theme.colors.error, | |||||
cancelTextColor: theme.colors.onPrimary, | |||||
cancelButtonColor: theme.colors.primary | |||||
}) | |||||
.then(() => | |||||
getGenericPassword().then((credentials) => { | |||||
if (credentials) { | |||||
onChangeText(credentials.password); | |||||
} | |||||
}) | |||||
) | |||||
.catch(console.log); | |||||
}} | |||||
/> | |||||
</View> | |||||
); | |||||
} | } | ||||
export default connect(mapStateToProps)(TouchId); |
@@ -109,8 +109,6 @@ export default function ProfilesScreen() { | |||||
description={ | description={ | ||||
"There is no password profile that matches this search." | "There is no password profile that matches this search." | ||||
} | } | ||||
titleStyle={{ color: theme.colors.primary }} | |||||
descriptionStyle={{ color: theme.colors.primary }} | |||||
/> | /> | ||||
) : ( | ) : ( | ||||
sortByNewestFirst(results).map((profile) => ( | sortByNewestFirst(results).map((profile) => ( | ||||
@@ -123,8 +121,6 @@ export default function ProfilesScreen() { | |||||
setQuery(""); | setQuery(""); | ||||
navigation.navigate(routes.PASSWORD_GENERATOR); | navigation.navigate(routes.PASSWORD_GENERATOR); | ||||
}} | }} | ||||
titleStyle={{ color: theme.colors.primary }} | |||||
descriptionStyle={{ color: theme.colors.primary }} | |||||
right={(props) => ( | right={(props) => ( | ||||
<IconButton | <IconButton | ||||
{...props} | {...props} | ||||
@@ -175,7 +175,6 @@ export class SettingsScreen extends Component { | |||||
/> | /> | ||||
<Divider /> | <Divider /> | ||||
<List.Item title={`LessPass version: ${version}`} /> | <List.Item title={`LessPass version: ${version}`} /> | ||||
<Divider /> | |||||
</List.Section> | </List.Section> | ||||
</ScrollView> | </ScrollView> | ||||
); | ); | ||||
@@ -17,11 +17,9 @@ export default StyleSheet.create({ | |||||
}, | }, | ||||
fingerprint: { | fingerprint: { | ||||
position: "absolute", | position: "absolute", | ||||
right: 3, | |||||
top: 0, | |||||
right: 0, | |||||
top: 6, | |||||
bottom: 0, | bottom: 0, | ||||
justifyContent: "center", | |||||
alignItems: "center", | |||||
zIndex: 4, | zIndex: 4, | ||||
}, | }, | ||||
switch: { | switch: { | ||||
@@ -31,11 +29,4 @@ export default StyleSheet.create({ | |||||
paddingVertical: 8, | paddingVertical: 8, | ||||
paddingHorizontal: 10, | paddingHorizontal: 10, | ||||
}, | }, | ||||
loginSignInButton: { | |||||
marginTop: 10, | |||||
marginBottom: 30, | |||||
}, | |||||
loginSignUpButton: { | |||||
marginTop: 10, | |||||
}, | |||||
}); | }); |
@@ -1,21 +1,19 @@ | |||||
import React, { Component } from "react"; | |||||
import React from "react"; | |||||
import { View } from "react-native"; | import { View } from "react-native"; | ||||
import { TextInput } from "react-native-paper"; | |||||
import styles from "./Styles"; | |||||
import { TextInput, useTheme } from "react-native-paper"; | |||||
export default class Input extends Component { | |||||
render() { | |||||
const { showError = false, errorText, ...props } = this.props; | |||||
return ( | |||||
<View> | |||||
<TextInput | |||||
autoCapitalize="none" | |||||
autoCorrect={false} | |||||
style={styles.input} | |||||
mode="outlined" | |||||
{...props} | |||||
/> | |||||
</View> | |||||
); | |||||
} | |||||
export default function Input({ showError = false, errorText, ...props }) { | |||||
const theme = useTheme(); | |||||
return ( | |||||
<TextInput | |||||
autoCapitalize="none" | |||||
autoCorrect={false} | |||||
outlineStyle={{ | |||||
borderRadius: 5 * theme.roundness, | |||||
}} | |||||
style={{marginBottom:5}} | |||||
mode="outlined" | |||||
{...props} | |||||
/> | |||||
); | |||||
} | } |
@@ -1,62 +0,0 @@ | |||||
import { DefaultTheme } from "react-native-paper"; | |||||
import { | |||||
DefaultTheme as DefaultThemeReactNavigation, | |||||
DarkTheme as DarkThemeReactNavigation, | |||||
} from "@react-navigation/native"; | |||||
export function getReactNavigationTheme(colorScheme) { | |||||
const isDarkTheme = colorScheme === "dark"; | |||||
if (isDarkTheme) { | |||||
return { | |||||
...DarkThemeReactNavigation, | |||||
dark: isDarkTheme, | |||||
colors: { | |||||
...DarkThemeReactNavigation.colors, | |||||
card: "#2E2E2E", | |||||
primary: "#F3F3F3", | |||||
text: "#F3F3F3", | |||||
border: "#2E2E2E", | |||||
background: "#4D4D4D", | |||||
}, | |||||
}; | |||||
} | |||||
return { | |||||
...DefaultThemeReactNavigation, | |||||
dark: isDarkTheme, | |||||
colors: { | |||||
...DefaultThemeReactNavigation.colors, | |||||
card: "#FFFFFF", | |||||
text: "#4D4D4D", | |||||
primary: "#2E2E2E", | |||||
}, | |||||
}; | |||||
} | |||||
export function getTheme(colorScheme) { | |||||
const isDarkTheme = colorScheme === "dark"; | |||||
if (isDarkTheme) { | |||||
return { | |||||
...DefaultTheme, | |||||
dark: isDarkTheme, | |||||
colors: { | |||||
...DefaultTheme.colors, | |||||
primary: "#F3F3F3", | |||||
accent: "#DEDEDE", | |||||
text: "#F3F3F3", | |||||
placeholder: "#DEDEDE", | |||||
background: "#4D4D4D", | |||||
red: "#f32c1e", | |||||
}, | |||||
}; | |||||
} | |||||
return { | |||||
...DefaultTheme, | |||||
dark: isDarkTheme, | |||||
colors: { | |||||
...DefaultTheme.colors, | |||||
primary: "#2E2E2E", | |||||
accent: "#2E2E2E", | |||||
red: "#f32c1e", | |||||
}, | |||||
}; | |||||
} |
@@ -0,0 +1,126 @@ | |||||
import { DefaultTheme, MD3Theme } from "react-native-paper"; | |||||
import { | |||||
DefaultTheme as DefaultThemeReactNavigation, | |||||
DarkTheme as DarkThemeReactNavigation, | |||||
Theme, | |||||
} from "@react-navigation/native"; | |||||
export function getReactNavigationTheme(theme: MD3Theme): Theme { | |||||
const reactNativeTheme = theme.dark | |||||
? DarkThemeReactNavigation | |||||
: DefaultThemeReactNavigation; | |||||
return { | |||||
...reactNativeTheme, | |||||
dark: theme.dark, | |||||
colors: { | |||||
...reactNativeTheme.colors, | |||||
text: theme.colors.secondary, | |||||
primary: theme.colors.primary, | |||||
card: theme.colors.background, | |||||
}, | |||||
}; | |||||
} | |||||
export function getTheme(colorScheme: string | undefined | null): MD3Theme { | |||||
const isDarkTheme = typeof colorScheme === "string" && colorScheme === "dark"; | |||||
if (isDarkTheme) { | |||||
return { | |||||
...DefaultTheme, | |||||
dark: isDarkTheme, | |||||
mode: "adaptive", | |||||
roundness: 1, | |||||
colors: { | |||||
...DefaultTheme.colors, | |||||
primary: "#bfc2ff", | |||||
onPrimary: "#141994", | |||||
primaryContainer: "#3037aa", | |||||
onPrimaryContainer: "#e0e0ff", | |||||
secondary: "#c5c4dd", | |||||
onSecondary: "#2e2f42", | |||||
secondaryContainer: "#444559", | |||||
onSecondaryContainer: "#e1e0f9", | |||||
tertiary: "#e8b9d5", | |||||
onTertiary: "#46263b", | |||||
tertiaryContainer: "#5e3c52", | |||||
onTertiaryContainer: "#ffd8ee", | |||||
error: "#ffb4ab", | |||||
onError: "#690005", | |||||
errorContainer: "#93000a", | |||||
onErrorContainer: "#ffb4ab", | |||||
background: "#1b1b1f", | |||||
onBackground: "#e5e1e6", | |||||
surface: "#1b1b1f", | |||||
onSurface: "#e5e1e6", | |||||
surfaceVariant: "#46464f", | |||||
onSurfaceVariant: "#c7c5d0", | |||||
outline: "#918f9a", | |||||
outlineVariant: "#46464f", | |||||
shadow: "#000000", | |||||
scrim: "#000000", | |||||
inverseSurface: "#e5e1e6", | |||||
inverseOnSurface: "#303034", | |||||
inversePrimary: "#4951c3", | |||||
elevation: { | |||||
level0: "transparent", | |||||
level1: "#23232a", | |||||
level2: "#282831", | |||||
level3: "#2d2d38", | |||||
level4: "#2f2f3a", | |||||
level5: "#32323e", | |||||
}, | |||||
surfaceDisabled: "#e5e1e6ff", | |||||
onSurfaceDisabled: "#e5e1e6ff", | |||||
backdrop: "#303038ff", | |||||
}, | |||||
}; | |||||
} | |||||
return { | |||||
...DefaultTheme, | |||||
dark: isDarkTheme, | |||||
mode: "adaptive", | |||||
roundness: 1, | |||||
colors: { | |||||
...DefaultTheme.colors, | |||||
primary: "#4951c3", | |||||
onPrimary: "#ffffff", | |||||
primaryContainer: "#e0e0ff", | |||||
onPrimaryContainer: "#00006e", | |||||
secondary: "#5c5d72", | |||||
onSecondary: "#ffffff", | |||||
secondaryContainer: "#e1e0f9", | |||||
onSecondaryContainer: "#191a2c", | |||||
tertiary: "#78536b", | |||||
onTertiary: "#ffffff", | |||||
tertiaryContainer: "#ffd8ee", | |||||
onTertiaryContainer: "#2e1126", | |||||
error: "#ba1a1a", | |||||
onError: "#ffffff", | |||||
errorContainer: "#ffdad6", | |||||
onErrorContainer: "#410002", | |||||
background: "#fffbff", | |||||
onBackground: "#1b1b1f", | |||||
surface: "#fffbff", | |||||
onSurface: "#1b1b1f", | |||||
surfaceVariant: "#e4e1ec", | |||||
onSurfaceVariant: "#46464f", | |||||
outline: "#777680", | |||||
outlineVariant: "#c7c5d0", | |||||
shadow: "#000000", | |||||
scrim: "#000000", | |||||
inverseSurface: "#303034", | |||||
inverseOnSurface: "#f3eff4", | |||||
inversePrimary: "#bfc2ff", | |||||
elevation: { | |||||
level0: "transparent", | |||||
level1: "#f6f3fc", | |||||
level2: "#f0edfa", | |||||
level3: "#ebe8f8", | |||||
level4: "#e9e7f8", | |||||
level5: "#e6e3f7", | |||||
}, | |||||
surfaceDisabled: "#1b1b1f1e", | |||||
onSurfaceDisabled: "#1b1b1f60", | |||||
backdrop: "#30303866", | |||||
}, | |||||
}; | |||||
} |
@@ -1,3 +1,3 @@ | |||||
{ | { | ||||
"version": "9.7.2" | |||||
"version": "9.7.3" | |||||
} | } |