aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSkitty1238 <157652284+Skitty1238@users.noreply.github.com>2025-06-05 13:34:53 -0400
committerSkitty1238 <157652284+Skitty1238@users.noreply.github.com>2025-06-05 13:34:53 -0400
commit1ec0291a689ed0aea0bb6fdd91e3c08113bfac46 (patch)
tree5b0d5c21f4a866942bae9a55bbac4fc8c190ac3a
parentd73f7d9960285aceec01ef41a80bbb19e5d86f8c (diff)
changed add to google tasks button to sync to google button. Implemented auto creation and auto deletion in Google when node is created/deleted in dash
-rw-r--r--src/client/documents/Documents.ts1
-rw-r--r--src/client/views/nodes/TaskBox.tsx97
-rw-r--r--src/server/ApiManagers/GeneralGoogleManager.ts65
-rw-r--r--src/server/RouteManager.ts8
4 files changed, 160 insertions, 11 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4ad9c9bd8..120ad0688 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -528,6 +528,7 @@ export class DocumentOptions {
$task_endTime?: DateInfo | DateField = new DateInfo('end date and time', /*filterable*/ false);
$task_allDay?: BoolInfo | boolean = new BoolInfo('whether task is all-day or not', /*filterable*/ false);
$task_completed?: BoolInfo | boolean = new BoolInfo('whether the task is completed', /*filterable*/ false);
+ $googleTaskId?: STRt = new StrInfo('Google Task ID for syncing');
_calendar_date?: DateInfo | DateField = new DateInfo('current selected date of a calendar', /*filterable*/ false);
_calendar_dateRange?: STRt = new StrInfo('date range shown on a calendar', false);
diff --git a/src/client/views/nodes/TaskBox.tsx b/src/client/views/nodes/TaskBox.tsx
index b59599052..7df3876c3 100644
--- a/src/client/views/nodes/TaskBox.tsx
+++ b/src/client/views/nodes/TaskBox.tsx
@@ -159,13 +159,62 @@ export class TaskBox extends React.Component<TaskBoxProps> {
makeObservable(this);
}
+ _googleTaskCreateDisposer?: IReactionDisposer;
_heightDisposer?: IReactionDisposer;
_widthDisposer?: IReactionDisposer;
componentDidMount() {
this.setTaskDateRange();
-
const doc = this.props.Document;
+
+ // adding task on creation to google
+
+ (async () => {
+ if (!doc.$googleTaskId && doc.title) {
+ try {
+ const token = await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+ if (!token) return;
+
+ const body: any = {
+ title: doc.title || 'Untitled Task',
+ notes: doc.text || '',
+ status: doc.$task_completed ? 'completed' : 'needsAction',
+ completed: doc.$task_completed ? new Date().toISOString() : undefined,
+ };
+
+ if (doc.$task_allDay && typeof doc.$task_dateRange === 'string') {
+ const datePart = doc.$task_dateRange.split('|')[0];
+ if (datePart && !isNaN(new Date(datePart).getTime())) {
+ const baseDate = datePart.includes('T') ? datePart : datePart + 'T00:00:00Z';
+ body.due = new Date(baseDate).toISOString();
+ }
+ } else if (doc.$task_endTime instanceof DateField) {
+ body.due = doc.$task_endTime.date.toISOString();
+ }
+
+ const res = await fetch('/googleTasks/create', {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify(body),
+ });
+
+ const result = await res.json();
+ if (result?.id) {
+ doc.$googleTaskId = result.id;
+ console.log('✅ Google Task created on mount:', result);
+ } else {
+ console.warn('❌ Google Task creation failed:', result);
+ }
+ } catch (err) {
+ console.error('❌ Error creating Google Task:', err);
+ }
+ }
+ })();
+
this._heightDisposer = reaction(
() => Number(doc._height),
height => {
@@ -188,8 +237,32 @@ export class TaskBox extends React.Component<TaskBoxProps> {
}
componentWillUnmount() {
+ const doc = this.props.Document;
+ this._googleTaskCreateDisposer?.();
this._heightDisposer?.();
this._widthDisposer?.();
+
+ // task deletion
+ if (doc.$googleTaskId) {
+ (async () => {
+ try {
+ const token = await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+ if (!token) return;
+
+ await fetch(`/googleTasks/${doc.$googleTaskId}`, {
+ method: 'DELETE',
+ credentials: 'include',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ console.log(`✅ Deleted Google Task ${doc.$googleTaskId}`);
+ } catch (err) {
+ console.warn('❌ Failed to delete Google Task:', err);
+ }
+ })();
+ }
}
/**
@@ -213,7 +286,7 @@ export class TaskBox extends React.Component<TaskBoxProps> {
const startTime = doc.$task_startTime instanceof DateField && doc.$task_startTime.date instanceof Date ? toLocalDateTimeString(doc.$task_startTime.date) : '';
const endTime = doc.$task_endTime instanceof DateField && doc.$task_endTime.date instanceof Date ? toLocalDateTimeString(doc.$task_endTime.date) : '';
- const handleGoogleTaskClick = async () => {
+ const handleGoogleTaskSync = async () => {
console.log('GT button clicked');
try {
@@ -221,9 +294,9 @@ export class TaskBox extends React.Component<TaskBoxProps> {
if (!token) {
const listener = () => {
window.removeEventListener('focusin', listener);
- if (confirm('✅ Authorization complete. Try adding the task again?')) {
+ if (confirm('✅ Authorization complete. Try syncing the task again?')) {
// you could refactor the click handler here
- handleGoogleTaskClick();
+ handleGoogleTaskSync();
}
window.removeEventListener('focusin', listener);
};
@@ -252,8 +325,12 @@ export class TaskBox extends React.Component<TaskBoxProps> {
due = undefined;
}
- const response = await fetch('/googleTasks/create', {
- method: 'POST',
+ const isUpdate = !!doc.$googleTaskId;
+ const endpoint = isUpdate ? `/googleTasks/${doc.$googleTaskId}` : '/googleTasks/create';
+ const method = isUpdate ? 'PATCH' : 'POST';
+
+ const response = await fetch(endpoint, {
+ method,
credentials: 'include',
headers: {
'Content-Type': 'application/json',
@@ -272,13 +349,13 @@ export class TaskBox extends React.Component<TaskBoxProps> {
console.log('Google Task result:', result);
if (result?.id) {
- alert('✅ Task sent to Google Tasks!');
+ alert('✅ Task synced with Google Tasks!');
} else {
alert(`❌ Failed: ${result?.error?.message || 'Unknown error'}`);
}
} catch (err) {
console.error('Fetch error:', err);
- alert('❌ Task creation failed.');
+ alert('❌ Task syncing failed.');
}
};
@@ -327,9 +404,9 @@ export class TaskBox extends React.Component<TaskBoxProps> {
className="task-manager-google"
onClick={event => {
event.preventDefault();
- handleGoogleTaskClick();
+ handleGoogleTaskSync();
}}>
- Add to Google Tasks
+ Sync to Google
</button>
</div>
diff --git a/src/server/ApiManagers/GeneralGoogleManager.ts b/src/server/ApiManagers/GeneralGoogleManager.ts
index 4f0b8c02b..79ca55444 100644
--- a/src/server/ApiManagers/GeneralGoogleManager.ts
+++ b/src/server/ApiManagers/GeneralGoogleManager.ts
@@ -63,8 +63,9 @@ export default class GeneralGoogleManager extends ApiManager {
},
});
- // AARAV ADD (creating a task)
+ // AARAV ADD
+ // Task Creation
register({
method: Method.POST,
subscription: new RouteSubscriber('googleTasks').add('create'),
@@ -92,6 +93,68 @@ export default class GeneralGoogleManager extends ApiManager {
},
});
+ // Task Update
+ register({
+ method: Method.PATCH,
+ subscription: new RouteSubscriber('googleTasks').add('taskId'),
+ // any way to add static params? like /update (this is not very descriptive)
+ secureHandler: async ({ req, res, user }) => {
+ try {
+ const auth = await GoogleApiServerUtils.retrieveOAuthClient(user.id);
+
+ if (!auth) {
+ return res.status(401).send('Google credentials missing or invalid.');
+ }
+
+ const tasks = google.tasks({ version: 'v1', auth });
+
+ const { taskId } = req.params;
+ const { title, notes, due, status, completed } = req.body;
+
+ const result = await tasks.tasks.patch({
+ tasklist: '@default',
+ task: taskId,
+ requestBody: { title, notes, due, status, completed },
+ });
+
+ res.status(200).send(result.data);
+ } catch (err) {
+ console.error('Google Tasks update error:', err);
+ res.status(500).send('Failed to update task.');
+ }
+ },
+ });
+
+ // Task Deletion
+ register({
+ method: Method.DELETE,
+ subscription: new RouteSubscriber('googleTasks').add('taskId'),
+ secureHandler: async ({ req, res, user }) => {
+ try {
+ const auth = await GoogleApiServerUtils.retrieveOAuthClient(user.id);
+
+ if (!auth) {
+ return res.status(401).send('Google credentials missing or invalid.');
+ }
+
+ const tasks = google.tasks({ version: 'v1', auth });
+ const { taskId } = req.params;
+
+ await tasks.tasks.delete({
+ tasklist: '@default',
+ task: taskId,
+ });
+
+ res.status(200).send({ success: true });
+ } catch (err) {
+ console.error('Google Tasks delete error:', err);
+ res.status(500).send('Failed to delete task.');
+ }
+ },
+ });
+
+
+
register({
method: Method.GET,
subscription: '/refreshGoogle',
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 2f6cf80b5..c5d70da3d 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -8,6 +8,8 @@ import { DashUserModel } from './authentication/DashUserModel';
export enum Method {
GET,
POST,
+ PATCH,
+ DELETE,
}
export interface CoreArguments {
@@ -207,6 +209,12 @@ export default class RouteManager {
case Method.POST:
this.server.post(route, supervised);
break;
+ case Method.PATCH:
+ this.server.patch(route, supervised);
+ break;
+ case Method.DELETE:
+ this.server.delete(route, supervised);
+ break;
default:
}
}