Skip to content

Commit

Permalink
Make settings a modal dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
micahflee committed Jun 9, 2024
1 parent aa45178 commit 40b4b92
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 26 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@types/bootstrap": "^5.2.10",
"bootstrap": "^5.3.3",
"electron-log": "^5.1.4",
"moment": "^2.30.1",
"vue": "^3.4.21",
"vue-router": "^4.3.2"
},
Expand Down
30 changes: 28 additions & 2 deletions packages/renderer/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getDeviceInfo } from './helpers';
import Header from './components/Header.vue';
import ErrorMessage from './components/ErrorMessage.vue';
import Settings from './components/Settings.vue';
const router = useRouter();
Expand Down Expand Up @@ -46,6 +47,13 @@ const showError = (message: string) => {
};
provide('showError', showError);
// Settings
const showSettingsModal = ref(false);
const showSettings = () => {
showSettingsModal.value = true;
};
provide('showSettings', showSettings);
// Navigation
const navigate = (path: string) => {
router.push(path);
Expand All @@ -69,8 +77,13 @@ onMounted(async () => {
<div class="flex-grow-1 m-2">
<RouterView />
</div>
<ErrorMessage v-if="showErrorModal" :message="errorMessage" :show-error-message="showErrorModal"
@update:show-error-message="showErrorModal = $event" @close="showErrorModal = false" />

<!-- Settings modal -->
<Settings v-if="showSettingsModal" @hide="showSettingsModal = false" @close="showSettingsModal = false" />

<!-- Error message modal -->
<ErrorMessage v-if="showErrorModal" :message="errorMessage" @hide="showErrorModal = false"
@close="showErrorModal = false" />
</div>
</template>

Expand Down Expand Up @@ -107,6 +120,15 @@ body {
z-index: 1050;
}
/* Headers */
h1 {
font-size: 1.5rem;
}
h2 {
font-size: 1.25rem;
}
/* Bootstrap style that for some reason aren't making it */
.mr-1 {
margin-right: 0.25rem;
Expand All @@ -119,4 +141,8 @@ body {
.mr-3 {
margin-right: 1rem;
}
.mb-3 {
margin-bottom: 1rem;
}
</style>
2 changes: 0 additions & 2 deletions packages/renderer/src/ServerAPI.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,3 @@ test('ServerAPI.getNewApiToken() returns false on invalid device token', async (
expect(result).toBe(false);
});

// Authentication tests

19 changes: 19 additions & 0 deletions packages/renderer/src/ServerAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,25 @@ export default class ServerAPI {
}
}

async getDevices(): Promise<GetDevicesApiResponseArray | ApiErrorResponse> {
console.log("GET /devices");
if (!await this.validateApiToken()) {
return this.returnError("Failed to get a new API token.")
}
try {
const response = await this.fetchAuthenticated("GET", `${this.apiUrl}/devices`, null);
if (response.status != 200) {
return this.returnError("Failed to get devices. Got status code " + response.status + ".")
}
const data: GetDevicesApiResponseArray = {
devices: await response.json()
};
return data;
} catch {
return this.returnError("Failed to get devices. Maybe the server is down?")
}
}

async ping(): Promise<boolean> {
console.log("GET /ping");
if (!await this.validateApiToken()) {
Expand Down
23 changes: 10 additions & 13 deletions packages/renderer/src/components/ErrorMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ import { ref, onMounted, onUnmounted } from 'vue';
import Modal from 'bootstrap/js/dist/modal';
defineProps({
message: String,
showErrorMessage: Boolean
message: String
});
const emit = defineEmits(['update:showErrorMessage']);
const hideError = () => {
emit('update:showErrorMessage', false);
const emit = defineEmits(['hide']);
const hide = () => {
emit('hide');
};
const errorModal = ref<HTMLElement | null>(null);
Expand All @@ -24,34 +22,33 @@ onMounted(() => {
// The 'hidden.bs.modal' event is triggered when when the user clicks outside the modal
modalElement.addEventListener('hidden.bs.modal', () => {
hideError();
hide();
});
}
});
onUnmounted(() => {
if (errorModal.value && modalInstance) {
errorModal.value.removeEventListener('hidden.bs.modal', hideError);
errorModal.value.removeEventListener('hidden.bs.modal', hide);
}
});
</script>

<template>
<div class="modal fade" id="errorModal" tabindex="-1" role="dialog" aria-labelledby="errorModalLabel"
aria-hidden="true" ref="errorModal">
<div class="modal fade" id="errorModal" ref="errorModal" tabindex="-1" role="dialog"
aria-labelledby="errorModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Error</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"
@click="hideError"></button>
@click="hide"></button>
</div>
<div class="modal-body">
<p>{{ message }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"
@click="hideError">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @click="hide">Close</button>
</div>
</div>
</div>
Expand Down
22 changes: 14 additions & 8 deletions packages/renderer/src/components/Header.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
<script setup lang="ts">
import { inject, Ref } from 'vue';
import { useRouter } from 'vue-router'
import ServerAPI from '../ServerAPI';
const router = useRouter();
const showError = inject('showError') as (message: string) => void;
const showSettings = inject('showSettings') as () => void;
const navigate = inject('navigate') as (path: string) => void;
const userEmail = inject('userEmail') as Ref<string>;
const serverApi = inject('serverApi') as Ref<ServerAPI>;
const deviceInfo = inject('deviceInfo') as Ref<DeviceInfo | null>;
const refreshDeviceInfo = inject('refreshDeviceInfo') as () => Promise<void>;
const signOut = async () => {
const settingsClicked = async () => {
showSettings();
};
const signOutClicked = async () => {
if (deviceInfo.value === null) {
showError('Cannot sign out without device info');
return;
}
// Delete the device
const deleteDeviceResp = await serverApi.value.deleteDevice({
deviceToken: deviceInfo.value.deviceToken
// this API route takes either a UUID or a device token
uuid: deviceInfo.value.deviceToken
});
if (deleteDeviceResp !== undefined && deleteDeviceResp.error) {
console.log("Error deleting device", deleteDeviceResp.message)
Expand All @@ -35,12 +39,13 @@ const signOut = async () => {
// Delete the device from the local storage
await (window as any).electron.setConfig("apiToken", "");
await (window as any).electron.setConfig("deviceToken", "");
await (window as any).electron.setConfig("deviceUUID", "");
// Refresh the device info
await refreshDeviceInfo();
// Redirect to the login page
router.push('/');
navigate('/');
};
</script>

Expand All @@ -58,8 +63,9 @@ const signOut = async () => {
{{ userEmail }}
</div>
<div>
<button class="btn btn-secondary btn-sm mr-2"><i class="fa-solid fa-gear"></i></button>
<button class="btn btn-secondary btn-sm" @click="signOut">Sign out</button>
<button class="btn btn-secondary btn-sm mr-2" @click="settingsClicked"><i
class="fa-solid fa-gear"></i></button>
<button class="btn btn-secondary btn-sm" @click="signOutClicked">Sign out</button>
</div>
</div>
</header>
Expand Down
Loading

0 comments on commit 40b4b92

Please sign in to comment.