Skip to content
Draft
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
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v18
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
<!-- ix-docs-ignore -->

# Inkbox Fork
This fork is to support the legacy app data structure + added support to upload videos.

### Branches

- `main` branch is the original imgix app that we will continue to pull upstream changes from.
- `inkboxify` branch is the branch that we will use to make changes to the forked app for our use case and data structure.

**NOTE**: Inkbox Devs, please make sure to always create a new branch from `inkboxify` branch and then create a PR to `inkboxify` branch not upstream `main` branch.
We will break off non-inkbox specific changes into separate branches and PRs to contribute back to the upstream repo.


---

![imgix logo](https://assets.imgix.net/sdk-imgix-logo.svg)

A Contentful app that integrates with imgix's [Asset Manager](https://docs.imgix.com/setup/asset-manager). Browse, search, and insert assets into your content quickly and easily. Simplify your content editing workflow within Contentful and empower your developers with imgix’s powerful image rendering and optimization service.
Expand Down
41,441 changes: 25,208 additions & 16,233 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 16 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"@contentful/forma-36-fcss": "^0.3.0",
"@contentful/forma-36-react-components": "^3.97.1",
"@contentful/forma-36-tokens": "^0.11.0",
"@imgix/contentful": "^1.4.1",
"buffer": "^6.0.3",
"contentful-ui-extensions-sdk": "^3.31.0",
"cross-env": "^7.0.3",
"imgix-management-js": "^1.3.0",
Expand All @@ -34,11 +36,11 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-imgix": "^9.5.4",
"react-scripts": "4.0.3"
"react-scripts": "^5"
},
"scripts": {
"start": "cross-env BROWSER=none react-scripts start",
"build": "react-scripts build",
"build": "react-scripts build && node scripts/version-files.js",
"test": "react-scripts test",
"prepare": "react-scripts build",
"prepublishOnly": "react-scripts build",
Expand All @@ -62,18 +64,18 @@
]
},
"devDependencies": {
"@testing-library/jest-dom": "6.5.0",
"@testing-library/react": "16.0.1",
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.13",
"@types/lodash.debounce": "4.0.9",
"@types/node": "20.16.5",
"@types/react": "17.0.80",
"@types/react-dom": "17.0.25",
"@types/react-imgix": "9.5.3",
"cypress": "13.14.2",
"prettier": "3.2.5",
"typescript": "4.9.5"
"@testing-library/jest-dom": "^5.5.0",
"@testing-library/react": "^12.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.13",
"@types/lodash.debounce": "^4.0.9",
"@types/node": "^20.16.5",
"@types/react": "^16.0.80",
"@types/react-dom": "^16.0.25",
"@types/react-imgix": "^9.5.3",
"cypress": "^13.14.2",
"prettier": "^3.2.5",
"typescript": "^4.9.5"
},
"files": [
"dist",
Expand Down
23 changes: 23 additions & 0 deletions scripts/version-files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const fs = require('fs');
const path = require('path');

// Define the build directory
const buildDir = path.join(__dirname, '../build');

// Function to break cache by appending a timestamp
function breakCache() {
const indexPath = path.join(buildDir, 'index.html');
let indexContent = fs.readFileSync(indexPath, 'utf-8');

// Append a timestamp query parameter to JS file URLs
const timestamp = Date.now();
indexContent = indexContent.replace(
/<script defer="defer" src="([^"]+\.js)"><\/script>/g,
`<script defer="defer" src="$1?cache=${timestamp}"></script>`
);

fs.writeFileSync(indexPath, indexContent, 'utf-8');
console.log(`Cache broken with timestamp: ${timestamp}`);
}

breakCache();
12 changes: 10 additions & 2 deletions src/components/ConfigScreen/ConfigScreen.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
.ix-helper-text {
margin-top: 0.5rem;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
font-family:
-apple-system,
BlinkMacSystemFont,
Segoe UI,
Helvetica,
Arial,
sans-serif,
Apple Color Emoji,
Segoe UI Emoji,
Segoe UI Symbol;
font-size: 0.875rem;
line-height: 1.5;
display: block;
Expand Down
90 changes: 70 additions & 20 deletions src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
noOriginAssetsWebFolderError,
noSearchAssetsError,
noSourcesError,
fileTooLargeError,
} from '../../helpers/errors';
import { AppInstallationParameters } from '../ConfigScreen/';
import { ImageGallery } from '../Gallery/';
Expand All @@ -31,6 +32,10 @@ import packageJson from '../../../package.json';
import { UploadButton } from '../UploadButton/UploadButton';
import './Dialog.css';
import { stringifyJsonFields } from '../../helpers/utils';
import { Buffer } from 'buffer';

// @ts-ignore
window.Buffer = Buffer;

interface DialogProps {
sdk: DialogExtensionSDK;
Expand Down Expand Up @@ -356,7 +361,7 @@ export default class Dialog extends Component<DialogProps, DialogState> {
if (Object.keys(this.state.selectedSource).length) {
const defaultQuery = `?page[number]=${
currentIndex || this.state.page.currentIndex
}&page[size]=18`;
}&page[size]=18&filter[origin_path]=assets`; // INKBOX NOTE: Add assets filter to query

const assetObjects = query
? await this.getAssetObjects(query, noSearchAssetsError())
Expand Down Expand Up @@ -396,6 +401,10 @@ export default class Dialog extends Component<DialogProps, DialogState> {
}

let _destination, path;

// INKBOX NOTE: Force destination to be /assets/contentful
destination = '/assets/contentful';

if (destination) {
// strip the leading and trailing slash from destination
_destination = destination.replace(/^\//, '').replace(/\/$/, '');
Expand Down Expand Up @@ -458,20 +467,43 @@ export default class Dialog extends Component<DialogProps, DialogState> {
reader.addEventListener(
'load',
() => {
// convert image file to base64 string
const assetBase64String = reader.result as string;
const fileString = assetBase64String.replace(
/^data:image\/gif;base64,|^data:image\/png;base64,|^data:image\/jpeg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,|^data:image\/webp;base64,/,
'',
);
const buffer = Buffer.from(fileString, 'base64');
this.upload(buffer);
// Handle Image & Video Uploads
if (file?.type.includes('image')) {
this.handleImageUpload(reader, file);
} else if (file?.type.includes('video')) {
this.handleVideoUpload(reader, file);
} else {
// Error: Unsupported file type
console.error('imgix: unsupported file type');
}
},
false,
);
reader.readAsDataURL(file as File);
};

handleImageUpload = (reader: FileReader, file: File) => {
// convert image file to base64 string
const assetBase64String = reader.result as string;
const fileString = assetBase64String.replace(
/^data:image\/gif;base64,|^data:image\/png;base64,|^data:image\/jpeg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,|^data:image\/webp;base64,/,
'',
);
const buffer = Buffer.from(fileString, 'base64');
this.upload(buffer);
};

handleVideoUpload = (reader: FileReader, file: File) => {
// convert video file to base64 string
const assetBase64String = reader.result as string;
const fileString = assetBase64String.replace(
/^data:video\/mp4;base64,/,
'',
);
const buffer = Buffer.from(fileString, 'base64');
this.upload(buffer);
};

setIsUploading = (value: boolean) => {
this.setState({ isUploading: value });
};
Expand Down Expand Up @@ -506,6 +538,25 @@ export default class Dialog extends Component<DialogProps, DialogState> {
};

openFileForm = (file: File, previewSource: string, showUpload: boolean) => {

// INKBOX NOTE: Check video file size - we don't want anything larger than 50MB
if (file.type.includes('video') && file.size > 50 * 1024 * 1024) {
this.setState({
errors: [fileTooLargeError()],
});

return;
}

// INKBOX NOTE: Check image file size - we don't want anything larger than 10MB
if (file.type.includes('image') && file.size > 10 * 1024 * 1024) {
this.setState({
errors: [fileTooLargeError()],
});

return;
}

const uploadForm = {
...this.state.uploadForm,
file,
Expand Down Expand Up @@ -578,14 +629,6 @@ export default class Dialog extends Component<DialogProps, DialogState> {
</div>
)}
</div>
<ImageGallery
selectedSource={selectedSource}
sdk={sdk}
pageInfo={page}
changePage={this.debounceHandlePageChange}
assets={assets}
loading={this.state.loading}
/>
{/* { UI Error fallback } */}
{this.state.errors.length > 0 && (
<Note
Expand All @@ -595,6 +638,14 @@ export default class Dialog extends Component<DialogProps, DialogState> {
dismissable={this.state.errors[0].dismissable}
/>
)}
<ImageGallery
selectedSource={selectedSource}
sdk={sdk}
pageInfo={page}
changePage={this.debounceHandlePageChange}
assets={assets}
loading={this.state.loading}
/>
{this.state.showUpload && (
<div className="ix-upload-editor-container">
<div className="ix-upload-editor">
Expand Down Expand Up @@ -635,10 +686,9 @@ export default class Dialog extends Component<DialogProps, DialogState> {
className={
this.state.isUploading ? 'ix-input-readonly' : ''
}
value={this.state.uploadForm.destination || '/'}
value="/assets/contentful"
onChange={this.updateDestinationFilePath}
placeholder="/"
isReadOnly={this.state.isUploading}
isReadOnly={true}
></TextInput>
</div>
</form>
Expand Down
28 changes: 27 additions & 1 deletion src/components/Field/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,33 @@ export default class Field extends Component<FieldProps, FieldState> {

render() {
// Uncomment to test
// console.log({ ...this.state.selectedAsset });
//console.log({ ...this.state.selectedAsset });

// INKBOX NOTE: Undo all our shenanigans to get the selectedAsset back to the correct shape for the FieldImagePreview component

// undo the array wrapping
//@ts-ignore
this.state.selectedAsset = this.state.selectedAsset?.[0];

// ensure we have the correct src
if (this.state.selectedAsset && !this.state.selectedAsset.src) {
//@ts-ignore
this.state.selectedAsset.src = this.state.selectedAsset.original_url;

// get the correct content type by checking the file extension, check if image, video, or other
// we are assuming that the content type is image if it doesn't end with .mp4 - not the best way to do this, but it works for now
let contentType = 'image';
if (this.state.selectedAsset.src.endsWith('.mp4')) {
contentType = 'video';
}

// add the content type to the attributes back
this.state.selectedAsset.attributes = {
content_type: contentType,
};
}

// INKBOX NOTE: End of undoing shenanigans

const updateHeightHandler = this.props.sdk.window.updateHeight;
if (this.state.selectedAsset && this.state.selectedAsset.src) {
Expand Down
8 changes: 4 additions & 4 deletions src/components/Gallery/ImageGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ export class Gallery extends Component<GalleryProps, GalleryState> {
]),
};

this.props.sdk.close({
...stringifiedAsset,
selectedSource: this.props.selectedSource,
});
// INKBOX NOTE: ignore the asset because we need it in our format
// [ { original_url: url } ]

this.props.sdk.close([{ original_url: selectedAsset.src }]);
};

handleClose = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/UploadButton/UploadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function UploadButton(props: {
style={{ display: 'none' }}
ref={inputRef}
type="file"
accept="image/*"
accept="image/*, video/*"
onChange={handleFileChange}
/>
</div>
Expand Down
12 changes: 11 additions & 1 deletion src/helpers/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export type ErrorType =
| `NoSourcesError`
| `NoOriginAssetsError`
| `NoOriginAssetsWebFolderError`
| `noSearchAssetsError`;
| `noSearchAssetsError`
| `fileTooLargeError`;

const DASHBOARD_URL = 'https://dashboard.imgix.com';
const WEBFOLDER_DOCUMENTATION_URL = `https://docs.imgix.com/setup/serving-assets#web-folder`;
Expand Down Expand Up @@ -54,6 +55,12 @@ const ERROR_MESSAGES = {
name: 'No results found',
type: 'warning',
dismissable: true,
},
fileTooLargeError: {
message: `The file you are trying to upload is too large. Please try uploading a smaller file.`,
name: 'File too large',
type: 'negative',
dismissable: true,
}
} as const;

Expand Down Expand Up @@ -106,3 +113,6 @@ export const noOriginAssetsWebFolderError = (message?: string) =>

export const noSearchAssetsError = (message?: string) =>
new IxError('noSearchAssetsError', message);

export const fileTooLargeError = (message?: string) =>
new IxError('fileTooLargeError', message);
Loading