@@ -14,7 +14,8 @@ | |||
"test:watch": "jest --watch", | |||
"lint": "eslint .", | |||
"postinstall": "pod-install", | |||
"clean": "react-native-clean-project" | |||
"clean": "react-native-clean-project", | |||
"prettier": "prettier --write src" | |||
}, | |||
"dependencies": { | |||
"@react-native-community/async-storage": "^1.12.1", | |||
@@ -30,6 +31,7 @@ | |||
"lesspass-render-password": "latest", | |||
"lodash": "^4.17.21", | |||
"memoize-one": "^5.2.1", | |||
"prettier": "^2.3.1", | |||
"react": "17.0.2", | |||
"react-native": "0.64.1", | |||
"react-native-gesture-handler": "^1.10.3", | |||
@@ -42,9 +42,9 @@ function getEncryptedCredentials(credentials) { | |||
export function signIn(credentials, encryptMasterPassword) { | |||
return (dispatch) => { | |||
if (encryptMasterPassword) { | |||
return dispatch( | |||
getEncryptedCredentials(credentials) | |||
).then((encryptedCredentials) => dispatch(getJWT(encryptedCredentials))); | |||
return dispatch(getEncryptedCredentials(credentials)).then( | |||
(encryptedCredentials) => dispatch(getJWT(encryptedCredentials)) | |||
); | |||
} | |||
return dispatch(getJWT(credentials)); | |||
}; | |||
@@ -17,10 +17,10 @@ export class Errors extends Component { | |||
right: 0, | |||
top: 0, | |||
zIndex: 2, | |||
elevation: 6 | |||
elevation: 6, | |||
}} | |||
> | |||
{Object.values(errors).map(error => ( | |||
{Object.values(errors).map((error) => ( | |||
<View | |||
key={error.id} | |||
style={{ | |||
@@ -31,7 +31,7 @@ export class Errors extends Component { | |||
alignItems: "center", | |||
flexDirection: "row", | |||
padding: 10, | |||
paddingVertical: 20 | |||
paddingVertical: 20, | |||
}} | |||
> | |||
<View | |||
@@ -39,7 +39,7 @@ export class Errors extends Component { | |||
width: 85, | |||
alignItems: "center", | |||
justifyContent: "center", | |||
height: "100%" | |||
height: "100%", | |||
}} | |||
> | |||
<Icon size={22} name="warning" style={{ color: "#cc0000" }} /> | |||
@@ -47,7 +47,7 @@ export class Errors extends Component { | |||
<View | |||
style={{ | |||
flex: 1, | |||
backgroundColor: "#ffffff" | |||
backgroundColor: "#ffffff", | |||
}} | |||
> | |||
<Text style={{ color: "#cc0000", fontSize: 16 }}> | |||
@@ -59,7 +59,7 @@ export class Errors extends Component { | |||
width: 70, | |||
alignItems: "center", | |||
justifyContent: "center", | |||
backgroundColor: "#ffffff" | |||
backgroundColor: "#ffffff", | |||
}} | |||
> | |||
<Button onPress={() => deleteError(error)}> | |||
@@ -79,19 +79,16 @@ export class Errors extends Component { | |||
function mapStateToProps(state) { | |||
return { | |||
errors: state.errors | |||
errors: state.errors, | |||
}; | |||
} | |||
function mapDispatchToProps(dispatch) { | |||
return { | |||
deleteError: error => { | |||
deleteError: (error) => { | |||
dispatch(deleteError(error)); | |||
} | |||
}, | |||
}; | |||
} | |||
export default connect( | |||
mapStateToProps, | |||
mapDispatchToProps | |||
)(Errors); | |||
export default connect(mapStateToProps, mapDispatchToProps)(Errors); |
@@ -4,14 +4,14 @@ export function addError(message) { | |||
type: "ADD_ERROR", | |||
error: { | |||
id, | |||
message | |||
} | |||
message, | |||
}, | |||
}; | |||
} | |||
export function deleteError(error) { | |||
return { | |||
type: "DELETE_ERROR", | |||
error | |||
error, | |||
}; | |||
} |
@@ -1,13 +1,13 @@ | |||
const initialState = {}; | |||
export default function(state = initialState, action) { | |||
export default function (state = initialState, action) { | |||
switch (action.type) { | |||
case "ADD_ERROR": | |||
return { | |||
...state, | |||
[action.error.id]: { | |||
...action.error | |||
} | |||
...action.error, | |||
}, | |||
}; | |||
case "DELETE_ERROR": | |||
delete state[action.error.id]; | |||
@@ -11,8 +11,8 @@ describe("error reducer", () => { | |||
type: "ADD_ERROR", | |||
error: { | |||
id: Date.now().toString(), | |||
message: "an error message" | |||
} | |||
message: "an error message", | |||
}, | |||
} | |||
); | |||
const errorIds = Object.keys(state); | |||
@@ -25,14 +25,14 @@ describe("error reducer", () => { | |||
{ | |||
e1: { | |||
id: "e1", | |||
message: "an error message" | |||
} | |||
message: "an error message", | |||
}, | |||
}, | |||
{ | |||
type: "DELETE_ERROR", | |||
error: { | |||
id: "e1" | |||
} | |||
id: "e1", | |||
}, | |||
} | |||
) | |||
).toEqual({}); | |||
@@ -12,7 +12,7 @@ export default class Header extends Component { | |||
width: 180, | |||
height: 39, | |||
resizeMode: "contain", | |||
alignSelf: "center" | |||
alignSelf: "center", | |||
}} | |||
source={require("./logo.png")} | |||
/> | |||
@@ -6,10 +6,10 @@ import { isNaN } from "lodash"; | |||
export default class Counter extends Component { | |||
state = { | |||
isValid: true | |||
isValid: true, | |||
}; | |||
checkOptionsAreValid = value => { | |||
checkOptionsAreValid = (value) => { | |||
const { isValueValid } = this.props; | |||
if (isValueValid(value)) { | |||
this.setState({ isValid: true }); | |||
@@ -18,7 +18,7 @@ export default class Counter extends Component { | |||
} | |||
}; | |||
setNewValue = value => { | |||
setNewValue = (value) => { | |||
const { onValueChange } = this.props; | |||
if (isNaN(value)) { | |||
onValueChange(""); | |||
@@ -28,14 +28,8 @@ export default class Counter extends Component { | |||
}; | |||
render() { | |||
const { | |||
label, | |||
value, | |||
onValueChange, | |||
minValue, | |||
maxValue, | |||
...props | |||
} = this.props; | |||
const { label, value, onValueChange, minValue, maxValue, ...props } = | |||
this.props; | |||
const { isValid } = this.state; | |||
const isValidBackgroundColor = isValid | |||
? Theme.colors.primary | |||
@@ -45,7 +39,7 @@ export default class Counter extends Component { | |||
<Text | |||
style={{ | |||
marginBottom: 6, | |||
color: isValid ? Theme.colors.black : Theme.colors.red | |||
color: isValid ? Theme.colors.black : Theme.colors.red, | |||
}} | |||
> | |||
{label} | |||
@@ -54,7 +48,7 @@ export default class Counter extends Component { | |||
style={{ | |||
flexDirection: "row", | |||
justifyContent: "center", | |||
alignItems: "center" | |||
alignItems: "center", | |||
}} | |||
> | |||
<TouchableOpacity | |||
@@ -68,7 +62,7 @@ export default class Counter extends Component { | |||
borderColor: isValidBackgroundColor, | |||
backgroundColor: isValidBackgroundColor, | |||
paddingVertical: 6, | |||
paddingHorizontal: 16 | |||
paddingHorizontal: 16, | |||
}} | |||
onPress={() => { | |||
const newValue = value - 1; | |||
@@ -82,7 +76,7 @@ export default class Counter extends Component { | |||
size={12} | |||
name="minus" | |||
style={{ | |||
color: Theme.colors.white | |||
color: Theme.colors.white, | |||
}} | |||
/> | |||
</TouchableOpacity> | |||
@@ -95,7 +89,7 @@ export default class Counter extends Component { | |||
backgroundColor: Theme.colors.white, | |||
borderTopWidth: 1, | |||
borderBottomWidth: 1, | |||
borderColor: isValidBackgroundColor | |||
borderColor: isValidBackgroundColor, | |||
}} | |||
> | |||
<TextInput | |||
@@ -105,9 +99,9 @@ export default class Counter extends Component { | |||
style={{ | |||
paddingVertical: 0, | |||
color: isValid ? Theme.colors.primary : Theme.colors.red, | |||
textAlign: "center" | |||
textAlign: "center", | |||
}} | |||
onChangeText={text => { | |||
onChangeText={(text) => { | |||
try { | |||
const newValue = parseInt(text); | |||
this.checkOptionsAreValid(newValue); | |||
@@ -126,7 +120,7 @@ export default class Counter extends Component { | |||
borderWidth: 1, | |||
borderColor: isValidBackgroundColor, | |||
backgroundColor: isValidBackgroundColor, | |||
paddingHorizontal: 16 | |||
paddingHorizontal: 16, | |||
}} | |||
onPress={() => { | |||
const newValue = value + 1; | |||
@@ -140,7 +134,7 @@ export default class Counter extends Component { | |||
size={12} | |||
name="plus" | |||
style={{ | |||
color: Theme.colors.white | |||
color: Theme.colors.white, | |||
}} | |||
/> | |||
</TouchableOpacity> | |||
@@ -12,7 +12,7 @@ export default class Fingerprint extends Component { | |||
right: 10, | |||
top: 24, | |||
bottom: 0, | |||
flexDirection: "row" | |||
flexDirection: "row", | |||
}} | |||
> | |||
<Icon | |||
@@ -3,13 +3,13 @@ import { | |||
View, | |||
Text, | |||
TouchableNativeFeedback, | |||
TouchableWithoutFeedback | |||
TouchableWithoutFeedback, | |||
} from "react-native"; | |||
import Theme from "../ui/Theme"; | |||
export default class PasswordGeneratorScreen extends Component { | |||
state = { | |||
isGenerating: false | |||
isGenerating: false, | |||
}; | |||
render() { | |||
const { isGenerating } = this.state; | |||
@@ -31,14 +31,14 @@ export default class PasswordGeneratorScreen extends Component { | |||
? Theme.colors.primary | |||
: Theme.colors.disabled, | |||
borderRadius: Theme.roundness, | |||
padding: 14 | |||
padding: 14, | |||
}} | |||
> | |||
<Text | |||
style={{ | |||
color: Theme.colors.white, | |||
textAlign: "center", | |||
fontSize: 16 | |||
fontSize: 16, | |||
}} | |||
> | |||
GENERATE | |||
@@ -8,7 +8,7 @@ export default class GeneratedPassword extends Component { | |||
state = { | |||
copied: false, | |||
saved: false, | |||
seePassword: false | |||
seePassword: false, | |||
}; | |||
_copyPassword = () => { | |||
@@ -46,7 +46,7 @@ export default class GeneratedPassword extends Component { | |||
backgroundColor: Theme.colors.primary, | |||
borderRadius: Theme.roundness, | |||
marginTop: 5, | |||
padding: 14 | |||
padding: 14, | |||
}} | |||
> | |||
<Text | |||
@@ -54,7 +54,7 @@ export default class GeneratedPassword extends Component { | |||
color: Theme.colors.white, | |||
textAlign: "center", | |||
fontSize: 16, | |||
fontFamily: "Hack" | |||
fontFamily: "Hack", | |||
}} | |||
> | |||
{saved && "SAVED"} | |||
@@ -62,15 +62,15 @@ export default class GeneratedPassword extends Component { | |||
{saved || copied | |||
? null | |||
: seePassword | |||
? password | |||
: "*".repeat(password.length)} | |||
? password | |||
: "*".repeat(password.length)} | |||
</Text> | |||
</View> | |||
</TouchableNativeFeedback> | |||
<View | |||
style={{ | |||
flexDirection: "row", | |||
justifyContent: "space-between" | |||
justifyContent: "space-between", | |||
}} | |||
> | |||
<TouchableNativeFeedback onPress={() => this._copyPassword()}> | |||
@@ -80,7 +80,7 @@ export default class GeneratedPassword extends Component { | |||
marginTop: 5, | |||
padding: 14, | |||
flexDirection: "row", | |||
alignItems: "center" | |||
alignItems: "center", | |||
}} | |||
> | |||
<Icon | |||
@@ -92,7 +92,7 @@ export default class GeneratedPassword extends Component { | |||
style={{ | |||
color: Theme.colors.primary, | |||
textAlign: "center", | |||
fontSize: 16 | |||
fontSize: 16, | |||
}} | |||
> | |||
copy | |||
@@ -101,8 +101,8 @@ export default class GeneratedPassword extends Component { | |||
</TouchableNativeFeedback> | |||
<TouchableNativeFeedback | |||
onPress={() => | |||
this.setState(prevState => ({ | |||
seePassword: !prevState.seePassword | |||
this.setState((prevState) => ({ | |||
seePassword: !prevState.seePassword, | |||
})) | |||
} | |||
> | |||
@@ -112,7 +112,7 @@ export default class GeneratedPassword extends Component { | |||
marginTop: 5, | |||
padding: 14, | |||
flexDirection: "row", | |||
alignItems: "center" | |||
alignItems: "center", | |||
}} | |||
> | |||
<Icon | |||
@@ -124,7 +124,7 @@ export default class GeneratedPassword extends Component { | |||
style={{ | |||
color: Theme.colors.primary, | |||
textAlign: "center", | |||
fontSize: 16 | |||
fontSize: 16, | |||
}} | |||
> | |||
{seePassword ? "hide" : "show"} | |||
@@ -138,7 +138,7 @@ export default class GeneratedPassword extends Component { | |||
marginTop: 5, | |||
padding: 14, | |||
flexDirection: "row", | |||
alignItems: "center" | |||
alignItems: "center", | |||
}} | |||
> | |||
<Icon | |||
@@ -150,7 +150,7 @@ export default class GeneratedPassword extends Component { | |||
style={{ | |||
color: Theme.colors.primary, | |||
textAlign: "center", | |||
fontSize: 16 | |||
fontSize: 16, | |||
}} | |||
> | |||
clear | |||
@@ -165,7 +165,7 @@ export default class GeneratedPassword extends Component { | |||
marginTop: 5, | |||
padding: 14, | |||
flexDirection: "row", | |||
alignItems: "center" | |||
alignItems: "center", | |||
}} | |||
> | |||
<Icon | |||
@@ -177,7 +177,7 @@ export default class GeneratedPassword extends Component { | |||
style={{ | |||
color: Theme.colors.primary, | |||
textAlign: "center", | |||
fontSize: 16 | |||
fontSize: 16, | |||
}} | |||
> | |||
save | |||
@@ -22,19 +22,19 @@ export default class MasterPassword extends Component { | |||
this.setState({ fingerprint: createFingerprint(hmacSha256) }); | |||
}; | |||
calcFingerprint = masterPassword => { | |||
calcFingerprint = (masterPassword) => { | |||
if (masterPassword) { | |||
NativeModules.LessPass.createFingerprint(masterPassword).then( | |||
hmacSha256 => { | |||
(hmacSha256) => { | |||
this.setState({ | |||
fingerprint: createFingerprint(hmacSha256) | |||
fingerprint: createFingerprint(hmacSha256), | |||
}); | |||
} | |||
); | |||
} | |||
}; | |||
onChangeMasterPassword = masterPassword => { | |||
onChangeMasterPassword = (masterPassword) => { | |||
const { onChangeText } = this.props; | |||
onChangeText(masterPassword); | |||
this.calcFakedFingerprint(); | |||
@@ -45,7 +45,7 @@ export default class MasterPassword extends Component { | |||
const { | |||
masterPassword, | |||
hideFingerprint, | |||
label = "Master Password" | |||
label = "Master Password", | |||
} = this.props; | |||
const { fingerprint } = this.state; | |||
return ( | |||
@@ -131,12 +131,8 @@ export class PasswordGeneratorScreen extends Component { | |||
showAutocomplete, | |||
password, | |||
} = this.state; | |||
const { | |||
profiles, | |||
auth, | |||
savePasswordProfile, | |||
deletePasswordProfile, | |||
} = this.props; | |||
const { profiles, auth, savePasswordProfile, deletePasswordProfile } = | |||
this.props; | |||
return ( | |||
<TouchableWithoutFeedback | |||
onPress={() => this.setState({ showAutocomplete: false })} | |||
@@ -10,7 +10,7 @@ export function generatePassword(masterPassword, passwordProfile) { | |||
lowercase, | |||
uppercase, | |||
digits, | |||
symbols | |||
symbols, | |||
} = passwordProfile; | |||
const options = { length, counter, lowercase, uppercase, digits, symbols }; | |||
return NativeModules.LessPass.calcEntropy( | |||
@@ -18,7 +18,7 @@ export function generatePassword(masterPassword, passwordProfile) { | |||
login, | |||
masterPassword, | |||
counter.toString(16) | |||
).then(entropy => { | |||
).then((entropy) => { | |||
return renderPassword(entropy, options); | |||
}); | |||
} |
@@ -8,7 +8,7 @@ describe("generatePassword", () => { | |||
.fn() | |||
.mockResolvedValue( | |||
"03948309b088a53cdea276fa32a05988e9a6f2b57ef80aec664f668789b37711" | |||
) | |||
), | |||
}; | |||
}); | |||
@@ -21,10 +21,10 @@ describe("generatePassword", () => { | |||
lowercase: true, | |||
uppercase: true, | |||
digits: true, | |||
symbols: true | |||
symbols: true, | |||
}; | |||
return generatePassword("password", passwordProfile).then( | |||
generatedPassword => { | |||
(generatedPassword) => { | |||
expect(generatedPassword).toBe("\\g-A1-.OHEwrXjT#"); | |||
} | |||
); | |||
@@ -38,7 +38,7 @@ describe("generatePassword should not care about the extra number field used for | |||
.fn() | |||
.mockResolvedValue( | |||
"03948309b088a53cdea276fa32a05988e9a6f2b57ef80aec664f668789b37711" | |||
) | |||
), | |||
}; | |||
}); | |||
@@ -52,10 +52,10 @@ describe("generatePassword should not care about the extra number field used for | |||
uppercase: true, | |||
digits: true, | |||
number: true, | |||
symbols: true | |||
symbols: true, | |||
}; | |||
return generatePassword("password", passwordProfile).then( | |||
generatedPassword => { | |||
(generatedPassword) => { | |||
expect(generatedPassword).toBe("\\g-A1-.OHEwrXjT#"); | |||
} | |||
); | |||
@@ -10,12 +10,12 @@ describe("profiles reducer", () => { | |||
{}, | |||
{ | |||
type: "SET_PASSWORD_PROFILES", | |||
profiles: [{ id: "p1" }, { id: "p2" }] | |||
profiles: [{ id: "p1" }, { id: "p2" }], | |||
} | |||
) | |||
).toEqual({ | |||
p1: { id: "p1" }, | |||
p2: { id: "p2" } | |||
p2: { id: "p2" }, | |||
}); | |||
}); | |||
it("SET_PASSWORD_PROFILES numbers become digits", () => { | |||
@@ -24,12 +24,15 @@ describe("profiles reducer", () => { | |||
{}, | |||
{ | |||
type: "SET_PASSWORD_PROFILES", | |||
profiles: [{ id: "p1", numbers: true }, { id: "p2", numbers: false }] | |||
profiles: [ | |||
{ id: "p1", numbers: true }, | |||
{ id: "p2", numbers: false }, | |||
], | |||
} | |||
) | |||
).toEqual({ | |||
p1: { id: "p1", numbers: true, digits: true }, | |||
p2: { id: "p2", numbers: false, digits: false } | |||
p2: { id: "p2", numbers: false, digits: false }, | |||
}); | |||
}); | |||
it("REMOVE_PASSWORD_PROFILE", () => { | |||
@@ -37,15 +40,15 @@ describe("profiles reducer", () => { | |||
reducer( | |||
{ | |||
p1: { id: "p1", numbers: true, digits: true }, | |||
p2: { id: "p2", numbers: false, digits: false } | |||
p2: { id: "p2", numbers: false, digits: false }, | |||
}, | |||
{ | |||
type: "REMOVE_PASSWORD_PROFILE", | |||
profile: { id: "p1" } | |||
profile: { id: "p1" }, | |||
} | |||
) | |||
).toEqual({ | |||
p2: { id: "p2", numbers: false, digits: false } | |||
p2: { id: "p2", numbers: false, digits: false }, | |||
}); | |||
}); | |||
}); |
@@ -4,7 +4,7 @@ import { | |||
Text, | |||
TouchableWithoutFeedback, | |||
Platform, | |||
StyleSheet | |||
StyleSheet, | |||
} from "react-native"; | |||
import Icon from "react-native-vector-icons/FontAwesome"; | |||
import memoize from "memoize-one"; | |||
@@ -21,7 +21,7 @@ function highlight(text, i) { | |||
style={{ | |||
color: Theme.colors.primary, | |||
fontWeight: "bold", | |||
fontSize: 16 | |||
fontSize: 16, | |||
}} | |||
> | |||
{text} | |||
@@ -35,7 +35,7 @@ function noHighlight(text, i) { | |||
key={i} | |||
style={{ | |||
color: Theme.colors.primary, | |||
fontSize: 16 | |||
fontSize: 16, | |||
}} | |||
> | |||
{text} | |||
@@ -47,11 +47,11 @@ const style = StyleSheet.create({ | |||
container: { | |||
...Platform.select({ | |||
ios: { | |||
zIndex: 5 | |||
zIndex: 5, | |||
}, | |||
android: {} | |||
}) | |||
} | |||
android: {}, | |||
}), | |||
}, | |||
}); | |||
export default class AutocompleteSite extends Component { | |||
@@ -66,7 +66,7 @@ export default class AutocompleteSite extends Component { | |||
passwordProfileSelected, | |||
passwordProfileDeleted, | |||
showAutocomplete, | |||
hideAutocomplete | |||
hideAutocomplete, | |||
} = this.props; | |||
const items = this.returnMatchingData(value, data, dataKey); | |||
const highlightedItems = highlightSearch(items, highlight, noHighlight); | |||
@@ -76,7 +76,7 @@ export default class AutocompleteSite extends Component { | |||
mode="outlined" | |||
label="Site" | |||
value={value} | |||
onChangeText={site => onChangeText(site)} | |||
onChangeText={(site) => onChangeText(site)} | |||
onSubmitEditing={() => hideAutocomplete()} | |||
autoFocus | |||
/> | |||
@@ -88,7 +88,7 @@ export default class AutocompleteSite extends Component { | |||
top: 64, | |||
left: 0, | |||
right: 0, | |||
zIndex: 5 | |||
zIndex: 5, | |||
}} | |||
> | |||
{highlightedItems.map((highlightedItem, i) => { | |||
@@ -114,7 +114,7 @@ export default class AutocompleteSite extends Component { | |||
borderTopLeftRadius: isFirstElement ? Theme.roundness : 0, | |||
borderTopRightRadius: isFirstElement ? Theme.roundness : 0, | |||
borderColor: Theme.colors.primary, | |||
zIndex: 3 | |||
zIndex: 3, | |||
}} | |||
> | |||
<TouchableWithoutFeedback | |||
@@ -144,7 +144,7 @@ export default class AutocompleteSite extends Component { | |||
<View | |||
style={{ | |||
height: 100, | |||
backgroundColor: "transparent" | |||
backgroundColor: "transparent", | |||
}} | |||
/> | |||
</TouchableWithoutFeedback> | |||
@@ -6,7 +6,7 @@ export function returnMatchingData(query, data, dataKey) { | |||
const options = { | |||
keys: [dataKey], | |||
minMatchCharLength: 2, | |||
includeMatches: true | |||
includeMatches: true, | |||
}; | |||
const fuse = new Fuse(data, options); | |||
return fuse.search(query).slice(0, 3); | |||
@@ -2,7 +2,7 @@ function highlightMatch(match, highlight, noHightlight) { | |||
let index = 0; | |||
let regionIndex = 0; | |||
let regions = []; | |||
match.indices.map(indice => { | |||
match.indices.map((indice) => { | |||
const firstRegion = match.value.substring(regionIndex, indice[0]); | |||
if (firstRegion) { | |||
regions.push(noHightlight(firstRegion, index)); | |||
@@ -22,12 +22,12 @@ function highlightMatch(match, highlight, noHightlight) { | |||
export function highlightSearch(data, highlight, noHightlight) { | |||
const result = []; | |||
data.map(d => { | |||
data.map((d) => { | |||
const matches = d.matches; | |||
if (matches.length > 0) { | |||
result.push({ | |||
item: d.item, | |||
highlights: highlightMatch(matches[0], highlight, noHightlight) | |||
highlights: highlightMatch(matches[0], highlight, noHightlight), | |||
}); | |||
} | |||
}); | |||
@@ -7,113 +7,116 @@ test("highlightSearch", () => { | |||
{ | |||
item: { | |||
site: "www.example.org", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
}, | |||
matches: [ | |||
{ | |||
indices: [[4, 5], [7, 8]], | |||
indices: [ | |||
[4, 5], | |||
[7, 8], | |||
], | |||
value: "www.example.org", | |||
key: "site", | |||
arrayIndex: 0 | |||
} | |||
] | |||
} | |||
arrayIndex: 0, | |||
}, | |||
], | |||
}, | |||
]; | |||
expect(highlightSearch(data, hightlight, noHightlight)).toEqual([ | |||
{ | |||
item: { | |||
site: "www.example.org", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
}, | |||
highlights: [ | |||
"id=0*www.*", | |||
"id=1[ex]", | |||
"id=2*a*", | |||
"id=3[mp]", | |||
"id=4*le.org*" | |||
] | |||
} | |||
"id=4*le.org*", | |||
], | |||
}, | |||
]); | |||
}); | |||
test("highlightSearch first start indice equals 0", () => { | |||
const hightlight = item => `[${item}]`; | |||
const noHightlight = item => `*${item}*`; | |||
const hightlight = (item) => `[${item}]`; | |||
const noHightlight = (item) => `*${item}*`; | |||
const data = [ | |||
{ | |||
item: { | |||
site: "www.example.org", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
}, | |||
matches: [ | |||
{ | |||
indices: [[0, 2]], | |||
value: "www.example.org", | |||
key: "site", | |||
arrayIndex: 0 | |||
} | |||
] | |||
} | |||
arrayIndex: 0, | |||
}, | |||
], | |||
}, | |||
]; | |||
expect(highlightSearch(data, hightlight, noHightlight)).toEqual([ | |||
{ | |||
item: { | |||
site: "www.example.org", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
}, | |||
highlights: ["[www]", "*.example.org*"] | |||
} | |||
highlights: ["[www]", "*.example.org*"], | |||
}, | |||
]); | |||
}); | |||
test("highlightSearch last end indice equals value length", () => { | |||
const hightlight = item => `[${item}]`; | |||
const noHightlight = item => `*${item}*`; | |||
const hightlight = (item) => `[${item}]`; | |||
const noHightlight = (item) => `*${item}*`; | |||
const data = [ | |||
{ | |||
item: { | |||
site: "www.example.org", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
}, | |||
matches: [ | |||
{ | |||
indices: [[12, 14]], | |||
value: "www.example.org", | |||
key: "site", | |||
arrayIndex: 0 | |||
} | |||
] | |||
} | |||
arrayIndex: 0, | |||
}, | |||
], | |||
}, | |||
]; | |||
expect(highlightSearch(data, hightlight, noHightlight)).toEqual([ | |||
{ | |||
item: { | |||
site: "www.example.org", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
}, | |||
highlights: ["*www.example.*", "[org]"] | |||
} | |||
highlights: ["*www.example.*", "[org]"], | |||
}, | |||
]); | |||
}); | |||
test("highlightSearch no match", () => { | |||
const hightlight = item => `[${item}]`; | |||
const noHightlight = item => `*${item}*`; | |||
const hightlight = (item) => `[${item}]`; | |||
const noHightlight = (item) => `*${item}*`; | |||
const data = [ | |||
{ | |||
item: { | |||
site: "www.example.org", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
}, | |||
matches: [] | |||
} | |||
matches: [], | |||
}, | |||
]; | |||
expect(highlightSearch(data, hightlight, noHightlight)).toEqual([]); | |||
}); | |||
test("highlightSearch no data", () => { | |||
const hightlight = item => `[${item}]`; | |||
const noHightlight = item => `*${item}*`; | |||
const hightlight = (item) => `[${item}]`; | |||
const noHightlight = (item) => `*${item}*`; | |||
const data = []; | |||
expect(highlightSearch(data, hightlight, noHightlight)).toEqual([]); | |||
}); |
@@ -27,7 +27,7 @@ export function isProfileValid({ | |||
lowercase, | |||
uppercase, | |||
digits, | |||
symbols | |||
symbols, | |||
}) { | |||
return ( | |||
isLengthValid(length) && | |||
@@ -2,7 +2,7 @@ import { | |||
isProfileValid, | |||
isLengthValid, | |||
isCounterValid, | |||
areOptionsValid | |||
areOptionsValid, | |||
} from "./validations"; | |||
describe("validation", () => { | |||
@@ -68,7 +68,7 @@ describe("validation", () => { | |||
lowercase: true, | |||
uppercase: false, | |||
digits: false, | |||
symbols: false | |||
symbols: false, | |||
}) | |||
).toBe(true); | |||
}); | |||
@@ -78,7 +78,7 @@ describe("validation", () => { | |||
lowercase: false, | |||
uppercase: false, | |||
digits: false, | |||
symbols: false | |||
symbols: false, | |||
}) | |||
).toBe(false); | |||
}); | |||
@@ -94,7 +94,7 @@ describe("validation", () => { | |||
lowercase: true, | |||
uppercase: true, | |||
digits: true, | |||
symbols: true | |||
symbols: true, | |||
}) | |||
).toBe(false); | |||
}); | |||
@@ -108,7 +108,7 @@ describe("validation", () => { | |||
lowercase: true, | |||
uppercase: true, | |||
digits: true, | |||
symbols: true | |||
symbols: true, | |||
}) | |||
).toBe(true); | |||
}); | |||
@@ -122,7 +122,7 @@ describe("validation", () => { | |||
lowercase: false, | |||
uppercase: false, | |||
digits: false, | |||
symbols: false | |||
symbols: false, | |||
}) | |||
).toBe(false); | |||
}); | |||
@@ -136,7 +136,7 @@ describe("validation", () => { | |||
lowercase: true, | |||
uppercase: true, | |||
digits: true, | |||
symbols: true | |||
symbols: true, | |||
}) | |||
).toBe(false); | |||
}); | |||
@@ -1,13 +1,6 @@ | |||
import React, { Component } from "react"; | |||
import { View } from "react-native"; | |||
import { | |||
Button, | |||
Portal, | |||
Dialog, | |||
List, | |||
Text, | |||
Switch | |||
} from "react-native-paper"; | |||
import { Button, Portal, Dialog, List, Text, Switch } from "react-native-paper"; | |||
import MasterPassword from "../password/MasterPassword"; | |||
export default class KeepMasterPasswordOption extends Component { | |||
@@ -15,7 +8,7 @@ export default class KeepMasterPasswordOption extends Component { | |||
super(props); | |||
this.state = { | |||
showModal: false, | |||
masterPassword: "" | |||
masterPassword: "", | |||
}; | |||
} | |||
@@ -23,7 +16,7 @@ export default class KeepMasterPasswordOption extends Component { | |||
this.setState({ showModal: false }); | |||
}; | |||
_showModal = value => { | |||
_showModal = (value) => { | |||
const { onClear } = this.props; | |||
if (value) { | |||
onClear(); | |||
@@ -34,14 +27,8 @@ export default class KeepMasterPasswordOption extends Component { | |||
render() { | |||
const { showModal, masterPassword } = this.state; | |||
const { | |||
label, | |||
description, | |||
onOk, | |||
modalTitle, | |||
modalDescription, | |||
value | |||
} = this.props; | |||
const { label, description, onOk, modalTitle, modalDescription, value } = | |||
this.props; | |||
return ( | |||
<View> | |||
<Portal> | |||
@@ -51,7 +38,7 @@ export default class KeepMasterPasswordOption extends Component { | |||
<View style={{ padding: 10 }}> | |||
<MasterPassword | |||
masterPassword={masterPassword} | |||
onChangeText={value => | |||
onChangeText={(value) => | |||
this.setState({ masterPassword: value }) | |||
} | |||
/> | |||
@@ -6,7 +6,7 @@ import { | |||
Dialog, | |||
TextInput, | |||
List, | |||
Text | |||
Text, | |||
} from "react-native-paper"; | |||
import Styles from "../ui/Styles"; | |||
@@ -17,7 +17,7 @@ export default class TextInputModal extends Component { | |||
this.state = { | |||
value, | |||
isValid: this.checkInputIsValid(value, variant, isRequired), | |||
showModal: false | |||
showModal: false, | |||
}; | |||
} | |||
@@ -26,11 +26,11 @@ export default class TextInputModal extends Component { | |||
this.setState({ showModal: false, value }); | |||
}; | |||
getKeyboardType = variant => { | |||
getKeyboardType = (variant) => { | |||
const variants = { | |||
text: "default", | |||
email: "email-address", | |||
numeric: "numeric" | |||
numeric: "numeric", | |||
}; | |||
return variants[variant]; | |||
}; | |||
@@ -54,7 +54,7 @@ export default class TextInputModal extends Component { | |||
modalTitle, | |||
modalDescription, | |||
variant = "text", | |||
isRequired | |||
isRequired, | |||
} = this.props; | |||
return ( | |||
<View> | |||
@@ -68,7 +68,7 @@ export default class TextInputModal extends Component { | |||
value={variant === "numeric" ? value.toString() : value} | |||
keyboardType={this.getKeyboardType(variant)} | |||
secureTextEntry={variant === "password"} | |||
onChangeText={value => | |||
onChangeText={(value) => | |||
this.onChange(value, variant, isRequired) | |||
} | |||
/> | |||
@@ -1,6 +1,6 @@ | |||
export function setSettings(settings) { | |||
return { | |||
type: "SET_SETTINGS", | |||
settings | |||
settings, | |||
}; | |||
} |
@@ -8,10 +8,10 @@ const initialState = { | |||
defaultUppercase: true, | |||
defaultDigits: true, | |||
defaultSymbols: true, | |||
defaultCounter: 1 | |||
defaultCounter: 1, | |||
}; | |||
export default function(state = initialState, action) { | |||
export default function (state = initialState, action) { | |||
switch (action.type) { | |||
case "SET_SETTINGS": | |||
return { ...state, ...action.settings }; | |||
@@ -12,24 +12,24 @@ describe("settings reducer", () => { | |||
defaultUppercase: true, | |||
defaultDigits: true, | |||
defaultSymbols: true, | |||
defaultCounter: 1 | |||
defaultCounter: 1, | |||
}); | |||
}); | |||
it("SET_SETTINGS", () => { | |||
expect( | |||
reducer( | |||
{ | |||
keepMasterPasswordLocally: false | |||
keepMasterPasswordLocally: false, | |||
}, | |||
{ | |||
type: "SET_SETTINGS", | |||
settings: { | |||
keepMasterPasswordLocally: true | |||
} | |||
keepMasterPasswordLocally: true, | |||
}, | |||
} | |||
) | |||
).toEqual({ | |||
keepMasterPasswordLocally: true | |||
keepMasterPasswordLocally: true, | |||
}); | |||
}); | |||
it("SET_SETTINGS keep existing settings", () => { | |||
@@ -37,18 +37,18 @@ describe("settings reducer", () => { | |||
reducer( | |||
{ | |||
setting1: false, | |||
setting2: false | |||
setting2: false, | |||
}, | |||
{ | |||
type: "SET_SETTINGS", | |||
settings: { | |||
setting1: true | |||
} | |||
setting1: true, | |||
}, | |||
} | |||
) | |||
).toEqual({ | |||
setting1: true, | |||
setting2: false | |||
setting2: false, | |||
}); | |||
}); | |||
}); |
@@ -14,14 +14,14 @@ const rootReducer = combineReducers({ | |||
settings: settingsReducer, | |||
auth: authReducer, | |||
errors: errorsReducer, | |||
profiles: profilesReducer | |||
profiles: profilesReducer, | |||
}); | |||
const persistConfig = { | |||
key: "root", | |||
storage: AsyncStorage, | |||
stateReconciler, | |||
whitelist: ["settings", "auth"] | |||
whitelist: ["settings", "auth"], | |||
}; | |||
const persistedReducer = persistReducer(persistConfig, rootReducer); | |||
@@ -7,14 +7,14 @@ export default StyleSheet.create({ | |||
paddingHorizontal: 15, | |||
paddingTop: 15, | |||
marginBottom: 20, | |||
flex: 1 | |||
flex: 1, | |||
}, | |||
input: { | |||
marginBottom: 5 | |||
marginBottom: 5, | |||
}, | |||
header: { | |||
backgroundColor: Theme.colors.primary, | |||
paddingVertical: 16 | |||
paddingVertical: 16, | |||
}, | |||
fingerprint: { | |||
position: "absolute", | |||
@@ -23,18 +23,18 @@ export default StyleSheet.create({ | |||
bottom: 0, | |||
justifyContent: "center", | |||
alignItems: "center", | |||
zIndex: 4 | |||
zIndex: 4, | |||
}, | |||
switch: { | |||
flexDirection: "row", | |||
alignItems: "center", | |||
justifyContent: "space-between", | |||
paddingVertical: 8, | |||
paddingHorizontal: 10 | |||
paddingHorizontal: 10, | |||
}, | |||
sliderTrack: { | |||
height: 2, | |||
borderRadius: 1 | |||
borderRadius: 1, | |||
}, | |||
sliderTitleContainer: { | |||
flexDirection: "row", | |||
@@ -42,29 +42,29 @@ export default StyleSheet.create({ | |||
alignItems: "center", | |||
paddingLeft: 10, | |||
paddingRight: 20, | |||
paddingTop: 8 | |||
paddingTop: 8, | |||
}, | |||
sliderValue: { | |||
textAlign: "right", | |||
color: Theme.colors.text, | |||
paddingVertical: 1 | |||
paddingVertical: 1, | |||
}, | |||
slider: { | |||
marginHorizontal: 10, | |||
marginVertical: -8 | |||
marginVertical: -8, | |||
}, | |||
loginSignInButton: { | |||
marginTop: 10, | |||
marginBottom: 30 | |||
marginBottom: 30, | |||
}, | |||
loginSignUpButton: { | |||
marginTop: 10 | |||
marginTop: 10, | |||
}, | |||
forgotPasswordLink: { | |||
color: Theme.colors.link | |||
color: Theme.colors.link, | |||
}, | |||
snackbar: { | |||
backgroundColor: Theme.colors.red, | |||
marginBottom: 60 | |||
} | |||
marginBottom: 60, | |||
}, | |||
}); |
@@ -9,10 +9,7 @@ export default class LessPassSwitch extends Component { | |||
title={label} | |||
description={description} | |||
right={() => ( | |||
<Switch | |||
value={value} | |||
onValueChange={value => onChange(value)} | |||
/> | |||
<Switch value={value} onValueChange={(value) => onChange(value)} /> | |||
)} | |||
/> | |||
); | |||
@@ -17,8 +17,8 @@ const Theme = { | |||
brown: "#A28A7D", | |||
orange: "#CF9E38", | |||
purple: "#312430", | |||
link: "#2b72bb" | |||
} | |||
link: "#2b72bb", | |||
}, | |||
}; | |||
export default Theme; |
@@ -5925,6 +5925,11 @@ prettier@^2.0.2: | |||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" | |||
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== | |||
prettier@^2.3.1: | |||
version "2.3.1" | |||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" | |||
integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== | |||
pretty-format@^26.5.2, pretty-format@^26.6.2: | |||
version "26.6.2" | |||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" | |||