Skip to content

Commit

Permalink
Merge pull request #35 from glific/feature/attachments
Browse files Browse the repository at this point in the history
Added upload attachment functionality
  • Loading branch information
mdshamoon authored May 25, 2021
2 parents 5c51b67 + 8010f2f commit 3b7ca53
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 19 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@glific/flow-editor",
"license": "AGPL-3.0",
"repository": "git://github.com/nyaruka/floweditor.git",
"version": "1.13.9-1",
"repository": "git://github.com/glific/floweditor.git",
"version": "1.13.9-2",
"description": "'Standalone flow editing tool designed for use within the RapidPro suite of messaging tools but can be adopted for use outside of that ecosystem.'",
"browser": "umd/flow-editor.min.js",
"unpkg": "umd/flow-editor.min.js",
Expand Down
6 changes: 3 additions & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,9 @@
flow: uuid,
flowType: 'messaging',
localStorage: true,
attachmentsEnabled:true,
mutable: true,
filters: ['whatsapp'],
filters: ['whatsapp', 'classifier'],

excludeTypes: [
'add_contact_urn',
Expand All @@ -176,7 +177,6 @@
'start_session',
'open_ticket',
'transfer_airtime',
'split_by_intent',
'split_by_contact_field',
'split_by_random',
'split_by_groups',
Expand Down Expand Up @@ -235,7 +235,7 @@
recipients: base + 'recipients',
functions: base + 'functions',
completion: base + 'completion',
attachments: base + 'attachments',
attachments: base + 'flow-attachment',
activity: base + 'activity',
validateMedia: base + 'validate-media',

Expand Down
27 changes: 23 additions & 4 deletions src/components/flow/actions/localization/MsgLocalizationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { initializeLocalizedForm } from './helpers';
import i18n from 'config/i18n';
import { range } from 'utils';
import { renderIssues } from '../helpers';
import { Attachment, renderAttachments } from '../sendmsg/attachments';
import { Attachment, renderAttachments, validateURL } from '../sendmsg/attachments';
import { AxiosResponse } from 'axios';

export interface MsgLocalizationFormState extends FormState {
Expand All @@ -32,6 +32,7 @@ export default class MsgLocalizationForm extends React.Component<
LocalizationFormProps,
MsgLocalizationFormState
> {
private timeout: any;
constructor(props: LocalizationFormProps) {
super(props);
this.state = initializeLocalizedForm(this.props.nodeSettings);
Expand Down Expand Up @@ -165,21 +166,37 @@ export default class MsgLocalizationForm extends React.Component<

private handleAttachmentUploaded(response: AxiosResponse) {
const attachments: any = mutate(this.state.attachments, {
$push: [{ type: response.data.type, url: response.data.url, uploaded: true }]
$push: [{ type: 'document', url: response.data.url, uploaded: true }]
});
this.setState({ attachments });
}

private attachmentValidate(body: any, valid: boolean, validationFailures: any) {
const attachments: any = mutate(this.state.attachments, {
[0]: {
$set: { type: body.type, url: body.url, valid, validationFailures }
}
});
this.setState({ attachments });
}

private handleAttachmentChanged(index: number, type: string, url: string) {
let attachments: any = this.state.attachments;

if (type && url) {
window.clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
validateURL(this.props.assetStore.validateMedia.endpoint, attachments[0], this);
}, 1000);
}
if (index === -1) {
attachments = mutate(attachments, {
$push: [{ type, url }]
});
} else {
attachments = mutate(attachments, {
[index]: {
$set: { type, url }
$set: { type, url, valid: true }
}
});
}
Expand Down Expand Up @@ -250,12 +267,14 @@ export default class MsgLocalizationForm extends React.Component<
name: i18n.t('forms.attachments', 'Attachments'),
body: renderAttachments(
this.context.config.endpoints.attachments,
this.context.config.attachmentsEnabled,
this.state.attachments,
this.handleAttachmentUploaded,
this.handleAttachmentChanged,
this.handleAttachmentRemoved
),
checked: this.state.attachments.length > 0
checked: this.state.attachments.length > 0,
hasErrors: this.state.attachments.length > 0 && this.state.attachments[0].valid
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/components/flow/actions/sendmsg/SendMsgForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export default class SendMsgForm extends React.Component<ActionFormProps, SendMs

private handleAttachmentUploaded(response: AxiosResponse) {
const attachments: any = mutate(this.state.attachments, {
$push: [{ type: response.data.type, url: response.data.url, uploaded: true }]
$push: [{ type: 'document', url: response.data.url, uploaded: true }]
});
this.setState({ attachments });
}
Expand Down Expand Up @@ -345,6 +345,7 @@ export default class SendMsgForm extends React.Component<ActionFormProps, SendMs
name: i18n.t('forms.attachments', 'Attachments'),
body: renderAttachments(
this.context.config.endpoints.attachments,
this.context.config.attachmentsEnabled,
this.state.attachments,
this.handleAttachmentUploaded,
this.handleAttachmentChanged,
Expand Down
31 changes: 22 additions & 9 deletions src/components/flow/actions/sendmsg/attachments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,9 @@ const TYPE_OPTIONS: SelectOption[] = [
{ value: 'document', name: i18n.t('forms.pdf_url', 'PDF Document URL') }
];

// const NEW_TYPE_OPTIONS = TYPE_OPTIONS.concat([
// { value: 'upload', name: i18n.t('forms.upload_attachment', 'Upload Attachment') }
// ]);

const NEW_TYPE_OPTIONS = TYPE_OPTIONS;
const NEW_TYPE_OPTIONS = TYPE_OPTIONS.concat([
{ value: 'upload', name: i18n.t('forms.upload_attachment', 'Upload Attachment') }
]);

export const validateURL = (endpoint: any, body: any, msgForm: any) => {
axios
Expand Down Expand Up @@ -74,7 +72,10 @@ export const handleUploadFile = (
headers['X-Requested-With'] = 'XMLHttpRequest';

const data = new FormData();
data.append('file', files[0]);
data.append('media', files[0]);
const mediaName = files[0].name;
const extension = mediaName.slice((Math.max(0, mediaName.lastIndexOf('.')) || Infinity) + 1);
data.append('extension', extension);
axios
.post(endpoint, data, { headers })
.then(onSuccess)
Expand All @@ -85,6 +86,7 @@ export const handleUploadFile = (

export const renderAttachments = (
endpoint: string,
attachmentsEnabled: boolean,
attachments: Attachment[],
onUploaded: (response: AxiosResponse) => void,
onAttachmentChanged: (index: number, value: string, url: string) => void,
Expand All @@ -93,12 +95,19 @@ export const renderAttachments = (
const renderedAttachments = attachments.map((attachment, index: number) =>
attachment.uploaded
? renderUpload(index, attachment, onAttachmentRemoved)
: renderAttachment(index, attachment, onAttachmentChanged, onAttachmentRemoved)
: renderAttachment(
attachmentsEnabled,
index,
attachment,
onAttachmentChanged,
onAttachmentRemoved
)
);

const emptyOption =
attachments.length < MAX_ATTACHMENTS
? renderAttachment(
attachmentsEnabled,
-1,
{ url: '', type: '' },

Expand Down Expand Up @@ -147,7 +156,10 @@ export const renderUpload = (
name={i18n.t('forms.type', 'Type')}
style={TembaSelectStyle.small}
entry={{
value: { name: attachment.type }
value: {
name:
attachment.url.length > 20 ? `${attachment.url.slice(0, 20)}...` : attachment.url
}
}}
options={TYPE_OPTIONS}
disabled={true}
Expand Down Expand Up @@ -180,6 +192,7 @@ export const renderUpload = (
};

export const renderAttachment = (
attachmentsEnabled: boolean,
index: number,
attachment: Attachment,
onAttachmentChanged: (index: number, type: string, url: string) => void,
Expand Down Expand Up @@ -209,7 +222,7 @@ export const renderAttachment = (
onAttachmentChanged(index, option.value, index === -1 ? '' : attachment.url);
}
}}
options={index > -1 ? TYPE_OPTIONS : NEW_TYPE_OPTIONS}
options={attachmentsEnabled ? NEW_TYPE_OPTIONS : TYPE_OPTIONS}
/>
</div>
{index > -1 ? (
Expand Down
1 change: 1 addition & 0 deletions src/components/flow/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface RouterFormProps extends IssueProps {
export interface LocalizationFormProps extends IssueProps {
language: Asset;
nodeSettings: NodeEditorSettings;
assetStore?: AssetStore;
updateLocalizations(languageCode: string, localizations: any[]): void;
onClose(canceled: boolean): void;
helpArticles: { [key: string]: string };
Expand Down
1 change: 1 addition & 0 deletions src/components/nodeeditor/NodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export class NodeEditor extends React.Component<NodeEditorProps> {
onClose: this.close,
language: this.props.language,
helpArticles: this.props.helpArticles,
assetStore: this.props.assetStore,
issues: this.props.issues.filter(
(issue: FlowIssue) => issue.language === this.props.language.id
)
Expand Down
1 change: 1 addition & 0 deletions src/flowTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface Endpoints {
}

export interface FlowEditorConfig {
attachmentsEnabled: boolean;
localStorage: boolean;
endpoints: Endpoints;
flow: string;
Expand Down
1 change: 1 addition & 0 deletions src/test/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const config: FlowEditorConfig = {
flow: 'a4f64f1b-85bc-477e-b706-de313a022979',
localStorage: true,
showDownload: true,
attachmentsEnabled: false,
flowType: FlowTypes.MESSAGING,
mutable: true,
filters: ['whatsapp', 'airtime', 'resthook', 'classifier', 'ticketer'],
Expand Down

0 comments on commit 3b7ca53

Please sign in to comment.