Merge pull request #3382 from tylershuster/links-s3

Links s3
This commit is contained in:
matildepark 2020-09-02 18:06:53 -04:00 committed by GitHub
commit 6e2ce697f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 159 additions and 201 deletions

View File

@ -6,6 +6,7 @@ import InviteReducer from '../reducers/invite-update';
import LinkReducer from '../reducers/link-update';
import ListenReducer from '../reducers/listen-update';
import LocalReducer from '../reducers/local';
import S3Reducer from '../reducers/s3-update';
import BaseStore from './base';
@ -21,6 +22,7 @@ export default class LinksStore extends BaseStore {
this.localReducer = new LocalReducer();
this.linkReducer = new LinkReducer();
this.listenReducer = new ListenReducer();
this.s3Reducer = new S3Reducer();
}
initialState() {
@ -37,6 +39,7 @@ export default class LinksStore extends BaseStore {
comments: {},
seen: {},
permissions: {},
s3: {},
sidebarShown: true
};
}
@ -50,6 +53,7 @@ export default class LinksStore extends BaseStore {
this.localReducer.reduce(data, this.state);
this.linkReducer.reduce(data, this.state);
this.listenReducer.reduce(data, this.state);
this.s3Reducer.reduce(data, this.state);
}
}

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import ChatEditor from './chat-editor';
import { S3Upload } from './s3-upload'
import { S3Upload } from '~/views/components/s3-upload'
;
import { uxToHex } from '~/logic/lib/util';
import { Sigil } from '~/logic/lib/sigil';
@ -221,10 +221,10 @@ export class ChatInput extends Component {
style={{ flexGrow: 1 }}>
<div className="fl"
style={{
marginTop: 6,
flexBasis: 24,
height: 24
}}>
marginTop: 6,
flexBasis: 24,
height: 24
}}>
{avatar}
</div>
<ChatEditor
@ -235,32 +235,40 @@ export class ChatInput extends Component {
placeholder='Message...' />
<div className="ml2 mr2"
style={{
height: '16px',
width: '16px',
flexBasis: 16,
marginTop: 10
}}>
height: '16px',
width: '16px',
flexBasis: 16,
marginTop: 10
}}>
<S3Upload
configuration={props.s3.configuration}
credentials={props.s3.credentials}
uploadSuccess={this.uploadSuccess.bind(this)}
uploadError={this.uploadError.bind(this)}
/>
accept="image/*"
>
<img
className="invert-d"
src="/~chat/img/ImageUpload.png"
width="16"
height="16"
/>
</S3Upload>
</div>
<div style={{
height: '16px',
width: '16px',
flexBasis: 16,
marginTop: 10
height: '16px',
width: '16px',
flexBasis: 16,
marginTop: 10
}}>
<img style={{
filter: state.inCodeMode && 'invert(100%)',
height: '14px',
width: '14px',
}}
onClick={this.toggleCode}
src="/~chat/img/CodeEval.png"
className="contrast-10-d bg-white bg-none-d ba b--gray1-d br1" />
filter: state.inCodeMode && 'invert(100%)',
height: '14px',
width: '14px',
}}
onClick={this.toggleCode}
src="/~chat/img/CodeEval.png"
className="contrast-10-d bg-white bg-none-d ba b--gray1-d br1" />
</div>
</div>
);

View File

@ -1,105 +0,0 @@
import React, { Component } from 'react';
import S3Client from '~/logic/lib/s3';
export class S3Upload extends Component {
constructor(props) {
super(props);
this.s3 = new S3Client();
this.setCredentials(props.credentials, props.configuration);
this.inputRef = React.createRef();
}
isReady(creds, config) {
return (
Boolean(creds) &&
'endpoint' in creds &&
'accessKeyId' in creds &&
'secretAccessKey' in creds &&
creds.endpoint !== '' &&
creds.accessKeyId !== '' &&
creds.secretAccessKey !== '' &&
Boolean(config) &&
'currentBucket' in config &&
config.currentBucket !== ''
);
}
componentDidUpdate(prevProps) {
const { props } = this;
this.setCredentials(props.credentials, props.configuration);
}
setCredentials(credentials, configuration) {
if (!this.isReady(credentials, configuration)) {
return;
}
this.s3.setCredentials(
credentials.endpoint,
credentials.accessKeyId,
credentials.secretAccessKey
);
}
getFileUrl(endpoint, filename) {
return endpoint + '/' + filename;
}
onChange() {
const { props } = this;
if (!this.inputRef.current) {
return;
}
const files = this.inputRef.current.files;
if (files.length <= 0) {
return;
}
const file = files.item(0);
const bucket = props.configuration.currentBucket;
this.s3.upload(bucket, file.name, file).then((data) => {
if (!data || !('Location' in data)) {
return;
}
this.props.uploadSuccess(data.Location);
}).catch((err) => {
console.error(err);
this.props.uploadError(err);
});
}
onClick() {
if (!this.inputRef.current) {
return;
}
this.inputRef.current.click();
}
render() {
const { props } = this;
if (!this.isReady(props.credentials, props.configuration)) {
return <div></div>;
} else {
const classes = props.className ?
'pointer ' + props.className : 'pointer';
return (
<div className={classes}>
<input className="dn"
type="file"
id="fileElement"
ref={this.inputRef}
accept="image/*"
onChange={this.onChange.bind(this)}
/>
<img className="invert-d"
src="/~chat/img/ImageUpload.png"
width="16"
height="16"
onClick={this.onClick.bind(this)}
/>
</div>
);
}
}
}

View File

@ -5,7 +5,7 @@ import { Link } from 'react-router-dom';
import { EditElement } from './edit-element';
import { Spinner } from '~/views/components/Spinner';
import { uxToHex } from '~/logic/lib/util';
import { S3Upload } from './s3-upload';
import { S3Upload } from '~/views/components/s3-upload';
export class ContactCard extends Component {
constructor(props) {
@ -492,7 +492,15 @@ export class ContactCard extends Component {
credentials={props.s3.credentials}
uploadSuccess={this.uploadSuccess.bind(this)}
uploadError={this.uploadError.bind(this)}
/>
accept="image/*"
>
<img
className="invert-d"
src="/~chat/img/ImageUpload.png"
width="32"
height="32"
/>
</S3Upload>
</span>
<EditElement
className="fr w-80"

View File

@ -59,7 +59,7 @@ export class LinksApp extends Component {
props.invites : {};
const listening = props.linkListening;
const { api, sidebarShown, hideAvatars, hideNicknames } = this.props;
const { api, sidebarShown, hideAvatars, hideNicknames, s3 } = this.props;
return (
<>
@ -256,6 +256,7 @@ export class LinksApp extends Component {
api={api}
hideNicknames={hideNicknames}
hideAvatars={hideAvatars}
s3={s3}
/>
</Skeleton>
);

View File

@ -1,5 +1,8 @@
import React, { Component } from 'react';
import { S3Upload } from '~/views/components/s3-upload';
import { Spinner } from '~/views/components/Spinner';
import { Icon } from "@tlon/indigo-react";
export class LinkSubmit extends Component {
constructor() {
@ -60,23 +63,29 @@ export class LinkSubmit extends Component {
this.setState({ linkTitle: event.target.value });
}
uploadSuccess(url) {
this.setState({ linkValue: url });
this.setLinkValid(url);
}
uploadError(error) {
// no-op for now
}
render() {
console.log('s3', this.props.s3);
const activeClasses = (this.state.linkValid && !this.state.disabled)
? 'green2 pointer' : 'gray2';
const focus = (this.state.submitFocus)
? 'b--black b--white-d'
: 'b--gray4 b--gray2-d';
return (
<div className={'relative ba br1 w-100 mb6 ' + focus}>
<textarea
className="pl2 bg-gray0-d white-d w-100 f8"
style={{
resize: 'none',
height: 40,
paddingTop: 10
}}
<input
type="url"
className="pl2 bg-gray0-d white-d w-100 f8 pt2"
placeholder="Paste link here"
onChange={this.setLinkValue}
onBlur={() => this.setState({ submitFocus: false })}
@ -91,41 +100,51 @@ export class LinkSubmit extends Component {
}}
value={this.state.linkValue}
/>
<textarea
className="pl2 bg-gray0-d white-d w-100 f8"
style={{
resize: 'none',
height: 40,
paddingTop: 16
}}
placeholder="Enter title"
onChange={this.setLinkTitle}
onBlur={() => this.setState({ submitFocus: false })}
onFocus={() => this.setState({ submitFocus: true })}
spellCheck="false"
rows={1}
onKeyPress={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.onClickPost();
<div className="flex flex-auto mt2 pt2">
<input
type="text"
className="pl2 bg-gray0-d white-d w-100 f8"
style={{
resize: 'none',
height: 40
}}
placeholder="Enter title"
onChange={this.setLinkTitle}
onBlur={() => this.setState({ submitFocus: false })}
onFocus={() => this.setState({ submitFocus: true })}
spellCheck="false"
rows={1}
onKeyPress={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.onClickPost();
}
}}
value={this.state.linkTitle}
/>
{!this.state.disabled ? <S3Upload
configuration={this.props.s3.configuration}
credentials={this.props.s3.credentials}
uploadSuccess={this.uploadSuccess.bind(this)}
uploadError={this.uploadError.bind(this)}
className="nowrap flex items-center pr2 h-100"
><span className="green2 f8">Upload File</span></S3Upload> : null}
{!this.state.disabled ? <button
className={
'bg-gray0-d f8 flex-shrink-0 pr2 ' + activeClasses
}
}}
value={this.state.linkTitle}
/>
<button
className={
'absolute bg-gray0-d f8 ml2 flex-shrink-0 ' + activeClasses
}
disabled={!this.state.linkValid || this.state.disabled}
onClick={this.onClickPost.bind(this)}
style={{
bottom: 12,
right: 8
}}
>
Post
</button>
<Spinner awaiting={this.state.disabled} classes="mt3 absolute right-0" text="Posting to collection..." />
disabled={!this.state.linkValid || this.state.disabled}
onClick={this.onClickPost.bind(this)}
style={{
bottom: 12,
right: 8
}}
>
Post
</button> : null}
<Spinner awaiting={this.state.disabled} classes="nowrap flex items-center pr2" style={{flex: '1 1 14rem'}} text="Posting to collection..." />
</div>
</div>
) ;
}

View File

@ -139,7 +139,7 @@ export class Links extends Component {
<div className="w-100 mt6 flex justify-center overflow-y-scroll ph4 pb4">
<div className="w-100 mw7">
<div className="flex">
<LinkSubmit resourcePath={props.resourcePath} api={this.props.api} />
<LinkSubmit resourcePath={props.resourcePath} api={this.props.api} s3={props.s3} />
</div>
<div className="pb4">
{LinkList}

View File

@ -1,9 +1,10 @@
import React, { Component } from 'react';
import urbitOb from 'urbit-ob';
import { Link } from 'react-router-dom';
import { InviteSearch } from '~/views/components/InviteSearch';
import { Spinner } from '~/views/components/Spinner';
import { Link } from 'react-router-dom';
import { makeRoutePath, deSig } from '~/logic/lib/util';
import urbitOb from 'urbit-ob';
export class NewScreen extends Component {
constructor(props) {

View File

@ -8,7 +8,7 @@ export class Spinner extends Component {
if (awaiting) {
return (
<div className={classes + ' z-2 bg-white bg-gray0-d white-d'}>
<div className={classes + ' z-2 bg-white bg-gray0-d white-d flex'}>
<img className="invert-d spin-active v-mid"
src="/~landscape/img/Spinner.png"
width={16}

View File

@ -1,10 +1,16 @@
import React, { Component } from 'react'
import { Icon } from "@tlon/indigo-react";
import S3Client from '~/logic/lib/s3';
import { Spinner } from './Spinner';
export class S3Upload extends Component {
constructor(props) {
super(props);
this.state = {
isUploading: false
};
this.s3 = new S3Client();
this.setCredentials(props.credentials, props.configuration);
this.inputRef = React.createRef();
@ -52,15 +58,22 @@ export class S3Upload extends Component {
let file = files.item(0);
let bucket = props.configuration.currentBucket;
this.s3.upload(bucket, file.name, file).then((data) => {
if (!data || !('Location' in data)) {
return;
}
this.props.uploadSuccess(data.Location);
}).catch((err) => {
console.error(err);
this.props.uploadError(err);
});
this.setState({ isUploading: true });
this.s3.upload(bucket, file.name, file)
.then((data) => {
if (!data || !('Location' in data)) {
return;
}
this.props.uploadSuccess(data.Location);
})
.catch((err) => {
console.error(err);
this.props.uploadError(err);
})
.finally(() => {
this.setState({ isUploading: false });
});
}
onClick() {
@ -69,25 +82,34 @@ export class S3Upload extends Component {
}
render() {
const { props } = this;
if (!this.isReady(props.credentials, props.configuration)) {
const {
credentials,
configuration,
className,
accept = '*',
children = false
} = this.props;
if (!this.isReady(credentials, configuration)) {
return <div></div>;
} else {
let classes = !!props.className ?
"pointer " + props.className : "pointer";
let classes = !!className
? "pointer " + className
: "pointer";
const display = children || <Icon icon='ArrowNorth' />;
return (
<div className={classes}>
<input className="dn"
type="file"
id="fileElement"
ref={this.inputRef}
accept="image/*"
onChange={this.onChange.bind(this)} />
<img className="invert-d"
src="/~landscape/img/ImageUpload.png"
width="32"
height="32"
onClick={this.onClick.bind(this)} />
<div>
<input
className="dn"
type="file"
id="fileElement"
ref={this.inputRef}
accept={accept}
onChange={this.onChange.bind(this)} />
{this.state.isUploading
? <Spinner awaiting={true} classes={className} />
: <span className={`pointer ${className}`} onClick={this.onClick.bind(this)}>{display}</span>
}
</div>
);
}