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

2861: Add data privacy agreement for feedback help form #3041

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b8cf3a7
2861: Add privacy agreement to translations.json
simomps Nov 14, 2024
99f589f
2861: Add Checkbox to Feedback component
simomps Nov 14, 2024
d22b435
2861: Change Checkbox component to allow link in label
simomps Nov 15, 2024
d8e3bf7
2861: Make privacyAgreement modular with correct syntax
simomps Nov 25, 2024
eb22eaa
2861: Implement Trans element into checkbox
simomps Nov 27, 2024
a4069dd
2861: Remove unnecessary tags from translation.json
simomps Nov 27, 2024
d3404a6
2861: Use Trans component only if label prop is not a String
simomps Nov 28, 2024
c1352f0
2861: Use Checkbox with Send Feedback possible only if checked
simomps Nov 28, 2024
b404323
2861: Add Checkbox Component to native
simomps Nov 28, 2024
6c7c430
2861: Add not functioning Checkbox to Feedback in native
simomps Dec 2, 2024
ee19f9c
2861: Add CheckBox package
simomps Dec 16, 2024
2ef2a40
2861: Make Checkbox Component pressable
simomps Jan 19, 2025
e9f142c
2861: Change privacyAgreement translation text from feedback to consent
simomps Jan 20, 2025
7af0716
2861: Add PrivacyCheckbox as own component separate from Checkbox com…
simomps Jan 20, 2025
0110221
2861: Add PrivacyCheckbox as own component separate from Checkbox com…
simomps Jan 20, 2025
166996a
2861: Change privacyAgreement translation text to common and fix logic
simomps Jan 20, 2025
b309bae
2861: Change checkbox styling and fix linting errors
simomps Jan 20, 2025
de37572
2861: Add PrivacyCheckbox into MalteHelpForm and change checkbox styling
simomps Jan 23, 2025
4ed3da2
2861: Pass language to PrivacyCheckbox to get privacyUrl from config
simomps Jan 23, 2025
f898b55
2861: Pass language to PrivacyCheckbox to get privacyUrl from config …
simomps Jan 23, 2025
07b8215
2861: Add PrivacyCheckbox to MalteHelpFormOffer
simomps Jan 23, 2025
30f4f35
2861: Choose privacyUrl based on used language
simomps Jan 24, 2025
edde4fd
2861: Rename privacyAgreement to privacyPolicy and privacyCheckedFilt…
simomps Jan 28, 2025
162acae
2861: Change position of checkbox to left of label and update styling
simomps Jan 29, 2025
0551538
2861: Change text in Link.tsx to children and remove unnecessary snac…
simomps Jan 29, 2025
6222722
2861: Adjust typescript testing to new component PrivacyFeedback in web
simomps Jan 30, 2025
cc59495
2861: Fix usage of Link component and Link testing
simomps Jan 30, 2025
f6071ec
2861: Adjust typescript testing to new privacy policy in native
simomps Jan 30, 2025
3d33460
2861: Update podfile.lock
steffenkleinle Jan 31, 2025
465eb57
2861: Fix issues on iOS
steffenkleinle Jan 31, 2025
fa09f92
2861: Fix styling on android
steffenkleinle Feb 5, 2025
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
1 change: 1 addition & 0 deletions native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@react-native-community/netinfo": "^11.3.2",
"@react-native-firebase/app": "^19.1.2",
"@react-native-firebase/messaging": "^19.1.2",
"@react-native-community/checkbox": "0.5",
"@react-navigation/elements": "^1.3.31",
"@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.4.1",
Expand Down
13 changes: 11 additions & 2 deletions native/src/components/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { ReactElement } from 'react'
import React, { ReactElement, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Text } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import styled from 'styled-components/native'

import buildConfig from '../constants/buildConfig'
import useNavigate from '../hooks/useNavigate'
import useSnackbar from '../hooks/useSnackbar'
import Caption from './Caption'
import FeedbackButtons from './FeedbackButtons'
import { SendingStatusType } from './FeedbackContainer'
import HorizontalLine from './HorizontalLine'
import LoadingSpinner from './LoadingSpinner'
import Note from './Note'
import NothingFound from './NothingFound'
import PrivacyCheckbox from './PrivacyCheckbox'
import InputSection from './base/InputSection'
import TextButton from './base/TextButton'

Expand Down Expand Up @@ -59,55 +61,62 @@
const navigation = useNavigate().navigation

const isSearchFeedback = searchTerm !== undefined
const [privacyCheckedFilter, setPrivacyCheckedFilter] = useState(false)
simomps marked this conversation as resolved.
Show resolved Hide resolved
const showSnackbar = useSnackbar()
const submitDisabled = isPositiveFeedback === null && comment.trim().length === 0 && !searchTerm

if (sendingStatus === 'sending') {
return <LoadingSpinner />
}

if (sendingStatus === 'successful') {
return (
<Wrapper>
<Caption title={t('thanksHeadline')} />
<Text>{t('thanksMessage')}</Text>
<StyledButton onPress={navigation.goBack} text={t('common:close')} />
</Wrapper>
)
}

return (
<KeyboardAwareScrollView>
<Wrapper>
{isSearchFeedback ? (
<>
<NothingFound />
<HorizontalLine />
<InputSection title={t('searchTermDescription')} value={searchTerm} onChange={setSearchTerm} />
</>
) : (
<>
<Caption title={t('headline')} />
<FeedbackButtons isPositiveFeedback={isPositiveFeedback} setIsPositiveFeedback={setIsPositiveFeedback} />
</>
)}
<InputSection
title={t('commentHeadline')}
description={t('commentDescription', { appName: buildConfig().appName })}
value={comment}
onChange={onCommentChanged}
multiline
showOptional
/>
<InputSection
title={t('contactMailAddress')}
value={contactMail}
onChange={onFeedbackContactMailChanged}
keyboardType='email-address'
showOptional
/>
{sendingStatus === 'failed' && <Description>{t('failedSendingFeedback')}</Description>}
<PrivacyCheckbox
checked={privacyCheckedFilter}
setChecked={setPrivacyCheckedFilter}
steffenkleinle marked this conversation as resolved.
Show resolved Hide resolved
showSnackbar={showSnackbar}
/>
{!isSearchFeedback && submitDisabled && <Note text={t('note')} />}
<StyledButton disabled={submitDisabled} onPress={onSubmit} text={t('send')} />
<StyledButton disabled={submitDisabled || !privacyCheckedFilter} onPress={onSubmit} text={t('send')} />

Check warning on line 119 in native/src/components/Feedback.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Complex Method

Feedback has a cyclomatic complexity of 10, threshold = 10. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
</Wrapper>
</KeyboardAwareScrollView>
)
Expand Down
54 changes: 54 additions & 0 deletions native/src/components/PrivacyCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { ReactElement } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import styled from 'styled-components/native'

import openExternalUrl from '../utils/openExternalUrl'
import Link from './Link'
import { SnackbarType } from './SnackbarContainer'
import Checkbox from './base/Checkbox'

const FlexContainer = styled.Pressable`
display: flex;
flex-direction: row;
align-items: baseline;
flex: 1;
`

const StyledLink = styled(Link)`
color: ${props => props.theme.colors.linkColor};
text-decoration: underline solid ${props => props.theme.colors.linkColor};
align-self: center;
simomps marked this conversation as resolved.
Show resolved Hide resolved
`

const StyledLabel = styled.Text`
color: ${props => props.theme.colors.textColor};
font-family: ${props => props.theme.fonts.native.decorativeFontRegular};
padding: 4px;
cursor: pointer;
`

type PrivacyCheckboxProps = {
checked: boolean
setChecked: (checked: boolean) => void
showSnackbar: (snackbar: SnackbarType) => void
}

const PrivacyCheckbox = ({ checked, setChecked, showSnackbar }: PrivacyCheckboxProps): ReactElement => {
const { t } = useTranslation('common')
const link = 'https://integreat-app.de/datenschutz/'
return (
<FlexContainer onPress={() => setChecked(!checked)}>
<StyledLabel>
<Trans i18nKey='common:privacyAgreement'>
This gets replaced
<StyledLink url={link} onPress={() => openExternalUrl(link, showSnackbar)} text={t('privacyAgreementLink')}>
by react-i18next
</StyledLink>
</Trans>
</StyledLabel>
<Checkbox checked={checked} setChecked={setChecked} />
simomps marked this conversation as resolved.
Show resolved Hide resolved
</FlexContainer>
)
}

export default PrivacyCheckbox
33 changes: 33 additions & 0 deletions native/src/components/base/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import CheckBox from '@react-native-community/checkbox'
import React, { ReactElement } from 'react'
import styled from 'styled-components/native'

const StyledCheckbox = styled(CheckBox)`
cursor: pointer;
accent-color: ${props => props.theme.colors.themeColor};
width: 16px;
height: 16px;
align-self: end;
`

const FlexEnd = styled.View`
display: flex;
flex-direction: row;
flex: 1;
right: 12px;
simomps marked this conversation as resolved.
Show resolved Hide resolved
justify-content: flex-end;
`

type CheckboxProps = {
checked: boolean
setChecked: (checked: boolean) => void
}

const Checkbox = ({ checked, setChecked }: CheckboxProps): ReactElement => (
<FlexEnd>
{/* https://github.com/react-native-checkbox/react-native-checkbox/blob/develop/README.md */}
simomps marked this conversation as resolved.
Show resolved Hide resolved
<StyledCheckbox disabled={false} value={checked} onValueChange={() => setChecked(!checked)} />
</FlexEnd>
)

export default Checkbox
8 changes: 6 additions & 2 deletions translations/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,9 @@
"showLess": "Weniger anzeigen",
"optional": "optional",
"back": "Zurück",
"ok": "Ok"
"ok": "Ok",
"privacyAgreement": "Ich habe die <1>Datenschutzerklärung</1> gelesen und stimme der Verarbeitung und Speicherung meiner Daten zu.",
"privacyAgreementLink": "Datenschutzerklärung"
simomps marked this conversation as resolved.
Show resolved Hide resolved
},
"am": {
"copied": "ተቀድቷል",
Expand Down Expand Up @@ -1202,7 +1204,9 @@
"showLess": "Show less",
"optional": "optional",
"back": "Back",
"ok": "Ok"
"ok": "Ok",
"privacyAgreement": "I have read the <1>privacy policy</1> and agree to the processing and storage of my data.",
"privacyAgreementLink": "privacy policy"
},
"es": {
"copied": "Copiado",
Expand Down
7 changes: 5 additions & 2 deletions web/src/components/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactElement } from 'react'
import React, { ReactElement, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'

Expand All @@ -8,6 +8,7 @@
import FeedbackButtons from './FeedbackButtons'
import { SendingStatusType } from './FeedbackContainer'
import Note from './Note'
import PrivacyCheckbox from './PrivacyCheckbox'
import Input from './base/Input'
import InputSection from './base/InputSection'
import TextButton from './base/TextButton'
Expand Down Expand Up @@ -73,45 +74,47 @@

const isSearchFeedback = searchTerm !== undefined
const commentTitle = isSearchFeedback ? 'wantedInformation' : 'commentHeadline'
const [privacyCheckedFilter, setPrivacyCheckedFilter] = useState(false)
const sendFeedbackDisabled = isPositiveFeedback === null && comment.trim().length === 0 && !searchTerm

if (sendingStatus === 'successful') {
return (
<Container>
<div>{t('thanksMessage')}</div>
{!!closeFeedback && !isSearchFeedback && <TextButton onClick={closeFeedback} text={t('common:close')} />}
</Container>
)
}

return (
<Container $fullWidth={isSearchFeedback}>
{isSearchFeedback ? (
<>
{noResults && <Failure errorMessage='search:nothingFound' />}
<InputSection id='searchTerm' title={t('searchTermDescription')}>
<Input id='searchTerm' value={searchTerm} onChange={setSearchTerm} />
</InputSection>
</>
) : (
<FeedbackButtons isPositive={isPositiveFeedback} onRatingChange={onFeedbackChanged} />
)}

<InputSection
id='comment'
title={t(commentTitle)}
description={t('commentDescription', { appName: buildConfig().appName })}
showOptional>
<Input id='comment' value={comment} onChange={onCommentChanged} multiline />
</InputSection>

<InputSection id='email' title={t('contactMailAddress')} showOptional>
<Input id='email' value={contactMail} onChange={onContactMailChanged} />
</InputSection>

{!isSearchFeedback && sendFeedbackDisabled && <Note text={t('note')} />}
{sendingStatus === 'failed' && <ErrorSendingStatus role='alert'>{t('failedSendingFeedback')}</ErrorSendingStatus>}
<StyledTextButton disabled={sendFeedbackDisabled} onClick={onSubmit} text={t('send')} />
<PrivacyCheckbox checked={privacyCheckedFilter} setChecked={setPrivacyCheckedFilter} id='privacyAgreement' />
<StyledTextButton disabled={sendFeedbackDisabled || !privacyCheckedFilter} onClick={onSubmit} text={t('send')} />

Check warning on line 117 in web/src/components/Feedback.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ Getting worse: Complex Method

Feedback increases in cyclomatic complexity from 12 to 13, threshold = 10. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
</Container>
)
}
Expand Down
30 changes: 30 additions & 0 deletions web/src/components/PrivacyCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { ReactElement } from 'react'
import { Trans } from 'react-i18next'
import { Link } from 'react-router-dom'

import Checkbox from './base/Checkbox'

type PrivacyCheckboxProps = {
checked: boolean
setChecked: (checked: boolean) => void
id: string
}

const PrivacyCheckbox = ({ checked, setChecked, id }: PrivacyCheckboxProps): ReactElement => {
const link = 'https://integreat-app.de/datenschutz/'
return (
<Checkbox
checked={checked}
setChecked={setChecked}
label={
<Trans i18nKey='common:privacyAgreement'>
This gets replaced
<Link to={link}>by react-i18next</Link>
</Trans>
}
id={id}
/>
)
}

export default PrivacyCheckbox
4 changes: 2 additions & 2 deletions web/src/components/base/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ const FlexEnd = styled.div`
type CheckboxProps = {
checked: boolean
setChecked: (checked: boolean) => void
label: string
label: string | ReactElement
id: string
}

const Checkbox = ({ checked, setChecked, label, id }: CheckboxProps): ReactElement => (
<Container>
<StyledLabel htmlFor={id}>{label}</StyledLabel>
steffenkleinle marked this conversation as resolved.
Show resolved Hide resolved
<FlexEnd>
<StyledCheckbox type='checkbox' id={id} value={label} checked={checked} onChange={() => setChecked(!checked)} />
<StyledCheckbox type='checkbox' id={id} checked={checked} onChange={() => setChecked(!checked)} />
simomps marked this conversation as resolved.
Show resolved Hide resolved
</FlexEnd>
</Container>
)
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2614,6 +2614,11 @@
resolved "https://registry.yarnpkg.com/@react-native-clipboard/clipboard/-/clipboard-1.14.1.tgz#835f82fc86881a0808a8405f2576617bb5383554"
integrity sha512-SM3el0A28SwoeJljVNhF217o0nI4E7RfalLmuRQcT1/7tGcxUjgFa3jyrEndYUct8/uxxK5EUNGUu1YEDqzxqw==

"@react-native-community/[email protected]":
version "0.5.17"
resolved "https://registry.yarnpkg.com/@react-native-community/checkbox/-/checkbox-0.5.17.tgz#80dd335ca5c61ac2ca4525a21d8fef3f7a021ef7"
integrity sha512-ZZx/eOH3SIxBg+hvA2zRd7lX5zPQEWcZU8C8Dn7WLdpJkiTkPmIUDFHWrmzxCnwZcyoD+BIt6gUZPKGGb9WmeQ==

"@react-native-community/[email protected]":
version "12.3.6"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-12.3.6.tgz#e8a7910bebc97266fd5068649013a03958021fc4"
Expand Down
Loading