mirror of
https://github.com/wassname/talk.git
synced 2026-09-13 13:10:43 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8368eb78d | ||
|
|
e88bac6e0f | ||
|
|
583ab0ff74 | ||
|
|
fb7ad76b8b | ||
|
|
f7d1543aa3 | ||
|
|
950a35310b | ||
|
|
9af7ae0357 | ||
|
|
073eea2847 | ||
|
|
c699e044fd | ||
|
|
4ef7abc132 | ||
|
|
9e486f3a9d | ||
|
|
fbcd390220 | ||
|
|
f0330ed21c | ||
|
|
75a944eba8 | ||
|
|
6850dd1a53 | ||
|
|
98ef60a48e | ||
|
|
8323344d7d | ||
|
|
fbecc363cf | ||
|
|
df925f5a1a | ||
|
|
30073b88f1 |
@@ -128,7 +128,7 @@ class UserDetailContainer extends React.Component {
|
||||
const LOAD_MORE_QUERY = gql`
|
||||
query CoralAdmin_Moderation_LoadMore($limit: Int = 10, $cursor: Cursor, $author_id: ID!, $statuses: [COMMENT_STATUS!]) {
|
||||
comments(query: {limit: $limit, cursor: $cursor, author_id: $author_id, statuses: $statuses}) {
|
||||
...CoralAdmin_Moderation_CommentConnection
|
||||
...CoralAdmin_UserDetail_CommentConnection
|
||||
}
|
||||
}
|
||||
${commentConnectionFragment}
|
||||
|
||||
@@ -38,7 +38,7 @@ function applyToCommentsOrigin(root, callback) {
|
||||
function findAndInsertComment(parent, comment) {
|
||||
const isAsset = parent.__typename === 'Asset';
|
||||
const [connectionField, countField, action] = isAsset
|
||||
? ['comments', 'commentCount', '$unshift']
|
||||
? ['comments', 'totalCommentCount', '$unshift']
|
||||
: ['replies', 'replyCount', '$push'];
|
||||
|
||||
if (
|
||||
@@ -67,19 +67,12 @@ function findAndInsertComment(parent, comment) {
|
||||
}
|
||||
|
||||
export function insertCommentIntoEmbedQuery(root, comment) {
|
||||
|
||||
// Increase total comment count by one.
|
||||
root = update(root, {
|
||||
asset: {
|
||||
totalCommentCount: {$apply: (c) => c + 1},
|
||||
},
|
||||
});
|
||||
return applyToCommentsOrigin(root, (origin) => findAndInsertComment(origin, comment));
|
||||
}
|
||||
|
||||
function findAndRemoveComment(parent, id) {
|
||||
const [connectionField, countField] = parent.__typename === 'Asset'
|
||||
? ['comments', 'commentCount']
|
||||
? ['comments', 'totalCommentCount']
|
||||
: ['replies', 'replyCount'];
|
||||
|
||||
const connection = parent[connectionField];
|
||||
@@ -104,13 +97,6 @@ function findAndRemoveComment(parent, id) {
|
||||
}
|
||||
|
||||
export function removeCommentFromEmbedQuery(root, id) {
|
||||
|
||||
// Decrease total comment by one.
|
||||
root = update(root, {
|
||||
asset: {
|
||||
totalCommentCount: {$apply: (c) => c - 1},
|
||||
},
|
||||
});
|
||||
return applyToCommentsOrigin(root, (origin) => findAndRemoveComment(origin, id));
|
||||
}
|
||||
|
||||
|
||||
@@ -324,7 +324,6 @@ const fragments = {
|
||||
charCount
|
||||
requireEmailConfirmation
|
||||
}
|
||||
commentCount @skip(if: $hasComment)
|
||||
totalCommentCount @skip(if: $hasComment)
|
||||
comments(query: {limit: 10, excludeIgnored: $excludeIgnored, sortOrder: $sortOrder, sortBy: $sortBy}) @skip(if: $hasComment) {
|
||||
nodes {
|
||||
@@ -345,7 +344,6 @@ const fragments = {
|
||||
const mapStateToProps = (state) => ({
|
||||
auth: state.auth,
|
||||
refetching: state.embed.refetching,
|
||||
commentCountCache: state.stream.commentCountCache,
|
||||
activeReplyBox: state.stream.activeReplyBox,
|
||||
commentId: state.stream.commentId,
|
||||
assetId: state.stream.assetId,
|
||||
|
||||
@@ -70,10 +70,9 @@ export class Talk {
|
||||
// Extract the asset url.
|
||||
if (opts.asset_url) {
|
||||
query.asset_url = opts.asset_url;
|
||||
} else if (!opts.asset_id) {
|
||||
} else {
|
||||
|
||||
// The asset url was not provided and the asset id was also not provided,
|
||||
// we need to infer the asset url from details on the page.
|
||||
// The asset url was not provided so we need to infer the asset url from // details on the page.
|
||||
|
||||
try {
|
||||
query.asset_url = document.querySelector('link[rel="canonical"]').href;
|
||||
|
||||
@@ -24,6 +24,10 @@ const CONFIG = {
|
||||
// indexes.
|
||||
CREATE_MONGO_INDEXES: process.env.DISABLE_CREATE_MONGO_INDEXES !== 'TRUE',
|
||||
|
||||
// SETTINGS_CACHE_TIME is the time that we'll cache the settings in redis before
|
||||
// fetching again.
|
||||
SETTINGS_CACHE_TIME: ms(process.env.TALK_SETTINGS_CACHE_TIME || '1hr'),
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// JWT based configuration
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -26,7 +26,7 @@ to persist data. The following versions are supported:
|
||||
|
||||
An optional dependency for Talk is
|
||||
[Docker](https://www.docker.com/community-edition#/download){:target="_blank"}.
|
||||
It is used during [development](#development) to setup the database and can be
|
||||
It is used during [development](#development) to set up the database and can be
|
||||
used to [install via Docker](#installation-from-docker). We have tested Talk
|
||||
and this documentation with versions {{ site.versions.docker }}.
|
||||
|
||||
@@ -182,4 +182,4 @@ machine.
|
||||
|
||||
At this point you've successfully installed, configured, and ran your very own
|
||||
instance of Talk! Continue through this documentation on this site to learn more
|
||||
on how to configure, develop with, and contribute to Talk!
|
||||
on how to configure, develop with, and contribute to Talk!
|
||||
|
||||
@@ -178,7 +178,7 @@ const createComment = async (context, {tags = [], body, asset_id, parent_id = nu
|
||||
// just added a new comment, hence the counts should be updated. We should
|
||||
// perform these increments in the event that we do have a new comment that
|
||||
// is approved or without a comment.
|
||||
if (status === 'NONE' || status === 'APPROVED') {
|
||||
if (status === 'NONE' || status === 'ACCEPTED') {
|
||||
if (parent_id === null) {
|
||||
Comments.parentCountByAssetID.incr(asset_id);
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,13 +9,13 @@ module.exports = {
|
||||
globals_path: './test/e2e/globals',
|
||||
selenium: {
|
||||
start_process: true,
|
||||
server_path: 'node_modules/selenium-standalone/.selenium/selenium-server/3.6.0-server.jar',
|
||||
server_path: 'node_modules/selenium-standalone/.selenium/selenium-server/3.7.1-server.jar',
|
||||
log_path: './test/e2e/',
|
||||
host: '127.0.0.1',
|
||||
port: 6666,
|
||||
cli_args: {
|
||||
'webdriver.chrome.driver': 'node_modules/selenium-standalone/.selenium/chromedriver/2.33-x64-chromedriver',
|
||||
'webdriver.gecko.driver': 'node_modules/selenium-standalone/.selenium/geckodriver/0.19.0-x64-geckodriver',
|
||||
'webdriver.gecko.driver': 'node_modules/selenium-standalone/.selenium/geckodriver/0.19.1-x64-geckodriver',
|
||||
}
|
||||
},
|
||||
test_settings: {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "talk",
|
||||
"version": "3.8.2",
|
||||
"version": "3.8.3",
|
||||
"description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net",
|
||||
"main": "app.js",
|
||||
"private": true,
|
||||
|
||||
+2
-2
@@ -110,9 +110,9 @@ router.use('/api/v1/graph/ql', apollo.graphqlExpress(createGraphOptions));
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
|
||||
// Interactive graphiql interface.
|
||||
router.use('/api/v1/graph/iql', (req, res) => {
|
||||
router.use('/api/v1/graph/iql', staticTemplate, (req, res) => {
|
||||
res.render('graphiql', {
|
||||
endpointURL: `${req.app.locals.BASE_URL}api/v1/graph/ql`
|
||||
endpointURL: 'api/v1/graph/ql'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+4
-1
@@ -8,7 +8,10 @@ E2E_MAX_RETRIES=${E2E_MAX_RETRIES:-1}
|
||||
|
||||
# Safari >= 8 has issues connecting to browserstack-local. Safari < 8 is too old.
|
||||
# IE 64bit has issues with receiving keyboard input. Let's wait for them to fix it.
|
||||
BROWSERS="chrome,firefox,edge" #ie safari
|
||||
|
||||
# FIXME: disabled firefox,edge until fixing pass is done
|
||||
# BROWSERS="chrome,firefox,edge" #ie safari
|
||||
BROWSERS="chrome"
|
||||
|
||||
if [[ "${CIRCLE_BRANCH}" == "master" && -n "$BROWSERSTACK_KEY" ]]; then
|
||||
echo Testing on browserstack
|
||||
|
||||
+15
-4
@@ -9,7 +9,8 @@ const kue = require('kue');
|
||||
// singleton Queue instance. So you can configure and use only a single Queue
|
||||
// object within your node.js process.
|
||||
let queue = null;
|
||||
const getQueue = () => {
|
||||
let isManaging = false;
|
||||
const getQueue = ({managed = false} = {}) => {
|
||||
if (queue) {
|
||||
return queue;
|
||||
}
|
||||
@@ -21,8 +22,16 @@ const getQueue = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for stuck jobs to manage.
|
||||
queue.watchStuckJobs(1000);
|
||||
// If this is a managed queue, and we aren't managing yet, then start the
|
||||
// management.
|
||||
if (managed && !isManaging) {
|
||||
|
||||
// Watch for stuck jobs to manage.
|
||||
queue.watchStuckJobs(60000);
|
||||
|
||||
// Mark that we've now started management routines.
|
||||
isManaging = true;
|
||||
}
|
||||
|
||||
return queue;
|
||||
};
|
||||
@@ -67,7 +76,9 @@ class Task {
|
||||
* Process jobs for the queue.
|
||||
*/
|
||||
process(callback) {
|
||||
return getQueue().process(this.name, callback);
|
||||
|
||||
// Get the queue in managed mode.
|
||||
return getQueue({managed: true}).process(this.name, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ const SettingModel = require('../models/setting');
|
||||
const cache = require('./cache');
|
||||
const errors = require('../errors');
|
||||
const {dotize} = require('./utils');
|
||||
const {SETTINGS_CACHE_TIME} = require('../config');
|
||||
|
||||
/**
|
||||
* The selector used to uniquely identify the settings document.
|
||||
@@ -35,7 +36,7 @@ module.exports = class SettingsService {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
|
||||
// When in production, wrap the settings retrieval with a cache.
|
||||
const settings = await cache.h.wrap('settings', fields, 60, () => retrieve(fields));
|
||||
const settings = await cache.h.wrap('settings', fields, SETTINGS_CACHE_TIME / 1000, () => retrieve(fields));
|
||||
|
||||
return new SettingModel(settings);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,28 @@ class SortedWindowHandler {
|
||||
*/
|
||||
windowHandles(callback) {
|
||||
this.client.windowHandles((result) => {
|
||||
this.handles = this.handles.filter((handle) => result.value.includes(handle));
|
||||
const remaining = result.value.filter((handle) => !this.handles.includes(handle));
|
||||
if (Array.isArray(result.value)) {
|
||||
this.handles = this.handles.filter((handle) => {
|
||||
for (let i = 0; i < result.value.length; i++) {
|
||||
if (result.value[i] === handle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
this.handles = [];
|
||||
}
|
||||
const remaining = result.value.filter((handle) => {
|
||||
for (let i = 0; i < this.handles.length; i++) {
|
||||
if (this.handles[i] === handle) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
if (remaining.length === 1) {
|
||||
this.handles.push(remaining[0]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@
|
||||
}
|
||||
}
|
||||
// We don't use safe-serialize for location, because it's not client input.
|
||||
var fetchURL = locationQuery(otherParams, '<%= endpointURL %>');
|
||||
var fetchURL = locationQuery(otherParams, '<%= BASE_URL %><%= endpointURL %>');
|
||||
|
||||
// Defines a GraphQL fetcher using the fetch API.
|
||||
function graphQLFetcher(graphQLParams) {
|
||||
|
||||
Reference in New Issue
Block a user