Renamed /src to /frontend
This commit is contained in:
101
frontend/utils/ApiClient.js
Normal file
101
frontend/utils/ApiClient.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import _map from 'lodash/map';
|
||||
import stores from 'stores';
|
||||
|
||||
import constants from '../constants';
|
||||
|
||||
class ApiClient {
|
||||
constructor(options = {}) {
|
||||
this.baseUrl = options.baseUrl || constants.API_BASE_URL;
|
||||
this.userAgent = options.userAgent || constants.API_USER_AGENT;
|
||||
}
|
||||
|
||||
fetch = (path, method, data) => {
|
||||
let body;
|
||||
let modifiedPath;
|
||||
|
||||
if (method === 'GET') {
|
||||
modifiedPath = `${path}?${this.constructQueryString(data)}`;
|
||||
} else if (method === 'POST' || method === 'PUT') {
|
||||
body = JSON.stringify(data);
|
||||
}
|
||||
|
||||
// Construct headers
|
||||
const headers = new Headers({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': this.userAgent,
|
||||
});
|
||||
if (stores.user.authenticated) {
|
||||
headers.set('Authorization', `Bearer ${stores.user.token}`);
|
||||
}
|
||||
|
||||
// Construct request
|
||||
const request = fetch(this.baseUrl + (modifiedPath || path), {
|
||||
method,
|
||||
body,
|
||||
headers,
|
||||
redirect: 'follow',
|
||||
});
|
||||
|
||||
// Handle request promises and return a new promise
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
.then((response) => {
|
||||
// Handle successful responses
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Handle 401, log out user
|
||||
if (response.status === 401) {
|
||||
stores.user.logout();
|
||||
}
|
||||
|
||||
// Handle failed responses
|
||||
let error;
|
||||
try {
|
||||
// Expect API to return JSON
|
||||
error = JSON.parse(response);
|
||||
} catch (e) {
|
||||
// Expect call to fail without JSON response
|
||||
error = { error: response.statusText };
|
||||
}
|
||||
|
||||
error.statusCode = response.status;
|
||||
error.response = response;
|
||||
reject(error);
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((json) => {
|
||||
resolve(json);
|
||||
})
|
||||
.catch(() => {
|
||||
reject({ error: 'Unknown error' });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get = (path, data) => {
|
||||
return this.fetch(path, 'GET', data);
|
||||
}
|
||||
|
||||
post = (path, data) => {
|
||||
return this.fetch(path, 'POST', data);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
constructQueryString = (data) => {
|
||||
return _map(data, (v, k) => {
|
||||
return `${encodeURIComponent(k)}=${encodeURIComponent(v)}`;
|
||||
}).join('&');
|
||||
};
|
||||
}
|
||||
|
||||
export default ApiClient;
|
||||
|
||||
// In case you don't want to always initiate, just import with `import { client } ...`
|
||||
const client = new ApiClient();
|
||||
export { client };
|
||||
3
frontend/utils/History.js
Normal file
3
frontend/utils/History.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md
|
||||
import browserHistory from 'react-router/lib/browserHistory';
|
||||
export default browserHistory;
|
||||
9
frontend/utils/actions.js
Normal file
9
frontend/utils/actions.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export default (type, ...argNames) => {
|
||||
return function(...args) {
|
||||
let action = { type }
|
||||
argNames.forEach((arg, index) => {
|
||||
action[argNames[index]] = args[index]
|
||||
})
|
||||
return action
|
||||
}
|
||||
}
|
||||
1
frontend/utils/emoji-mapping.json
Normal file
1
frontend/utils/emoji-mapping.json
Normal file
File diff suppressed because one or more lines are too long
16
frontend/utils/emojify.js
Normal file
16
frontend/utils/emojify.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import emojiMapping from './emoji-mapping.json';
|
||||
|
||||
const EMOJI_REGEX = /:([A-Za-z0-9_\-\+]+?):/gm;
|
||||
|
||||
const emojify = (text='') => {
|
||||
const emojis = text.match(EMOJI_REGEX) || [];
|
||||
let emojifiedText = text;
|
||||
|
||||
emojifiedText = text.replace(EMOJI_REGEX, (match, p1, offset, string) => {
|
||||
return emojiMapping[p1] || match;
|
||||
});
|
||||
|
||||
return emojifiedText;
|
||||
};
|
||||
|
||||
export default emojify;
|
||||
43
frontend/utils/markdown.js
Normal file
43
frontend/utils/markdown.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import slug from 'slug';
|
||||
import marked, { Renderer } from 'marked';
|
||||
import highlight from 'highlight.js';
|
||||
import emojify from './emojify';
|
||||
import _escape from 'lodash/escape';
|
||||
|
||||
slug.defaults.mode ='rfc3986';
|
||||
|
||||
const renderer = new Renderer();
|
||||
renderer.code = (code, language) => {
|
||||
const validLang = !!(language && highlight.getLanguage(language));
|
||||
const highlighted = validLang ? highlight.highlight(language, code).value : _escape(code);
|
||||
return `<pre><code class="hljs ${language}">${ highlighted }</code></pre>`;
|
||||
};
|
||||
renderer.heading = (text, level) => {
|
||||
const headingSlug = slug(text);
|
||||
return `
|
||||
<h${level}>
|
||||
${text}
|
||||
<a name="${headingSlug}" class="anchor" href="#${headingSlug}">#</a>
|
||||
</h${level}>
|
||||
`;
|
||||
},
|
||||
|
||||
marked.setOptions({
|
||||
renderer: renderer,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: true,
|
||||
smartLists: true,
|
||||
smartypants: true,
|
||||
});
|
||||
|
||||
// TODO: This is syncronous and can be costly
|
||||
const convertToMarkdown = (text) => {
|
||||
return marked(emojify(text));
|
||||
};
|
||||
|
||||
export {
|
||||
convertToMarkdown,
|
||||
};
|
||||
7
frontend/utils/random.js
Normal file
7
frontend/utils/random.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const randomInteger = (min, max) => {
|
||||
return Math.floor(Math.random()*(max-min+1)+min);
|
||||
}
|
||||
|
||||
export {
|
||||
randomInteger
|
||||
};
|
||||
Reference in New Issue
Block a user