Skip to content

Commit

Permalink
Image file edit using luna paint extension for V1 and V2 data model (#…
Browse files Browse the repository at this point in the history
…733)

* Media file edit using luna paint extension for V1 and V2 data model

* Updated extension supported list
  • Loading branch information
tyaginidhi authored Oct 11, 2023
1 parent 6b9821a commit 94e295c
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 32 deletions.
8 changes: 4 additions & 4 deletions src/web/client/context/entityData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { IEntityInfo } from "../common/interfaces";

export interface IEntityData extends IEntityInfo {
entityEtag: string;
entityColumn: Map<string, string>;
entityColumn: Map<string, string | Uint8Array>;
mappingEntityId?: string;
}

export class EntityData implements IEntityData {
private _entityName!: string;
private _entityId!: string;
private _entityEtag!: string;
private _entityColumn!: Map<string, string>;
private _entityColumn!: Map<string, string | Uint8Array>;
private _mappingEntityId?: string;

public get entityName(): string {
Expand All @@ -27,7 +27,7 @@ export class EntityData implements IEntityData {
public get entityEtag(): string {
return this._entityEtag;
}
public get entityColumn(): Map<string, string> {
public get entityColumn(): Map<string, string | Uint8Array> {
return this._entityColumn;
}
public get mappingEntityId(): string | undefined {
Expand All @@ -43,7 +43,7 @@ export class EntityData implements IEntityData {
entityId: string,
entityName: string,
entityEtag: string,
entityColumn: Map<string, string>,
entityColumn: Map<string, string | Uint8Array>,
mappingEntityId?: string
) {
this._entityId = entityId;
Expand Down
6 changes: 3 additions & 3 deletions src/web/client/context/entityDataMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class EntityDataMap {
private updateEntityContent(
entityId: string,
columnName: string,
columnContent: string
columnContent: string | Uint8Array
) {
const existingEntity = this.entityMap.get(entityId);

Expand All @@ -36,7 +36,7 @@ export class EntityDataMap {
attributeContent: string,
mappingEntityId?: string
) {
let entityColumnMap = new Map<string, string>();
let entityColumnMap = new Map<string, string | Uint8Array>();
const existingEntity = this.entityMap.get(entityId);

if (existingEntity) {
Expand Down Expand Up @@ -84,7 +84,7 @@ export class EntityDataMap {
public updateEntityColumnContent(
entityId: string,
columnName: IAttributePath,
columnAttributeContent: string
columnAttributeContent: string | Uint8Array
) {
const existingEntity = this.entityMap.get(entityId);

Expand Down
21 changes: 17 additions & 4 deletions src/web/client/dal/fileSystemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
updateFileDirtyChanges,
updateFileEntityEtag,
} from "../utilities/fileAndEntityUtil";
import { isVersionControlEnabled } from "../utilities/commonUtil";
import { getImageFileContent, isImageFileSupportedForEdit, isVersionControlEnabled, updateFileContentInFileDataMap } from "../utilities/commonUtil";
import { IFileInfo } from "../common/interfaces";

export class File implements vscode.FileStat {
Expand Down Expand Up @@ -158,11 +158,13 @@ export class PortalsFS implements vscode.FileSystemProvider {
async writeFile(
uri: vscode.Uri,
content: Uint8Array,
options: { create: boolean; overwrite: boolean }
options: { create: boolean; overwrite: boolean },
isFirstTimeWrite = false
): Promise<void> {
const basename = path.posix.basename(uri.path);
const parent = await this._lookupParentDirectory(uri);
let entry = parent.entries.get(basename);
const isImageEdit = isImageFileSupportedForEdit(basename);

if (entry instanceof Directory) {
throw vscode.FileSystemError.FileIsADirectory(uri);
Expand All @@ -180,10 +182,21 @@ export class PortalsFS implements vscode.FileSystemProvider {
entry = new File(basename);
parent.entries.set(basename, entry);
this._fireSoon({ type: vscode.FileChangeType.Created, uri });
} else if (
WebExtensionContext.fileDataMap.getFileMap.get(uri.fsPath)
}

if (!isFirstTimeWrite &&
(WebExtensionContext.fileDataMap.getFileMap.get(uri.fsPath)
?.hasDirtyChanges
|| isImageEdit)
) {
if (isImageEdit) {
WebExtensionContext.telemetry.sendInfoTelemetry(
telemetryEventNames.WEB_EXTENSION_SAVE_IMAGE_FILE_TRIGGERED
);

updateFileContentInFileDataMap(uri.fsPath, getImageFileContent(uri.fsPath, content), true);
}

// Save data to dataverse
await vscode.window.withProgress(
{
Expand Down
3 changes: 2 additions & 1 deletion src/web/client/dal/remoteFetchProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,8 @@ async function createVirtualFile(
await portalsFS.writeFile(
vscode.Uri.parse(fileUri),
fileContent,
{ create: true, overwrite: true }
{ create: true, overwrite: true },
true
);

// Maintain entity details in context
Expand Down
21 changes: 4 additions & 17 deletions src/web/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ import {
showErrorDialog,
} from "./common/errorHandler";
import { WebExtensionTelemetry } from "./telemetry/webExtensionTelemetry";
import { convertContentToString, isCoPresenceEnabled } from "./utilities/commonUtil";
import { isCoPresenceEnabled, updateFileContentInFileDataMap } from "./utilities/commonUtil";
import { NPSService } from "./services/NPSService";
import { vscodeExtAppInsightsResourceProvider } from "../../common/telemetry-generated/telemetryConfiguration";
import { NPSWebView } from "./webViews/NPSWebView";
import {
updateFileDirtyChanges,
updateEntityColumnContent,
getFileEntityId,
getFileEntityName,
} from "./utilities/fileAndEntityUtil";
Expand Down Expand Up @@ -246,6 +244,8 @@ export function processWorkspaceStateChanges(context: vscode.ExtensionContext) {
);
}

// This function will not be triggered for image file content update
// Image file content write to images needs to be handled in writeFile call directly
export function processWillSaveDocument(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.workspace.onWillSaveTextDocument(async (e) => {
Expand All @@ -254,20 +254,7 @@ export function processWillSaveDocument(context: vscode.ExtensionContext) {
if (vscode.window.activeTextEditor === undefined) {
return;
} else if (isActiveDocument(fileFsPath)) {
const fileData =
WebExtensionContext.fileDataMap.getFileMap.get(fileFsPath);

// Update the latest content in context
if (fileData?.entityId && fileData.attributePath) {
let fileContent = e.document.getText();
fileContent = convertContentToString(fileContent, fileData.encodeAsBase64 as boolean);
updateEntityColumnContent(
fileData?.entityId,
fileData.attributePath,
fileContent
);
updateFileDirtyChanges(fileFsPath, true);
}
updateFileContentInFileDataMap(fileFsPath, e.document.getText());
}
})
);
Expand Down
2 changes: 2 additions & 0 deletions src/web/client/telemetry/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,6 @@ export enum telemetryEventNames {
WEB_EXTENSION_POWER_PAGES_WEB_VIEW_REGISTER_FAILED = 'webExtensionPowerPagesWebViewRegisterFailed',
WEB_EXTENSION_BACK_TO_STUDIO_TRIGGERED = 'webExtensionBackToStudioTriggered',
WEB_EXTENSION_PREVIEW_SITE_TRIGGERED = 'webExtensionPreviewSiteTriggered',
WEB_EXTENSION_IMAGE_EDIT_SUPPORTED_FILE_EXTENSION = 'webExtensionImageEditSupportedFileExtension',
WEB_EXTENSION_SAVE_IMAGE_FILE_TRIGGERED = 'webExtensionSaveImageFileTriggered'
}
45 changes: 43 additions & 2 deletions src/web/client/utilities/commonUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import { schemaEntityName } from "../schema/constants";
import { telemetryEventNames } from "../telemetry/constants";
import WebExtensionContext from "../WebExtensionContext";
import { SETTINGS_EXPERIMENTAL_STORE_NAME } from "../../../client/constants";
import { doesFileExist } from "./fileAndEntityUtil";
import { doesFileExist, getFileAttributePath, getFileEntityName, updateEntityColumnContent, updateFileDirtyChanges } from "./fileAndEntityUtil";
import { isWebFileV2 } from "./schemaHelperUtil";

// decodes file content to UTF-8
export function convertContentToUint8Array(content: string, isBase64Encoded: boolean): Uint8Array {
Expand All @@ -30,7 +31,7 @@ export function convertContentToUint8Array(content: string, isBase64Encoded: boo
}

// encodes file content to base64 or returns the content as is
export function convertContentToString(content: string, isBase64Encoded: boolean): string {
export function convertContentToString(content: string | Uint8Array, isBase64Encoded: boolean): string | Uint8Array {
return isBase64Encoded ? Buffer.from(content).toString(BASE_64) : content;
}

Expand Down Expand Up @@ -223,3 +224,43 @@ export function getBackToStudioURL() {
.replace("{.region}", region.toLowerCase() === STUDIO_PROD_REGION ? "" : `.${WebExtensionContext.urlParametersMap.get(queryParameters.REGION) as string}`)
.replace("{webSiteId}", WebExtensionContext.urlParametersMap.get(queryParameters.WEBSITE_ID) as string);
}
export function getSupportedImageFileExtensionsForEdit() {
return ['png', 'jpg', 'webp', 'bmp', 'tga', 'ico', 'jpeg', 'bmp', 'dib', 'jif', 'jpe', 'tpic']; // Luna paint supported image file extensions
}

export function isImageFileSupportedForEdit(fileName: string): boolean {
const fileExtension = getFileExtension(fileName) as string;
const supportedImageFileExtensions = getSupportedImageFileExtensionsForEdit();
const isSupported = fileExtension !== undefined ?
supportedImageFileExtensions.includes(fileExtension.toLowerCase()) : false;

if (isSupported) {
WebExtensionContext.telemetry.sendInfoTelemetry(telemetryEventNames.WEB_EXTENSION_IMAGE_EDIT_SUPPORTED_FILE_EXTENSION,
{ fileExtension: fileExtension });
}

return isSupported;
}

export function updateFileContentInFileDataMap(fileFsPath: string, fileContent: string | Uint8Array, isFileContentBase64Encoded = false) {
const fileData =
WebExtensionContext.fileDataMap.getFileMap.get(fileFsPath);

// Update the latest content in context
if (fileData?.entityId && fileData.attributePath) {
fileContent = convertContentToString(fileContent, isFileContentBase64Encoded ? false : fileData.encodeAsBase64 as boolean);

updateEntityColumnContent(
fileData?.entityId,
fileData.attributePath,
fileContent
);
updateFileDirtyChanges(fileFsPath, true);
}
}

export function getImageFileContent(fileFsPath: string, fileContent: Uint8Array) {
const webFileV2 = isWebFileV2(getFileEntityName(fileFsPath), getFileAttributePath(fileFsPath)?.source);

return webFileV2 ? fileContent : Buffer.from(fileContent).toString(BASE_64);
}
7 changes: 6 additions & 1 deletion src/web/client/utilities/fileAndEntityUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export function getFileEntityName(fileFsPath: string) {
?.entityName as string ?? WebExtensionContext.getVscodeWorkspaceState(fileFsPath)?.entityName as string;
}

export function getFileAttributePath(fileFsPath: string) {
return WebExtensionContext.fileDataMap.getFileMap.get(fileFsPath)
?.attributePath as IAttributePath;
}

export function getFileEntityEtag(fileFsPath: string) {
return WebExtensionContext.fileDataMap.getFileMap.get(fileFsPath)
?.entityEtag as string;
Expand Down Expand Up @@ -91,7 +96,7 @@ export function updateEntityEtag(entityId: string, entityEtag: string) {
export function updateEntityColumnContent(
entityId: string,
attributePath: IAttributePath,
fileContent: string
fileContent: string | Uint8Array
) {
WebExtensionContext.entityDataMap.updateEntityColumnContent(
entityId,
Expand Down

0 comments on commit 94e295c

Please sign in to comment.