diff --git a/static/app-strings.json b/static/app-strings.json index a1aed94e3..7623a60b7 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -989,8 +989,6 @@ "Share usage data with LBRY inc.": "Share usage data with LBRY inc.", "Required": "Required", "Email %help_link% or join our %chat_link% if you encounter any trouble verifying.": "Email %help_link% or join our %chat_link% if you encounter any trouble verifying.", - "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.", - "Add relevant tags...": "Add relevant tags...", "try again in a few seconds.": "try again in a few seconds.", "Any": "Any", "Video": "Video", @@ -1005,5 +1003,19 @@ "Model": "Model", "Binary": "Binary", "Other": "Other", - "For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.": "For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming." -} \ No newline at end of file + "For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.": "For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.", + "Show reposts": "Show reposts", + "Show reposts from the creators you follow.": "Show reposts from the creators you follow.", + "You can try refreshing to fix it. If you still have issues, your anti-virus software or firewall may be preventing startup.": "You can try refreshing to fix it. If you still have issues, your anti-virus software or firewall may be preventing startup.", + "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.", + "Add relevant tags...": "Add relevant tags...", + "Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.", + "Sorry, your request timed out. Modify your options or %again%": "Sorry, your request timed out. Modify your options or %again%", + "gaming, crypto": "gaming, crypto", + "Autocomplete": "Autocomplete", + "Followed Tags": "Followed Tags", + "Add tags that are relevant to your content. If mature content, ensure it is tagged mature. Tag abuse and missing mature tags will not be tolerated.": "Add tags that are relevant to your content. If mature content, ensure it is tagged mature. Tag abuse and missing mature tags will not be tolerated.", + "%selectTagsLabel% (%number% left)": "%selectTagsLabel% (%number% left)", + "Matching": "Matching", + "No matching tags": "No matching tags" +} diff --git a/ui/component/publishForm/view.jsx b/ui/component/publishForm/view.jsx index 7c8a3c8d1..efb53d9d2 100644 --- a/ui/component/publishForm/view.jsx +++ b/ui/component/publishForm/view.jsx @@ -87,6 +87,7 @@ function PublishForm(props: Props) { publish, disabled = false, } = props; + const TAGS_LIMIT = 5; const formDisabled = (!filePath && !editingURI) || publishing; const isInProgress = filePath || editingURI || name || title; @@ -152,10 +153,11 @@ function PublishForm(props: Props) { hideHeader label={__('Selected Tags')} empty={__('No tags added')} + limit={TAGS_LIMIT} help={__( - 'Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.' + 'Add tags that are relevant to your content. If mature content, ensure it is tagged mature. Tag abuse and missing mature tags will not be tolerated.' )} - placeholder={__('Add relevant tags...')} + placeholder={__('gaming, crypto')} onSelect={newTags => { const validatedTags = []; newTags.forEach(newTag => { diff --git a/ui/component/tag/view.jsx b/ui/component/tag/view.jsx index 26e1b5c70..10ea23e2a 100644 --- a/ui/component/tag/view.jsx +++ b/ui/component/tag/view.jsx @@ -31,6 +31,7 @@ export default function Tag(props: Props) { disabled={disabled} title={title} className={classnames('tag', { + 'tag--disabled': disabled === true, 'tag--large': type === 'large', 'tag--remove': type === 'remove', // tag--add only adjusts the color, which causes issues with mature tag color clashing diff --git a/ui/component/tagsSearch/view.jsx b/ui/component/tagsSearch/view.jsx index 2e6b8e2a9..9f94129b0 100644 --- a/ui/component/tagsSearch/view.jsx +++ b/ui/component/tagsSearch/view.jsx @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import { Form, FormField } from 'component/common/form'; import Tag from 'component/tag'; import { setUnion, setDifference } from 'util/set-operations'; +import I18nMessage from 'component/i18nMessage'; type Props = { tagsPassedIn: Array, @@ -16,6 +17,8 @@ type Props = { onRemove: Tag => void, placeholder?: string, label?: string, + disabled?: boolean, + limit?: number, }; /* @@ -38,6 +41,8 @@ export default function TagsSearch(props: Props) { disableAutoFocus, placeholder, label, + disabled, + limit, } = props; const [newTag, setNewTag] = useState(''); const doesTagMatch = name => { @@ -54,6 +59,8 @@ export default function TagsSearch(props: Props) { const remainingFollowedTagsSet = setDifference(followedTagsSet, selectedTagsSet); const suggestedTagsSet = setUnion(remainingFollowedTagsSet, unfollowedTagsSet); + const countWithoutMature = selectedTagsSet.has('mature') ? selectedTagsSet.size - 1 : selectedTagsSet.size; + const maxed = Boolean(limit && countWithoutMature >= limit); const suggestedTags = Array.from(suggestedTagsSet) .filter(doesTagMatch) .slice(0, 5); @@ -108,37 +115,70 @@ export default function TagsSearch(props: Props) { return (
- -
    - {tagsPassedIn.map(tag => ( - { - onRemove(tag); + +
      + {!tagsPassedIn.length && } + {Boolean(tagsPassedIn.length) && + tagsPassedIn.map(tag => ( + { + onRemove(tag); + }} + /> + ))}
    + +
    + +
      + {Boolean(newTag.length) && !suggestedTags.includes(newTag) && ( + handleSubmit(e) : e => handleTagClick(newTag)} + /> + )} + {suggestedTags.map(tag => ( + handleTagClick(tag)} + /> + ))} + + {!suggestedTags.length &&

      {__('No matching tags')}

      } +
    +
    - -
      - {suggestedTags.map(tag => ( - handleTagClick(tag)} /> - ))} - {!suggestedTags.length &&

      No suggested tags

      } -
    ); } diff --git a/ui/component/tagsSelect/view.jsx b/ui/component/tagsSelect/view.jsx index d3f0911a8..574a32f2b 100644 --- a/ui/component/tagsSelect/view.jsx +++ b/ui/component/tagsSelect/view.jsx @@ -24,6 +24,7 @@ type Props = { placeholder?: string, disableAutoFocus?: boolean, hideHeader?: boolean, + limit?: number, }; /* @@ -44,6 +45,7 @@ export default function TagsSelect(props: Props) { placeholder, hideHeader, label, + limit, } = props; const [hasClosed, setHasClosed] = usePersistedState('tag-select:has-closed', false); const tagsToDisplay = tagsChosen || followedTags; @@ -105,6 +107,7 @@ export default function TagsSelect(props: Props) { disableAutoFocus={disableAutoFocus} tagsPassedIn={tagsToDisplay} placeholder={placeholder} + limit={limit} /> } diff --git a/ui/scss/component/_tags.scss b/ui/scss/component/_tags.scss index 945b02af2..b92e24890 100644 --- a/ui/scss/component/_tags.scss +++ b/ui/scss/component/_tags.scss @@ -43,6 +43,7 @@ height: var(--tag-height); padding: calc(var(--spacing-miniscule) + 1px) var(--spacing-small); background-color: var(--color-input-bg); + margin-bottom: var(--spacing-medium); } } @@ -55,6 +56,10 @@ max-width: 20rem; } +.tag--disabled { + opacity: 0.3; +} + .tag--large { height: var(--height-input); padding: 0 var(--spacing-s);