diff --git a/build/xcode/Save to Raindrop.io/Save to Raindrop.io.xcodeproj/project.xcworkspace/xcuserdata/exentrich.xcuserdatad/UserInterfaceState.xcuserstate b/build/xcode/Save to Raindrop.io/Save to Raindrop.io.xcodeproj/project.xcworkspace/xcuserdata/exentrich.xcuserdatad/UserInterfaceState.xcuserstate index 8501f119..586849a3 100644 Binary files a/build/xcode/Save to Raindrop.io/Save to Raindrop.io.xcodeproj/project.xcworkspace/xcuserdata/exentrich.xcuserdatad/UserInterfaceState.xcuserstate and b/build/xcode/Save to Raindrop.io/Save to Raindrop.io.xcodeproj/project.xcworkspace/xcuserdata/exentrich.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/package.json b/package.json index 6987a026..7ed6df6d 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "5.6.73", + "version": "5.6.74", "description": "All-in-one bookmark manager", "author": "Rustem Mussabekov", "license": "MIT", diff --git a/src/co/bookmarks/add/fallback/link.js b/src/co/bookmarks/add/fallback/link.js index 3d4b6964..b08e9f4e 100644 --- a/src/co/bookmarks/add/fallback/link.js +++ b/src/co/bookmarks/add/fallback/link.js @@ -1,127 +1,70 @@ import s from './link.module.css' -import React, { useCallback, useState, useRef, useEffect, useMemo } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useState, useRef, useEffect } from 'react' +import { useDispatch } from 'react-redux' import t from '~t' -import useDebounce from '~modules/format/callback/use-debounce' -import isURL from 'validator/es/lib/isURL' -import { oneCreate } from '~data/actions/bookmarks' -import { suggestFields } from '~data/actions/bookmarks' -import { makeSuggestedFields } from '~data/selectors/bookmarks' -import { makeCollectionPath } from '~data/selectors/collections' -import { isPro } from '~data/selectors/user' +import { manyCreate, manyReparseInplace } from '~data/actions/bookmarks' +import { extractURLs } from '~modules/format/url' import { Error } from '~co/overlay/dialog' import Popover from '~co/overlay/popover' -import { Layout } from '~co/common/form' +import { Layout, Text, Label, Buttons } from '~co/common/form' import Button from '~co/common/button' import Icon from '~co/common/icon' -import ItemLink from '~co/bookmarks/edit/form/link' - -function Suggestion({ id, primary, disabled, onClick }) { - const getCollectionPath = useMemo(()=>makeCollectionPath(), []) - const path = useSelector(state=>getCollectionPath(state, id, { self: true })) - const shortPath = useMemo(()=>path.map((p)=>p.title).slice(-2).join(' / '), [path]) - const fullPath = useMemo(()=>path.map((p)=>p.title).join(' / '), [path]) - const collection = useMemo(()=>path?.[path.length-1], [path]) - const onSelfClick = useCallback(e=>onClick(parseInt(e.currentTarget.getAttribute('data-id'))), [onClick]) - - if (!collection?.title) - return null - - return ( - - ) -} - -function Suggestions({ item, loading, onClick }) { - const dispatch = useDispatch() - - //load suggestions - const debounced = useDebounce(item, 300) - useEffect(()=>dispatch(suggestFields(debounced)), [debounced.link]) - - //get suggestions - const enabled = useSelector(state=>state.config.ai_suggestions) - const pro = useSelector(state=>isPro(state)) - const getSuggestedFields = useMemo(()=>makeSuggestedFields(), []) - const { collections=[] } = useSelector(state=>getSuggestedFields(state, item)) - - return ( -
- - - {enabled && pro && collections.length ? (<> - {t.s('or')} {t.s('to')} - - {collections.map(id=>( - - ))} - ) : null} -
- ) -} function AddForm({ spaceId, onEdit, pin, onClose }) { const dispatch = useDispatch() - const [item, setItem] = useState(()=>({ collectionId: parseInt(spaceId)||-1 })) - const [loading, setLoading] = useState(true) + const input = useRef(null) + const [text, setText] = useState('') + const [loading, setLoading] = useState(false) - const onChangeField = useCallback((changed)=> - setItem(item=>({...item, ...changed})), - [setItem] - ) + //grab link from clipboard + useEffect(()=>{ + (async()=>{ + //in safari readText works bad, so this line prevents run on safari + await navigator.permissions.query({name: 'clipboard-read'}) + return extractURLs(await navigator.clipboard.readText()) + })() + .then(urls=>{ + setText(urls.join('\n')) + input.current?.select?.() + }) + .catch(e=>console.error(e)) + }, [setText, input]) - const onAddTo = useCallback((collectionId)=>{ - setLoading(true) + const onChangeInput = useCallback(e=>{ + e.currentTarget?.setCustomValidity('') + setText(e.currentTarget.value) + }, [setText]) - dispatch(oneCreate({ ...item, collectionId }, (items)=>{ - onEdit && onEdit(items) - onClose() - }, e=>{ - Error(e) - setLoading(false) - })) - }, [item, onEdit, setLoading, onClose, dispatch]) - const onSubmitForm = useCallback(e=>{ e.preventDefault() e.stopPropagation() - onAddTo(item.collectionId) - }, [onAddTo, item.collectionId]) - //grab link from clipboard - useEffect(()=>{ - async function getLink() { - //in safari readText works bad, so this line prevents run on safari - await navigator.permissions.query({name: 'clipboard-read'}) - const link = await navigator.clipboard.readText() - if (isURL(link, { require_protocol: true })) return link + const urls = extractURLs(text) + input.current?.setCustomValidity?.(urls.length ? '' : 'Please enter a valid URL(s)') + input.current?.reportValidity?.() + + if (urls.length) { + setLoading(true) + + dispatch( + manyCreate( + urls.map(link=>({ + link, + collectionId: parseInt(spaceId)||-1 + })), + (items)=>{ + dispatch(manyReparseInplace(items)) + onClose() + }, e=>{ + Error(e) + setLoading(false) + } + ) + ) } - - getLink() - .then(link=>onChangeField({ link })) - .catch(e=>console.error(e)) - .finally(()=>setLoading(false)) - }, [setLoading, onChangeField]) + }, [text, input, spaceId, dispatch, setLoading]) return (
- URL + - - + autoSize={true} + type='text' + autoComplete='off' + spellCheck='false' + autoFocus={true} + maxRows={5} + value={text} + placeholder='https://' + onChange={onChangeInput} /> + + + + +