Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kj/wir 13 user experience enhancements settings conversation creation #37

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
367 changes: 196 additions & 171 deletions src/calendarIntegration/addMeetingLink.ts
Original file line number Diff line number Diff line change
@@ -1,171 +1,196 @@
/* global Office, console */

import { appendToBody, getBody, getLocation, getMailboxItemSubject, getOrganizer, setLocation } from "../utils/mailbox";
import { createMeetingSummary } from "./createMeetingSummary";
import { setCustomPropertyAsync, getCustomPropertyAsync } from "../utils/customProperties";
import { showNotification, removeNotification } from "../utils/notifications";
import { isOutlookCalIntegrationEnabled } from "./isOutlookCalIntegrationEnabled";
import { createEvent } from "./createEvent";
import { mailboxItem } from "../commands/commands";
import { EventResult } from "../types/EventResult";

const defaultSubjectValue = "New Appointment";
let createdMeeting: EventResult;

/**
* Checks if the feature is enabled by calling isOutlookCalIntegrationEnabled.
*
* @return {Promise<boolean>} Whether the feature is enabled or not.
*/
async function isFeatureEnabled(): Promise<boolean> {
const isEnabled = await isOutlookCalIntegrationEnabled();

if (!isEnabled) {
console.log("Outlook calendar integration is disabled for this team. Contact your Wire system administrator.");
removeNotification("wire-for-outlook-disabled");
showNotification(
"wire-for-outlook-disabled",
"Wire for Outlook is disabled for your team. Please contact your Wire system administrator.",
Office.MailboxEnums.ItemNotificationMessageType.ErrorMessage
);
return false;
}
return true;
}

/**
* Fetches custom properties from the mailbox item and sets the createdMeeting object if both wireId and wireLink are present.
*
* @return {Promise<void>} A promise that resolves when the custom properties are fetched and the createdMeeting object is set.
*/
async function fetchCustomProperties(): Promise<void> {
const wireId = await getCustomPropertyAsync(mailboxItem, "wireId");
const wireLink = await getCustomPropertyAsync(mailboxItem, "wireLink");

if (wireId && wireLink) {
createdMeeting = { id: wireId, link: wireLink } as EventResult;
}
}

/**
* Creates a new meeting by calling the createEvent function with a subject obtained from the mailboxItem.
* If the eventResult is not null, it sets the createdMeeting object to eventResult, updates the meeting details,
* and sets the custom properties wireId and wireLink on the mailboxItem.
*
* @return {Promise<void>} A promise that resolves when the new meeting is created and the custom properties are set.
*/
async function createNewMeeting(): Promise<void> {
removeNotification("adding-wire-meeting");
showNotification(
"adding-wire-meeting",
"Adding Wire meeting...",
Office.MailboxEnums.ItemNotificationMessageType.ProgressIndicator
);

const subject = await getMailboxItemSubject(mailboxItem);
const eventResult = await createEvent(subject || defaultSubjectValue);

if (eventResult) {
createdMeeting = eventResult;
await updateMeetingDetails(eventResult);
await setCustomPropertyAsync(mailboxItem, "wireId", eventResult.id);
await setCustomPropertyAsync(mailboxItem, "wireLink", eventResult.link);
}

removeNotification("adding-wire-meeting");
}

/**
* Updates the meeting details by setting the location and appending the meeting summary to the body of the mailbox item.
*
* @param {EventResult} eventResult - The event result containing the link for the meeting.
* @return {Promise<void>} A promise that resolves when the meeting details are updated.
*/
async function updateMeetingDetails(eventResult: EventResult): Promise<void> {
getOrganizer(mailboxItem, async (organizer) => {
await setLocation(mailboxItem, eventResult.link, () => {});
const meetingSummary = createMeetingSummary(eventResult.link, organizer);
await appendToBody(mailboxItem, meetingSummary);
});
}

/**
* Handles an existing meeting by updating meeting details and setting custom properties.
*
* @return {Promise<void>} A promise that resolves when the existing meeting is handled.
*/
async function handleExistingMeeting(): Promise<void> {
if (!createdMeeting) {
throw new Error("createdMeeting is undefined");
}

const currentBody = await getBody(mailboxItem);
const currentLocation = await getLocation(mailboxItem);
const normalizedCurrentBody = currentBody.replace(/&amp;/g, "&");
const normalizedMeetingLink = createdMeeting.link?.replace(/&amp;/g, "&");

getOrganizer(mailboxItem, async (organizer) => {
if (!currentLocation) {
await setLocation(mailboxItem, createdMeeting.link, () => {});
}
const meetingSummary = createMeetingSummary(createdMeeting.link, organizer);
if (!normalizedCurrentBody.includes(normalizedMeetingLink)) {
await appendToBody(mailboxItem, meetingSummary);
}
});

await setCustomPropertyAsync(mailboxItem, "wireId", createdMeeting.id);
await setCustomPropertyAsync(mailboxItem, "wireLink", createdMeeting.link);
}

/**
* Adds a meeting link to the Outlook calendar.
*
* @param {Office.AddinCommands.Event} event - The event object.
* @return {Promise<void>} A promise that resolves when the meeting link is added.
*/
async function addMeetingLink(event: Office.AddinCommands.Event): Promise<void> {
try {
const isEnabled = await isFeatureEnabled();
if (!isEnabled) return;

await fetchCustomProperties();
if (!createdMeeting) {
await createNewMeeting();
} else {
await handleExistingMeeting();
}
} catch (error) {
console.error("Error during adding Wire meeting link", error);
handleAddMeetingLinkError(error);
} finally {
event.completed();
}
}

/**
* Handles errors that occur when adding a meeting link.
*
* @param {Error} error - The error that occurred.
* @return {void} This function does not return anything.
*/
function handleAddMeetingLinkError(error: Error): void {
removeNotification("adding-wire-meeting");
removeNotification("adding-wire-meeting-error");

if (error.message.includes("authorization failed")) {
showNotification(
"adding-wire-meeting-error",
"Authorization failed.",
Office.MailboxEnums.ItemNotificationMessageType.ErrorMessage
);
} else {
showNotification(
"adding-wire-meeting-error",
"There was an error while adding the Wire meeting.",
Office.MailboxEnums.ItemNotificationMessageType.ErrorMessage
);
}
}

export { addMeetingLink };
/* global Office, console */

import { appendToBody, getBody, getLocation, getMailboxItemSubject, getMeetingTime, getOrganizer, setLocation, setSubject } from "../utils/mailbox";
import { createMeetingSummary } from "./createMeetingSummary";
import { setCustomPropertyAsync, getCustomPropertyAsync } from "../utils/customProperties";
import { showNotification, removeNotification } from "../utils/notifications";
import { isOutlookCalIntegrationEnabled } from "./isOutlookCalIntegrationEnabled";
import { createEvent } from "./createEvent";
import { mailboxItem } from "../commands/commands";
import { EventResult } from "../types/EventResult";

const defaultSubjectValue = "New Appointment";
let createdMeeting: EventResult;

/**
* Checks if the feature is enabled by calling isOutlookCalIntegrationEnabled.
*
* @return {Promise<boolean>} Whether the feature is enabled or not.
*/
async function isFeatureEnabled(): Promise<boolean> {
const isEnabled = await isOutlookCalIntegrationEnabled();

if (!isEnabled) {
console.log("Outlook calendar integration is disabled for this team. Contact your Wire system administrator.");
removeNotification("wire-for-outlook-disabled");
showNotification(
"wire-for-outlook-disabled",
"Wire for Outlook is disabled for your team. Please contact your Wire system administrator.",
Office.MailboxEnums.ItemNotificationMessageType.ErrorMessage
);
return false;
}
return true;
}

/**
* Fetches custom properties from the mailbox item and sets the createdMeeting object if both wireId and wireLink are present.
*
* @return {Promise<void>} A promise that resolves when the custom properties are fetched and the createdMeeting object is set.
*/
async function fetchCustomProperties(): Promise<void> {
const wireId = await getCustomPropertyAsync(mailboxItem, "wireId");
const wireLink = await getCustomPropertyAsync(mailboxItem, "wireLink");

if (wireId && wireLink) {
createdMeeting = { id: wireId, link: wireLink } as EventResult;
}
}

/**
* Modifies the conversation subject by appending the meeting date.
*
* @param {string} conversationSubject - The current subject of the conversation.
* @return {Promise<string>} The modified conversation subject.
*/
async function modifyConversationName(conversationSubject: string): Promise<string> {
const meetingDate = await getMeetingTime(mailboxItem);

const modifiedSubject = conversationSubject ? `CLDR:: ${conversationSubject}` : `CLDR:: ${meetingDate}`;

return modifiedSubject;
}


/**
* Creates a new meeting by calling the createEvent function with a subject obtained from the mailboxItem.
* If the eventResult is not null, it sets the createdMeeting object to eventResult, updates the meeting details,
* and sets the custom properties wireId and wireLink on the mailboxItem.
*
* @return {Promise<void>} A promise that resolves when the new meeting is created and the custom properties are set.
*/
async function createNewMeeting(): Promise<void> {
removeNotification("adding-wire-meeting");
showNotification(
"adding-wire-meeting",
"Adding Wire meeting...",
Office.MailboxEnums.ItemNotificationMessageType.ProgressIndicator
);

let subject = await getMailboxItemSubject(mailboxItem);
subject = await modifyConversationName(subject);

const eventResult = await createEvent(subject || defaultSubjectValue);

if (eventResult) {
createdMeeting = eventResult;
await updateMeetingDetails(eventResult);
await setCustomPropertyAsync(mailboxItem, "wireId", eventResult.id);
await setCustomPropertyAsync(mailboxItem, "wireLink", eventResult.link);
}

removeNotification("adding-wire-meeting");
}


/**
* Updates the meeting details by setting the location and appending the meeting summary to the body of the mailbox item.
*
* @param {EventResult} eventResult - The event result containing the link for the meeting.
* @return {Promise<void>} A promise that resolves when the meeting details are updated.
*/
async function updateMeetingDetails(eventResult: EventResult): Promise<void> {
getOrganizer(mailboxItem, async (organizer) => {
await setLocation(mailboxItem, eventResult.link);
const meetingSummary = createMeetingSummary(eventResult.link, organizer);
await appendToBody(mailboxItem, meetingSummary);
});
}

/**
* Handles an existing meeting by updating meeting details and setting custom properties.
*
* @return {Promise<void>} A promise that resolves when the existing meeting is handled.
*/
async function handleExistingMeeting(): Promise<void> {
if (!createdMeeting) {
throw new Error("createdMeeting is undefined");
}

const currentBody = await getBody(mailboxItem);
const currentSubject = await getMailboxItemSubject(mailboxItem);
const currentLocation = await getLocation(mailboxItem);
const normalizedCurrentBody = currentBody.replace(/&amp;/g, "&");
const normalizedMeetingLink = createdMeeting.link?.replace(/&amp;/g, "&");

getOrganizer(mailboxItem, async (organizer) => {

//Check if location is empty
if (!currentLocation) {
await setLocation(mailboxItem, createdMeeting.link);
}

const meetingSummary = createMeetingSummary(createdMeeting.link, organizer);

//Check if body is empty
if (!normalizedCurrentBody.includes(normalizedMeetingLink)) {
await appendToBody(mailboxItem, meetingSummary);
}

});

await setCustomPropertyAsync(mailboxItem, "wireId", createdMeeting.id);
await setCustomPropertyAsync(mailboxItem, "wireLink", createdMeeting.link);
}

/**
* Adds a meeting link to the Outlook calendar.
*
* @param {Office.AddinCommands.Event} event - The event object.
* @return {Promise<void>} A promise that resolves when the meeting link is added.
*/
async function addMeetingLink(event: Office.AddinCommands.Event): Promise<void> {
try {
const isEnabled = await isFeatureEnabled();
if (!isEnabled) return;

await fetchCustomProperties();
if (!createdMeeting) {
await createNewMeeting();
} else {
await handleExistingMeeting();
}
} catch (error) {
console.error("Error during adding Wire meeting link", error);
handleAddMeetingLinkError(error);
} finally {
event.completed();
}
}

/**
* Handles errors that occur when adding a meeting link.
*
* @param {Error} error - The error that occurred.
* @return {void} This function does not return anything.
*/
function handleAddMeetingLinkError(error: Error): void {
removeNotification("adding-wire-meeting");
removeNotification("adding-wire-meeting-error");

if (error.message.includes("authorization failed")) {
showNotification(
"adding-wire-meeting-error",
"Authorization failed.",
Office.MailboxEnums.ItemNotificationMessageType.ErrorMessage
);
} else {
showNotification(
"adding-wire-meeting-error",
"There was an error while adding the Wire meeting.",
Office.MailboxEnums.ItemNotificationMessageType.ErrorMessage
);
}
}

export { addMeetingLink };
2 changes: 1 addition & 1 deletion src/taskpane/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export default class App extends React.Component<AppProps, AppState> {
<div className="ms-Grid">
<div className="ms-Grid-row">
<div className="ms-Grid-col">
<h1>Settings</h1>
<h3>Wire for Outlook</h3>

{isLoggedIn && user ? <LoggedIn user={user} onLogout={this.logout} /> : <LoggedOut onLogin={this.login} />}
</div>
Expand Down
Loading
Loading