Skip to content

Commit

Permalink
Rework options page structure (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikescops authored Jun 16, 2024
1 parent 764c052 commit 6e6b395
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 235 deletions.
5 changes: 3 additions & 2 deletions src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { getLatestDataFromGitLab, setTodoAsDone } from './endpoints';
import { getProjectsList } from './endpoints/getProjectsList';
import { getMembersOfGroup } from './endpoints/getMembersOfGroup';
import { setGlobalError } from '../common/globalError';
import { readConfiguration } from '../common/configuration';

console.log('background script loaded');

let time: number; // dynamic interval
browser.storage.local.get(['refreshRate']).then((settings) => {
time = settings.refreshRate ? settings.refreshRate : 40;
readConfiguration<{ refreshRate: number }>(['refreshRate']).then((settings) => {
time = settings.refreshRate;

browser.alarms.create('fetchGitLab', { when: Date.now() });

Expand Down
4 changes: 2 additions & 2 deletions src/background/utils/initGitlabApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ export const initGitlabApi = (settings: GetSettingsResponse): GitlabAPI => {

const account = settings.accounts[0];

if (!account.token) {
if (!account.token || account.token === '') {
throw new GitLabTokenNotSet();
}

if (!account.address) {
if (!account.address || account.address === '') {
throw new GitLabAddressNotSet();
}

Expand Down
1 change: 1 addition & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface Configuration {
defaultTab: TabId;
accounts: Account[];
alertBadgeCounters: number[];
refreshRate: number;
}

export interface Account {
Expand Down
5 changes: 3 additions & 2 deletions src/config/config.prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export const config = {
{
gitlabCE: false,
token: '',
address: 'https://gitlab.com',
address: '',
draftInToReviewTab: true,
projectDirectoryPrefix: ''
}
]
],
refreshRate: 60
} satisfies Configuration;
273 changes: 49 additions & 224 deletions src/options/App.tsx
Original file line number Diff line number Diff line change
@@ -1,184 +1,46 @@
import * as browser from 'webextension-polyfill';
import { useState, useCallback, useEffect } from 'react';
import {
Button,
Box,
Checkbox,
TextInput,
Text,
Tooltip,
Octicon,
Link,
FormControl,
Select,
ThemeProvider
} from '@primer/react';
import {
KeyIcon,
ServerIcon,
PackageDependenciesIcon,
CheckIcon,
InfoIcon,
ClockIcon,
FileDirectoryIcon
} from '@primer/octicons-react';
import { useState, useEffect } from 'react';
import { Box, TextInput, Tooltip, Octicon, FormControl, Select, ThemeProvider } from '@primer/react';
import { CheckIcon, InfoIcon, ClockIcon } from '@primer/octicons-react';
import './style.css';
import { updateConfiguration, readConfiguration, updateAccountConfiguration } from '../common/configuration';
import { Account, TabId } from '../common/types';
import { updateConfiguration, readConfiguration } from '../common/configuration';
import { Account, Configuration, TabId } from '../common/types';
import { AccountConfiguration } from './components/Account';

const getSettings = readConfiguration<{
accounts: Account[];
refreshRate: number;
defaultTab: TabId;
alertBadgeCounters: number[];
}>(['accounts', 'refreshRate', 'defaultTab', 'alertBadgeCounters']);
mode: 'production' | 'development';
}>(['accounts', 'refreshRate', 'defaultTab', 'alertBadgeCounters', 'mode']);

export const App = () => {
const [gitlabCE, setGitlabCE] = useState<boolean>(false);
const [gitlabToken, setGitlabToken] = useState('');
const [gitlabAddress, setGitlabAddress] = useState('');
const [refreshRate, setRefreshRate] = useState(40);
const [defaultTab, setDefaultTab] = useState<TabId>('to_review');
const [alertBadgeCounters, setAlertBadgeCounters] = useState([0]);
const [draftInToReviewTab, setDraftInToReviewTab] = useState<boolean>(true);
const [projectDirectoryPrefix, setProjectDirectoryPrefix] = useState<string>('');
const [testSuccess, setTestSuccess] = useState(null);
const [isGitlabTokenInLocalStorage, setIsGitlabTokenInLocalStorage] = useState<boolean>(false);
const [isGitlabAddressInLocalStorage, setIsGitlabAddressInLocalStorage] = useState<boolean>(false);
const [isRefreshRateInLocalStorage, setIsRefreshRateInLocalStorage] = useState<boolean>(false);
const [configuration, setConfiguration] = useState<Configuration>();

useEffect(() => {
getSettings.then((settings) => {
setGitlabCE(Boolean(settings.accounts[0].gitlabCE));

setGitlabToken(settings.accounts[0].token ?? '');
setIsGitlabTokenInLocalStorage(Boolean(settings.accounts[0].token));

setGitlabAddress(settings.accounts[0].address ?? '');
setIsGitlabAddressInLocalStorage(Boolean(settings.accounts[0].address));

setRefreshRate(settings.refreshRate ?? 40);
setIsRefreshRateInLocalStorage(Boolean(settings.refreshRate));

setDefaultTab(settings.defaultTab ?? 'to_review');

setAlertBadgeCounters(settings.alertBadgeCounters ? Array.from(settings.alertBadgeCounters) : []);

setDraftInToReviewTab(settings.accounts[0].draftInToReviewTab ?? true);

setProjectDirectoryPrefix(settings.accounts[0].projectDirectoryPrefix ?? '');
if (!settings) {
return;
}
setConfiguration(settings);
});
}, []);

const updateGitlabCE = async (event: any) => {
setGitlabCE(event.target.checked);
await updateAccountConfiguration(0, { gitlabCE: event.target.checked });
};

const updateGitlabToken = async (event: any) => {
setGitlabToken(event.target.value);
await updateAccountConfiguration(0, { gitlabToken: event.target.value });
setIsGitlabTokenInLocalStorage(true);
};

const updateGitlabAddress = async (event: any) => {
setGitlabAddress(event.target.value);
await updateAccountConfiguration(0, { gitlabAddress: event.target.value });
setIsGitlabAddressInLocalStorage(true);
};

const updateRefreshRate = async (event: any) => {
setRefreshRate(event.target.value);
await updateConfiguration({ refreshRate: parseInt(event.target.value) });
setIsRefreshRateInLocalStorage(true);
await browser.runtime.sendMessage({ type: 'updateRefreshRate', interval: event.target.value });
};

const updateDefaultTab = async (event: any) => {
setDefaultTab(event.target.value);
await updateConfiguration({ defaultTab: event.target.value });
const updateConfigurationInMemory = async (data: Partial<Configuration>) => {
if (!configuration) {
return;
}
setConfiguration({ ...configuration, ...data });
await updateConfiguration(data);
if (data.refreshRate) {
await browser.runtime.sendMessage({ type: 'updateRefreshRate', interval: data.refreshRate });
}
};

const updateAlertBadgeCounters = async (event: any) => {
const options = [...event.target.selectedOptions].map((option) => parseInt(option.value));
setAlertBadgeCounters(options);
await updateConfiguration({ alertBadgeCounters: options });
};

const updateDraftInToReviewTab = async (event: any) => {
setDraftInToReviewTab(event.target.checked);
await updateAccountConfiguration(0, { draftInToReviewTab: event.target.checked });
};

const updateProjectDirectoryPrefix = async (event: any) => {
setProjectDirectoryPrefix(event.target.value);
await updateAccountConfiguration(0, { projectDirectoryPrefix: event.target.value });
};

const testConnection = useCallback(() => {
browser.runtime.sendMessage({ type: 'getLatestDataFromGitLab' }).then((success) => setTestSuccess(success));
}, []);

return (
<ThemeProvider colorMode="auto">
<Box display="grid" gridGap={3} sx={{ width: 500, p: 2, pl: 4, pr: 6, bg: 'canvas.default' }}>
<FormControl>
<FormControl.Label>Using GitLab Community Edition</FormControl.Label>
<Checkbox
type="checkbox"
name="gitlabCE"
value="GitLab CE Mode"
onChange={updateGitlabCE}
checked={gitlabCE}
/>
<FormControl.Caption>(approvals are a premium feature)</FormControl.Caption>
</FormControl>
<FormControl>
<FormControl.Label>
Personal GitLab Token{' '}
<Link
href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html"
target="_blank"
>
<Tooltip
wrap={true}
aria-label="Click to open GitLab documentation.
The extension requires 'api' right (or just 'read_api' but all write operations will fail)."
>
<Octicon icon={InfoIcon} size={15} color="blue.5" />
</Tooltip>
</Link>
</FormControl.Label>
<TextInput
type="password"
leadingVisual={KeyIcon}
trailingVisual={isGitlabTokenInLocalStorage ? CheckIcon : undefined}
block
name="gitlab-token"
value={gitlabToken}
placeholder="<your_token_here>"
onChange={updateGitlabToken}
aria-label="gitlab-token"
/>
</FormControl>
<FormControl>
<FormControl.Label>
GitLab Host Address{' '}
<Tooltip aria-label="Example: https://gitlab.com">
<Octicon icon={InfoIcon} size={15} color="blue.5" />
</Tooltip>
</FormControl.Label>
<TextInput
leadingVisual={ServerIcon}
trailingVisual={isGitlabAddressInLocalStorage ? CheckIcon : undefined}
block
name="gitlab-address"
value={gitlabAddress}
placeholder="<host_address_here>"
onChange={updateGitlabAddress}
aria-label="gitlab-address"
/>
</FormControl>
<FormControl>
<FormControl.Label>
Refresh rate in seconds{' '}
Expand All @@ -188,32 +50,35 @@ export const App = () => {
</FormControl.Label>
<TextInput
leadingVisual={ClockIcon}
trailingVisual={isRefreshRateInLocalStorage ? CheckIcon : undefined}
trailingVisual={configuration?.refreshRate ? CheckIcon : undefined}
block
type="number"
name="refreshRate"
min="20"
value={refreshRate}
value={configuration?.refreshRate}
placeholder="0"
onChange={updateRefreshRate}
onChange={(e) => updateConfigurationInMemory({ refreshRate: parseInt(e.target.value) })}
/>
</FormControl>
<FormControl>
<FormControl.Label>Default tab</FormControl.Label>
<Select name="default-tab" onChange={updateDefaultTab}>
<Select.Option selected={defaultTab === 'to_review'} value="to_review">
<Select
name="default-tab"
onChange={(e) => updateConfigurationInMemory({ defaultTab: e.target.value as TabId })}
>
<Select.Option selected={configuration?.defaultTab === 'to_review'} value="to_review">
To Review
</Select.Option>
<Select.Option selected={defaultTab === 'under_review'} value="under_review">
<Select.Option selected={configuration?.defaultTab === 'under_review'} value="under_review">
Under Review
</Select.Option>
<Select.Option selected={defaultTab === 'drafts'} value="drafts">
<Select.Option selected={configuration?.defaultTab === 'drafts'} value="drafts">
Drafts
</Select.Option>
<Select.Option selected={defaultTab === 'issues'} value="issues">
<Select.Option selected={configuration?.defaultTab === 'issues'} value="issues">
Issues
</Select.Option>
<Select.Option selected={defaultTab === 'todo_list'} value="todo_list">
<Select.Option selected={configuration?.defaultTab === 'todo_list'} value="todo_list">
To-Do List
</Select.Option>
</Select>
Expand All @@ -225,71 +90,31 @@ export const App = () => {
<Octicon icon={InfoIcon} size={15} color="blue.5" />
</Tooltip>
</FormControl.Label>
<select name="alert-badge-counters" multiple onChange={updateAlertBadgeCounters}>
<option selected={alertBadgeCounters.includes(0)} value="0">
<select
name="alert-badge-counters"
multiple
onChange={(event) => {
const options = [...event.target.selectedOptions].map((option) => parseInt(option.value));
updateConfigurationInMemory({ alertBadgeCounters: options });
}}
>
<option selected={configuration?.alertBadgeCounters.includes(0)} value="0">
To Review
</option>
<option selected={alertBadgeCounters.includes(1)} value="1">
<option selected={configuration?.alertBadgeCounters.includes(1)} value="1">
Reviewed by others
</option>
<option selected={alertBadgeCounters.includes(2)} value="2">
<option selected={configuration?.alertBadgeCounters.includes(2)} value="2">
Issues
</option>
<option selected={alertBadgeCounters.includes(3)} value="3">
<option selected={configuration?.alertBadgeCounters.includes(3)} value="3">
To-Do List
</option>
</select>
</FormControl>
<FormControl>
<FormControl.Label>View draft MRs in &quot;To Review&quot; tab</FormControl.Label>
<Checkbox
type="checkbox"
name="draftInToReviewTab"
value="View draft in To Review tab"
onChange={updateDraftInToReviewTab}
checked={draftInToReviewTab}
/>
<FormControl.Caption>
(merge requests marked as &quot;Draft:&quot; will be ignored if unchecked)
</FormControl.Caption>
</FormControl>
<FormControl>
<FormControl.Label>
Projects directory prefix{' '}
<Tooltip
aria-label="Sometimes your project are in a sub-directory like 'teams/code/projects/'
which makes the project name difficult to read.
Set a prefix here that will be substitute each time."
>
<Octicon icon={InfoIcon} size={15} color="blue.5" />
</Tooltip>
</FormControl.Label>
<TextInput
leadingVisual={FileDirectoryIcon}
block
name="project-directory-prefix"
value={projectDirectoryPrefix}
placeholder="teams/code/projects/"
onChange={updateProjectDirectoryPrefix}
aria-label="project-directory-prefix"
/>
</FormControl>

<>
<Button onClick={testConnection} block variant="default">
<PackageDependenciesIcon /> Test my settings
</Button>
<Text
opacity={testSuccess === null ? 0 : 100}
sx={{
color: testSuccess === true ? 'success.fg' : 'danger.fg',
fontSize: 18,
textAlign: 'center'
}}
>
{testSuccess === true ? 'Success' : 'Could not connect'}
</Text>
</>
{configuration?.accounts.map((account, index) => (
<AccountConfiguration key={index} accountIndex={index} account={account} />
))}
</Box>
</ThemeProvider>
);
Expand Down
Loading

0 comments on commit 6e6b395

Please sign in to comment.