Skip to content

Commit a54f674

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

File tree

6 files changed

+165
-19
lines changed

6 files changed

+165
-19
lines changed

Diff for: 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>

Diff for: 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
});

Diff for: 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 {

Diff for: src/routes/Settings.test.tsx

+96-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ 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';
1313

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

3232
await act(async () => {
3333
tree = TestRenderer.create(
34-
<AppContext.Provider value={{ settings: mockSettings }}>
34+
<AppContext.Provider
35+
value={{ settings: mockSettings, accounts: mockAccounts }}
36+
>
3537
<MemoryRouter>
3638
<SettingsRoute />
3739
</MemoryRouter>
@@ -48,7 +50,11 @@ describe('routes/Settings.tsx', () => {
4850
await act(async () => {
4951
const { getByLabelText: getByLabelTextLocal } = render(
5052
<AppContext.Provider
51-
value={{ settings: mockSettings, logout: logoutMock }}
53+
value={{
54+
settings: mockSettings,
55+
accounts: mockAccounts,
56+
logout: logoutMock,
57+
}}
5258
>
5359
<Router location={history.location} navigator={history}>
5460
<SettingsRoute />
@@ -73,7 +79,9 @@ describe('routes/Settings.tsx', () => {
7379

7480
await act(async () => {
7581
const { getByLabelText: getByLabelTextLocal } = render(
76-
<AppContext.Provider value={{ settings: mockSettings }}>
82+
<AppContext.Provider
83+
value={{ settings: mockSettings, accounts: mockAccounts }}
84+
>
7785
<Router location={history.location} navigator={history}>
7886
<SettingsRoute />
7987
</Router>
@@ -91,7 +99,13 @@ describe('routes/Settings.tsx', () => {
9199

92100
await act(async () => {
93101
const { getByLabelText: getByLabelTextLocal } = render(
94-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
102+
<AppContext.Provider
103+
value={{
104+
settings: mockSettings,
105+
accounts: mockAccounts,
106+
updateSetting,
107+
}}
108+
>
95109
<MemoryRouter>
96110
<SettingsRoute />
97111
</MemoryRouter>
@@ -113,7 +127,13 @@ describe('routes/Settings.tsx', () => {
113127

114128
await act(async () => {
115129
const { getByLabelText: getByLabelTextLocal } = render(
116-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
130+
<AppContext.Provider
131+
value={{
132+
settings: mockSettings,
133+
accounts: mockAccounts,
134+
updateSetting,
135+
}}
136+
>
117137
<MemoryRouter>
118138
<SettingsRoute />
119139
</MemoryRouter>
@@ -135,7 +155,13 @@ describe('routes/Settings.tsx', () => {
135155

136156
await act(async () => {
137157
const { getByLabelText: getByLabelTextLocal } = render(
138-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
158+
<AppContext.Provider
159+
value={{
160+
settings: mockSettings,
161+
accounts: mockAccounts,
162+
updateSetting,
163+
}}
164+
>
139165
<MemoryRouter>
140166
<SettingsRoute />
141167
</MemoryRouter>
@@ -157,7 +183,13 @@ describe('routes/Settings.tsx', () => {
157183

158184
await act(async () => {
159185
const { getByLabelText: getByLabelTextLocal } = render(
160-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
186+
<AppContext.Provider
187+
value={{
188+
settings: mockSettings,
189+
accounts: mockAccounts,
190+
updateSetting,
191+
}}
192+
>
161193
<MemoryRouter>
162194
<SettingsRoute />
163195
</MemoryRouter>
@@ -179,7 +211,13 @@ describe('routes/Settings.tsx', () => {
179211

180212
await act(async () => {
181213
const { getByLabelText: getByLabelTextLocal } = render(
182-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
214+
<AppContext.Provider
215+
value={{
216+
settings: mockSettings,
217+
accounts: mockAccounts,
218+
updateSetting,
219+
}}
220+
>
183221
<MemoryRouter>
184222
<SettingsRoute />
185223
</MemoryRouter>
@@ -201,7 +239,13 @@ describe('routes/Settings.tsx', () => {
201239

202240
await act(async () => {
203241
const { getByLabelText: getByLabelTextLocal } = render(
204-
<AppContext.Provider value={{ settings: mockSettings, updateSetting }}>
242+
<AppContext.Provider
243+
value={{
244+
settings: mockSettings,
245+
accounts: mockAccounts,
246+
updateSetting,
247+
}}
248+
>
205249
<MemoryRouter>
206250
<SettingsRoute />
207251
</MemoryRouter>
@@ -221,7 +265,9 @@ describe('routes/Settings.tsx', () => {
221265

222266
await act(async () => {
223267
const { getByLabelText: getByLabelTextLocal } = render(
224-
<AppContext.Provider value={{ settings: mockSettings }}>
268+
<AppContext.Provider
269+
value={{ settings: mockSettings, accounts: mockAccounts }}
270+
>
225271
<Router location={history.location} navigator={history}>
226272
<SettingsRoute />
227273
</Router>
@@ -241,7 +287,9 @@ describe('routes/Settings.tsx', () => {
241287

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

Diff for: 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"

Diff for: 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)