Skip to content

Commit 7cb68bc

Browse files
committed
feat: check for github scopes
1 parent babe143 commit 7cb68bc

File tree

6 files changed

+166
-19
lines changed

6 files changed

+166
-19
lines changed

src/components/fields/Checkbox.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface IFieldCheckbox {
66
checked: boolean;
77
onChange: any;
88
placeholder?: string;
9+
disabled?: boolean;
910
}
1011

1112
export const FieldCheckbox = (props: IFieldCheckbox) => {
@@ -18,13 +19,15 @@ export const FieldCheckbox = (props: IFieldCheckbox) => {
1819
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
1920
checked={props.checked}
2021
onChange={props.onChange}
22+
disabled={props.disabled}
2123
/>
2224
</div>
2325

2426
<div className="ml-3 text-sm">
2527
<label
2628
htmlFor={props.name}
2729
className="font-medium text-gray-700 dark:text-gray-200"
30+
style={{ textDecoration: props.disabled && 'line-through' }}
2831
>
2932
{props.label}
3033
</label>

src/context/App.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ describe('context/App.tsx', () => {
264264
participating: true,
265265
playSound: true,
266266
showNotifications: true,
267-
colors: true,
267+
colors: false,
268268
},
269269
);
270270
});
@@ -302,7 +302,7 @@ describe('context/App.tsx', () => {
302302
participating: false,
303303
playSound: true,
304304
showNotifications: true,
305-
colors: true,
305+
colors: false,
306306
},
307307
);
308308
});

src/context/App.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const defaultSettings: SettingsState = {
3737
markOnClick: false,
3838
openAtStartup: false,
3939
appearance: Appearance.SYSTEM,
40-
colors: true,
40+
colors: false,
4141
};
4242

4343
interface AppContextState {

src/routes/Settings.test.tsx

+97-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const { ipcRenderer } = require('electron');
99

1010
import { SettingsRoute } from './Settings';
1111
import { AppContext } from '../context/App';
12-
import { mockSettings } from '../__mocks__/mock-state';
12+
import { mockAccounts, mockSettings } from '../__mocks__/mock-state';
13+
import Constants from '../utils/constants';
1314

1415
const mockNavigate = jest.fn();
1516
jest.mock('react-router-dom', () => ({
@@ -31,7 +32,9 @@ describe('routes/Settings.tsx', () => {
3132

3233
await act(async () => {
3334
tree = TestRenderer.create(
34-
<AppContext.Provider value={{ settings: mockSettings }}>
35+
<AppContext.Provider
36+
value={{ settings: mockSettings, accounts: mockAccounts }}
37+
>
3538
<MemoryRouter>
3639
<SettingsRoute />
3740
</MemoryRouter>
@@ -48,7 +51,11 @@ describe('routes/Settings.tsx', () => {
4851
await act(async () => {
4952
const { getByLabelText: getByLabelTextLocal } = render(
5053
<AppContext.Provider
51-
value={{ settings: mockSettings, logout: logoutMock }}
54+
value={{
55+
settings: mockSettings,
56+
accounts: mockAccounts,
57+
logout: logoutMock,
58+
}}
5259
>
5360
<Router location={history.location} navigator={history}>
5461
<SettingsRoute />
@@ -73,7 +80,9 @@ describe('routes/Settings.tsx', () => {
7380

7481
await act(async () => {
7582
const { getByLabelText: getByLabelTextLocal } = render(
76-
<AppContext.Provider value={{ settings: mockSettings }}>
83+
<AppContext.Provider
84+
value={{ settings: mockSettings, accounts: mockAccounts }}
85+
>
7786
<Router location={history.location} navigator={history}>
7887
<SettingsRoute />
7988
</Router>
@@ -91,7 +100,13 @@ describe('routes/Settings.tsx', () => {
91100

92101
await act(async () => {
93102
const { getByLabelText: getByLabelTextLocal } = render(
94-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
103+
<AppContext.Provider
104+
value={{
105+
settings: mockSettings,
106+
accounts: mockAccounts,
107+
updateSetting,
108+
}}
109+
>
95110
<MemoryRouter>
96111
<SettingsRoute />
97112
</MemoryRouter>
@@ -113,7 +128,13 @@ describe('routes/Settings.tsx', () => {
113128

114129
await act(async () => {
115130
const { getByLabelText: getByLabelTextLocal } = render(
116-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
131+
<AppContext.Provider
132+
value={{
133+
settings: mockSettings,
134+
accounts: mockAccounts,
135+
updateSetting,
136+
}}
137+
>
117138
<MemoryRouter>
118139
<SettingsRoute />
119140
</MemoryRouter>
@@ -135,7 +156,13 @@ describe('routes/Settings.tsx', () => {
135156

136157
await act(async () => {
137158
const { getByLabelText: getByLabelTextLocal } = render(
138-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
159+
<AppContext.Provider
160+
value={{
161+
settings: mockSettings,
162+
accounts: mockAccounts,
163+
updateSetting,
164+
}}
165+
>
139166
<MemoryRouter>
140167
<SettingsRoute />
141168
</MemoryRouter>
@@ -157,7 +184,13 @@ describe('routes/Settings.tsx', () => {
157184

158185
await act(async () => {
159186
const { getByLabelText: getByLabelTextLocal } = render(
160-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
187+
<AppContext.Provider
188+
value={{
189+
settings: mockSettings,
190+
accounts: mockAccounts,
191+
updateSetting,
192+
}}
193+
>
161194
<MemoryRouter>
162195
<SettingsRoute />
163196
</MemoryRouter>
@@ -179,7 +212,13 @@ describe('routes/Settings.tsx', () => {
179212

180213
await act(async () => {
181214
const { getByLabelText: getByLabelTextLocal } = render(
182-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
215+
<AppContext.Provider
216+
value={{
217+
settings: mockSettings,
218+
accounts: mockAccounts,
219+
updateSetting,
220+
}}
221+
>
183222
<MemoryRouter>
184223
<SettingsRoute />
185224
</MemoryRouter>
@@ -201,7 +240,13 @@ describe('routes/Settings.tsx', () => {
201240

202241
await act(async () => {
203242
const { getByLabelText: getByLabelTextLocal } = render(
204-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
243+
<AppContext.Provider
244+
value={{
245+
settings: mockSettings,
246+
accounts: mockAccounts,
247+
updateSetting,
248+
}}
249+
>
205250
<MemoryRouter>
206251
<SettingsRoute />
207252
</MemoryRouter>
@@ -221,7 +266,9 @@ describe('routes/Settings.tsx', () => {
221266

222267
await act(async () => {
223268
const { getByLabelText: getByLabelTextLocal } = render(
224-
<AppContext.Provider value={{ settings: mockSettings }}>
269+
<AppContext.Provider
270+
value={{ settings: mockSettings, accounts: mockAccounts }}
271+
>
225272
<Router location={history.location} navigator={history}>
226273
<SettingsRoute />
227274
</Router>
@@ -241,7 +288,9 @@ describe('routes/Settings.tsx', () => {
241288

242289
await act(async () => {
243290
const { getByLabelText: getByLabelTextLocal } = render(
244-
<AppContext.Provider value={{ settings: mockSettings }}>
291+
<AppContext.Provider
292+
value={{ settings: mockSettings, accounts: mockAccounts }}
293+
>
245294
<MemoryRouter>
246295
<SettingsRoute />
247296
</MemoryRouter>
@@ -253,4 +302,40 @@ describe('routes/Settings.tsx', () => {
253302
fireEvent.click(getByLabelText('Quit Gitify'));
254303
expect(ipcRenderer.send).toHaveBeenCalledWith('app-quit');
255304
});
305+
306+
it('should not be able to disable colors', async () => {
307+
let queryByLabelText;
308+
jest.mock('../utils/helpers', () => ({
309+
...jest.requireActual('../utils/helpers'),
310+
apiRequestAuth: jest.fn().mockResolvedValue({
311+
headers: {
312+
'x-oauth-scopes': 'repo, notifications',
313+
},
314+
}),
315+
}));
316+
317+
await act(async () => {
318+
const { queryByLabelText: queryByLabelLocal } = render(
319+
<AppContext.Provider
320+
value={{
321+
settings: mockSettings,
322+
accounts: mockAccounts,
323+
}}
324+
>
325+
<MemoryRouter>
326+
<SettingsRoute />
327+
</MemoryRouter>
328+
</AppContext.Provider>,
329+
);
330+
queryByLabelText = queryByLabelLocal;
331+
});
332+
333+
console.log(
334+
queryByLabelText('Use GitHub-like state colors (requires re-auth)'),
335+
);
336+
337+
expect(
338+
queryByLabelText('Use GitHub-like state colors (requires re-auth)'),
339+
).toBeDefined();
340+
});
256341
});

src/routes/Settings.tsx

+31-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ import { IconLogOut } from '../icons/Logout';
1212
import { IconQuit } from '../icons/Quit';
1313
import { updateTrayIcon } from '../utils/comms';
1414
import { setAppearance } from '../utils/appearance';
15+
import { apiRequestAuth } from '../utils/api-requests';
16+
import { generateGitHubAPIUrl } from '../utils/helpers';
17+
import Constants from '../utils/constants';
1518

1619
export const SettingsRoute: React.FC = () => {
17-
const { settings, updateSetting, logout } = useContext(AppContext);
20+
const { accounts, settings, updateSetting, logout } = useContext(AppContext);
1821
const navigate = useNavigate();
1922

2023
const [isLinux, setIsLinux] = useState<boolean>(false);
2124
const [appVersion, setAppVersion] = useState<string | null>(null);
25+
const [colorScope, setColorScope] = useState<boolean>(false);
2226

2327
useEffect(() => {
2428
ipcRenderer.invoke('get-platform').then((result: string) => {
@@ -28,6 +32,27 @@ export const SettingsRoute: React.FC = () => {
2832
ipcRenderer.invoke('get-app-version').then((result: string) => {
2933
setAppVersion(result);
3034
});
35+
36+
(async () => {
37+
const response = await apiRequestAuth(
38+
`${generateGitHubAPIUrl(Constants.DEFAULT_AUTH_OPTIONS.hostname)}`,
39+
'GET',
40+
accounts.token,
41+
);
42+
43+
console.log(JSON.stringify(response.headers));
44+
const missingScopes = Constants.AUTH_SCOPE.filter((scope) => {
45+
return !response.headers['x-oauth-scopes'].includes(scope);
46+
});
47+
if (missingScopes.length > 0) {
48+
new Notification('Gitify - Permissions', {
49+
body:
50+
'You need to grant all the permissions to use this app. Missing scopes: ' +
51+
missingScopes.join(', ') +
52+
'.',
53+
});
54+
} else setColorScope(true);
55+
})();
3156
}, []);
3257

3358
ipcRenderer.on('update-native-theme', (_, updatedAppearance: Appearance) => {
@@ -84,9 +109,12 @@ export const SettingsRoute: React.FC = () => {
84109

85110
<FieldCheckbox
86111
name="colors"
87-
label="Use colors to indicate state"
88-
checked={settings.colors}
112+
label={`Use GitHub-like state colors${
113+
!colorScope ? ' (requires re-auth)' : ''
114+
}`}
115+
checked={colorScope && settings.colors}
89116
onChange={(evt) => updateSetting('colors', evt.target.checked)}
117+
disabled={!colorScope}
90118
/>
91119
<FieldCheckbox
92120
name="showOnlyParticipating"

src/routes/__snapshots__/Settings.test.tsx.snap

+32-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
128128
<input
129129
checked={false}
130130
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
131+
disabled={true}
131132
id="colors"
132133
onChange={[Function]}
133134
type="checkbox"
@@ -139,8 +140,13 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
139140
<label
140141
className="font-medium text-gray-700 dark:text-gray-200"
141142
htmlFor="colors"
143+
style={
144+
{
145+
"textDecoration": "line-through",
146+
}
147+
}
142148
>
143-
Use colors to indicate state
149+
Use GitHub-like state colors (requires re-auth)
144150
</label>
145151
</div>
146152
</div>
@@ -164,6 +170,11 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
164170
<label
165171
className="font-medium text-gray-700 dark:text-gray-200"
166172
htmlFor="showOnlyParticipating"
173+
style={
174+
{
175+
"textDecoration": undefined,
176+
}
177+
}
167178
>
168179
Show only participating
169180
</label>
@@ -189,6 +200,11 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
189200
<label
190201
className="font-medium text-gray-700 dark:text-gray-200"
191202
htmlFor="playSound"
203+
style={
204+
{
205+
"textDecoration": undefined,
206+
}
207+
}
192208
>
193209
Play sound
194210
</label>
@@ -214,6 +230,11 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
214230
<label
215231
className="font-medium text-gray-700 dark:text-gray-200"
216232
htmlFor="showNotifications"
233+
style={
234+
{
235+
"textDecoration": undefined,
236+
}
237+
}
217238
>
218239
Show notifications
219240
</label>
@@ -239,6 +260,11 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
239260
<label
240261
className="font-medium text-gray-700 dark:text-gray-200"
241262
htmlFor="onClickMarkAsRead"
263+
style={
264+
{
265+
"textDecoration": undefined,
266+
}
267+
}
242268
>
243269
Mark as read on click
244270
</label>
@@ -264,6 +290,11 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
264290
<label
265291
className="font-medium text-gray-700 dark:text-gray-200"
266292
htmlFor="openAtStartUp"
293+
style={
294+
{
295+
"textDecoration": undefined,
296+
}
297+
}
267298
>
268299
Open at startup
269300
</label>

0 commit comments

Comments
 (0)