Skip to content
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

LUN-400: Image for own vocabulary #377

Merged
merged 12 commits into from
Oct 11, 2022
4 changes: 2 additions & 2 deletions src/components/NotAuthorisedView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ const NotAuthorisedView = ({ setVisible }: NotAuthorizedViewProps): ReactElement

return (
<Container testID='no-auth'>
<Description>{getLabels().addCustomDiscipline.qrCodeScanner.noAuthorization.description}</Description>
<Description>{getLabels().general.camera.noAuthorization.description}</Description>
<Button onPress={() => setVisible(false)} label={getLabels().general.back} buttonTheme={BUTTONS_THEME.outlined} />
<Button
onPress={openSettings}
label={getLabels().addCustomDiscipline.qrCodeScanner.noAuthorization.settings}
label={getLabels().general.camera.noAuthorization.settings}
buttonTheme={BUTTONS_THEME.contained}
/>
</Container>
Expand Down
18 changes: 9 additions & 9 deletions src/constants/labels.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@
"alreadyAdded": "Dieser Code wurde bereits erfolgreich hinzufügt.",
"wrongCode": "Dieser Code existiert nicht (ungültiger Code).",
"technical": "Ein technischer Fehler ist aufgetreten. Prüfen Sie Ihre Internetverbindung."
},
"qrCodeScanner": {
"noAuthorization": {
"description": "Kamerazugriff zum Scannen von QR-Codes erforderlich. Öffnen Sie die Einstellungen, tippen Sie auf Berechtigungen und tippen Sie auf Erlauben.",
"settings": "Einstellungen"
}
}
},
"exercises": {
Expand Down Expand Up @@ -164,7 +158,13 @@
"discipline": "Modul",
"disciplines": "Module",
"word": "Wort",
"words": "Wörter"
"words": "Wörter",
"camera": {
"noAuthorization": {
"description": "Kamerazugriff erforderlich. Öffnen Sie die Einstellungen, tippen Sie auf Berechtigungen und tippen Sie auf Erlauben.",
"settings": "Einstellungen"
}
}
},
"favorites": "Favoriten",
"dictionary": {
Expand Down Expand Up @@ -199,9 +199,9 @@
"wordPlaceholder": "Wort eingeben*",
"articlePlaceholder": "Artikel auswählen*",
"saveButton": "Speichern",
"addImage": "Bild hochladen*",
"addImage": "Bild hinzufügen*",
"addAudio": "Audio aufnehmen",
"maxPictureUpload": "Du kannst bis zu 3 Bilder hochladen",
"maxPictureUpload": "Du kannst bis zu 3 Bilder hinzufügen",
"requiredFields": "*Pflichtfelder",
"errorMessage": "Dies ist ein Pflichtfeld.",
"imageErrorMessage": "Du musst 1 bis 3 Bilder auswählen"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface AddCustomDisciplineScreenProps {
setCode: (code: string) => void
}

const AddCustomDisciplineScreen = ({ setVisible, setCode }: Props): ReactElement => {
const AddCustomDisciplineScreen = ({ setVisible, setCode }: AddCustomDisciplineScreenProps): ReactElement => {
const onBarCodeRead = (scanResult: BarCodeReadEvent) => {
setCode(scanResult.data)
setVisible(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,33 +85,32 @@ const UserVocabularyProcessScreen = ({ navigation }: UserVocabularyProcessScreen
return
}

await getNextUserVocabularyId().then(id => {
incrementNextUserVocabularyId()
.then(() => {
const imagePaths: Images = []
images.forEach(async (image, index) => {
const path = `${DocumentDirectoryPath}/image-${id}-${index}.txt`
imagePaths.push({ id: index, image: path })
await writeFile(path, image, 'utf8')
})

addUserDocument({
id,
word,
article: ARTICLES[articleId],
document_image: imagePaths,
audio: '',
alternatives: [],
})
.then(() => navigation.navigate('UserVocabularyList', { headerBackLabel: getLabels().general.back }))
.finally(() => {
setWord('')
setArticleId(null)
setImages([])
})
})
const id = await getNextUserVocabularyId()
await incrementNextUserVocabularyId()

const imagePaths: Images = []
images.forEach(image => {
const index = images.indexOf(image)
ztefanie marked this conversation as resolved.
Show resolved Hide resolved
const path = `${DocumentDirectoryPath}/image-${id}-${index}.txt`
writeFile(path, image, 'utf8')
.then(() => imagePaths.push({ id: index, image: path }))
.catch(reportError)
})
ztefanie marked this conversation as resolved.
Show resolved Hide resolved

await addUserDocument({
id,
word,
article: ARTICLES[articleId],
document_image: imagePaths,
audio: '',
alternatives: [],
})

navigation.navigate('UserVocabularyList', { headerBackLabel: getLabels().general.back })

setWord('')
setArticleId(null)
setImages([])
}
ztefanie marked this conversation as resolved.
Show resolved Hide resolved

const pushImage = (uri: string): void => setImages(old => [...old, uri])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { CircleIconWhite, ImageIcon } from '../../../../assets/images'
import CameraOverlay from '../../../components/CameraOverlay'
import PressableOpacity from '../../../components/PressableOpacity'

const GALLERY_IMAGE_SIZE = 30
const TAKE_IMAGE_SIZE = 50
const GALLERY_ICON_SIZE = 30
const TAKE_IMAGE_ICON_SIZE = 50

const Camera = styled(RNCamera)`
flex: 1;
Expand All @@ -25,37 +25,31 @@ const ActionBar = styled.View`
const TakeImageButton = styled.Pressable`
flex: 2;
align-items: center;
margin-right: ${GALLERY_IMAGE_SIZE}px;
margin-right: ${GALLERY_ICON_SIZE}px;
`

const Container = styled.View`
flex-direction: row;
justify-content: center;
`

interface Props {
interface ImageSelectionOverlayProps {
setVisible: (visible: boolean) => void
numberOfImages: number
pushImage: (imageUri: string) => void
}

// TODO
// adjust and test on ios
// write test

const ImageSelectionOverlay = ({ setVisible, pushImage, numberOfImages }: Props): ReactElement => {
const ImageSelectionOverlay = ({ setVisible, pushImage, numberOfImages }: ImageSelectionOverlayProps): ReactElement => {
const camera = useRef<RNCamera>(null)

const takePicture = async () => {
const takeImage = async () => {
if (camera.current) {
const options = { quality: 0.5, base64: true }
const data = await camera.current.takePictureAsync(options)
setVisible(false)
if (numberOfImages < 3) {
pushImage(data.uri)
}
ztefanie marked this conversation as resolved.
Show resolved Hide resolved
if (numberOfImages >= 2) {
setVisible(false)
}
}
}

Expand All @@ -64,13 +58,13 @@ const ImageSelectionOverlay = ({ setVisible, pushImage, numberOfImages }: Props)
<Camera ref={camera} captureAudio={false} testID='camera'>
<Container>
<ActionBar>
{/* TODO LUN-440 implement gallery */}
{/* eslint-disable-next-line no-console */}
<PressableOpacity onPress={() => console.log('open gallery')}>
{/* TODO LUN-440 implement gallery */}
<ImageIcon width={GALLERY_IMAGE_SIZE} height={GALLERY_IMAGE_SIZE} testID='gallery-icon' />
<ImageIcon width={GALLERY_ICON_SIZE} height={GALLERY_ICON_SIZE} testID='gallery-icon' />
</PressableOpacity>
<TakeImageButton onPress={takePicture}>
<CircleIconWhite width={TAKE_IMAGE_SIZE} height={TAKE_IMAGE_SIZE} testID='shutter-button' />
<TakeImageButton onPress={takeImage}>
<CircleIconWhite width={TAKE_IMAGE_ICON_SIZE} height={TAKE_IMAGE_ICON_SIZE} testID='take-image-icon' />
ztefanie marked this conversation as resolved.
Show resolved Hide resolved
</TakeImageButton>
</ActionBar>
</Container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('ImageSelectionOverlay', () => {
const { getByTestId } = render(
<ImageSelectionOverlay setVisible={jest.fn()} numberOfImages={0} pushImage={jest.fn()} />
)
expect(getByTestId('shutter-button')).toBeDefined()
expect(getByTestId('take-image-icon')).toBeDefined()
expect(getByTestId('gallery-icon')).toBeDefined()
})
})
2 changes: 1 addition & 1 deletion src/services/AsyncStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export const setUserVocabulary = async (userDocument: Document[]): Promise<void>
}

export const addUserDocument = async (userDocument: Document): Promise<void> => {
const userVocabulary = await getUserVocabulary()
const userVocabulary = await getUserVocabularyWithoutImage()
if (userVocabulary.find(item => item.word === userDocument.word)) {
return
}
Expand Down
10 changes: 6 additions & 4 deletions src/services/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Progress,
SCORE_THRESHOLD_UNLOCK,
} from '../constants/data'
import { AlternativeWord, Discipline, Document, ENDPOINTS, Images } from '../constants/endpoints'
import { AlternativeWord, Discipline, Document, ENDPOINTS, Image, Images } from '../constants/endpoints'
import labels from '../constants/labels.json'
import { COLORS } from '../constants/theme/colors'
import { ServerResponseDiscipline } from '../hooks/helpers'
Expand Down Expand Up @@ -203,13 +203,15 @@ export const getSortedAndFilteredDocuments = (documents: Document[] | null, sear
export const willNextExerciseUnlock = (previousScore: number | undefined, score: number): boolean =>
score > SCORE_THRESHOLD_UNLOCK && (previousScore ?? 0) <= SCORE_THRESHOLD_UNLOCK

export const getImages = (item: Document): Promise<Images> =>
Promise.all(
export const getImages = async (item: Document): Promise<Images> => {
const images = await Promise.all(
item.document_image.map(async image => ({
id: image.id,
image: await readFile(image.image).catch(err => {
reportError(err)
return ''
return null
}),
}))
)
return images.filter((item): item is Image => item.image !== null)
}