diff --git a/client/src/redux/propTypes.js b/client/src/redux/propTypes.js index 370e759001..3d2abd23b4 100644 --- a/client/src/redux/propTypes.js +++ b/client/src/redux/propTypes.js @@ -20,6 +20,7 @@ export const MarkdownRemark = PropTypes.shape({ export const ChallengeNode = PropTypes.shape({ block: PropTypes.string, + challengeOrder: PropTypes.number, challengeType: PropTypes.number, dashedName: PropTypes.string, description: PropTypes.string, @@ -31,9 +32,9 @@ export const ChallengeNode = PropTypes.shape({ slug: PropTypes.string, blockName: PropTypes.string }), + forumTopicId: PropTypes.number, guideUrl: PropTypes.string, head: PropTypes.arrayOf(PropTypes.string), - challengeOrder: PropTypes.number, instructions: PropTypes.string, isBeta: PropTypes.bool, isComingSoon: PropTypes.bool, diff --git a/client/src/templates/Challenges/backend/Show.js b/client/src/templates/Challenges/backend/Show.js index 1b574341bd..237605d9ca 100644 --- a/client/src/templates/Challenges/backend/Show.js +++ b/client/src/templates/Challenges/backend/Show.js @@ -17,7 +17,7 @@ import { updateProjectFormValues, backendNS } from '../redux'; -import { createGuideUrl } from '../utils'; +import { getGuideUrl } from '../utils'; import LearnLayout from '../../../components/layouts/Learn'; import ChallengeTitle from '../components/Challenge-Title'; @@ -44,6 +44,7 @@ const propTypes = { }), description: PropTypes.string, executeChallenge: PropTypes.func.isRequired, + forumTopicId: PropTypes.number, id: PropTypes.string, initConsole: PropTypes.func.isRequired, initTests: PropTypes.func.isRequired, @@ -155,8 +156,9 @@ export class BackEnd extends Component { const { data: { challengeNode: { - fields: { blockName, slug }, + fields: { blockName }, challengeType, + forumTopicId, title, description, instructions @@ -210,7 +212,9 @@ export class BackEnd extends Component { updateProjectForm={updateProjectFormValues} /> )} - +
this.getChallenge().videoUrl; getChallengeFile() { @@ -200,11 +193,13 @@ class ShowClassic extends Component { nextChallengePath, prevChallengePath } = this.props.pageContext.challengeMeta; + + const { forumTopicId, title } = this.getChallenge(); return ( - + @@ -148,6 +149,7 @@ export default connect( export const query = graphql` query ProjectChallenge($slug: String!) { challengeNode(fields: { slug: { eq: $slug } }) { + forumTopicId title description challengeType diff --git a/client/src/templates/Challenges/redux/create-question-epic.js b/client/src/templates/Challenges/redux/create-question-epic.js index 80073664f9..37c6ca4feb 100644 --- a/client/src/templates/Challenges/redux/create-question-epic.js +++ b/client/src/templates/Challenges/redux/create-question-epic.js @@ -1,3 +1,4 @@ +import dedent from 'dedent'; import { ofType } from 'redux-observable'; import { types, @@ -7,6 +8,7 @@ import { } from '../redux'; import { tap, mapTo } from 'rxjs/operators'; import { helpCategory } from '../../../../utils/challengeTypes'; +import { forumLocation } from '../../../../../config/env.json'; function filesToMarkdown(files = {}) { const moreThenOneFile = Object.keys(files).length > 1; @@ -17,17 +19,7 @@ function filesToMarkdown(files = {}) { } const fileName = moreThenOneFile ? `\\ file: ${file.contents}` : ''; const fileType = file.ext; - return ( - fileString + - '```' + - fileType + - '\n' + - fileName + - '\n' + - file.contents + - '\n' + - '```\n\n' - ); + return `${fileString}\`\`\`${fileType}\n${fileName}\n${file.contents}\n\`\`\`\n\n`; }, '\n'); } @@ -44,28 +36,59 @@ function createQuestionEpic(action$, state$, { window }) { navigator: { userAgent }, location: { href } } = window; - const textMessage = [ - "**Tell us what's happening:**\n\n\n\n", - '**Your code so far**\n', - filesToMarkdown(files), - '**Your browser information:**\n\n', - 'User Agent is: `', - userAgent, - '`.\n\n', - '**Challenge:**\n', - challengeTitle, - '\n**Link to the challenge:**\n', - href - ].join(''); - window.open( - 'https://forum.freecodecamp.org/new-topic' + - '?category=' + - window.encodeURIComponent(helpCategory[challengeType] || 'Help') + - '&title=' + - '&body=' + - window.encodeURIComponent(textMessage), - '_blank' + + const endingText = dedent( + `**Your browser information:** + + User Agent is: ${userAgent}. + + **Challenge:** ${challengeTitle} + + **Link to the challenge:** + ${href}` ); + + let textMessage = dedent( + `**Tell us what's happening:**\n\n\n\n**Your code so far** + ${filesToMarkdown(files)}\n${endingText}` + ); + + const altTextMessage = dedent( + `**Tell us what's happening:** + + + + **Your code so far** + + WARNING + + The challenge seed code and/or your solution exceeded the maximum length we can port over from the challenge. + + You will need to take an additional step here so the code you wrote presents in an easy to read format. + + Please copy/paste all the editor code showing in the challenge from where you just linked. + + \`\`\` + + Replace these two sentences with your copied code. + Please leave the \`\`\` line above and the \`\`\` line below, + because they allow your code to properly format in the post. + + \`\`\`\n${endingText}` + ); + + const category = window.encodeURIComponent( + helpCategory[challengeType] || 'Help' + ); + + const studentCode = window.encodeURIComponent(textMessage); + const altStudentCode = window.encodeURIComponent(altTextMessage); + + const baseURI = `${forumLocation}/new-topic?category=${category}&title=&body=`; + const defaultURI = `${baseURI}${studentCode}`; + const altURI = `${baseURI}${altStudentCode}`; + + window.open(defaultURI.length < 8000 ? defaultURI : altURI, '_blank'); }), mapTo(closeModal('help')) ); diff --git a/client/src/templates/Challenges/utils/index.js b/client/src/templates/Challenges/utils/index.js index 3d87cbd444..bc1e64bccc 100644 --- a/client/src/templates/Challenges/utils/index.js +++ b/client/src/templates/Challenges/utils/index.js @@ -1,7 +1,10 @@ -const guideBase = 'https://www.freecodecamp.org/forum/search?q='; +import { forumLocation } from '../../../../../config/env.json'; -export function createGuideUrl(title = '') { - return guideBase + title + '%20in%3Atitle%20order%3Aviews'; +export function getGuideUrl({ forumTopicId, title = '' }) { + title = encodeURIComponent(title); + return forumTopicId + ? `${forumLocation}/t/${forumTopicId}` + : `${forumLocation}/search?q=${title}%20in%3Atitle%20order%3Aviews`; } export function isGoodXHRStatus(status) { diff --git a/client/src/templates/Challenges/utils/index.test.js b/client/src/templates/Challenges/utils/index.test.js new file mode 100644 index 0000000000..6388a8e601 --- /dev/null +++ b/client/src/templates/Challenges/utils/index.test.js @@ -0,0 +1,26 @@ +/* global expect */ + +import { forumLocation } from '../../../../../config/env.json'; + +const { getGuideUrl } = require('./'); + +describe('index', () => { + describe('getGuideUrl', () => { + it('should use forum topic url when forumTopicId is supplied', () => { + const value = getGuideUrl({ + forumTopicId: 12345, + title: 'a sample title' + }); + expect(value).toEqual(`${forumLocation}/t/12345`); + }); + + it('should use search endpoint when no forumTopicId is supplied', () => { + const value = getGuideUrl({ + title: '& a sample title?' + }); + expect(value).toEqual( + `${forumLocation}/search?q=%26%20a%20sample%20title%3F%20in%3Atitle%20order%3Aviews` + ); + }); + }); +});