2019-11-10 17:38:47 +00:00
import platform from 'platform' ;
import { browser } from 'webextension-polyfill-ts' ;
2019-11-11 14:40:30 +00:00
import {
importSettingsHandler ,
importFileHandler ,
exportSettingsHandler
} from './import-export' ;
2019-11-10 17:38:47 +00:00
import {
getSettings ,
Settings ,
camelToKebab ,
log ,
kebabToCamel ,
querySelector ,
setSettings ,
createElementFromString ,
2020-03-04 12:24:22 +00:00
flashMessage ,
querySelectorAll
2019-11-10 17:38:47 +00:00
} from './utilities' ;
window . addEventListener (
'load' ,
async ( ) : Promise < void > = > {
2019-11-12 21:18:48 +00:00
const settings : Settings = await getSettings ( ) ;
2019-11-10 17:38:47 +00:00
// Grab the version anchor from the header and add the tag link to it.
const versionSpan : HTMLAnchorElement = querySelector ( '#version' ) ;
const { version } = browser . runtime . getManifest ( ) ;
versionSpan . setAttribute (
'href' ,
2019-11-10 22:19:27 +00:00
` https://gitlab.com/tildes-community/tildes-reextended/-/tags/ ${ version } `
2019-11-10 17:38:47 +00:00
) ;
versionSpan . textContent = ` v ${ version } ` ;
2020-03-04 12:24:22 +00:00
const gitlabReportTemplate : string = createReportTemplate ( 'gitlab' ) ;
const tildesReportTemplate : string = createReportTemplate ( 'tildes' ) ;
// Grab the "Report A Bug" anchors and add a prefilled template for them.
const gitlabReportAnchors : HTMLAnchorElement [ ] = querySelectorAll (
'.report-a-bug-gitlab'
) ;
for ( const element of gitlabReportAnchors ) {
element . setAttribute (
'href' ,
encodeURI (
` https://gitlab.com/tildes-community/tildes-reextended/issues/new?issue[description]= ${ gitlabReportTemplate } `
)
) ;
}
const tildesReportAnchors : HTMLAnchorElement [ ] = querySelectorAll (
'.report-a-bug-tildes'
2019-11-10 17:38:47 +00:00
) ;
2020-03-04 12:24:22 +00:00
for ( const element of tildesReportAnchors ) {
element . setAttribute (
'href' ,
encodeURI (
` https://tildes.net/user/Bauke/new_message?subject=Tildes ReExtended Bug&message= ${ tildesReportTemplate } `
)
) ;
}
2019-11-10 17:38:47 +00:00
2019-11-12 21:18:48 +00:00
[ 'comments' , 'topics' , 'own-comments' , 'own-topics' ] . forEach (
2020-06-05 21:55:11 +00:00
( value : string ) : void = > {
2019-11-12 21:18:48 +00:00
const hideCheckbox : HTMLInputElement = querySelector (
2020-06-05 21:55:11 +00:00
` #hide-votes- ${ value } `
2019-11-12 21:18:48 +00:00
) ;
2020-06-05 21:55:11 +00:00
const settingsKey : string = kebabToCamel ( value ) ;
2019-11-12 21:18:48 +00:00
hideCheckbox . checked = settings . data . hideVotes [ settingsKey ] ;
hideCheckbox . addEventListener (
'change' ,
async ( ) : Promise < void > = > {
settings . data . hideVotes [ settingsKey ] = hideCheckbox . checked ;
await setSettings ( settings ) ;
}
) ;
}
) ;
2019-11-11 14:40:30 +00:00
const importSettingsButton : HTMLButtonElement = querySelector (
'#import-button'
) ;
importSettingsButton . addEventListener ( 'click' , importSettingsHandler ) ;
const importFileInput : HTMLInputElement = querySelector ( '#import-file' ) ;
importFileInput . addEventListener ( 'change' , importFileHandler ) ;
const exportSettingsButton : HTMLButtonElement = querySelector (
'#export-button'
) ;
exportSettingsButton . addEventListener ( 'click' , exportSettingsHandler ) ;
2019-11-10 17:38:47 +00:00
const copyBugTemplateButton : HTMLButtonElement = querySelector (
'#copy-bug-template-button'
) ;
copyBugTemplateButton . addEventListener ( 'click' , copyBugTemplateHandler ) ;
const logStoredDataButton : HTMLButtonElement = querySelector (
'#log-stored-data-button'
) ;
logStoredDataButton . addEventListener ( 'click' , logStoredDataHandler ) ;
const removeAllDataButton : HTMLButtonElement = querySelector (
'#remove-all-data-button'
) ;
removeAllDataButton . addEventListener ( 'click' , removeAllDataHandler ) ;
// Set the latest feature to active.
const latestActiveListItem : HTMLAnchorElement = querySelector (
` # ${ settings . data . latestActiveFeatureTab } -list `
) ;
latestActiveListItem . classList . add ( 'active' ) ;
const latestActiveContent : HTMLDivElement = querySelector (
` # ${ settings . data . latestActiveFeatureTab } `
) ;
latestActiveContent . classList . add ( 'active' ) ;
for ( const key in settings . features ) {
if ( Object . hasOwnProperty . call ( settings . features , key ) ) {
const value : boolean = settings . features [ key ] ;
// Convert the camelCase key to a kebab-case string.
const id : string = camelToKebab ( key ) ;
const settingContent : HTMLDivElement | null = document . querySelector (
` # ${ id } `
) ;
if ( settingContent === null ) {
log (
` New setting key: ${ key } id: ${ id } does not have an entry in the settings content! ` ,
true
) ;
continue ;
}
if ( value ) {
settingContent . classList . add ( 'enabled' ) ;
}
// Set the button text to enable/disable based on the current setting.
const toggleButton : HTMLButtonElement = querySelector ( ` # ${ id } -button ` ) ;
toggleButton . addEventListener ( 'click' , toggleButtonClickHandler ) ;
2020-03-03 18:16:40 +00:00
toggleButton . textContent = value ? 'Enabled' : 'Disabled' ;
2019-11-10 17:38:47 +00:00
// Add a checkmark to the list item if the feature is enabled.
const listItem : HTMLAnchorElement = querySelector ( ` # ${ id } -list ` ) ;
listItem . addEventListener ( 'click' , listItemClickHandler ) ;
if ( value ) {
listItem . textContent += ' ✔' ;
}
}
}
if ( typeof settings . data . version !== 'undefined' ) {
if ( settings . data . version !== version ) {
flashMessage ( ` Updated to ${ version } ! ` ) ;
}
}
settings . data . version = version ;
await setSettings ( settings ) ;
}
) ;
async function toggleButtonClickHandler ( event : MouseEvent ) : Promise < void > {
event . preventDefault ( ) ;
const target : HTMLButtonElement = event . target as HTMLButtonElement ;
const settings : Settings = await getSettings ( ) ;
// Convert the kebab-case ID to camelCase and remove the `-button` suffix.
const wantedSettingKey : string = kebabToCamel (
target . id . slice ( 0 , target . id . lastIndexOf ( '-' ) )
) ;
// Toggle the value and update it in the settings.
const wantedSettingValue = ! settings . features [ wantedSettingKey ] ;
settings . features [ wantedSettingKey ] = wantedSettingValue ;
await setSettings ( settings ) ;
// Update the button text.
2020-03-03 18:16:40 +00:00
target . textContent = wantedSettingValue ? 'Enabled' : 'Disabled' ;
2019-11-10 17:38:47 +00:00
// Grab the equivalent list item and update the checkmark.
const listItem : HTMLAnchorElement = querySelector (
` # ${ camelToKebab ( wantedSettingKey ) } -list `
) ;
if ( wantedSettingValue ) {
listItem . textContent += ' ✔' ;
} else {
listItem . textContent = listItem . textContent ! . slice (
0 ,
listItem . textContent ! . lastIndexOf ( ' ' )
) ;
}
const settingContent : HTMLDivElement = querySelector (
` # ${ camelToKebab ( wantedSettingKey ) } `
) ;
if ( wantedSettingValue ) {
settingContent . classList . add ( 'enabled' ) ;
} else {
settingContent . classList . remove ( 'enabled' ) ;
}
}
async function listItemClickHandler ( event : MouseEvent ) : Promise < void > {
const target : HTMLAnchorElement = event . target as HTMLAnchorElement ;
const id : string = target . id . slice ( 0 , target . id . lastIndexOf ( '-' ) ) ;
const currentActiveListItem : HTMLAnchorElement = querySelector (
'#settings-list > .active'
) ;
// If the currently selected item is the same as the new one, do nothing.
if ( target . id === currentActiveListItem . id ) {
return ;
}
// Hide the currently active settings content.
const currentActiveContent : HTMLDivElement = querySelector (
'#settings-content > .active'
) ;
currentActiveContent . classList . remove ( 'active' ) ;
// And show the newly selected active settings content.
const newActiveContent : HTMLDivElement = querySelector ( ` # ${ id } ` ) ;
newActiveContent . classList . add ( 'active' ) ;
// Remove the active style from the currently active settings list item.
currentActiveListItem . classList . remove ( 'active' ) ;
// And add it to the newly selected settings list item.
target . classList . add ( 'active' ) ;
// Update the latest active feature data.
const settings : Settings = await getSettings ( ) ;
settings . data . latestActiveFeatureTab = id ;
await setSettings ( settings ) ;
}
function copyBugTemplateHandler ( event : MouseEvent ) : void {
event . preventDefault ( ) ;
const temporaryElement : HTMLTextAreaElement = createElementFromString (
2020-03-04 12:24:22 +00:00
` <textarea> ${ createReportTemplate ( 'tildes' ) } </textarea> `
2019-11-10 17:38:47 +00:00
) ;
temporaryElement . classList . add ( 'trx-offscreen' ) ;
document . body . append ( temporaryElement ) ;
temporaryElement . select ( ) ;
try {
document . execCommand ( 'copy' ) ;
flashMessage ( 'Copied bug report template to clipboard.' ) ;
} catch ( error ) {
flashMessage (
'Failed to copy bug report template to clipboard. Check the console for an error.' ,
true
) ;
log ( error , true ) ;
} finally {
temporaryElement . remove ( ) ;
log ( 'Removed temporary textarea from DOM.' ) ;
}
}
async function logStoredDataHandler ( event : MouseEvent ) : Promise < void > {
event . preventDefault ( ) ;
log ( JSON . stringify ( await getSettings ( ) , null , 2 ) , true ) ;
}
async function removeAllDataHandler ( event : MouseEvent ) : Promise < void > {
event . preventDefault ( ) ;
if (
// eslint-disable-next-line no-alert
! confirm (
'Are you sure you want to delete your data? There is no way to recover your data once it has been deleted.'
)
) {
return ;
}
2019-11-10 18:37:31 +00:00
await browser . storage . sync . clear ( ) ;
2019-11-10 17:38:47 +00:00
flashMessage (
'Data removed, reloading this page to reinitialize default settings.'
) ;
setTimeout ( ( ) = > {
window . location . reload ( ) ;
} , 2500 ) ;
}
2020-03-04 12:24:22 +00:00
function createReportTemplate ( location : 'gitlab' | 'tildes' ) : string {
let introText =
"Thank you for taking the time to report a bug! Don't forget to fill in an\n appropriate title above, and make sure the information below is correct." ;
if ( location === 'tildes' ) {
introText =
'Thank you for taking the time to report a bug! Please make sure the\n information below is correct.' ;
}
2019-11-10 17:38:47 +00:00
// Set the headers using HTML tags, these can't be with #-style Markdown
// headers as they'll be interpreted as an ID instead of Markdown content
// and so GitLab won't add it to the description.
let reportTemplate = ` <h2>Bug Report</h2>
<!--
2020-03-04 12:24:22 +00:00
$ { introText }
2019-11-10 17:38:47 +00:00
-- >
< h3 > Info < / h3 > \ n
| Type | Value |
| -- -- -- | -- -- -- - |
2020-03-03 18:16:40 +00:00
| Operating System | $ { platform . os ! . toString ( ) } |
| Browser | $ { platform . name ! } $ { platform . version ! } ( $ { platform . layout ! } ) | \ n ` ;
2019-11-10 17:38:47 +00:00
// The platform manufacturer and product can be null in certain cases (such as
// desktops) so only when they're both not null include them.
if ( platform . manufacturer !== null && platform . product !== null ) {
2020-03-03 18:16:40 +00:00
reportTemplate += ` | Device | ${ platform . manufacturer ! } ${ platform . product ! } | \ n ` ;
2019-11-10 17:38:47 +00:00
}
reportTemplate += ` \ n<h3>The Problem</h3>
<!--
Please explain in sufficient detail what the problem is . When suitable ,
including an image or video showing the problem will also help immensely .
-- > \ n \ n
< h3 > A Solution < / h3 >
<!--
If you know of any possible solutions , feel free to include them . If the
solution is just something like "it should work" then you can safely omit
this section .
-- > \ n \ n \ n ` ;
return reportTemplate ;
}