Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Changelog

## [1.5.16] - 2025-12-18

### Added
- Added timestamp toggle in sidebar to show/hide absolute timestamps alongside GitHub's relative timestamps

Check out the K2 repo on GitHub to see what's new!

https://github.com/Expensify/k2-extension
2 changes: 1 addition & 1 deletion assets/manifest-firefox.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,

"name": "K2 for GitHub",
"version": "1.5.15",
"version": "1.5.16",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to manually update these anymore. There is a GH action that will automatically bump them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I realized that afterwards haha

"description": "Manage your Kernel Scheduling from directly inside GitHub",

"browser_specific_settings": {
Expand Down
2 changes: 1 addition & 1 deletion assets/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,

"name": "K2 for GitHub",
"version": "1.5.15",
"version": "1.5.16",
"description": "Manage your Kernel Scheduling from directly inside GitHub",

"icons": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "k2-extension",
"version": "1.5.15",
"version": "1.5.16",
"description": "A browser extension for Kernel Schedule",
"private": true,
"scripts": {
Expand Down
16 changes: 16 additions & 0 deletions src/js/lib/actions/Preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ReactNativeOnyx from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';

let ghToken;
let useAbsoluteTimestamps;
ReactNativeOnyx.connect({
key: ONYXKEYS.PREFERENCES,
callback: (preferences) => {
Expand All @@ -11,6 +12,7 @@ ReactNativeOnyx.connect({
}

ghToken = preferences.ghToken;
useAbsoluteTimestamps = preferences.useAbsoluteTimestamps || false;
},
});

Expand All @@ -26,7 +28,21 @@ function setGitHubToken(value) {
ReactNativeOnyx.merge(ONYXKEYS.PREFERENCES, {ghToken: value});
}

function getUseAbsoluteTimestamps() {
return useAbsoluteTimestamps || false;
}

/**
* @param {Boolean} value
*/
function setUseAbsoluteTimestamps(value) {
useAbsoluteTimestamps = value;
ReactNativeOnyx.merge(ONYXKEYS.PREFERENCES, {useAbsoluteTimestamps: value});
}

export {
getGitHubToken,
setGitHubToken,
getUseAbsoluteTimestamps,
setUseAbsoluteTimestamps,
};
40 changes: 40 additions & 0 deletions src/js/lib/pages/github/_base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import $ from 'jquery';
import _ from 'underscore';
import ReactNativeOnyx from 'react-native-onyx';
import * as API from '../../api';
import * as Preferences from '../../actions/Preferences';
import ONYXKEYS from '../../../ONYXKEYS';
import convertTimestamps from '../../timestampConverter';

/**
* This class is to be extended by each of the distinct types of webpages that the extension works on
Expand Down Expand Up @@ -280,5 +284,41 @@ export default function () {
});
};

/**
* Applies timestamp format based on user preference
* Sets up Onyx connection to listen for preference changes and returns a function
* that can be called periodically to handle dynamically loaded timestamps
* @returns {Function} Function to call periodically for dynamic content
*/
Page.applyTimestampFormat = function () {
let currentPreference = false;

// Connect to Onyx to listen for preference changes
ReactNativeOnyx.connect({
key: ONYXKEYS.PREFERENCES,
callback: (preferences) => {
if (!preferences) {
return;
}
currentPreference = preferences.useAbsoluteTimestamps || false;
convertTimestamps(currentPreference);
},
});

// Also try to get initial preference
const initialPreference = Preferences.getUseAbsoluteTimestamps();
if (initialPreference !== undefined) {
currentPreference = initialPreference;
convertTimestamps(currentPreference);
}

// Return a function that can be called periodically
// This function reads the current preference and converts timestamps
return function applyTimestampFormatPeriodic() {
const useAbsoluteTimestamps = Preferences.getUseAbsoluteTimestamps();
convertTimestamps(useAbsoluteTimestamps);
};
};

return Page;
}
5 changes: 5 additions & 0 deletions src/js/lib/pages/github/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export default function () {
$('nav.js-repo-nav *[data-selected-links*="repo_pulls"]')
.parent().after(k2Button({url: currentUrl}));
}

// Set up timestamp format conversion
const applyTimestampFormatPeriodic = AllPages.applyTimestampFormat();
setTimeout(() => applyTimestampFormatPeriodic(), 500);
setInterval(() => applyTimestampFormatPeriodic(), 2000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too sure how this works, but a constant loop to do this seems a bit excessive. Could we just apply timestamp on page load rather than every 2 seconds?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so I think this is to allow for things like "load more" button presses, so we probably want it at least somewhat periodically? Can definitely slow it down, though - let me know what you think!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the changes that k2 makes on typical pages are done repeatedly, but I don't know how frequent it needs to be. I bet we could consolidate them all down to like every 5 sec and no one would notice a difference

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me, thanks!

};

return AllPages;
Expand Down
49 changes: 42 additions & 7 deletions src/js/lib/pages/github/issue.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import K2picker from '../../../module/K2picker/K2picker';
import K2pickerarea from '../../../module/K2pickerarea/K2pickerarea';
import K2pickerType from '../../../module/K2pickertype/K2pickertype';
import ToggleReview from '../../../module/ToggleReview/ToggleReview';
import ToggleTimestamps from '../../../module/ToggleTimestamps/ToggleTimestamps';
import K2comments from '../../../module/K2comments/K2comments';
import K2previousissues from '../../../module/K2previousissues/K2previousissues';
import ONYXKEYS from '../../../ONYXKEYS';
Expand Down Expand Up @@ -133,12 +134,33 @@ const refreshPicker = function () {
.before(sidebarWrapperHTML);
}

// Add timestamp toggle wrapper at the bottom if it doesn't exist
if (!$('.k2toggletimestamps-wrapper').length) {
// Find the last sidebar item or sidebar container and append to bottom
const lastSidebarItem = $('.discussion-sidebar-item').last();
if (lastSidebarItem.length) {
lastSidebarItem.after('<div class="discussion-sidebar-item js-discussion-sidebar-item k2toggletimestamps-wrapper"></div>');
} else {
// Fallback: append to sidebar container
const sidebar = $('.discussion-sidebar, [role="complementary"]').first();
if (sidebar.length) {
sidebar.append('<div class="discussion-sidebar-item js-discussion-sidebar-item k2toggletimestamps-wrapper"></div>');
}
}
}

new K2picker().draw();
new K2pickerType().draw();
new K2pickerarea().draw();
new ToggleReview().draw();
new K2comments().draw();
new K2previousissues().draw();

// Draw timestamp toggle if wrapper exists and is empty
const timestampWrapper = $('.k2toggletimestamps-wrapper');
if (timestampWrapper.length && timestampWrapper.children().length === 0) {
new ToggleTimestamps().draw();
}
};

/**
Expand All @@ -163,12 +185,22 @@ export default function () {
}
alreadySetup = true;

// Draw them once when the page is loaded
setTimeout(refreshPicker, 500);
setTimeout(renderAssignees, 500);
// Set up timestamp format conversion
const applyTimestampFormatPeriodic = IssuePage.applyTimestampFormat();

// Initial setup - combine all 500ms timeouts
setTimeout(() => {
refreshPicker();
renderAssignees();
applyTimestampFormatPeriodic();
}, 500);

// Every second, check to see if the pickers are still there, and if not, redraw them
// Unified interval running every 1000ms
let intervalCounter = 0;
setInterval(() => {
intervalCounter++;

// Tasks that run every 1s
if (!$('.k2picker-wrapper')) {
refreshPicker();
}
Expand All @@ -177,10 +209,13 @@ export default function () {
if (!$('div[data-testid="sidebar-assignees-section"] .k2-element').length) {
renderAssignees();
}
}, 1000);

// Waiting 2 seconds to call this gives the page enough time to load so that there is a better chance that all the comments will be rendered
setInterval(() => IssuePage.renderCopyChecklistButtons('bugzero'), 2000);
// Tasks that run every 2s (when counter is even)
if (intervalCounter % 2 === 0) {
applyTimestampFormatPeriodic();
IssuePage.renderCopyChecklistButtons('bugzero');
}
}, 1000);
};

return IssuePage;
Expand Down
78 changes: 73 additions & 5 deletions src/js/lib/pages/github/pr.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import $ from 'jquery';
import Base from './_base';
import ToggleTimestamps from '../../../module/ToggleTimestamps/ToggleTimestamps';

/**
* Replaces all `- [ ]` with `- [x]` in textareas with specific names
Expand All @@ -21,6 +22,48 @@ const renderReplaceChecklistButton = () => {
$('.k2-replace-checklist').off().on('click', replaceChecklistItems);
};

const refreshSidebar = function () {
// Add the timestamp toggle wrapper to the DOM if it doesn't exist
if ($('.k2toggletimestamps-wrapper').length) {
// Draw the toggle if wrapper exists but is empty
const wrapper = $('.k2toggletimestamps-wrapper');
if (wrapper.children().length === 0) {
try {
new ToggleTimestamps().draw();
} catch (e) {
// eslint-disable-next-line no-console
console.warn('[K2 PR Page] Failed to draw timestamp toggle:', e);
}
}
return;
}

// Add timestamp toggle wrapper at the bottom of the sidebar
const lastSidebarItem = $('.discussion-sidebar-item').last();
if (lastSidebarItem.length) {
lastSidebarItem.after('<div class="discussion-sidebar-item js-discussion-sidebar-item k2toggletimestamps-wrapper"></div>');
} else {
// Fallback: append to sidebar container
const sidebar = $('.discussion-sidebar, [role="complementary"], .Layout-sidebar').first();
if (sidebar.length) {
sidebar.append('<div class="discussion-sidebar-item js-discussion-sidebar-item k2toggletimestamps-wrapper"></div>');
}
}

// Draw the timestamp toggle if the wrapper exists and is empty
const wrapper = $('.k2toggletimestamps-wrapper');
if (!wrapper.length || wrapper.children().length > 0) {
return;
}

try {
new ToggleTimestamps().draw();
} catch (e) {
// eslint-disable-next-line no-console
console.warn('[K2 PR Page] Failed to draw timestamp toggle:', e);
}
};

const refreshHold = function () {
const prTitle = $('.js-issue-title').text();

Expand Down Expand Up @@ -93,12 +136,37 @@ export default function () {
* Add buttons to the page and setup the event handler
*/
PrPage.setup = function () {
setInterval(refreshHold, 1000);
setInterval(renderReplaceChecklistButton, 2000);
// Set up timestamp format conversion
const applyTimestampFormatPeriodic = PrPage.applyTimestampFormat();

// Initial setup - combine all 500ms timeouts
setTimeout(() => {
refreshSidebar();
applyTimestampFormatPeriodic();
}, 500);

// Unified interval running every 1000ms
let intervalCounter = 0;
setInterval(() => {
intervalCounter++;

// Tasks that run every 1s
refreshHold();

// Check if sidebar components need to be redrawn
const wrapper = $('.k2toggletimestamps-wrapper');
if (!wrapper.length || wrapper.children().length === 0) {
refreshSidebar();
}

// Waiting 2 seconds to call this gives the page enough time to load so that there is a better chance that all the comments will be rendered
setInterval(() => PrPage.renderCopyChecklistButtons('reviewer'), 2000);
setInterval(() => PrPage.renderTranslationWorkflowButtons(), 2000);
// Tasks that run every 2s (when counter is even)
if (intervalCounter % 2 === 0) {
renderReplaceChecklistButton();
applyTimestampFormatPeriodic();
PrPage.renderCopyChecklistButtons('reviewer');
PrPage.renderTranslationWorkflowButtons();
}
}, 1000);
};

return PrPage;
Expand Down
Loading
Loading