Skip to content

Commit

Permalink
feat: allow to import poll from JSON file
Browse files Browse the repository at this point in the history
Signed-off-by: Maksim Sukharev <[email protected]>
  • Loading branch information
Antreesy committed Oct 18, 2024
1 parent 5962ae0 commit e920cff
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/components/NewMessage/NewMessagePollEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,25 @@
</p>
<div class="poll-editor__wrapper">
<NcTextField :value.sync="pollForm.question" :label="t('spreed', 'Ask a question')" v-on="$listeners" />
<!--native file picker, hidden -->
<input id="poll-upload"
ref="pollImport"
type="file"
class="hidden-visually"
@change="importPoll">
<NcActions v-if="supportPollDrafts" force-menu>
<NcActionButton v-if="isModerator" close-after-click @click="openPollDraftHandler">
<template #icon>
<IconFileEdit :size="20" />
</template>
{{ t('spreed', 'Browse poll drafts') }}
</NcActionButton>
<NcActionButton close-after-click @click="triggerImport">
<template #icon>
<IconFileUpload :size="20" />
</template>
{{ t('spreed', 'Import draft from file') }}
</NcActionButton>
</NcActions>
</div>

Expand Down Expand Up @@ -103,8 +115,10 @@ import IconArrowLeft from 'vue-material-design-icons/ArrowLeft.vue'
import Close from 'vue-material-design-icons/Close.vue'
import IconFileDownload from 'vue-material-design-icons/FileDownload.vue'
import IconFileEdit from 'vue-material-design-icons/FileEdit.vue'
import IconFileUpload from 'vue-material-design-icons/FileUpload.vue'
import Plus from 'vue-material-design-icons/Plus.vue'

import { showError } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'

import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
Expand All @@ -121,6 +135,7 @@ import { hasTalkFeature } from '../../services/CapabilitiesManager.ts'
import { EventBus } from '../../services/EventBus.js'
import { usePollsStore } from '../../stores/polls.ts'
import type { createPollParams } from '../../types/index.ts'
import { validatePollForm } from '../../utils/validatePollForm.ts'

const props = defineProps<{
token: string,
Expand All @@ -139,6 +154,7 @@ const pollsStore = usePollsStore()

const isOpenedFromDraft = ref(false)
const pollOption = ref(null)
const pollImport = ref(null)

const pollForm = reactive<createPollParams>({
question: '',
Expand Down Expand Up @@ -223,6 +239,36 @@ function fillPollEditorFromDraft(id: number|null, isAlreadyOpened: boolean) {
}
}

/**
* Call native input[type='file'] to import a file
*/
function triggerImport() {
pollImport.value.click()
}

/**
* Validate imported file and insert data into form fields
* @param event import event
*/
function importPoll(event: Event) {
if (!(event.target as HTMLInputElement).files?.[0]) {
return
}

const reader = new FileReader()
reader.onload = (e: ProgressEvent) => {
try {
const parsedObject = validatePollForm(JSON.parse((e.target as FileReader).result as string))
fillPollForm(parsedObject)
} catch (error) {
showError(t('spreed', 'Error while importing poll'))
console.error('Error while importing poll:', error)
}
}

reader.readAsText((event.target as HTMLInputElement).files[0])
}

/**
* Insert data into form fields
* @param payload data to fill with
Expand Down
46 changes: 46 additions & 0 deletions src/utils/validatePollForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { createPollParams } from '../types/index.ts'

type requiredPollParams = Omit<createPollParams, 'draft'>
const pollFormExample = {
question: '',
options: ['', ''],
resultMode: 0,
maxVotes: 0,
}
const REQUIRED_KEYS: Array<keyof requiredPollParams> = Object.keys(pollFormExample) as Array<keyof requiredPollParams>

/**
* Parses a given JSON object and validates with required poll form object.
* Throws an error if parsed object doesn't match
* @param jsonObject The object to validate
*/
function validatePollForm(jsonObject: requiredPollParams): requiredPollParams {
if (typeof jsonObject !== 'object') {
throw new Error('Invalid parsed object')
}

for (const key of REQUIRED_KEYS) {
if (jsonObject[key] === undefined) {
throw new Error('Missing required key')
}

if (typeof pollFormExample[key] !== typeof jsonObject[key]) {
throw new Error('Invalid parsed value')
}

if (key === 'options' && jsonObject[key]?.some((opt: unknown) => typeof opt !== 'string')) {
throw new Error('Invalid parsed option values')
}
}

return jsonObject
}

export {
validatePollForm,
}

0 comments on commit e920cff

Please sign in to comment.