aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/GoogleAuthenticationManager.tsx2
-rw-r--r--src/client/views/nodes/TaskBox.tsx40
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.scss9
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.tsx99
-rw-r--r--src/server/ApiManagers/GeneralGoogleManager.ts35
5 files changed, 172 insertions, 13 deletions
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index 94ce42d8d..46581397d 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -41,6 +41,8 @@ export class GoogleAuthenticationManager extends React.Component<object> {
if (new RegExp(AuthenticationUrl).test(response)) {
this.isOpen = true;
this.authenticationLink = response;
+
+ // GETS STUCK AT THIS PROMISE!!
return new Promise<string>(resolve => {
this.disposer?.();
this.disposer = reaction(
diff --git a/src/client/views/nodes/TaskBox.tsx b/src/client/views/nodes/TaskBox.tsx
index 9d59746f8..1c7aeeb82 100644
--- a/src/client/views/nodes/TaskBox.tsx
+++ b/src/client/views/nodes/TaskBox.tsx
@@ -8,6 +8,7 @@ import { DateField } from '../../../fields/DateField';
import { Doc } from '../../../fields/Doc';
import './TaskBox.scss';
+import { GoogleAuthenticationManager } from '../../apis/GoogleAuthenticationManager';
/**
* Props (reference to document) for Task Box
@@ -267,6 +268,45 @@ export class TaskBox extends React.Component<TaskBoxProps> {
</label>
</div>
)}
+
+ {/** test button */}
+ <button
+ className="task-manager-google"
+ onClick={async () => {
+ console.log('GT button clicked');
+ const token = await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+ console.log('Got token', token);
+
+ try {
+ const response = await fetch('/googleTasks/create', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify({
+ title: taskTitle || 'Untitled Task',
+ notes: taskDesc,
+ due: allDay
+ ? String(doc.$task_dateRange)?.split('|')[0] + 'T23:59:00Z'
+ : (doc.$task_endTime instanceof DateField && doc.$task_endTime.date?.toISOString()) || undefined,
+ }),
+ });
+
+ const result = await response.json();
+ console.log("📬 Google Task result:", result);
+ alert(result?.id ? 'Task sent to Google Tasks!' : `Failed: ${result?.error?.message || 'Unknown error'}`);
+ } catch (err) {
+ console.error("Fetch error:", err);
+ alert("Fetch failed: ");
+ }
+ }}
+ >
+ GT
+ </button>
+
+
+
</div>
);
}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.scss b/src/client/views/nodes/calendarBox/CalendarBox.scss
index 891db9d90..89dc294a5 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.scss
+++ b/src/client/views/nodes/calendarBox/CalendarBox.scss
@@ -50,3 +50,12 @@
pointer-events: unset;
}
}
+
+.custom-drag-mirror {
+ transition: none !important;
+ transform: none !important;
+}
+
+.fc-event-dragging {
+ opacity: 0 !important;
+}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 26aed72c3..a4183a11a 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -22,6 +22,7 @@ import './CalendarBox.scss';
import { DateField } from '../../../../fields/DateField';
import { undoable } from '../../../util/UndoManager';
import { DocumentType } from '../../../documents/DocumentTypes';
+import { truncate } from 'fs/promises';
type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
@@ -74,8 +75,20 @@ export class CalendarBox extends CollectionSubView() {
@computed get calendarEvents(): EventSourceInput | undefined {
return this.childDocs.map(doc => {
- const { start, end } = dateRangeStrToDates(StrCast(doc.$task_dateRange));
- const isCompleted = BoolCast(doc.$task_completed); // AARAV ADD
+ // const { start, end } = dateRangeStrToDates(StrCast(doc.$task_dateRange));
+ const isCompleted = BoolCast(doc.$task_completed);
+
+ const rangeStr = StrCast(doc.$task_dateRange);
+ const [startStr, endStr] = rangeStr.split('|');
+ let start: string | Date, end: string | Date;
+
+ if (BoolCast(doc.$task_allDay)) {
+ start = startStr;
+ end = endStr;
+ } else {
+ ({ start, end } = dateRangeStrToDates(rangeStr));
+ }
+
return {
title: StrCast(doc.title),
start,
@@ -157,7 +170,7 @@ export class CalendarBox extends CollectionSubView() {
delete doc.$task_endTime;
} else {
doc.$task_startTime = new DateField(startDate);
- doc.task_endTime = new DateField(endDate);
+ doc.$task_endTime = new DateField(endDate);
}
}, 'change event date');
@@ -220,7 +233,7 @@ export class CalendarBox extends CollectionSubView() {
}}
initialDate={untracked(() => this.dateSelect.start)}
navLinks={true}
- editable={false}
+ editable={true}
// expandRows={true}
// handleWindowResize={true}
displayEventTime={false}
@@ -231,6 +244,7 @@ export class CalendarBox extends CollectionSubView() {
events={this.calendarEvents}
eventClick={this.handleEventClick}
eventDrop={this.handleEventDrop}
+ eventResize={this.handleEventDrop}
unselectAuto={false}
// unselect={() => {}}
select={(info: DateSelectArg) => {
@@ -284,15 +298,74 @@ export class CalendarBox extends CollectionSubView() {
ev.preventDefault();
});
}}
+
+ // for dragging and dropping (mirror)
+
+ eventDragStart={(arg) => {
+ const mirror = arg.el.cloneNode(true) as HTMLElement;
+ const rect = arg.el.getBoundingClientRect();
+
+ mirror.style.position = 'fixed';
+ mirror.style.pointerEvents = 'none';
+ mirror.style.opacity = '0.8';
+ mirror.style.zIndex = '10000';
+ mirror.classList.add('custom-drag-mirror');
+ mirror.style.width = `${rect.width}px`;
+ mirror.style.height = `${rect.height}px`;
+
+ document.body.appendChild(mirror);
+
+ const moveListener = (ev: MouseEvent) => {
+ mirror.style.left = `${ev.clientX}px`;
+ mirror.style.top = `${ev.clientY}px`;
+ };
+
+ window.addEventListener('mousemove', moveListener);
+
+ // hide the actual box
+ arg.el.style.visibility = 'hidden';
+ arg.el.style.opacity = '0';
+
+ (arg.el as any)._mirrorElement = mirror;
+ (arg.el as any)._moveListener = moveListener;
+ }}
+
+ eventDragStop={(arg) => {
+ const el = arg.el as any;
+ const mirror = el._mirrorElement;
+ const moveListener = el._moveListener;
+
+ // show the actual box
+ el.style.visibility = 'visible';
+ el.style.opacity = '1';
+
+ if (mirror) document.body.removeChild(mirror);
+ if (moveListener) window.removeEventListener('mousemove', moveListener);
+ }}
+
/>
);
}
render() {
+ const scale = this._props.ScreenToLocalTransform().Scale;
+ const scaledWidth = this._props.PanelWidth();
+ const scaledHeight = this._props.PanelHeight();
+
return (
<div
key={this.calendarViewType}
className={`calendarBox${this._props.isContentActive() ? '-interactive' : ''}`}
+ style={{
+ width: scaledWidth,
+ height: scaledHeight,
+ overflow: 'hidden',
+ position: 'relative',
+ }}
+ ref={r => {
+ this.createDashEventsTarget(r);
+ this.fixWheelEvents(r, this._props.isContentActive);
+ }}
onPointerDown={e => {
setTimeout(
action(() => {
@@ -303,17 +376,17 @@ export class CalendarBox extends CollectionSubView() {
if (cname.includes('timeGridDay')) this.dataDoc[this.calTypeFieldKey] = 'timeGridDay';
})
);
- }}
- style={{
- width: this._props.PanelWidth() / this._props.ScreenToLocalTransform().Scale,
- height: this._props.PanelHeight() / this._props.ScreenToLocalTransform().Scale,
- transform: `scale(${this._props.ScreenToLocalTransform().Scale})`,
- }}
- ref={r => {
- this.createDashEventsTarget(r);
- this.fixWheelEvents(r, this._props.isContentActive);
}}>
+ <div
+ style={{
+ transform: `scale(${scale})`,
+ transformOrigin: 'top left',
+ width: scaledWidth / scale,
+ height: scaledHeight / scale,
+ }}
+ >
{this.renderCalendar}
+ </div>
</div>
);
}
diff --git a/src/server/ApiManagers/GeneralGoogleManager.ts b/src/server/ApiManagers/GeneralGoogleManager.ts
index 12913b1ef..aa06ca1b3 100644
--- a/src/server/ApiManagers/GeneralGoogleManager.ts
+++ b/src/server/ApiManagers/GeneralGoogleManager.ts
@@ -3,6 +3,8 @@ import { Method } from '../RouteManager';
import { GoogleApiServerUtils } from '../apis/google/GoogleApiServerUtils';
import RouteSubscriber from '../RouteSubscriber';
import { Database } from '../database';
+import { google } from 'googleapis';
+
const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerUtils.ApiRouter>([
['create', (api, params) => api.create(params)],
@@ -61,5 +63,38 @@ export default class GeneralGoogleManager extends ApiManager {
res.send(undefined);
},
});
+
+ // AARAV ADD (creating a task)
+
+ register({
+ method: Method.POST,
+ subscription: new RouteSubscriber('googleTasks').add('create'),
+ secureHandler: async ({ req, res, user }) => {
+ try {
+ const { credentials } = await GoogleApiServerUtils.retrieveCredentials(user.id);
+ if (!credentials?.access_token) {
+ return res.status(401).send('Google access token not found.');
+ }
+
+ const auth = new google.auth.OAuth2();
+ auth.setCredentials({ access_token: credentials.access_token });
+
+ const tasks = google.tasks({ version: 'v1', auth });
+
+ const { title, notes, due } = req.body;
+ const result = await tasks.tasks.insert({
+ tasklist: '@default',
+ requestBody: { title, notes, due },
+ });
+
+ res.status(200).send(result.data);
+ } catch (err) {
+ console.error('Google Tasks error:', err);
+ res.status(500).send('Failed to create task.');
+ }
+ },
+ });
+
+
}
}