Add routes to allow creating / editing events from outside

Signed-off-by: Georg Ehrke <developer@georgehrke.com>
This commit is contained in:
Georg Ehrke 2020-08-21 14:43:16 +02:00
parent eca3a76ad0
commit a699f6b862
No known key found for this signature in database
GPG Key ID: 9D98FD9380A1CB43
5 changed files with 121 additions and 40 deletions

View File

@ -27,6 +27,10 @@ return [
'routes' => [
// User views
['name' => 'view#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'view#index', 'url' => '/new', 'verb' => 'GET', 'postfix' => 'direct.new'],
['name' => 'view#index', 'url' => '/new/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'postfix' => 'direct.new.timerange'],
['name' => 'view#index', 'url' => '/edit/{objectId}', 'verb' => 'GET', 'postfix' => 'direct.edit'],
['name' => 'view#index', 'url' => '/edit/{objectId}/{recurrenceId}', 'verb' => 'GET', 'postfix' => 'direct.edit.recurrenceId'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth'], 'postfix' => 'view.timerange'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/new/{mode}/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth'], 'postfix' => 'view.timerange.new'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/edit/{mode}/{objectId}/{recurrenceId}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth'], 'postfix' => 'view.timerange.edit'],

View File

@ -31,43 +31,14 @@
</template>
<script>
import { dateFactory } from '../../../utils/date'
export default {
name: 'AppNavigationHeaderNewEvent',
methods: {
/**
* Opens the new event dialog
*/
newEvent() {
let name = this.$store.state.settings.skipPopover
? 'NewSidebarView'
: 'NewPopoverView'
if (window.innerWidth <= 768 && name === 'NewPopoverView') {
name = 'NewSidebarView'
}
const start = dateFactory()
// Setting a value greater than 23 is actually supported with the expected behavior:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setHours
start.setHours(start.getHours() + Math.ceil(start.getMinutes() / 60))
start.setMinutes(0)
const end = new Date(start.getTime())
end.setHours(start.getHours() + 1)
const params = Object.assign({}, this.$store.state.route.params, {
allDay: '0',
dtstart: String(Math.floor(start.getTime() / 1000)),
dtend: String(Math.floor(end.getTime() / 1000)),
})
// Don't push new route when day didn't change
if (name === this.$store.state.route.name
&& params.allDay === this.$store.state.route.params.allDay
&& params.dtstart === this.$store.state.route.params.dtstart
&& params.dtend === this.$store.state.route.params.dtend) {
return
}
this.$router.push({ name, params })
this.$router.push('/new')
},
},
}

View File

@ -26,7 +26,12 @@ import { getRootUrl, generateUrl } from '@nextcloud/router'
import Calendar from './views/Calendar'
import EditSimple from './views/EditSimple'
import EditSidebar from './views/EditSidebar'
import { getInitialView } from './utils/router.js'
import {
getDefaultEndDateForNewEvent,
getDefaultStartDateForNewEvent,
getInitialView,
getPreferredEditorRoute,
} from './utils/router.js'
Vue.use(Router)
@ -97,20 +102,25 @@ const router = new Router({
path: '/embed/:tokens',
redirect: `/embed/:tokens/${getInitialView()}/now`,
},
{
path: '/new',
redirect: () => `/${getInitialView()}/now/new/${getPreferredEditorRoute()}/0/${getDefaultStartDateForNewEvent()}/${getDefaultEndDateForNewEvent()}`,
},
{
path: '/new/:allDay/:dtstart/:dtend',
redirect: () => `/${getInitialView()}/:dtstart/new/${getPreferredEditorRoute()}/:allDay/:dtstart/:dtend`,
},
{
path: '/edit/:object',
redirect: `/${getInitialView()}/now/edit/sidebar/:object/next`,
redirect: () => `/${getInitialView()}/now/edit/${getPreferredEditorRoute()}/:object/next`,
},
{
path: '/edit/:object/:recurrenceId',
redirect: `/${getInitialView()}/now/edit/sidebar/:object/:recurrenceId`,
redirect: () => `/${getInitialView()}/now/edit/${getPreferredEditorRoute()}/:object/:recurrenceId`,
},
/**
* This is the main route that contains the current view and viewed day
* It has to be last, so that other routes starting with /p/, etc. match first
*
*
*
*/
{
path: '/:view/:firstDay',

View File

@ -19,6 +19,10 @@
*
*/
import { loadState } from '@nextcloud/initial-state'
import {
dateFactory,
getUnixTimestampFromDate,
} from './date.js'
/**
* Gets the initial view
@ -33,6 +37,56 @@ export function getInitialView() {
}
}
/**
* Gets the preferred editor view
*
* @returns {string} Either popover or sidebar
*/
export function getPreferredEditorRoute() {
let skipPopover
try {
skipPopover = loadState('calendar', 'skip_popover')
} catch (error) {
skipPopover = false
}
if (window.innerWidth <= 768) {
skipPopover = true
}
return skipPopover
? 'sidebar'
: 'popover'
}
/**
* Gets the default start-date for a new event
*
* @returns {string}
*/
export function getDefaultStartDateForNewEvent() {
const start = dateFactory()
start.setHours(start.getHours() + Math.ceil(start.getMinutes() / 60))
start.setMinutes(0)
return String(getUnixTimestampFromDate(start))
}
/**
* Gets the default end-date for a new event
*
* @returns {string}
*/
export function getDefaultEndDateForNewEvent() {
// When we have a setting for default event duration,
// this needs to be taken into consideration here
const start = getDefaultStartDateForNewEvent()
const end = new Date(Number(start) * 1000)
end.setHours(end.getHours() + 1)
return String(getUnixTimestampFromDate(end))
}
/**
* Prefixes a desired route name based on the current route
*

View File

@ -21,7 +21,9 @@
*/
import {
getInitialView,
getPrefixedRoute, isPublicOrEmbeddedRoute
getPrefixedRoute,
isPublicOrEmbeddedRoute,
getPreferredEditorRoute,
} from '../../../../src/utils/router.js'
import { loadState } from '@nextcloud/initial-state'
@ -29,6 +31,10 @@ jest.mock('@nextcloud/initial-state')
describe('utils/router test suite', () => {
beforeEach(() => {
loadState.mockClear()
})
it('should get the initial view', () => {
loadState
.mockReturnValueOnce('dayGridView')
@ -42,6 +48,42 @@ describe('utils/router test suite', () => {
expect(loadState).toHaveBeenNthCalledWith(2, 'calendar', 'initial_view')
})
it('should get the preferred editor view (big screens)', () => {
window.innerWidth = 1920
loadState
.mockReturnValueOnce(true)
.mockReturnValueOnce(false)
.mockImplementationOnce(() => { throw new Error() })
expect(getPreferredEditorRoute()).toEqual('sidebar')
expect(getPreferredEditorRoute()).toEqual('popover')
expect(getPreferredEditorRoute()).toEqual('popover')
expect(loadState).toHaveBeenCalledTimes(3)
expect(loadState).toHaveBeenNthCalledWith(1, 'calendar', 'skip_popover')
expect(loadState).toHaveBeenNthCalledWith(2, 'calendar', 'skip_popover')
expect(loadState).toHaveBeenNthCalledWith(3, 'calendar', 'skip_popover')
})
it('should get the preferred editor view (small screens)', () => {
window.innerWidth = 760
loadState
.mockReturnValueOnce(true)
.mockReturnValueOnce(false)
.mockImplementationOnce(() => { throw new Error() })
expect(getPreferredEditorRoute()).toEqual('sidebar')
expect(getPreferredEditorRoute()).toEqual('sidebar')
expect(getPreferredEditorRoute()).toEqual('sidebar')
expect(loadState).toHaveBeenCalledTimes(3)
expect(loadState).toHaveBeenNthCalledWith(1, 'calendar', 'skip_popover')
expect(loadState).toHaveBeenNthCalledWith(2, 'calendar', 'skip_popover')
expect(loadState).toHaveBeenNthCalledWith(3, 'calendar', 'skip_popover')
})
it('should provide the prefixed route name to navigate to', () => {
expect(getPrefixedRoute('PublicCalendarView', 'EditPopoverView')).toEqual('PublicEditPopoverView')
expect(getPrefixedRoute('PublicEditPopoverView', 'CalendarView')).toEqual('PublicCalendarView')