-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add last contributor to each document #980
Changes from 10 commits
c0cc79f
a76a887
370f398
2801979
f4199c0
2c54925
88f5d89
5b2f9ab
9fedb24
4bb321f
0b753b2
dc6f24f
a39d2b0
4a7e1ea
009fa62
3f84e4f
821331e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ const OnPageNav = require('./nav/OnPageNav.js'); | |
const Site = require('./Site.js'); | ||
const translation = require('../server/translation.js'); | ||
const docs = require('../server/docs.js'); | ||
const {idx, getGitLastUpdated} = require('./utils.js'); | ||
const {idx, getGitLastUpdatedTime, getGitLastUpdatedBy} = require('./utils.js'); | ||
|
||
// component used to generate whole webpage for docs, including sidebar/header/footer | ||
class DocsLayout extends React.Component { | ||
|
@@ -45,16 +45,19 @@ class DocsLayout extends React.Component { | |
if (this.props.Doc) { | ||
DocComponent = this.props.Doc; | ||
} | ||
const filepath = docs.getFilePath(metadata); | ||
|
||
let updateTime; | ||
if (this.props.config.enableUpdateTime) { | ||
const filepath = docs.getFilePath(metadata); | ||
updateTime = getGitLastUpdated(filepath); | ||
} | ||
const updateTime = this.props.config.enableUpdateTime | ||
? getGitLastUpdatedTime(filepath) | ||
: null; | ||
const updateAuthor = this.props.config.enableUpdateBy | ||
? getGitLastUpdatedBy(filepath) | ||
: null; | ||
|
||
const title = | ||
idx(i18n, ['localized-strings', 'docs', id, 'title']) || defaultTitle; | ||
const hasOnPageNav = this.props.config.onPageNav === 'separate'; | ||
|
||
const previousTitle = | ||
idx(i18n, ['localized-strings', metadata.previous_id]) || | ||
idx(i18n, ['localized-strings', 'previous']) || | ||
|
@@ -90,15 +93,24 @@ class DocsLayout extends React.Component { | |
version={metadata.version} | ||
language={metadata.language} | ||
/> | ||
{this.props.config.enableUpdateTime && | ||
updateTime && ( | ||
<div className="docLastUpdateTimestamp"> | ||
{(updateTime || updateAuthor) && ( | ||
<div className="docLastUpdate"> | ||
{updateTime && ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use a combined format:
WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's nice 😍 ! I will implement this. |
||
<em> | ||
<strong>Last updated: </strong> | ||
{updateTime} | ||
</em> | ||
</div> | ||
)} | ||
)} | ||
<br /> | ||
{updateAuthor && ( | ||
<em> | ||
<strong>Last updated by: </strong> | ||
{updateAuthor} | ||
</em> | ||
)} | ||
</div> | ||
)} | ||
|
||
<div className="docs-prevnext"> | ||
{metadata.previous_id && ( | ||
<a | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,53 +38,90 @@ function idx(target, keyPaths) { | |
); | ||
} | ||
|
||
function getGitLastUpdated(filepath) { | ||
function isTimestamp(str) { | ||
return /^\d+$/.test(str); | ||
const GIT_LAST_UPDATED_TYPE = { | ||
TIME: 'time', | ||
AUTHOR: 'author', | ||
}; | ||
|
||
function getGitLastUpdated(filepath, type) { | ||
if (Object.values(GIT_LAST_UPDATED_TYPE).indexOf(type) === -1) { | ||
return null; | ||
} | ||
|
||
// Wrap in try/catch in case the shell commands fail (e.g. project doesn't use Git, etc). | ||
try { | ||
const format = GIT_LAST_UPDATED_TYPE.TIME === type ? '%ct' : 'author=%an'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the Let's do away with this
which would generate something like:
and then parse each line for the timestamp and author using a regex that extracts the timestamp and the author. Exampe regex here - https://regex101.com/r/O0HQrc/1 This method will just return both the relevant timestamp and author as an object There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I initially put the author to differentiate author vs commit summary since both are just plain string 😁 Wdyt about making it stronger like |
||
// To differentiate between content change and file renaming / moving, use --summary | ||
// To follow the file history until before it is moved (when we create new version), use | ||
// --follow. | ||
const silentState = shell.config.silent; // Save old silent state. | ||
shell.config.silent = true; | ||
const result = shell | ||
.exec(`git log --follow --summary --format=%ct ${filepath}`) | ||
.exec(`git log --follow --summary --format=${format} ${filepath}`) | ||
.stdout.trim(); | ||
shell.config.silent = silentState; | ||
|
||
// Format the log results to be | ||
// Format the log results to be either | ||
// ['1234567', 'rename ...', '1234566', 'move ...', '1234565', '1234564'] | ||
// OR | ||
// ['Yangshun Tay', 'rename ...', 'Endiliey', 'move ...', 'Joel', 'Fienny'] | ||
const records = result | ||
.toString('utf-8') | ||
.replace(/\n\s*\n/g, '\n') | ||
.split('\n') | ||
.filter(String); | ||
|
||
const timeSpan = records.find((item, index, arr) => { | ||
const currentItemIsTimestamp = isTimestamp(item); | ||
const isLastTwoItem = index + 2 >= arr.length; | ||
const nextItemIsTimestamp = isTimestamp(arr[index + 1]); | ||
return currentItemIsTimestamp && (isLastTwoItem || nextItemIsTimestamp); | ||
}); | ||
|
||
if (timeSpan) { | ||
const date = new Date(parseInt(timeSpan, 10) * 1000); | ||
return date.toLocaleString(); | ||
} | ||
return records; | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
return null; | ||
} | ||
function getGitLastUpdatedTime(filepath) { | ||
function isTimestamp(str) { | ||
return /^\d+$/.test(str); | ||
} | ||
|
||
const records = getGitLastUpdated(filepath, GIT_LAST_UPDATED_TYPE.TIME); | ||
|
||
const timeSpan = records.find((item, index, arr) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This finding logic can be shifted into the And function getGitLastUpdatedTime(filepath) {
const commit = getGitLastUpdated(filepath);
if (!commit) {
return null;
}
const date = new Date(parseInt(commit.timestamp, 10) * 1000);
return date.toLocaleString();
} |
||
const currentItemIsTimestamp = isTimestamp(item); | ||
const isLastTwoItem = index + 2 >= arr.length; | ||
const nextItemIsTimestamp = isTimestamp(arr[index + 1]); | ||
return currentItemIsTimestamp && (isLastTwoItem || nextItemIsTimestamp); | ||
}); | ||
|
||
if (timeSpan) { | ||
const date = new Date(parseInt(timeSpan, 10) * 1000); | ||
return date.toLocaleString(); | ||
} | ||
return null; | ||
} | ||
|
||
function getGitLastUpdatedBy(filepath) { | ||
function isAuthor(str) { | ||
return str && str.startsWith('author='); | ||
} | ||
|
||
const records = getGitLastUpdated(filepath, GIT_LAST_UPDATED_TYPE.AUTHOR); | ||
|
||
const lastContentModifierAuthor = records.find((item, index, arr) => { | ||
const currentItemIsAuthor = isAuthor(item); | ||
const isLastTwoItem = index + 2 >= arr.length; | ||
const nextItemIsAuthor = isAuthor(arr[index + 1]); | ||
return currentItemIsAuthor && (isLastTwoItem || nextItemIsAuthor); | ||
}); | ||
if (lastContentModifierAuthor) { | ||
return lastContentModifierAuthor.slice(7); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
module.exports = { | ||
blogPostHasTruncateMarker, | ||
extractBlogPostBeforeTruncate, | ||
getGitLastUpdated, | ||
getGitLastUpdatedTime, | ||
getGitLastUpdatedBy, | ||
getPath, | ||
removeExtension, | ||
idx, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a wrong place to put this. Now it's in between the
fonts
config.