Skip to content

Commit

Permalink
Invite a collaborator now without sending an email. Instead, you'll r…
Browse files Browse the repository at this point in the history
…eceive a link that you can share manually. This change was made to prevent abuse by individuals who used the email feature for spamming.
  • Loading branch information
Rustem Mussabekov committed Sep 16, 2024
1 parent e8382da commit 7f3da7e
Show file tree
Hide file tree
Showing 17 changed files with 129 additions and 159 deletions.
4 changes: 2 additions & 2 deletions build/xcode/Save to Raindrop.io/App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>5.6.50</string>
<string>5.6.55</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand All @@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>360</string>
<string>361</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>
Expand Down
4 changes: 2 additions & 2 deletions build/xcode/Save to Raindrop.io/Extension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>5.6.50</string>
<string>5.6.55</string>
<key>CFBundleVersion</key>
<string>360</string>
<string>361</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSExtension</key>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@
CODE_SIGN_ENTITLEMENTS = Extension/Extension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 360;
CURRENT_PROJECT_VERSION = 361;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 7459JWM5TY;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -452,7 +452,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
MARKETING_VERSION = 5.6.50;
MARKETING_VERSION = 5.6.55;
PRODUCT_BUNDLE_IDENTIFIER = io.raindrop.safari.extension;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -467,7 +467,7 @@
CODE_SIGN_ENTITLEMENTS = Extension/Extension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 360;
CURRENT_PROJECT_VERSION = 361;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 7459JWM5TY;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -478,7 +478,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
MARKETING_VERSION = 5.6.50;
MARKETING_VERSION = 5.6.55;
PRODUCT_BUNDLE_IDENTIFIER = io.raindrop.safari.extension;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -497,7 +497,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 360;
CURRENT_PROJECT_VERSION = 361;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 7459JWM5TY;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -508,7 +508,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
MARKETING_VERSION = 5.6.50;
MARKETING_VERSION = 5.6.55;
PRODUCT_BUNDLE_IDENTIFIER = io.raindrop.safari;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
Expand All @@ -525,7 +525,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 360;
CURRENT_PROJECT_VERSION = 361;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 7459JWM5TY;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -536,7 +536,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
MARKETING_VERSION = 5.6.50;
MARKETING_VERSION = 5.6.55;
PRODUCT_BUNDLE_IDENTIFIER = io.raindrop.safari;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "app",
"version": "5.6.55",
"version": "5.6.56",
"description": "All-in-one bookmark manager",
"author": "Rustem Mussabekov",
"license": "MIT",
Expand Down
10 changes: 4 additions & 6 deletions src/co/collections/sharing/collaborators/invite/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react'
import { connect } from 'react-redux'
import { sharingSendInvites } from '~data/actions/collections'
import { getSharingSendInvitesStatus, getSharingSendInvitesTo, collection } from '~data/selectors/collections'
import { collection } from '~data/selectors/collections'

import View from './view'

class CollectionSharingInvite extends React.Component {
handlers = {
onInvite: (emails, role)=>{
this.props.sharingSendInvites(this.props._id, emails, role)
onInvite: (role, onSuccess, onFail)=>{
this.props.sharingSendInvites(this.props._id, role, onSuccess, onFail)
}
}

Expand All @@ -21,9 +21,7 @@ class CollectionSharingInvite extends React.Component {

export default connect(
(state, { _id })=>({
access: collection(state, _id).access,
status: getSharingSendInvitesStatus(state, _id),
sendTo: getSharingSendInvitesTo(state, _id)
access: collection(state, _id).access
}),
{ sharingSendInvites }
)(CollectionSharingInvite)
170 changes: 80 additions & 90 deletions src/co/collections/sharing/collaborators/invite/view.js
Original file line number Diff line number Diff line change
@@ -1,108 +1,98 @@
import React from 'react'
import React, { useState, useCallback } from 'react'
import t from '~t'
import links from '~config/links'

import { Alert } from '~co/overlay/dialog'
import { Layout, Title, Label, Text, Radio, Buttons } from '~co/common/form'
import { Prompt, Error } from '~co/overlay/dialog'
import { Layout, Title, Label, Radio, Buttons } from '~co/common/form'
import Button from '~co/common/button'
import Icon from '~co/common/icon'

export default class CollectionSharingInviteView extends React.PureComponent {
state = {
emails: '',
role: 'member'
}
export default function CollectionSharingInviteView({ access, onInvite }) {
const [role, setRole] = useState('member')
const [loading, setLoading] = useState(false)

componentDidUpdate(prev) {
if (prev.status != this.props.status)
switch(this.props.status) {
case 'error':
Alert(t.s('sendInvites')+' '+t.s('server').toLowerCase(), { variant: 'error' })
break

case 'done':
this.setState({ emails: '' })
Alert(t.s('invitesSendTo')+' '+this.props.sendTo.join(', '))
break
}
}
const onRoleChange = useCallback(e=>{
setRole(e.currentTarget.value)
}, [setRole])

handleEmailsChange = (e)=>
this.setState({ emails: e.target.value })

handleChangeRole = (e)=>
this.setState({ role: e.currentTarget.value })

onSubmit = e=>{
const onSubmit = useCallback(e=>{
e.preventDefault()
setLoading(true)

if (!this.state.emails)
return

this.props.onInvite(this.state.emails.split(/,|\s/), this.state.role)
}
onInvite(
role,
async(link)=>{
const copy = await Prompt(
t.s('invite'),
link,
{
description: `
Share this link with the person you want to invite to the collection.
Anyone with this link and a Raindrop.io account can join and ${role == 'member' ? 'edit' : 'view'} the collection.
Please note, this link can only be used once and will expire in a week.
`,
ok: t.s('copyLinkToClipboard')
}
)

render() {
const { emails, role } = this.state
const { status, access } = this.props

const loading = status == 'loading'
if (copy) {
await navigator.permissions.query({name: 'clipboard-write'})
await await navigator.clipboard.writeText(link)
}
setLoading(false)
},
(error)=>{
Error(error, {
message: t.s('sendInvites')+' '+t.s('server').toLowerCase()
})
setLoading(false)
}
)
}, [onInvite, role, setLoading])

if (!access || access.level < 3)
return null
if (!access || access.level < 3)
return null

return (
<form onSubmit={this.onSubmit}>
<Layout type='grid'>
<Title>{t.s('invite')}</Title>
<Label>Email</Label>
<Text
type='email'
multiple={true}
placeholder={t.s('enterEmails')}
return (
<form onSubmit={onSubmit}>
<Layout type='grid'>
<Title>{t.s('invite')}</Title>

<Label>{t.s('withAccessLevel')}</Label>
<div>
<Radio
checked={role=='member'}
value='member'
disabled={loading}
value={emails}
required
autoFocus
onChange={this.handleEmailsChange} />
onChange={onRoleChange}>
{t.s('role_members')+' '+t.s('und')+' '+t.s('invite').toLowerCase()}
</Radio>

<Label>{t.s('withAccessLevel')}</Label>
<div>
<Radio
checked={role=='member'}
value='member'
disabled={loading}
onChange={this.handleChangeRole}>
{t.s('role_members')+' '+t.s('und')+' '+t.s('invite').toLowerCase()}
</Radio>

<Radio
checked={role=='viewer'}
value='viewer'
disabled={loading}
onChange={this.handleChangeRole}>
{t.s('role_viewer')}
</Radio>
</div>
<Radio
checked={role=='viewer'}
value='viewer'
disabled={loading}
onChange={onRoleChange}>
{t.s('role_viewer')}
</Radio>
</div>

<Buttons variant='between'>
<Button
as='input'
type='submit'
variant='primary'
disabled={loading}
value={t.s('sendInvites')+(loading ? '…' : '')} />
<Buttons variant='between'>
<Button
as='input'
type='submit'
variant='primary'
disabled={loading}
value={t.s('invite')+(loading ? '…' : '')} />

<Button
href={links.help.collaboration}
target='_blank'>
<Icon name='help' />
{t.s('howToUse')}
</Button>
</Buttons>
</Layout>
</form>
)
}
<Button
href={links.help.collaboration}
target='_blank'>
<Icon name='help' />
{t.s('howToUse')}
</Button>
</Buttons>
</Layout>
</form>
)
}
4 changes: 2 additions & 2 deletions src/co/overlay/dialog/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export async function Confirm(message, custom={}) {
}

//Open prompt
export async function Prompt(message, value='') {
return DialogsContainer.openDialog('prompt', { message, value })
export async function Prompt(message, value='', custom={}) {
return DialogsContainer.openDialog('prompt', { message, value, ...custom })
}

//Include this component anywhere in the app once!
Expand Down
8 changes: 5 additions & 3 deletions src/co/overlay/dialog/view/prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default class DialogPromptView extends React.PureComponent {
}

render() {
const { message } = this.props
const { message, ok, cancel, description } = this.props
const { value } = this.state

return (
Expand All @@ -38,6 +38,8 @@ export default class DialogPromptView extends React.PureComponent {

<form onSubmit={this.onSubmit}>
<Layout>
{description ? <div className={s.description}>{description}</div> : null}

<Text
autoFocus
selectAll
Expand All @@ -50,13 +52,13 @@ export default class DialogPromptView extends React.PureComponent {
as='input'
type='submit'
variant='primary'
value='OK' />
value={ok || 'OK'} />

<Button
data-block
variant='outline'
onClick={this.onCancel}>
{t.s('cancel')}
{cancel || t.s('cancel')}
</Button>
</Layout>
</form>
Expand Down
4 changes: 4 additions & 0 deletions src/co/overlay/dialog/view/prompt.module.styl
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.prompt {
width: 10rem //400px
}

.description {
color: var(--primary-text-color)
}
Loading

0 comments on commit 7f3da7e

Please sign in to comment.