Skip to content

Support new content version release in codebase #4441

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 19 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 155 additions & 83 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const fs = require("fs")
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
const gatsbyConfig = require(`./gatsby-config.js`)
const { getLangContentVersion } = require(`./src/utils/translations`)

const supportedLanguages = gatsbyConfig.siteMetadata.supportedLanguages
const defaultLanguage = gatsbyConfig.siteMetadata.defaultLanguage
Expand Down Expand Up @@ -42,16 +41,121 @@ const getMessages = (path, language) => {
}
}

const outdatedMarkdownPages = [
"/dapps/",
"/enterprise/",
"/eth/",
"/learn/",
"/wallets/",
"/what-is-ethereum/",
]
/**
* Markdown isOutdated check
* Parse header ids in markdown file (both translated and english) and compare their info structure.
* If this structure is not the same, then the file isOutdated.
* If there is not english file, return true
* @param {string} path filepath for translated mdx file
* @returns boolean for if file is outdated or not
*/
const checkIsMdxOutdated = (path) => {
const splitPath = path.split(__dirname)
const tempSplitPath = splitPath[1]
const tempSplit = tempSplitPath.split("/")
tempSplit.splice(3, 2)
const englishPath = `${__dirname}${tempSplit.join("/")}`

exports.onCreateNode = ({ node, getNode, actions }) => {
const re = /([#]+) [^\{]+\{#([^\}]+)\}/gim
let translatedData, englishData

try {
translatedData = fs.readFileSync(path, "utf-8")
englishData = fs.readFileSync(englishPath, "utf-8")
} catch {
return true
}

let englishMatch = ""
let intlMatch = ""
try {
englishData.match(re).forEach((match) => {
englishMatch += match.replace(re, (_, p1, p2) => p1 + p2)
})
translatedData.match(re).forEach((match) => {
intlMatch += match.replace(re, (_, p1, p2) => p1 + p2)
})
} catch {
console.error("regex error")
return true
}

return englishMatch !== intlMatch
}

/**
* JSON isOutdated check
* Checks if translation JSON file exists.
* If translation file exists, checks that all translations are present (checks keys), and that all the keys are the same.
* If translation file exists, isContentEnglish will be false
* @param {*} path url path used to derive file path from
* @param {*} lang language abbreviation for language path
* @returns {{isOutdated: boolean, isContentEnglish: boolean}}
*/
const checkIsPageOutdated = async (path, lang) => {
// Files that need index appended on the end. Ex page-index.json, page-developers-index.json, page-eth2-index.json
const indexFilePaths = ["", "developers", "eth2"]
const filePath = path.split("/").filter((text) => text !== "")

if (
indexFilePaths.includes(filePath[filePath.length - 1]) ||
filePath.length === 0
) {
filePath.push("index")
}

const joinedFilepath = filePath.join("-")
const srcPath = `${__dirname}/src/intl/${lang}/page-${joinedFilepath}.json`
const englishPath = `${__dirname}/src/intl/en/page-${joinedFilepath}.json`

// If no file exists, default to english
if (!fs.existsSync(srcPath)) {
return {
isOutdated: true,
isContentEnglish: true,
}
} else {
let translatedData, englishData, translatedKeys, englishKeys
try {
translatedData = JSON.parse(fs.readFileSync(srcPath))
englishData = JSON.parse(fs.readFileSync(englishPath))
translatedKeys = Object.keys(translatedData)
englishKeys = Object.keys(englishData)
} catch (err) {
return {
isOutdated: true,
isContentEnglish: true,
}
}
// Check if same amount of keys
if (translatedKeys.length !== englishKeys.length) {
return {
isOutdated: true,
isContentEnglish: false,
}
}

// Check if all the keys are the same
if (
JSON.stringify(translatedKeys.sort()) !==
JSON.stringify(englishKeys.sort())
) {
return {
isOutdated: true,
isContentEnglish: false,
}
}

return {
isOutdated: false,
isContentEnglish: false,
}
}
}

// Loops through all the files dictated by Gatsby (building pages folder), as well as
// folders flagged through the gatsby-source-filesystem plugin in gatsby-config
exports.onCreateNode = async ({ node, getNode, actions }) => {
const { createNodeField } = actions

// Edit markdown nodes
Expand All @@ -63,10 +167,8 @@ exports.onCreateNode = ({ node, getNode, actions }) => {
slug = slug.replace("/translations", "")
const split = slug.split("/")
split.splice(1, 1)
const originalPath = split.join("/")
if (outdatedMarkdownPages.includes(originalPath)) {
isOutdated = true
}

isOutdated = await checkIsMdxOutdated(node.fileAbsolutePath)
} else {
slug = `/${defaultLanguage}${slug}`
}
Expand Down Expand Up @@ -123,7 +225,8 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
reporter.panicOnBuild('🚨 ERROR: Loading "createPages" query')
}

result.data.allMdx.edges.forEach(({ node }) => {
// For all markdown nodes, create a page
result.data.allMdx.edges.filter(({ node }) => {
const slug = node.fields.slug

// Set template of markdown files
Expand Down Expand Up @@ -207,94 +310,63 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
})
})

// Create contentVersion v2.0 pages
const contentV2Pages = [`eth`, `dapps`, `wallets/index`, `what-is-ethereum`]
const contentV2Languages = supportedLanguages.filter(
(lang) => getLangContentVersion(lang) >= 2.0
)
contentV2Pages.forEach((page) => {
const component = page
// Account for nested pages
if (page.includes("/index")) {
page = page.replace("/index", "")
}
contentV2Languages.forEach((lang) => {
createPage({
path: `/${lang}/${page}/`,
component: path.resolve(`./src/pages-conditional/${component}.js`),
context: {
slug: `/${lang}/${page}/`,
intl: {
language: lang,
languages: supportedLanguages,
defaultLanguage,
messages: getMessages("./src/intl/", lang),
routed: true,
originalPath: `/${page}/`,
redirect: false,
},
},
})
})
})

// Create contentVersion v1.0 pages
// v1.0 doesn't have existing markdown files for these pages
const contentV1Pages = [`eth`, `dapps`, `wallets/index`]
const contentV1Languages = supportedLanguages.filter(
(lang) => getLangContentVersion(lang) === 1.0
)
contentV1Pages.forEach((page) => {
const component = page
// Account for nested pages
if (page.includes("/index")) {
page = page.replace("/index", "")
}
contentV1Languages.forEach((lang) => {
createPage({
path: `/${lang}/${page}/`,
component: path.resolve(`./src/pages-conditional/${component}.js`),
context: {
slug: `/${lang}/${page}/`,
isContentEnglish: true,
intl: {
language: lang,
languages: supportedLanguages,
defaultLanguage,
messages: getMessages("./src/intl/", lang),
routed: true,
originalPath: `/${page}/`,
redirect: false,
// Create `/pages-conditional/` pages for each language unless a markdown page already exists.
// This avoids overwriting markdown pages with a component page of the same name.
// Note: once all these markdown pages have been replaced with updated JSON translation files,
// we can remove this logic and the `/pages-conditional/` directory.
const outdatedMarkdown = [`eth`, `dapps`, `wallets`, `what-is-ethereum`]
outdatedMarkdown.forEach((page) => {
supportedLanguages.forEach((lang) => {
const markdownPath = `${__dirname}/src/content/translations/${lang}/${page}/index.md`
const langHasOutdatedMarkdown = fs.existsSync(markdownPath)
if (!langHasOutdatedMarkdown) {
createPage({
path: `/${lang}/${page}/`,
component: path.resolve(
page === "wallets"
? `./src/pages-conditional/${page}/index.js`
: `./src/pages-conditional/${page}.js`
),
context: {
slug: `/${lang}/${page}/`,
intl: {
language: lang,
languages: supportedLanguages,
defaultLanguage,
messages: getMessages("./src/intl/", lang),
routed: true,
originalPath: `/${page}/`,
redirect: false,
},
},
},
})
})
}
})
})
}

// Add additional context to translated pages
// Only ran when creating component pages
// https://www.gatsbyjs.com/docs/creating-and-modifying-pages/#pass-context-to-pages
exports.onCreatePage = ({ page, actions }) => {
exports.onCreatePage = async ({ page, actions }) => {
const { createPage, deletePage } = actions

const isTranslated = page.context.language !== defaultLanguage
const hasNoContext = page.context.isOutdated === undefined
const langVersion = getLangContentVersion(page.context.language)

if (isTranslated && hasNoContext) {
let isOutdated = false
if (page.component.includes("src/pages/index.js")) {
isOutdated = true
}
const { isOutdated, isContentEnglish } = await checkIsPageOutdated(
page.context.intl.originalPath,
page.context.language
)
deletePage(page)
createPage({
...page,
context: {
...page.context,
isOutdated,
//display TranslationBanner for translation-component pages that are still in English
isContentEnglish:
langVersion < 2 && !page.component.includes("/index.js"),
isContentEnglish,
},
})
}
Expand Down
4 changes: 0 additions & 4 deletions src/components/LegacyPageHome.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ const Page = styled.div`
display: flex;
flex-direction: column;
align-items: center;

width: 100%;
margin: 0 auto;
`
Expand All @@ -49,7 +48,6 @@ const H1 = styled.h1`
font-weight: 400;
font-size: 1.5rem;
margin: 1.5rem 0;

max-width: 80%;
@media (max-width: ${(props) => props.theme.breakpoints.m}) {
max-width: 100%;
Expand Down Expand Up @@ -79,12 +77,10 @@ const Section = styled.div`
@media (max-width: ${(props) => props.theme.breakpoints.s}) {
margin-right: 0;
}

& > h2 {
margin-top: 1rem;
font-size: 1.25rem;
}

& > p {
color: ${(props) => props.theme.colors.text200};
max-width: 400px;
Expand Down
5 changes: 3 additions & 2 deletions src/components/SharedStyledComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,14 @@ export const NavLink = styled(Link)`
}
`

export const FakeLink = styled.div`
// Avoid DOM error for nested links
export const FakeLink = styled.span`
color: ${(props) => props.theme.colors.primary};
cursor: pointer;
`

// Avoid DOM error for nested links
export const FakeLinkExternal = styled.div`
export const FakeLinkExternal = styled.span`
color: ${(props) => props.theme.colors.primary};
&:after {
margin-left: 0.125em;
Expand Down
14 changes: 7 additions & 7 deletions src/content/about/product-designer/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,24 @@ link: https://ethereum.bamboohr.com/jobs/view.php?id=50
image: ../../../assets/eth.png
---

## Our background
## Our background {#our-background}

The Ethereum Foundation (EF) is a global non-profit organization dedicated to supporting Ethereum and related technologies. Our mission is to do what is best for Ethereum’s long-term success. Our role is to allocate resources to critical projects, be a valued voice within the Ethereum ecosystem, and advocate for Ethereum to the outside world.

## Your mission
## Your mission {#your-mission}

The journey into Ethereum is complicated and intimidating for the average person, whether it's learning the basics of the technology, buying their first ETH and setting up a wallet, discovering dapps, or exploring how to get involved in the ecosystem, e.g. by running a node, staking, or learning to build their first decentralized application with smart contracts. We're looking for a talented product designer who can bring simplicity, excitement, and inspiration to this journey.

You'll lead the design of [ethereum.org](/) and other Ethereum Foundation products that onboard millions of monthly visitors into the Ethereum ecosystem. You'll work hands-on with our scrappy product and engineering team to create intuitive, rewarding, and accessible web applications for a diverse range of global users. You'll leverage user testing and design best practices to create a world-class experience that empowers the next generation of Ethereum users and builders.

## About you
## About you {#about-you}

- **You're an ambitious product designer.** You're interested in owning the full design lifecycle of our web products. You're able to take rough concepts and ideas and turn them into simple, easy-to-follow flows and mocks, and take product specifications and turn them into production-ready screens. You are growth-minded and enjoy some amount of ambiguity, autonomy, and freedom to experiment in a fast-paced environment. You care about design principles and design systems and have a vision of how to iterate towards excellence.
- **You're a strong collaborator.** You have excellent attention to detail and organizational skills. You're an effective communicator with the ability to articulate your ideas, present your work, and adapt to feedback or engineering constraints. You may not know how to code, but you know how to create designs with consideration for ease of implementation and maintainability in the code. You're comfortable collaborating with a cross-functional team in a remote environment to achieve consensus on design directions.
- **You’re a user advocate.** You craft UX solutions that meet critical objectives and inspire the team to push the status quo. You can moderate research sessions to test your ideas and back your product thinking with qualitative and quantitative data.
- **You're fascinated by Ethereum...** the community as much as the technology. You're enthusiastic about open source and de# public. You're a self-starter with a curiosity to learn about the fast-changing and exciting crypto industry. You care deeply about bringing this technology mainstream and improving the onboarding experience for a diverse range of Ethereum users.

## Core responsibilities
## Core responsibilities {#core-responsibilities}

- Take ownership over the core design strategies for [ethereum.org](/) and the suite of web applications we develop from initial concept to deployment and beyond.
- Create storyboards, wireframes, lo-fi sketches, prototypes, and high-fidelity visual designs to meet the needs of different stages of our product development lifecycle.
Expand All @@ -39,15 +39,15 @@ You'll lead the design of [ethereum.org](/) and other Ethereum Foundation produc
- Help establish the Ethereum brand on [ethereum.org](/) by creating guidelines, utilizing and expanding on our design system and visual language.
- Push to improve design and development processes by building design systems, research methodologies, identifying efficiencies, and communicating best practices.

## Required skills and experience
## Required skills and experience {#required-skills-and-experience}

- You have 3+ years of experience designing production-ready digital products.
- Strong portfolio showcasing your design thinking and process, as well as excellent visual design skills.
- Fluency in Figma or similar design/prototyping tools and comfort with iterative product development lifecycles.
- You're passionate about the crypto/blockchain space, have dabbled with Ethereum applications, and are excited to dive deeper into the world of Web3 product design.
- Flexibility and excitement for early-stage teams with evolving frameworks, processes, and roadmaps.

## Bonus points
## Bonus points {#bonus-points}

- Content design or copywriting experience is a big plus.
- Experience with mobile-first designs.
Expand All @@ -59,7 +59,7 @@ You'll lead the design of [ethereum.org](/) and other Ethereum Foundation produc
- Comfort with fundamental web development technologies (HTML, CSS, DOM, etc).
- Interest in learning to prototype designs directly in the codebase.

## Contract details
## Contract details {#contract-details}

- This is a full-time, remote position.
- Compensation will ultimately depend on your experience, skillset, and the contract structure but you can expect $50-70 USD per hour or $95,000-$135,000 USD annual salary.
Expand Down
Loading