@@ -2063,6 +2062,10 @@ window.TogglButton = {
resolve({
currentEntry: TogglButton.$curEntry
});
+ } else if (request.type === 'getClubhouseCustomTemplateSettings') {
+ const useCustomTemplate = await db.get('clubhouseUseCustomTemplate');
+ const customTemplate = await db.get('clubhouseCustomTemplate');
+ return resolve({ useCustomTemplate, customTemplate });
} else if (request.type === 'error') {
// Handling integration errors
error = new Error();
diff --git a/src/scripts/content/clubhouse.js b/src/scripts/content/clubhouse.js
index d05a58e56..87ed79e5c 100644
--- a/src/scripts/content/clubhouse.js
+++ b/src/scripts/content/clubhouse.js
@@ -1,24 +1,46 @@
'use strict';
+import CustomTemplateMessenger from '../lib/customTemplateMessenger';
+import CustomTemplateParser from '../lib/customTemplateParser';
-togglbutton.render('.story-state:not(.toggl)', { observe: true }, function (
+togglbutton.render('.story-state:not(.toggl)', { observe: true }, async function (
elem
) {
const wrap = createTag('div');
const element = elem;
elem = elem.parentNode.parentNode.parentNode;
- const getDescription = function () {
- return $('h2.story-name', elem).textContent;
+ const getEpicName = function () {
+ return $('#story-dialog-epic-dropdown .value', elem).textContent;
};
- const getProject = function () {
+ const getProjectName = function () {
return $('.story-project .value', elem).textContent;
};
+ const getStoryId = function () {
+ return $('.story-dialog .right-column .story-id input', elem).value;
+ };
+
+ const getStoryTitle = function () {
+ return $('h2.story-name', elem).textContent;
+ };
+
+ // Matches field names to appropriate get function
+ const customTemplateMap = {
+ epicName: getEpicName,
+ projectName: getProjectName,
+ storyId: getStoryId,
+ storyTitle: getStoryTitle
+ };
+
+ const templateSettings = new CustomTemplateMessenger('getClubhouseCustomTemplateSettings');
+ await templateSettings.fetchSettings();
+ const templateParser = new CustomTemplateParser(customTemplateMap, templateSettings.customTemplate);
+
const link = togglbutton.createTimerLink({
className: 'clubhouse',
- description: getDescription,
- projectName: getProject
+ description: templateSettings.useCustomTemplate ? templateParser.parse() : getStoryTitle,
+ projectName: getProjectName
});
wrap.className = 'attribute editable-attribute';
diff --git a/src/scripts/index.d.ts b/src/scripts/index.d.ts
index fed542c34..acfa9a1b1 100644
--- a/src/scripts/index.d.ts
+++ b/src/scripts/index.d.ts
@@ -217,3 +217,13 @@ interface FailureResponse {
}
type ButtonResponse = SuccessResponse | FailureResponse;
+
+interface CustomTemplateMessage {
+ customTemplate: string;
+ useCustomTemplate: boolean;
+
+}
+
+interface CustomTemplateParserMap {
+ [key: string]: string|((...args: any[]) => string);
+}
\ No newline at end of file
diff --git a/src/scripts/lib/customTemplateMessenger.ts b/src/scripts/lib/customTemplateMessenger.ts
new file mode 100644
index 000000000..6334d94e5
--- /dev/null
+++ b/src/scripts/lib/customTemplateMessenger.ts
@@ -0,0 +1,32 @@
+const browser = require('webextension-polyfill');
+
+class CustomTemplateMessenger {
+ customTemplate = "";
+ customTemplateMessageName = "";
+ useCustomTemplate = false;
+
+ constructor(customTemplateMessageName: string){
+ this.customTemplateMessageName = customTemplateMessageName;
+ }
+
+ async fetchSettings () {
+ await browser.runtime.sendMessage({
+ type: this.customTemplateMessageName
+ }).then(
+ (res) => this.handleResponse(res),
+ (err) => this.handleError(err)
+ );
+ }
+
+ handleError (error: any) {
+ console.error('handleError: ', error);
+ }
+
+ handleResponse = ({ useCustomTemplate, customTemplate }: CustomTemplateMessage):void => {
+ this.useCustomTemplate = useCustomTemplate;
+ this.customTemplate = customTemplate;
+ }
+
+}
+
+export default CustomTemplateMessenger;
diff --git a/src/scripts/lib/customTemplateParser.ts b/src/scripts/lib/customTemplateParser.ts
new file mode 100644
index 000000000..9c073f655
--- /dev/null
+++ b/src/scripts/lib/customTemplateParser.ts
@@ -0,0 +1,20 @@
+class CustomTemplateParser {
+ templateMap = {};
+ templateStr = "";
+ regexParser: RegExp;
+ constructor(templateMap: CustomTemplateParserMap, templateStr: string){
+ this.templateMap = templateMap;
+ this.templateStr = templateStr;
+ this.regexParser = new RegExp(/{{\s*(.*?)\s*}}/);
+ }
+ parse():string {
+ let strMatch = this.templateStr.match(this.regexParser);
+ while (strMatch) {
+ this.templateStr = this.templateStr.replace(strMatch[0], this.templateMap[strMatch[1]]());
+ strMatch = this.templateStr.match(this.regexParser);
+ }
+ return this.templateStr;
+ };
+}
+
+export default CustomTemplateParser;
diff --git a/src/scripts/lib/db.js b/src/scripts/lib/db.js
index d3ce951a8..acfd2ec32 100644
--- a/src/scripts/lib/db.js
+++ b/src/scripts/lib/db.js
@@ -31,6 +31,10 @@ const DEFAULT_SETTINGS = {
rememberProjectPer: 'false',
enableAutoTagging: false,
+ // Clubhouse Settings
+ clubhouseUseCustomTemplate: false,
+ clubhouseCustomTemplate: '[{{ epicName }}] ({{ storyId }}) - {{ storyTitle }}',
+
sendErrorReports: true,
sendUsageStatistics: true
};
diff --git a/src/scripts/settings.js b/src/scripts/settings.js
index 6edd7fb06..edb5fc72e 100644
--- a/src/scripts/settings.js
+++ b/src/scripts/settings.js
@@ -55,6 +55,9 @@ const Settings = {
$pomodoroTickerVolume: null,
$pomodoroTickerVolumeLabel: null,
+ $clubhouseUseCustomTemplate: null,
+ $clubhouseCustomTemplate: null,
+
$sendUsageStatistics: null,
$sendErrorReports: null,
$enableAutoTagging: null,
@@ -90,6 +93,10 @@ const Settings = {
const stopAtDayEnd = await db.get('stopAtDayEnd');
const dayEndTime = await db.get('dayEndTime');
+ Settings.$clubhouseUseCustomTemplate.checked = await (db.get('clubhouseUseCustomTemplate'));
+ Settings.$clubhouseCustomTemplate.disabled = !Settings.$clubhouseUseCustomTemplate.checked;
+ Settings.$clubhouseCustomTemplate.value = await db.get('clubhouseCustomTemplate');
+
Settings.$loginInfo.textContent = TogglButton.$user && TogglButton.$user.email || '';
document.querySelector('.expand').addEventListener('click', e => {
@@ -678,6 +685,9 @@ document.addEventListener('DOMContentLoaded', async function (e) {
Settings.$pomodoroTickerVolume = document.querySelector('#ticker-sound-volume');
Settings.$pomodoroTickerVolumeLabel = document.querySelector('#ticker-volume-label');
+ Settings.$clubhouseUseCustomTemplate = document.querySelector('#clubhouse-use-custom-template');
+ Settings.$clubhouseCustomTemplate = document.querySelector('#clubhouse-custom-template');
+
// Change active tab if present in search param
const activeTabParam = getUrlParam(document.location, 'tab');
changeActiveTab(activeTabParam || DEFAULT_TAB);
@@ -829,6 +839,14 @@ document.addEventListener('DOMContentLoaded', async function (e) {
Settings.$pomodoroTickerVolumeLabel.textContent = e.target.value + '%';
});
+ Settings.$clubhouseUseCustomTemplate.addEventListener('input', async function (e) {
+ await db.set('clubhouseUseCustomTemplate', e.target.checked);
+ Settings.$clubhouseCustomTemplate.disabled = !e.target.checked;
+ });
+ Settings.$clubhouseCustomTemplate.addEventListener('input', async function (e) {
+ await db.set('clubhouseCustomTemplate', e.target.value);
+ });
+
const tickerSoundTest = document.querySelector('#ticker-sound-test');
const tickerSound = new Audio();
let isPlaying = false;
diff --git a/src/styles/settings.css b/src/styles/settings.css
index d63a8b358..893fd7dba 100644
--- a/src/styles/settings.css
+++ b/src/styles/settings.css
@@ -46,7 +46,7 @@ body {
color: var(--text-color);
font-family: Roboto, Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
-}
+}
body {
height: 100vh;
@@ -674,3 +674,9 @@ section .field--showDetails .subsettings-details {
body[data-show-review-banner="true"] #review-prompt {
transform: translateY(0);
}
+
+.custom-template-input:disabled {
+ background-color: var(--border-color);
+ color: var(--light-text);
+ cursor: not-allowed;
+}