Skip to content
This repository has been archived by the owner on Oct 4, 2023. It is now read-only.

[C-1461] Implement license-type/isrc upload flow #2248

Merged
merged 9 commits into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/mobile/src/assets/images/creativeCommons/by.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/mobile/src/assets/images/creativeCommons/cc.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/mobile/src/assets/images/creativeCommons/nc.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/mobile/src/assets/images/creativeCommons/nd.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/mobile/src/assets/images/creativeCommons/sa.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 25 additions & 10 deletions packages/mobile/src/components/core/ContextualSubmenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ const useStyles = makeStyles(({ spacing }) => ({
optionPills: {
flexDirection: 'row',
flexWrap: 'wrap',
marginTop: spacing(4)
marginTop: spacing(2)
},
pill: {
marginTop: spacing(2),
marginRight: spacing(2)
},
optionPillText: {
textTransform: 'uppercase'
Expand All @@ -68,15 +72,26 @@ export const ContextualSubmenu = (props: ContextualSubmenuProps) => {
navigation.push(submenuScreenName)
}, [navigation, submenuScreenName])

const defaultRenderValue = (value: any) => (
<View style={styles.optionPills}>
<Pill>
<Text fontSize='small' weight='demiBold' style={styles.optionPillText}>
{value}
</Text>
</Pill>
</View>
)
const defaultRenderValue = (value: string | string[]) => {
const values = typeof value === 'string' ? [value] : value

return (
<View style={styles.optionPills}>
{values.map((value) => (
<Pill key={value} style={styles.pill}>
<Text
fontSize='small'
weight='demiBold'
style={styles.optionPillText}
>
{value}
</Text>
</Pill>
))}
</View>
)
}

const renderValue = renderValueProp ?? defaultRenderValue

return (
Expand Down
22 changes: 12 additions & 10 deletions packages/mobile/src/components/core/SegmentedControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ import { makeStyles } from 'app/styles'
// Note, offset is the inner padding of the container div
const offset = 3

export type Option = {
key: string
export type Option<Value> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brain snagged on this one for a sec. Reminded me of https://en.wikipedia.org/wiki/Option_type, though the name collision is probably fine here

key: Value
text: string
}

export type SegmentedControlProps = {
export type SegmentedControlProps<Value> = {
// The options to display for the tab slider
options: Array<Option>
options: Array<Option<Value>>

// Key of selected option
selected?: string
selected?: Value

// Key of initially selected option
defaultSelected?: string
defaultSelected?: Value

// Callback fired when new option is selected
onSelectOption: (key: string) => void
onSelectOption: (key: Value) => void

fullWidth?: boolean
} & StylesProps<{
Expand Down Expand Up @@ -99,7 +99,9 @@ const useStyles = makeStyles(({ palette, typography, spacing }) => ({
}
}))

export const SegmentedControl = (props: SegmentedControlProps) => {
export const SegmentedControl = <Value,>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the , required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's this annoying issue with generic types and anonymous functions, we need it sadly

props: SegmentedControlProps<Value>
) => {
const {
options,
selected: selectedProp,
Expand All @@ -117,7 +119,7 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
const [selected, setSelected] = useState(defaultSelected)
const selectedOption = selectedProp ?? selected

const handleSelectOption = (option: string) => {
const handleSelectOption = (option: Value) => {
light()
onSelectOption?.(option)
setSelected(option)
Expand Down Expand Up @@ -195,7 +197,7 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
selectedOption === options[index + 1].key

return (
<Fragment key={option.key}>
<Fragment key={option.text}>
<Pressable
onLayout={setOptionWidth(index)}
style={[
Expand Down
2 changes: 2 additions & 0 deletions packages/mobile/src/components/core/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const TextInput = forwardRef<RNTextInput, TextInputProps>(
value,
onFocus,
onBlur,
placeholder,
...other
} = props
const { autoFocus } = other
Expand Down Expand Up @@ -242,6 +243,7 @@ export const TextInput = forwardRef<RNTextInput, TextInputProps>(
value={value}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder={label && !isFocused ? undefined : placeholder}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need !isFocused? Does RNTextInput not hide the placeholder while you're typing by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good q so for cases when there is a label, that's in the placeholder spot till you click into it, the label animates up, and then a placeholder text can appear. Otherwise there is a collision between the two

{...other}
/>
{clearable ? (
Expand Down
15 changes: 13 additions & 2 deletions packages/mobile/src/screens/upload-screen/CompleteTrackScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,23 @@ export const CompleteTrackScreen = () => {
const { metadata, file } = params
const navigation = useNavigation<UploadParamList>()

const initialValues = metadata
const initialValues = {
...metadata,
licenseType: {
allowAttribution: false,
commercialUse: false,
derivativeWorks: false
}
}

const handleSubmit = useCallback(
(values) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { licenseType, ...trackValues } = values
navigation.push('UploadingTracks', {
tracks: [{ file, preview: null, metadata: { ...metadata, ...values } }]
tracks: [
{ file, preview: null, metadata: { ...metadata, ...trackValues } }
]
})
},
[navigation, file, metadata]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { useAppScreenOptions } from '../app-screen/useAppScreenOptions'
import { CompleteTrackForm } from './CompleteTrackScreen'
import {
AdvancedOptionsScreen,
IsrcIswcScreen,
LicenseTypeScreen,
RemixSettingsScreen,
SelectGenreScreen,
SelectMoodScreen,
Expand All @@ -30,6 +32,8 @@ export const CompleteTrackStack = (props: FormikProps<TrackMetadata>) => {
<Stack.Screen name='RemixSettings' component={RemixSettingsScreen} />
<Stack.Screen name='AdvancedOptions' component={AdvancedOptionsScreen} />
<Stack.Screen name='TrackVisibility' component={TrackVisibilityScreen} />
<Stack.Screen name='IsrcIswc' component={IsrcIswcScreen} />
<Stack.Screen name='LicenseType' component={LicenseTypeScreen} />
</Stack.Navigator>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useThemeColors } from 'app/utils/theme'
import { TopBarIconButton } from '../app-screen'

import type { UploadParamList } from './ParamList'
import { processTrackFile } from './processTrackFile'
import { processTrackFile } from './utils/processTrackFile'

const messages = {
screenTitle: 'Upload',
Expand Down
27 changes: 27 additions & 0 deletions packages/mobile/src/screens/upload-screen/fields/IsrcIswcField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { removeNullable } from '@audius/common'
import { useField } from 'formik'

import type { ContextualSubmenuProps } from 'app/components/core'
import { ContextualSubmenu } from 'app/components/core'

const messages = {
label: 'ISRC/ISWC'
}

type IsrcFieldProps = Partial<ContextualSubmenuProps>

export const IsrcField = (props: IsrcFieldProps) => {
const [{ value: isrc }] = useField<string>('isrc')
const [{ value: iswc }] = useField<string>('iswc')

const values = [isrc, iswc].filter(removeNullable)

return (
<ContextualSubmenu
value={values}
label={messages.label}
submenuScreenName='IsrcIswc'
{...props}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useCallback } from 'react'

import type { Nullable } from '@audius/common'
import { useField } from 'formik'
import { View } from 'react-native'

import type { ContextualSubmenuProps } from 'app/components/core'
import { ContextualSubmenu, Pill } from 'app/components/core'
import { makeStyles } from 'app/styles'
import { useThemeColors } from 'app/utils/theme'

import { computeLicenseIcons } from '../utils/computeLicenseIcons'

const messages = {
licenseType: 'License Type',
noLicense: 'All rights reserved'
}

const useStyles = makeStyles(({ spacing }) => ({
licenseIcons: {
marginTop: spacing(4),
alignItems: 'flex-start'
},
licenseIcon: {
marginRight: spacing(1)
}
}))

type LicenseTypeFieldProps = Partial<ContextualSubmenuProps>

export const LicenseTypeField = (props: LicenseTypeFieldProps) => {
const [{ value: license }] = useField<Nullable<string>>('license')
const [{ value: allowAttribution }] = useField<boolean>(
'licenseType.allowAttribution'
)
const [{ value: commercialUse }] = useField<boolean>(
'licenseType.commercialUse'
)
const [{ value: derivativeWorks }] = useField<Nullable<boolean>>(
'licenseType.derivativeWorks'
)

const styles = useStyles()
const { neutral } = useThemeColors()

const renderValue = useCallback(() => {
const licenseIcons = computeLicenseIcons(
allowAttribution,
commercialUse,
derivativeWorks
)

return licenseIcons ? (
<View style={styles.licenseIcons}>
<Pill>
{licenseIcons.map(([Icon, key]) => (
<Icon
key={key}
fill={neutral}
style={styles.licenseIcon}
height={20}
width={20}
/>
))}
</Pill>
</View>
) : null
}, [allowAttribution, commercialUse, derivativeWorks, styles, neutral])

return (
<ContextualSubmenu
value={license || messages.noLicense}
label={messages.licenseType}
submenuScreenName='LicenseType'
renderValue={
license && license !== messages.noLicense ? renderValue : undefined
}
{...props}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const useStyles = makeStyles(({ spacing }) => ({
marginTop: spacing(1)
},
dateText: {
marignTop: spacing(1),
marginTop: spacing(1),
textTransform: 'uppercase'
},
datePickerModal: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { useField } from 'formik'
import { View } from 'react-native'

import type { ContextualSubmenuProps } from 'app/components/core'
import { Pill, Text } from 'app/components/core'
import { makeStyles } from 'app/styles'

import { ContextualSubmenuField } from './ContextualSubmenuField'
import { ContextualSubmenu } from 'app/components/core'

const messages = {
label: 'Track Visibility',
Expand All @@ -28,55 +24,24 @@ const fieldVisibilityLabelMap = {

const fieldVisibilityKeys = Object.keys(fieldVisibilityLabelMap)

const useStyles = makeStyles(({ spacing }) => ({
pills: {
flexDirection: 'row',
flexWrap: 'wrap',
marginTop: spacing(2)
},
pill: {
marginTop: spacing(2),
marginRight: spacing(2)
},
pillText: {
marginTop: spacing(1),
textTransform: 'uppercase'
}
}))

type TrackVisibilityFieldProps = Partial<ContextualSubmenuProps>

export const TrackVisibilityField = (props: TrackVisibilityFieldProps) => {
const [{ value: isUnlisted }] = useField('is_unlisted')
const [{ value: fieldVisibility }] = useField('field_visibility')
const styles = useStyles()

const renderValue = () => {
const trackVisibilityLabel = isUnlisted ? messages.hidden : messages.public
const fieldVisibilityLabels = fieldVisibilityKeys
.filter((visibilityKey) => fieldVisibility[visibilityKey])
.map((visibilityKey) => fieldVisibilityLabelMap[visibilityKey])
const trackVisibilityLabel = isUnlisted ? messages.hidden : messages.public
const fieldVisibilityLabels = fieldVisibilityKeys
.filter((visibilityKey) => fieldVisibility[visibilityKey])
.map((visibilityKey) => fieldVisibilityLabelMap[visibilityKey])

const labels = [trackVisibilityLabel, ...fieldVisibilityLabels]
return (
<View style={styles.pills}>
{labels.map((label) => (
<Pill key={label} style={styles.pill}>
<Text fontSize='small' weight='demiBold' style={styles.pillText}>
{label}
</Text>
</Pill>
))}
</View>
)
}
const values = [trackVisibilityLabel, ...fieldVisibilityLabels]

return (
<ContextualSubmenuField
<ContextualSubmenu
label={messages.label}
name='field_visibility'
submenuScreenName='TrackVisibility'
renderValue={renderValue}
value={values}
{...props}
/>
)
Expand Down
Loading