mirror of
https://github.com/wassname/talk.git
synced 2026-06-30 01:58:00 +08:00
264 lines
7.0 KiB
JavaScript
264 lines
7.0 KiB
JavaScript
import { gql } from 'react-apollo';
|
|
import t from 'coral-framework/services/i18n';
|
|
import union from 'lodash/union';
|
|
import { capitalize } from 'coral-framework/helpers/strings';
|
|
import assignWith from 'lodash/assignWith';
|
|
import mapValues from 'lodash/mapValues';
|
|
export * from 'coral-framework/helpers/strings';
|
|
|
|
export const getTotalActionCount = (type, comment) => {
|
|
return comment.action_summaries
|
|
.filter(s => s.__typename === type)
|
|
.reduce((total, summary) => {
|
|
return total + summary.count;
|
|
}, 0);
|
|
};
|
|
|
|
export const iPerformedThisAction = (type, comment) => {
|
|
// if there is a current_user on any of the ActionSummary(s), the user performed this action
|
|
return comment.action_summaries
|
|
.filter(a => a.__typename === type)
|
|
.some(a => a.current_user);
|
|
};
|
|
|
|
export const getMyActionSummary = (type, comment) => {
|
|
return comment.action_summaries
|
|
.filter(a => a.__typename === type)
|
|
.find(a => a.current_user);
|
|
};
|
|
|
|
/**
|
|
* getActionSummary
|
|
* retrieves the action summaries based on the type and the comment
|
|
* array could be length > 1, as in the case of FlagActionSummary
|
|
*/
|
|
|
|
export const getActionSummary = (type, comment) => {
|
|
return comment.action_summaries.filter(a => a.__typename === type);
|
|
};
|
|
|
|
/**
|
|
* Get name of first (or $pos-th) definition
|
|
*/
|
|
export function getDefinitionName(doc, pos = 0) {
|
|
return doc.definitions[pos].name.value;
|
|
}
|
|
|
|
/**
|
|
* Separate apollo `data` props into `data` and `root`.
|
|
* `data` will contain props like `loading`, `fetchMore`...
|
|
* while `root` contains the actual query data.
|
|
*/
|
|
export function separateDataAndRoot({
|
|
fetchMore,
|
|
loading,
|
|
networkStatus,
|
|
refetch,
|
|
startPolling,
|
|
stopPolling,
|
|
subscribeToMore,
|
|
updateQuery,
|
|
variables,
|
|
error,
|
|
...root
|
|
}) {
|
|
return {
|
|
data: {
|
|
fetchMore,
|
|
loading,
|
|
networkStatus,
|
|
refetch,
|
|
startPolling,
|
|
stopPolling,
|
|
subscribeToMore,
|
|
updateQuery,
|
|
variables,
|
|
error,
|
|
},
|
|
root,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Taken from: http://stackoverflow.com/questions/1197928/how-to-add-30-minutes-to-a-javascript-date-object.
|
|
* Adds time to a date. Modelled after MySQL DATE_ADD function.
|
|
* Example: dateAdd(new Date(), 'minute', 30) //returns 30 minutes from now.
|
|
*
|
|
* @param date Date to start with
|
|
* @param interval One of: year, quarter, month, week, day, hour, minute, second
|
|
* @param units Number of units of the given interval to add.
|
|
*/
|
|
export function dateAdd(date, interval, units) {
|
|
let ret = new Date(date); // don't change original date
|
|
const checkRollover = () => {
|
|
if (ret.getDate() !== date.getDate()) {
|
|
ret.setDate(0);
|
|
}
|
|
};
|
|
switch (interval.toLowerCase()) {
|
|
case 'year':
|
|
ret.setFullYear(ret.getFullYear() + units);
|
|
checkRollover();
|
|
break;
|
|
case 'quarter':
|
|
ret.setMonth(ret.getMonth() + 3 * units);
|
|
checkRollover();
|
|
break;
|
|
case 'month':
|
|
ret.setMonth(ret.getMonth() + units);
|
|
checkRollover();
|
|
break;
|
|
case 'week':
|
|
ret.setDate(ret.getDate() + 7 * units);
|
|
break;
|
|
case 'day':
|
|
ret.setDate(ret.getDate() + units);
|
|
break;
|
|
case 'hour':
|
|
ret.setTime(ret.getTime() + units * 3600000);
|
|
break;
|
|
case 'minute':
|
|
ret.setTime(ret.getTime() + units * 60000);
|
|
break;
|
|
case 'second':
|
|
ret.setTime(ret.getTime() + units * 1000);
|
|
break;
|
|
default:
|
|
ret = undefined;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
export function mergeDocuments(documents) {
|
|
const main =
|
|
typeof documents[0] === 'string'
|
|
? documents[0]
|
|
: documents[0].loc.source.body;
|
|
const substitutions = documents.slice(1);
|
|
const literals = [main, ...substitutions.map(() => '\n')];
|
|
return gql.apply(null, [literals, ...substitutions]);
|
|
}
|
|
|
|
export function getResponseErrors(mutationResult) {
|
|
const result = [];
|
|
Object.keys(mutationResult.data).forEach(response => {
|
|
const errors =
|
|
mutationResult.data[response] && mutationResult.data[response].errors;
|
|
if (errors && errors.length) {
|
|
result.push(...errors);
|
|
}
|
|
});
|
|
return result.length ? result : false;
|
|
}
|
|
|
|
export function createDefaultResponseFragments(...names) {
|
|
const result = {};
|
|
names.forEach(response => {
|
|
result[response] = gql`
|
|
fragment Coral_${response} on ${response} {
|
|
errors {
|
|
translation_key
|
|
}
|
|
}
|
|
`;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
export function forEachError(error, callback) {
|
|
const errors = error.errors || [error];
|
|
errors.forEach(e => {
|
|
console.error(e);
|
|
|
|
let msg = '';
|
|
if (e.translation_key) {
|
|
msg = t(`error.${e.translation_key}`);
|
|
} else if (error.networkError) {
|
|
msg = t('error.network_error');
|
|
} else {
|
|
msg = t('error.unexpected');
|
|
}
|
|
callback({ error: e, msg });
|
|
});
|
|
}
|
|
|
|
export function getErrorMessages(error) {
|
|
const result = [];
|
|
forEachError(error, ({ msg }) => result.push(msg));
|
|
return result;
|
|
}
|
|
|
|
export function appendNewNodes(nodesA, nodesB) {
|
|
return nodesA.concat(
|
|
nodesB.filter(nodeB => !nodesA.some(nodeA => nodeA.id === nodeB.id))
|
|
);
|
|
}
|
|
|
|
export function prependNewNodes(nodesA, nodesB) {
|
|
return nodesB
|
|
.filter(nodeB => !nodesA.some(nodeA => nodeA.id === nodeB.id))
|
|
.concat(nodesA);
|
|
}
|
|
|
|
export const isTagged = (tags, which) => tags.some(t => t.tag.name === which);
|
|
|
|
export * from './url';
|
|
|
|
/**
|
|
* getSlotFragmentSpreads will return a string in the
|
|
* expected format for slot fragments, given `slots` and `resource`.
|
|
* e.g. `getSlotFragmentSpreads(['slotName'], 'root')` returns
|
|
* `...TalkSlot_SlotName_root`.
|
|
*/
|
|
export function getSlotFragmentSpreads(slots, resource) {
|
|
return `...${slots
|
|
.map(s => `TalkSlot_${capitalize(s)}_${resource}`)
|
|
.join('\n...')}\n`;
|
|
}
|
|
|
|
export function isCommentActive(commentStatus) {
|
|
return ['NONE', 'ACCEPTED'].indexOf(commentStatus) >= 0;
|
|
}
|
|
|
|
export function getShallowChanges(a, b) {
|
|
return union(Object.keys(a), Object.keys(b)).filter(key => a[key] !== b[key]);
|
|
}
|
|
|
|
// TODO: replace with something less fragile.
|
|
// NOT_REACTION_TYPES are the action summaries that are not reactions.
|
|
const NOT_REACTION_TYPES = ['FlagActionSummary', 'DontAgreeActionSummary'];
|
|
|
|
export function getTotalReactionsCount(actionSummaries) {
|
|
return actionSummaries
|
|
.filter(({ __typename }) => !NOT_REACTION_TYPES.includes(__typename))
|
|
.reduce((total, { count }) => total + count, 0);
|
|
}
|
|
|
|
// Like lodash merge but does not recurse into arrays.
|
|
export function mergeExcludingArrays(objValue, srcValue) {
|
|
if (typeof srcValue === 'object' && !Array.isArray(srcValue)) {
|
|
return assignWith({}, objValue, srcValue, mergeExcludingArrays);
|
|
}
|
|
return srcValue;
|
|
}
|
|
|
|
// Map nested object leaves. Array objects are considered leaves.
|
|
export function mapLeaves(o, mapper) {
|
|
return mapValues(o, val => {
|
|
if (typeof val === 'object' && !Array.isArray(val)) {
|
|
return mapLeaves(val, mapper);
|
|
}
|
|
return mapper(val);
|
|
});
|
|
}
|
|
|
|
export function translateError(error) {
|
|
if (error.translation_key) {
|
|
return t(`error.${error.translation_key}`);
|
|
} else if (error.networkError) {
|
|
return t('error.network_error');
|
|
}
|
|
return error.toString();
|
|
}
|