aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/reportManager
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util/reportManager')
-rw-r--r--src/client/util/reportManager/ReportManager.tsx67
-rw-r--r--src/client/util/reportManager/ReportManagerComponents.tsx131
-rw-r--r--src/client/util/reportManager/reportManagerSchema.ts1
-rw-r--r--src/client/util/reportManager/reportManagerUtils.ts17
4 files changed, 118 insertions, 98 deletions
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx
index 0c49aeed4..2224e642d 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -1,33 +1,39 @@
-import * as React from 'react';
-import * as uuid from 'uuid';
-import '.././SettingsManager.scss';
-import './ReportManager.scss';
-import ReactLoading from 'react-loading';
+/* eslint-disable jsx-a11y/label-has-associated-control */
+/* eslint-disable jsx-a11y/media-has-caption */
+/* eslint-disable react/no-unused-class-component-methods */
+import { Octokit } from '@octokit/core';
+import { Button, Dropdown, DropdownType, IconButton, Type } from 'browndash-components';
import { action, makeObservable, observable } from 'mobx';
-import { BsX, BsArrowsAngleExpand, BsArrowsAngleContract } from 'react-icons/bs';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { BsArrowsAngleContract, BsArrowsAngleExpand, BsX } from 'react-icons/bs';
import { CgClose } from 'react-icons/cg';
import { HiOutlineArrowLeft } from 'react-icons/hi';
-import { Issue } from './reportManagerSchema';
-import { observer } from 'mobx-react';
+import { MdRefresh } from 'react-icons/md';
+import ReactLoading from 'react-loading';
+import * as uuid from 'uuid';
+import { ClientUtils } from '../../../ClientUtils';
import { Doc } from '../../../fields/Doc';
-import { MainViewModal } from '../../views/MainViewModal';
-import { Octokit } from '@octokit/core';
-import { Button, Dropdown, DropdownType, IconButton, Type } from 'browndash-components';
-import { BugType, FileData, Priority, ReportForm, ViewState, bugDropdownItems, darkColors, emptyReportForm, formatTitle, getAllIssues, isDarkMode, lightColors, passesTagFilter, priorityDropdownItems, uploadFilesToServer } from './reportManagerUtils';
-import { Filter, FormInput, FormTextArea, IssueCard, IssueView, Tag } from './ReportManagerComponents';
import { StrCast } from '../../../fields/Types';
-import { MdRefresh } from 'react-icons/md';
+import { MainViewModal } from '../../views/MainViewModal';
+import '../SettingsManager.scss';
import { SettingsManager } from '../SettingsManager';
+import './ReportManager.scss';
+import { Filter, FormInput, FormTextArea, IssueCard, IssueView } from './ReportManagerComponents';
+import { Issue } from './reportManagerSchema';
+import { BugType, FileData, Priority, ReportForm, ViewState, bugDropdownItems, darkColors, emptyReportForm, formatTitle, getAllIssues, isDarkMode, lightColors, passesTagFilter, priorityDropdownItems, uploadFilesToServer } from './reportManagerUtils';
/**
* Class for reporting and viewing Github issues within the app.
*/
@observer
export class ReportManager extends React.Component<{}> {
+ // eslint-disable-next-line no-use-before-define
public static Instance: ReportManager;
@observable private isOpen = false;
@observable private query = '';
+ // eslint-disable-next-line react/sort-comp
@action private setQuery = (q: string) => {
this.query = q;
};
@@ -82,7 +88,9 @@ export class ReportManager extends React.Component<{}> {
this.formData = newData;
});
- public close = action(() => (this.isOpen = false));
+ public close = action(() => {
+ this.isOpen = false;
+ });
public open = action(async () => {
this.isOpen = true;
if (this.shownIssues.length === 0) {
@@ -133,7 +141,7 @@ export class ReportManager extends React.Component<{}> {
const req = await this.octokit.request('POST /repos/{owner}/{repo}/issues', {
owner: 'brown-dash',
repo: 'Dash-Web',
- title: formatTitle(this.formData.title, Doc.CurrentUserEmail),
+ title: formatTitle(this.formData.title, ClientUtils.CurrentUserEmail()),
body: `${this.formData.description} ${formattedLinks.length > 0 ? `\n\nFiles:\n${formattedLinks.join('\n')}` : ''}`,
labels: ['from-dash-app', this.formData.type, this.formData.priority],
});
@@ -164,7 +172,7 @@ export class ReportManager extends React.Component<{}> {
* @returns JSX element of a piece of media (image, video, audio)
*/
private getMediaPreview = (fileData: FileData): JSX.Element => {
- const file = fileData.file;
+ const { file } = fileData;
const mimeType = file.type;
const preview = URL.createObjectURL(file);
@@ -179,7 +187,8 @@ export class ReportManager extends React.Component<{}> {
</div>
</div>
);
- } else if (mimeType.startsWith('video/')) {
+ }
+ if (mimeType.startsWith('video/')) {
return (
<div key={fileData._id} className="report-media-wrapper">
<div className="report-media-content">
@@ -193,7 +202,8 @@ export class ReportManager extends React.Component<{}> {
</div>
</div>
);
- } else if (mimeType.startsWith('audio/')) {
+ }
+ if (mimeType.startsWith('audio/')) {
return (
<div key={fileData._id} className="report-audio-wrapper">
<audio src={preview} controls />
@@ -203,7 +213,7 @@ export class ReportManager extends React.Component<{}> {
</div>
);
}
- return <></>;
+ return <div />;
};
/**
@@ -306,8 +316,8 @@ export class ReportManager extends React.Component<{}> {
<div className="report-selects">
<Dropdown
color={StrCast(Doc.UserDoc().userColor)}
- formLabel={'Type'}
- closeOnSelect={true}
+ formLabel="Type"
+ closeOnSelect
items={bugDropdownItems}
selectedVal={this.formData.type}
setSelectedVal={val => {
@@ -319,8 +329,8 @@ export class ReportManager extends React.Component<{}> {
/>
<Dropdown
color={StrCast(Doc.UserDoc().userColor)}
- formLabel={'Priority'}
- closeOnSelect={true}
+ formLabel="Priority"
+ closeOnSelect
items={priorityDropdownItems}
selectedVal={this.formData.priority}
setSelectedVal={val => {
@@ -346,7 +356,7 @@ export class ReportManager extends React.Component<{}> {
text="Submit"
type={Type.TERT}
color={StrCast(Doc.UserDoc().userVariantColor)}
- icon={<ReactLoading type="spin" color={'#ffffff'} width={20} height={20} />}
+ icon={<ReactLoading type="spin" color="#ffffff" width={20} height={20} />}
iconPlacement="right"
onClick={() => {
this.reportIssue();
@@ -363,7 +373,7 @@ export class ReportManager extends React.Component<{}> {
/>
)}
<div style={{ position: 'absolute', top: '4px', right: '4px' }}>
- <IconButton color={StrCast(Doc.UserDoc().userColor)} tooltip="close" icon={<CgClose size={'16px'} />} onClick={this.close} />
+ <IconButton color={StrCast(Doc.UserDoc().userColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={this.close} />
</div>
</div>
);
@@ -375,9 +385,8 @@ export class ReportManager extends React.Component<{}> {
private reportComponent = () => {
if (this.viewState === ViewState.VIEW) {
return this.viewIssuesComponent();
- } else {
- return this.reportIssueComponent();
}
+ return this.reportIssueComponent();
};
render() {
@@ -385,7 +394,7 @@ export class ReportManager extends React.Component<{}> {
<MainViewModal
contents={this.reportComponent()}
isDisplayed={this.isOpen}
- interactive={true}
+ interactive
closeOnExternalClick={this.close}
dialogueBoxStyle={{ width: 'auto', minWidth: '300px', height: '85vh', maxHeight: '90vh', background: StrCast(Doc.UserDoc().userBackgroundColor), borderRadius: '8px' }}
/>
diff --git a/src/client/util/reportManager/ReportManagerComponents.tsx b/src/client/util/reportManager/ReportManagerComponents.tsx
index 1e226bf6d..cecebc648 100644
--- a/src/client/util/reportManager/ReportManagerComponents.tsx
+++ b/src/client/util/reportManager/ReportManagerComponents.tsx
@@ -1,9 +1,15 @@
+/* eslint-disable react/require-default-props */
+/* eslint-disable prefer-destructuring */
+/* eslint-disable jsx-a11y/label-has-associated-control */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable no-use-before-define */
import * as React from 'react';
-import { Issue } from './reportManagerSchema';
-import { darkColors, dashBlue, getLabelColors, isDarkMode, lightColors } from './reportManagerUtils';
import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
+import { darkColors, dashBlue, getLabelColors, isDarkMode, lightColors } from './reportManagerUtils';
+import { Issue } from './reportManagerSchema';
import { StrCast } from '../../../fields/Types';
import { Doc } from '../../../fields/Doc';
@@ -18,7 +24,7 @@ interface FilterProps<T> {
}
// filter ui for issues (horizontal list of tags)
-export const Filter = <T extends string>({ items, activeValue, setActiveValue }: FilterProps<T>) => {
+export function Filter<T extends string>({ items, activeValue, setActiveValue }: FilterProps<T>) {
// establishing theme
const darkMode = isDarkMode(StrCast(Doc.UserDoc().userBackgroundColor));
const colors = darkMode ? darkColors : lightColors;
@@ -28,7 +34,7 @@ export const Filter = <T extends string>({ items, activeValue, setActiveValue }:
return (
<div className="issues-filter">
<Tag
- text={'All'}
+ text="All"
onClick={() => {
setActiveValue(null);
}}
@@ -38,25 +44,23 @@ export const Filter = <T extends string>({ items, activeValue, setActiveValue }:
borderColor={activeValue === null ? StrCast(Doc.UserDoc().userColor) : colors.border}
border
/>
- {items.map(item => {
- return (
- <Tag
- key={item}
- text={item}
- onClick={() => {
- setActiveValue(item);
- }}
- fontSize="12px"
- backgroundColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : 'transparent'}
- color={activeValue === item ? activeTagTextColor : colors.textGrey}
- border
- borderColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : colors.border}
- />
- );
- })}
+ {items.map(item => (
+ <Tag
+ key={item}
+ text={item}
+ onClick={() => {
+ setActiveValue(item);
+ }}
+ fontSize="12px"
+ backgroundColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : 'transparent'}
+ color={activeValue === item ? activeTagTextColor : colors.textGrey}
+ border
+ borderColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : colors.border}
+ />
+ ))}
</div>
);
-};
+}
interface IssueCardProps {
issue: Issue;
@@ -64,7 +68,7 @@ interface IssueCardProps {
}
// Component for the issue cards list on the left
-export const IssueCard = ({ issue, onSelect }: IssueCardProps) => {
+export function IssueCard({ issue, onSelect }: IssueCardProps) {
const [textColor, setTextColor] = React.useState('');
const [bgColor, setBgColor] = React.useState('transparent');
const [borderColor, setBorderColor] = React.useState('transparent');
@@ -103,14 +107,14 @@ export const IssueCard = ({ issue, onSelect }: IssueCardProps) => {
<h3 className="issue-title">{issue.title}</h3>
</div>
);
-};
+}
interface IssueViewProps {
issue: Issue;
}
// Detailed issue view that displays on the right
-export const IssueView = ({ issue }: IssueViewProps) => {
+export function IssueView({ issue }: IssueViewProps) {
const [issueBody, setIssueBody] = React.useState('');
// Parses the issue body into a formatted markdown (main functionality is replacing urls with tags)
@@ -127,15 +131,16 @@ export const IssueView = ({ issue }: IssueViewProps) => {
parts.map(async part => {
if (imgTagRegex.test(part) || videoTagRegex.test(part) || audioTagRegex.test(part)) {
return `\n${await parseFileTag(part)}\n`;
- } else if (fileRegex.test(part)) {
+ }
+ if (fileRegex.test(part)) {
const tag = await parseDashFiles(part);
return tag;
- } else if (localRegex.test(part)) {
+ }
+ if (localRegex.test(part)) {
const tag = await parseLocalFiles(part);
return tag;
- } else {
- return part;
}
+ return part;
})
);
@@ -143,7 +148,7 @@ export const IssueView = ({ issue }: IssueViewProps) => {
};
// Extracts the src from an image tag and either returns the raw url if not accessible or a new image tag
- const parseFileTag = async (tag: string): Promise<string> => {
+ const parseFileTag = async (tag: string): Promise<string | undefined> => {
const regex = /src="([^"]+)"/;
let url = '';
const match = tag.match(regex);
@@ -160,18 +165,19 @@ export const IssueView = ({ issue }: IssueViewProps) => {
case '.png':
case '.jpeg':
case '.gif':
- return await getDisplayedFile(url, 'image');
+ return getDisplayedFile(url, 'image');
// video
case '.mp4':
case '.mpeg':
case '.webm':
case '.mov':
- return await getDisplayedFile(url, 'video');
- //audio
+ return getDisplayedFile(url, 'video');
+ // audio
case '.mp3':
case '.wav':
case '.ogg':
- return await getDisplayedFile(url, 'audio');
+ return getDisplayedFile(url, 'audio');
+ default:
}
return tag;
};
@@ -183,14 +189,15 @@ export const IssueView = ({ issue }: IssueViewProps) => {
const dashAudioRegex = /https:\/\/browndash\.com\/files[/\\]audio/;
if (dashImgRegex.test(url)) {
- return await getDisplayedFile(url, 'image');
- } else if (dashVideoRegex.test(url)) {
- return await getDisplayedFile(url, 'video');
- } else if (dashAudioRegex.test(url)) {
- return await getDisplayedFile(url, 'audio');
- } else {
- return url;
+ return getDisplayedFile(url, 'image');
}
+ if (dashVideoRegex.test(url)) {
+ return getDisplayedFile(url, 'video');
+ }
+ if (dashAudioRegex.test(url)) {
+ return getDisplayedFile(url, 'audio');
+ }
+ return url;
};
// Returns the corresponding HTML tag for a src url
@@ -200,31 +207,37 @@ export const IssueView = ({ issue }: IssueViewProps) => {
const dashAudioRegex = /http:\/\/localhost:1050\.com\/files[/\\]audio/;
if (imgRegex.test(url)) {
- return await getDisplayedFile(url, 'image');
- } else if (dashVideoRegex.test(url)) {
- return await getDisplayedFile(url, 'video');
- } else if (dashAudioRegex.test(url)) {
- return await getDisplayedFile(url, 'audio');
- } else {
- return url;
+ return getDisplayedFile(url, 'image');
+ }
+ if (dashVideoRegex.test(url)) {
+ return getDisplayedFile(url, 'video');
+ }
+ if (dashAudioRegex.test(url)) {
+ return getDisplayedFile(url, 'audio');
}
+ return url;
};
- const getDisplayedFile = async (url: string, fileType: 'image' | 'video' | 'audio'): Promise<string> => {
+ const getDisplayedFile = async (url: string, fileType: 'image' | 'video' | 'audio'): Promise<string | undefined> => {
switch (fileType) {
- case 'image':
+ case 'image': {
const imgValid = await isImgValid(url);
if (!imgValid) return `\n${url} (This image could not be loaded)\n`;
return `\n${url}\n<img width="100%" alt="Issue asset" src=${url} />\n`;
- case 'video':
+ }
+ case 'video': {
const videoValid = await isVideoValid(url);
if (!videoValid) return `\n${url} (This video could not be loaded)\n`;
return `\n${url}\n<video class="report-default-video" width="100%" controls alt="Issue asset" src=${url} />\n`;
- case 'audio':
+ }
+ case 'audio': {
const audioValid = await isAudioValid(url);
if (!audioValid) return `\n${url} (This audio could not be loaded)\n`;
return `\n${url}\n<audio src=${url} controls />\n`;
+ }
+ default:
}
+ return undefined;
};
// Loads an image and returns a promise that resolves as whether the image is valid or not
@@ -270,7 +283,7 @@ export const IssueView = ({ issue }: IssueViewProps) => {
<div className="issue-view">
<span className="issue-label">
Issue{' '}
- <a className="issue-link" href={issue.html_url} target="_blank">
+ <a className="issue-link" href={issue.html_url} target="_blank" rel="noreferrer">
#{issue.number}
</a>
</span>
@@ -292,7 +305,7 @@ export const IssueView = ({ issue }: IssueViewProps) => {
<ReactMarkdown children={issueBody} className="issue-content" remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} />
</div>
);
-};
+}
interface TagProps {
text: string;
@@ -305,7 +318,7 @@ interface TagProps {
}
// Small tag for labels of the issue
-export const Tag = ({ text, color, backgroundColor, fontSize, border, borderColor, onClick }: TagProps) => {
+export function Tag({ text, color, backgroundColor, fontSize, border, borderColor, onClick }: TagProps) {
return (
<div
onClick={onClick ?? (() => {})}
@@ -314,14 +327,14 @@ export const Tag = ({ text, color, backgroundColor, fontSize, border, borderColo
{text}
</div>
);
-};
+}
interface FormInputProps {
value: string;
placeholder: string;
onChange: (val: string) => void;
}
-export const FormInput = ({ value, placeholder, onChange }: FormInputProps) => {
+export function FormInput({ value, placeholder, onChange }: FormInputProps) {
const [inputBorderColor, setInputBorderColor] = React.useState('');
return (
@@ -349,9 +362,9 @@ export const FormInput = ({ value, placeholder, onChange }: FormInputProps) => {
}}
/>
);
-};
+}
-export const FormTextArea = ({ value, placeholder, onChange }: FormInputProps) => {
+export function FormTextArea({ value, placeholder, onChange }: FormInputProps) {
const [textAreaBorderColor, setTextAreaBorderColor] = React.useState('');
return (
@@ -378,4 +391,4 @@ export const FormTextArea = ({ value, placeholder, onChange }: FormInputProps) =
}}
/>
);
-};
+}
diff --git a/src/client/util/reportManager/reportManagerSchema.ts b/src/client/util/reportManager/reportManagerSchema.ts
index 9a1c7c3e9..171c24393 100644
--- a/src/client/util/reportManager/reportManagerSchema.ts
+++ b/src/client/util/reportManager/reportManagerSchema.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-use-before-define */
/**
* Issue interface schema from Github.
*/
diff --git a/src/client/util/reportManager/reportManagerUtils.ts b/src/client/util/reportManager/reportManagerUtils.ts
index 22e5eebbb..f14967e0a 100644
--- a/src/client/util/reportManager/reportManagerUtils.ts
+++ b/src/client/util/reportManager/reportManagerUtils.ts
@@ -63,9 +63,8 @@ export const getAllIssues = async (octokit: Octokit): Promise<any[]> => {
// 200 status means success
if (res.status === 200) {
return res.data;
- } else {
- throw new Error('Error getting issues');
}
+ throw new Error('Error getting issues');
};
/**
@@ -104,9 +103,7 @@ export const fileLinktoServerLink = (fileLink: string): string => {
* @param link response from file upload
* @returns server file path
*/
-export const getServerPath = (link: any): string => {
- return link.result.accessPaths.agnostic.server as string;
-};
+export const getServerPath = (link: any): string => link.result.accessPaths.agnostic.server as string;
/**
* Uploads media files to the server.
@@ -124,6 +121,7 @@ export const uploadFilesToServer = async (mediaFiles: FileData[]): Promise<strin
alert(err);
}
}
+ return undefined;
};
// helper functions
@@ -141,18 +139,16 @@ export const passesTagFilter = (issue: Issue, priorityFilter: string | null, bug
passesPriority = issue.labels.some(label => {
if (typeof label === 'string') {
return label === priorityFilter;
- } else {
- return label.name === priorityFilter;
}
+ return label.name === priorityFilter;
});
}
if (bugFilter) {
passesBug = issue.labels.some(label => {
if (typeof label === 'string') {
return label === bugFilter;
- } else {
- return label.name === bugFilter;
}
+ return label.name === bugFilter;
});
}
return passesPriority && passesBug;
@@ -217,7 +213,8 @@ export const bugColors: { [key: string]: string[] } = {
export const getLabelColors = (label: string): string[] => {
if (prioritySet.has(label as Priority)) {
return priorityColors[label];
- } else if (bugSet.has(label as BugType)) {
+ }
+ if (bugSet.has(label as BugType)) {
return bugColors[label];
}
return ['#0f73f6', '#ffffff'];