mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-26 04:19:49 +03:00
fixed filtering bug and cleaned up code
This commit is contained in:
parent
f2543c6a1a
commit
55d724771a
@ -69,7 +69,7 @@ export const sendFilecoin = async (data) => {
|
||||
};
|
||||
|
||||
export const checkUsername = async (data) => {
|
||||
return await returnJSON(`/api/users/check`, {
|
||||
return await returnJSON(`/api/users/check-username`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
@ -91,13 +91,6 @@ export const removeFromBucket = async (data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getNetworkDirectory = async () => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/directory`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
});
|
||||
};
|
||||
|
||||
export const getSlateById = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/slates/get`, {
|
||||
@ -167,25 +160,18 @@ export const search = async (data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const createPendingFiles = async (data) => {
|
||||
export const createFile = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/data/create-pending`, {
|
||||
return await returnJSON(`/api/data/create`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
|
||||
export const processPendingFiles = async () => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/data/process-pending`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify(),
|
||||
});
|
||||
};
|
||||
|
||||
export const addFileToSlate = async (data) => {
|
||||
console.log(data);
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/slates/add-url`, {
|
||||
return await returnJSON(`/api/slates/add-file`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
@ -241,12 +227,12 @@ export const updateSearch = async (data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const checkCIDStatus = async (data) => {
|
||||
return await returnJSON(`/api/data/cid-status`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
// export const checkCIDStatus = async (data) => {
|
||||
// return await returnJSON(`/api/data/cid-status`, {
|
||||
// ...DEFAULT_OPTIONS,
|
||||
// body: JSON.stringify({ data }),
|
||||
// });
|
||||
// };
|
||||
|
||||
export const createSlate = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
@ -264,6 +250,14 @@ export const updateSlate = async (data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const updateSlateLayout = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/slates/update-layout`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteSlate = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/slates/delete`, {
|
||||
@ -274,7 +268,7 @@ export const deleteSlate = async (data) => {
|
||||
|
||||
export const removeFileFromSlate = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/slates/remove`, {
|
||||
return await returnJSON(`/api/slates/remove-file`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
@ -295,15 +289,15 @@ export const deleteAPIKey = async (data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const addCIDToData = async (data) => {
|
||||
export const saveCopy = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/data/add`, {
|
||||
return await returnJSON(`/api/data/save-copy`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
|
||||
export const updateData = async (data) => {
|
||||
export const updateFile = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/data/update`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
@ -319,9 +313,9 @@ export const toggleFilePrivacy = async (data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteBucketItems = async (data) => {
|
||||
export const deleteFiles = async (data) => {
|
||||
await Websockets.checkWebsocket();
|
||||
return await returnJSON(`/api/data/remove`, {
|
||||
return await returnJSON(`/api/data/delete`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
@ -349,7 +343,14 @@ export const createSupportMessage = async (data) => {
|
||||
};
|
||||
|
||||
export const getActivity = async (data) => {
|
||||
return await returnJSON(`/api/activity/get`, {
|
||||
return await returnJSON(`/api/activity/get-activity`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
|
||||
export const getExplore = async (data) => {
|
||||
return await returnJSON(`/api/activity/get-explore`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
|
@ -30,8 +30,9 @@ export const init = ({ resource = "", viewer, onUpdate, onNewActiveUser = () =>
|
||||
return null;
|
||||
}
|
||||
|
||||
const payload = { type: "SUBSCRIBE_VIEWER", data: viewer };
|
||||
|
||||
const payload = { type: "SUBSCRIBE_VIEWER", data: { id: viewer.id } };
|
||||
console.log(payload);
|
||||
console.log("inside browser websockets init");
|
||||
client.send(JSON.stringify(payload));
|
||||
});
|
||||
|
||||
|
@ -126,6 +126,7 @@ export const upload = async ({ file, context, bucketName, routes, excludeFromLib
|
||||
});
|
||||
}
|
||||
};
|
||||
console.log(formData);
|
||||
XHR.send(formData);
|
||||
});
|
||||
|
||||
@ -152,7 +153,7 @@ export const upload = async ({ file, context, bucketName, routes, excludeFromLib
|
||||
res = await _privateUploadMethod(`${generalRoute}${file.name}`, file);
|
||||
}
|
||||
|
||||
if (!res || res.error || !res.data) {
|
||||
if (!res?.data || res.error) {
|
||||
if (context) {
|
||||
await context.setState({
|
||||
fileLoading: {
|
||||
@ -169,43 +170,16 @@ export const upload = async ({ file, context, bucketName, routes, excludeFromLib
|
||||
return !res ? { decorator: "NO_RESPONSE_FROM_SERVER", error: true } : res;
|
||||
}
|
||||
|
||||
if (res.data.data.type.startsWith("image/")) {
|
||||
let url = `${Constants.gateways.ipfs}/${res.data.data.cid}`;
|
||||
let item = res.data.data;
|
||||
if (item.data.type.startsWith("image/")) {
|
||||
let url = Strings.getURLfromCID(item.cid);
|
||||
try {
|
||||
let blurhash = await encodeImageToBlurhash(url);
|
||||
res.data.data.blurhash = blurhash;
|
||||
item.data.blurhash = blurhash;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!excludeFromLibrary) {
|
||||
await Actions.createPendingFiles({ data: res.data });
|
||||
}
|
||||
|
||||
res.data = res.data.data;
|
||||
|
||||
return { file, json: res };
|
||||
};
|
||||
|
||||
export const uploadToSlate = async ({ responses, slate }) => {
|
||||
let added;
|
||||
let skipped;
|
||||
if (responses && responses.length) {
|
||||
const addResponse = await Actions.addFileToSlate({
|
||||
slate,
|
||||
data: responses.map((res) => {
|
||||
return { title: res.file.name, ...res.json.data };
|
||||
}),
|
||||
});
|
||||
|
||||
if (Events.hasError(addResponse)) {
|
||||
return;
|
||||
}
|
||||
|
||||
added = addResponse.added;
|
||||
skipped = addResponse.skipped;
|
||||
}
|
||||
let message = Strings.formatAsUploadMessage(added, skipped, true);
|
||||
Events.dispatchMessage({ message, status: !added ? null : "INFO" });
|
||||
return item;
|
||||
};
|
||||
|
@ -1,354 +1,190 @@
|
||||
export const error = {
|
||||
//Slate Create
|
||||
SERVER_FIND_USER_CREATE_SLATE: "There seems to be an issue with your account",
|
||||
SERVER_FIND_USER_CREATE_SLATE_USER_NOT_FOUND: "Login to create a Slate!",
|
||||
SERVER_EXISTING_SLATE: "You already have a slate with that name. Please choose another",
|
||||
SERVER_CREATE_SLATE: "There was an error when creating the Slate",
|
||||
FIND_USER_CREATE_SLATE: "There seems to be an issue with your account",
|
||||
FIND_USER_CREATE_SLATE_USER_NOT_FOUND: "Login to create a Slate!",
|
||||
EXISTING_SLATE: "A Slate with that name already exists. Please try another",
|
||||
CREATE_SLATE: "There was an error when creating the Slate",
|
||||
SERVER_SLATE_LIMIT: "You've reached the limit for number of slates!",
|
||||
//General
|
||||
SERVER_NOT_AUTHENTICATED:
|
||||
"You are not currently logged in. Please refresh the page and sign in to perform this action",
|
||||
SERVER_USER_NOT_FOUND: "We're having trouble locating your information right now",
|
||||
SERVER_NO_BUCKET_DATA: "We ran into issues while trying to locate your data",
|
||||
|
||||
//Slate Add URL
|
||||
ADD_TO_SLATE_USER_NOT_FOUND: "Login to add a Slate URL",
|
||||
ADD_TO_SLATE_SLATE_NOT_FOUND: "Sorry, we couldn't find that Slate! Please try another",
|
||||
ADD_TO_SLATE_ERROR: "There was an error retriving this Slate",
|
||||
//Activity and explore
|
||||
SERVER_GET_ACTIVITY_NOT_FOUND: "We're having trouble loading activity right now",
|
||||
SERVER_GET_EXPLORE_NOT_FOUND: "We're having trouble loading explore right now",
|
||||
|
||||
//Slate remove file
|
||||
SERVER_REMOVE_FROM_SLATE_USER_NOT_FOUND: "Login to manage your slates",
|
||||
SERVER_REMOVE_FROM_SLATE_NO_ID_PROVIDED: "Please specify which item you would like to remove",
|
||||
SERVER_REMOVE_FROM_SLATE_SLATE_NOT_FOUND: "We're having trouble locating that slate",
|
||||
SERVER_REMOVE_FROM_SLATE_ERROR: "We're having trouble removing from this slate right now",
|
||||
//Filecoin and archiving
|
||||
SERVER_SEND_FILECOIN_ACTION_FAILURE: "We're having trouble sending out Filecoin right now",
|
||||
SERVER_ARCHIVE_NO_FILES: "You have no files to archive",
|
||||
SERVER_ARCHIVE_BUCKET_TOO_SMALL:
|
||||
"Your deal size is too small to archive. Deals should be a minimum of 100MB",
|
||||
SERVER_ARCHIVE_BUCKET_COUNT_VERIFICATION_FAILED:
|
||||
"We ran into issues while verifying your ability to make storage deals",
|
||||
SERVER_ARCHIVE_MAX_NUMBER_BUCKETS:
|
||||
"You have reached the maximum number of storage deals for this account",
|
||||
SERVER_ARCHIVE_ENCRYPTION_FAILED:
|
||||
"We encountered issues while encrypting your files for archiving",
|
||||
SERVER_ARCHIVE_BUCKET_CLONING_FAILED:
|
||||
"We ran into issues while replicating your files for archiving",
|
||||
SERVER_ARCHIVE_DEAL_FAILED: "The storage deal wasn't successful",
|
||||
|
||||
//Slate Delete
|
||||
DELETE_SLATE_BY_ID:
|
||||
"Sorry, there was an error while trying to delete that slate. Please try again",
|
||||
DELETE_SLATES_FOR_USER_ID:
|
||||
"Sorry, we're having trouble deleting all your slates. Please try again later",
|
||||
SERVER_DELETE_SLATE: "We're having trouble deleting that at the moment. Please try again later",
|
||||
SERVER_DELETE_SLATE_USER_NOT_FOUND: "Login to manage your slates!",
|
||||
//Bucket remove
|
||||
SERVER_BUCKET_REMOVE_NO_CID:
|
||||
"We ran into issues while removing a file. There was no file specified",
|
||||
SERVER_BUCKET_REMOVE_BUCKET_NOT_FOUND: "We couldn't locate your files to delete",
|
||||
SERVER_BUCKET_REMOVE_NO_BUCKET_ITEMS: "We couldn't locate your files to delete",
|
||||
SERVER_BUCKET_REMOVE_NO_MATCHING_CID: "There were no matching files found",
|
||||
SERVER_BUCKET_REMOVE_FAILED: "We were not able to delete that file",
|
||||
|
||||
//File create
|
||||
CREATE_FILE_NO_FILE_PROVIDED: "We ran into issues while creating a file. No file was provided",
|
||||
CREATE_FILE_DUPLICATE: "The uploaded file(s) were duplicates and were skipped",
|
||||
CREATE_FILE_FAILED: "We ran into issues while creating that file",
|
||||
|
||||
//File delete
|
||||
SERVER_REMOVE_DATA_NO_IDS: "The file to delete was not specified",
|
||||
|
||||
//Save copy
|
||||
SERVER_SAVE_COPY_NO_CIDS: "The file to save was not specified",
|
||||
|
||||
//Toggle file privacy
|
||||
SERVER_TOGGLE_FILE_PRIVACY_NO_FILE:
|
||||
"We ran into issues while editing a file's privacy. No file was specified",
|
||||
SERVER_TOGGLE_FILE_PRIVACY_UPDATE_FAILED:
|
||||
"We're having trouble updating the privacy of that file right now",
|
||||
|
||||
//Update file
|
||||
SERVER_EDIT_DATA_NO_FILE: "We ran into issues while editing a file. No file was provided",
|
||||
SERVER_EDIT_DATA_FAILED: "We're having trouble updating that file right now",
|
||||
|
||||
//Delete api key
|
||||
SERVER_DELETE_API_KEY_NOT_FOUND:
|
||||
"We weren't able to locate that API key. It may have already been deleted",
|
||||
SERVER_DELETE_API_KEY_ERROR: "We weren't able to delete that API key. Please try again later",
|
||||
|
||||
//Create api key
|
||||
SERVER_GENERATE_API_KEY_TOO_MANY_KEYS: "You've reached the limit for number of API keys",
|
||||
SERVER_GENERATE_API_KEY_ERROR:
|
||||
"We're having trouble generating an API key right now, please try again later",
|
||||
|
||||
//Add to slate
|
||||
SERVER_ADD_TO_SLATE_NO_SLATE:
|
||||
"We ran into issues while adding files to that slate. No slate was specified",
|
||||
SERVER_ADD_TO_SLATE_SLATE_NOT_FOUND: "We're having trouble locating that slate right now",
|
||||
SERVER_ADD_TO_SLATE_NO_FILES:
|
||||
"We ran into issues while adding files to that slate. No files selected",
|
||||
SERVER_ADD_TO_SLATE_FAILED: "We're having trouble adding those files to that slate right now",
|
||||
|
||||
//Slate create
|
||||
SERVER_CREATE_SLATE_EXISTING_SLATE_NAME:
|
||||
"You already have a slate with that name. Slate names must be unique",
|
||||
SERVER_CREATE_SLATE_FAILED:
|
||||
"We are having trouble creating that slate right now, please try again later",
|
||||
|
||||
//Slate delete
|
||||
SERVER_DELETE_SLATE_SLATE_NOT_FOUND:
|
||||
"We're having trouble deleting that at the moment. Please try again later",
|
||||
SERVER_DELETE_SLATE: "We're having trouble deleting that at the moment. Please try again later",
|
||||
"We're having difficulty locating that slate. It may have already been deleted",
|
||||
SERVER_DELETE_SLATE_FAILED:
|
||||
"We're having trouble deleting that slate right now, please try again later",
|
||||
|
||||
//Slate Get
|
||||
GET_SLATE_BY_ID:
|
||||
"We're having trouble retrieving information on that slate right now. Please try again later",
|
||||
GET_SLATE_BY_NAME:
|
||||
"We're having trouble retrieving information on that slate right now. Please try again later",
|
||||
GET_SLATES_BY_USER_ID:
|
||||
"We're having trouble retrieving that user's information. Please try again later",
|
||||
SLATE_NOT_FOUND: "That slate could not be found. It may have been deleted",
|
||||
SLATE_OWNER_NOT_FOUND:
|
||||
"We're having trouble retrieving that slate's owner right now. Please try again",
|
||||
SERVER_GET_SLATE_USER_NOT_FOUND:
|
||||
"We're having trouble retrieving that slate's owner right now. Please try again",
|
||||
SERVER_GET_SLATE:
|
||||
"We're having trouble retrieving information on that slate right now. Please try again later",
|
||||
//Get slate
|
||||
SERVER_GET_SERIALIZED_SLATE_SLATE_NOT_FOUND:
|
||||
"We were unable to locate that slate. It may be private or it may not exist",
|
||||
SERVER_GET_SERIALIZED_SLATE_PRIVATE_ACCESS_DENIED:
|
||||
"We were unable to locate that slate. It may be private or it may not exist",
|
||||
SERVER_GET_SLATE_NOT_FOUND:
|
||||
"We're having trouble retrieving information on that slate right now. Please try again later",
|
||||
"We were unable to locate that slate. It may be private or it may not exist",
|
||||
SERVER_GET_SLATE_PRIVATE_ACCESS_DENIED:
|
||||
"We were unable to locate that slate. It may be private or it may not exist",
|
||||
|
||||
//Slate Update
|
||||
UPDATE_SLATE_BY_ID: "We ran into an issue while saving your slate. Please try again",
|
||||
SERVER_ADD_TO_SLATE_USER_NOT_FOUND: "Login to upload files!",
|
||||
SERVER_FIND_USER_UPDATE_SLATE_USER_NOT_FOUND: "Login to upload files!",
|
||||
SERVER_ADD_TO_SLATE_SLATE_NOT_FOUND:
|
||||
"We ran into issues while uploading that file. Please try again",
|
||||
SERVER_ADD_TO_SLATE_ERROR: "We ran into issues while uploading that file. Please try again",
|
||||
SERVER_FIND_USER_UPDATE_SLATE: "We ran into issues while uploading that file. Please try again",
|
||||
SERVER_UPDATE_SLATE_NOT_FOUND: "We ran into issues while locating that slate. Please try again",
|
||||
SERVER_UPDATE_SLATE_MUST_PROVIDE_DATA: "The input cannot be blank. Please check your input",
|
||||
SERVER_UPDATE_SLATE_MUST_PROVIDE_NAME: "Please provide a slate name",
|
||||
SERVER_UPDATE_SLATE: "We're having trouble updating that slate right now. Please try again later",
|
||||
SERVER_UPDATE_SLATE_NAME_TAKEN: "You already have a slate with that name. Please choose another",
|
||||
V1_SERVER_UPLOAD_SLATE_NOT_FOUND:
|
||||
"We're having trouble locating that slate right now. Please try again later",
|
||||
V1_SERVER_API_KEY_NOT_FOUND:
|
||||
"We can't seem to find your API key right now. Please try again later",
|
||||
V1_SERVER_API_UPLOAD_ERROR:
|
||||
"We're having trouble uploading that right now. Please try again later",
|
||||
V1_SERVER_UPLOAD_SLATE_NOT_FOUND: "We're having trouble locating that slate right now",
|
||||
V1_SERVER_UPLOAD_TO_SLATE_ERROR:
|
||||
"We're ran into issues while adding that to the slate. Please try again",
|
||||
V1_GET_SLATE_NOT_FOUND:
|
||||
"We're having trouble locating that slate right now. Please try again later",
|
||||
V1_GET_SLATE_USER_NOT_FOUND:
|
||||
"We're having trouble locating the owner of that slate right now. Please try again later",
|
||||
V1_GET_SLATE_SLATE_NOT_FOUND:
|
||||
"We're having trouble locating that slate right now. Please try again later",
|
||||
V1_GET_SLATES_NOT_FOUND:
|
||||
"We're having trouble locating those slates right now. Please try again later",
|
||||
//Remove from slate
|
||||
SERVER_REMOVE_FROM_SLATE_NO_ID_PROVIDED:
|
||||
"Unable to remove from slate because no slate was specified",
|
||||
SERVER_REMOVE_FROM_SLATE_SLATE_NOT_FOUND: "We are having trouble locating that slate",
|
||||
SERVER_REMOVE_FROM_SLATE_FAILED: "We are having trouble removing from that slate right now",
|
||||
|
||||
//Address Send
|
||||
SERVER_SEND_FILECOIN_USER_NOT_FOUND: "Sorry, we couldn't find that user!",
|
||||
SERVER_SEND_FILECOIN_NO_ID: "That user doesn't seem to exist. Please try another",
|
||||
SERVER_SEND_FILECOIN_ACTION_FAILURE:
|
||||
"There was an error sending the transaction. We're looking into it",
|
||||
SEND_FILECOIN_USER_NOT_FOUND: "Sorry, we couldn't find that user!",
|
||||
SEND_FILECOIN: "There was an error sending the transaction. We're looking into it",
|
||||
SEND_FILECOIN_ACTION_FAILURE: "There was an error sending the transaction. We're looking into it",
|
||||
//Update slate layout
|
||||
SERVER_UPDATE_SLATE_LAYOUT_MUST_PROVIDE_DATA: "No layout was provided to update",
|
||||
SERVER_UPDATE_SLATE_LAYOUT_NOT_FOUND: "We are having trouble locating that slate",
|
||||
SERVER_UPDATE_SLATE_LAYOUT:
|
||||
"We are unable to update that slate's layout right now. Please try again later",
|
||||
|
||||
//Address Create
|
||||
CREATE_FILECOIN_ADDRESS: "There was an error when creating the Filecoin address.",
|
||||
//Update slate
|
||||
SERVER_UPDATE_SLATE_MUST_PROVIDE_DATA:
|
||||
"We are unable to update that slate because no data was provided",
|
||||
SERVER_UPDATE_SLATE_NOT_FOUND: "We are having trouble locating that slate",
|
||||
SERVER_UPDATE_SLATE_UPDATE_PRIVACY_FAILED:
|
||||
"We are having trouble updating the privacy of that slate",
|
||||
SERVER_UPDATE_SLATE_INVALID_NAME: "Please use a valid slate name",
|
||||
SERVER_UPDATE_SLATE_NAME_TAKEN:
|
||||
"You already have a slate with that name. Slate names must be unique",
|
||||
SERVER_UPDATE_SLATE_FAILED: "We are having trouble updating that slate right now",
|
||||
|
||||
//Data Upload
|
||||
UPLOAD_PARSE_FAILURE: "There was an error when parsing the upload. Please try again",
|
||||
UPLOAD_NOT_IMAGE_TYPE:
|
||||
"We are only accepting JPG and PNG files at this time. Try uploading a different file type!",
|
||||
BUCKETS_PUSH_ISSUE: "There was an error uploading the data",
|
||||
SERVER_UPLOAD_ERROR: "We're having issues uploading that file right now",
|
||||
SERVER_API_KEY_MISSING: "We can't seem to find your API key right now. Please try again later",
|
||||
CREATE_PENDING_DATA: "We ran into issues while uploading your data, please try again later",
|
||||
CREATE_PENDING_ERROR: "We ran into issues while uploading your data, please try again later",
|
||||
PROCESS_PENDING_ERROR:
|
||||
"We ran into an error while updating your uploaded data. Please try again later",
|
||||
PROCESS_PENDING_USER_NOT_FOUND: "Please log in to upload files",
|
||||
//Create user
|
||||
SERVER_CREATE_USER_NOT_ALLOWED: "You can only create users while on slate.host",
|
||||
SERVER_CREATE_USER_ACCEPT_TERMS: "You must accept the terms of service to create an account",
|
||||
SERVER_CREATE_USER_USERNAME_TAKEN: "There is already an account with that username",
|
||||
SERVER_CREATE_USER_INVALID_USERNAME: "Please choose a valid username",
|
||||
SERVER_CREATE_USER_INVALID_PASSWORD: "Please chooose a valid password",
|
||||
SERVER_CREATE_USER_BUCKET_INIT_FAILURE:
|
||||
"We're having trouble setting up your storage, please try again later",
|
||||
SERVER_CREATE_USER_FAILED:
|
||||
"We're having trouble creating your account right now. Please try again later",
|
||||
|
||||
//Data CID Status
|
||||
NO_CIDS_TO_CHECK: "There are no CIDs to check",
|
||||
//Get user
|
||||
SERVER_GET_USER_NO_USER_PROVIDED:
|
||||
"We were not able to fetch that user because no user was specified",
|
||||
SERVER_GET_USER_USER_NOT_FOUND: "We were not able to locate that user",
|
||||
|
||||
//Data Get
|
||||
SERVER_GET_BUCKET_DATA: "We ran into an issue fetching that data. Please try again later",
|
||||
//Get user social
|
||||
SERVER_USER_SOCIAL_NO_USER_ID:
|
||||
"We were not able to fetch that user's subscriptions and following because no user was specified",
|
||||
SERVER_USER_SOCIAL_SUBSCRIPTIONS_NOT_FOUND:
|
||||
"We were not able to locate that user's subscriptions",
|
||||
SERVER_USER_SOCIAL_FOLLOWING_NOT_FOUND: "We were not able to locate that user's following",
|
||||
SERVER_USER_SOCIAL_FOLLOWERS_NOT_FOUND: "We were not able to locate that user's followers",
|
||||
|
||||
//Data Remove
|
||||
SERVER_REMOVE_DATA_NO_CID:
|
||||
"Slate is having trouble deleting some files right now. We're working on fixing this soon!",
|
||||
SERVER_REMOVE_DATA_NOT_ALLOWED: "You aren't authorized to remove that file",
|
||||
SERVER_REMOVE_DATA_NO_LINK: "We couldn't remove that data. Please try again later",
|
||||
//Status update
|
||||
SERVER_STATUS_UPDATE_FAILED: "We're having trouble making that change right now",
|
||||
SERVER_STATUS_UPDATE_MUST_PROVIDE_UPDATE: "No update was provided",
|
||||
|
||||
//Data Storage Deals
|
||||
SERVER_STORAGE_DEAL_USER_NOT_FOUND: "Sorry, we couldn't find that user!",
|
||||
SERVER_NO_CID: "IPFS CID is required",
|
||||
SERVER_NO_IPFS: "There was a issue retriving data from IPFS",
|
||||
SERVER_FILECOIN_STORAGE_DEAL_CID_ERROR: "There was a issue making a storage deal",
|
||||
SERVER_NO_JOB: "Sorry, this job doesn't exist!",
|
||||
STORAGE_DEAL_USER_NOT_FOUND: "Sorry, we couldn't find that user!",
|
||||
NO_CID: "IPFS CID is required",
|
||||
NO_IPFS: "There was a issue retriving data from IPFS",
|
||||
FILECOIN_STORAGE_DEAL_CID_ERROR: "There was a issue making a storage deal",
|
||||
NO_JOB: "Sorry, this job doesn't exist!",
|
||||
//Update user
|
||||
SERVER_USER_UPDATE_INVALID_USERNAME: "Please choose a valid username",
|
||||
SERVER_USER_UPDATE_USERNAME_IS_TAKEN: "There is already an account with that username",
|
||||
SERVER_USER_UPDATE_DEFAULT_ARCHIVE_CONFIG:
|
||||
"We're having trouble updating your settings right now",
|
||||
SERVER_USER_UPDATE_INVALID_PASSWORD: "Please chooose a valid password",
|
||||
|
||||
//Archive Deal
|
||||
SERVER_REMOVE_DATA_NOT_ALLOWED: "Please login first to archive a deal!",
|
||||
SERVER_BUCKET_ARCHIVE_DEAL_USER_NOT_FOUND: "Please login first to archive a deal!",
|
||||
SERVER_BUCKET_INIT_FAILURE: "Something went wrong while locating your storage bucket",
|
||||
STORAGE_DEAL_MAKING_NO_BUCKET: "We could not locate your storage bucket",
|
||||
STORAGE_DEAL_MAKING_NO_FILES: "No file selected. Please select a file to archive then try again",
|
||||
STORAGE_BUCKET_TOO_SMALL: "Deal size too small. Deals must be 100MB or larger",
|
||||
BUCKET_SPAWN_VERIFICATION_FAILED_FOR_BUCKET_COUNT:
|
||||
"We ran into an issue while verifying how much storage space you have",
|
||||
TOO_MANY_BUCKETS: "You've reached the maximum number of storage buckets for an account",
|
||||
FORCED_ENCRYPTION_FAILED_FOR_DATA: "We ran into an issue while encrypting your data",
|
||||
BUCKET_CLONING_FAILED: "We ran into an issue while creating a storage bucket for you",
|
||||
STORAGE_DEAL_MAKING_NOT_SANITARY: "We ran into an issue while creating that storage deal",
|
||||
|
||||
//CID Status
|
||||
SERVER_NO_CIDS_TO_CHECK: "No CIDs were entered, please check your input",
|
||||
|
||||
//Users Create
|
||||
SIGN_UP_RATE_LIMITED: "Too many sign up attempts. Please try again in 10 minutes",
|
||||
SERVER_EXISTING_USER_ALREADY: "That username is taken. Please try another one",
|
||||
EXISTING_USER_ALREADY: "That username is taken. Please try another one",
|
||||
INVALID_USERNAME: "Invalid username. Please include only letters and numbers",
|
||||
SERVER_INVALID_USERNAME: "Invalid username. Please include only letters and numbers",
|
||||
INVALID_PASSWORD: "Password length must be more than 8 characters",
|
||||
SERVER_INVALID_PASSWORD: "Password length must be more than 8 characters",
|
||||
USER_CREATE_USER_NOT_FOUND: "Sorry we weren't able to create your account. Please try again",
|
||||
SERVER_USER_CREATE_USER_NOT_FOUND:
|
||||
"Sorry we weren't able to create your account. Please try again",
|
||||
CREATE_USER: "Sorry we weren't able to create your account. Please try again",
|
||||
|
||||
//Users Delete
|
||||
USER_DELETE: "That user doesn't seem to exist. Please try another",
|
||||
SERVER_USER_DELETE: "That user doesn't seem to exist. Please try another",
|
||||
USER_DELETE_USER_NOT_FOUND: "Sorry, we couldn't find that user!",
|
||||
SERVER_USER_DELETE_USER_NOT_FOUND: "Sorry, we couldn't find that user!",
|
||||
DELETE_USER_BY_USERNAME: "We're having trouble deleting your account right now",
|
||||
SERVER_USER_DELETE: "We're having trouble deleting your account right now",
|
||||
USER_NOT_FOUND: "We're having trouble connecting right now",
|
||||
SLATES_NOT_FOUND: "We weren't able to locate the slates for that user",
|
||||
|
||||
//Users Update
|
||||
USER_UPDATE: "That user doesn't seem to exist. Please try another",
|
||||
USER_UPDATE_USER_NOT_FOUND: "Sorry, we couldn't find that user!",
|
||||
UPDATE_USER_BY_ID: "We ran into an issue while updating your information. Please try again",
|
||||
USER_UPDATE_SETTINGS_CONFIG: "Error when updating user settings",
|
||||
SERVER_USER_UPDATE: "Please make sure you are signed in first",
|
||||
SERVER_USER_UPDATE_USER_NOT_FOUND:
|
||||
"We're having trouble locating your information. Please try again",
|
||||
SERVER_STATUS_UPDATE: "Please log in first to access this page",
|
||||
SERVER_STATUS_UPDATE_USER_NOT_FOUND: "We're having trouble locating your information right now",
|
||||
SERVER_STATUS_UPDATE_MUST_PROVIDE_UPDATE:
|
||||
"We're having trouble updating your information right now",
|
||||
SERVER_USERNAME_IS_TAKEN: "That username is taken",
|
||||
|
||||
//Users Get
|
||||
GET_USER_BY_ID: "We weren't able to fetch information on that user. Please try again later",
|
||||
GET_USER_BY_USERNAME:
|
||||
"We weren't able to fetch information on that user. Please check your input",
|
||||
USER_NOT_FOUND: "We aren't able to locate that user at the moment. Please try again",
|
||||
SERVER_USER_SUBSCRIPTIONS_NOT_FOUND:
|
||||
"We weren't able to fetch information on that user. Please try again later",
|
||||
SERVER_USER_SUBSCRIBERS_NOT_FOUND:
|
||||
"We weren't able to fetch information on that user. Please try again later",
|
||||
//Zip files
|
||||
GET_ZIP_FILES_PATHS_BUCKET_CHECK_FAILED: "We're having trouble locating those files right now",
|
||||
|
||||
//Hydrate
|
||||
HYDRATE_FAILURE: "Please make sure you're logged in",
|
||||
SERVER_HYDRATE_FAILURE: "Please make sure you're logged in",
|
||||
SERVER_VIEWER_DATA_ERROR:
|
||||
"We're havign trouble fetching your information right now. Please try again",
|
||||
"We're having trouble fetching your information right now, please refresh the page",
|
||||
|
||||
//Sign-in
|
||||
SIGN_IN_RATE_LIMITED: "Too many sign in attempts. Please try again in 10 minutes",
|
||||
SIGN_IN: "Your username/password can't be blank",
|
||||
SERVER_SIGN_IN: "Your username/password can't be blank",
|
||||
SIGN_IN_USER_NOT_FOUND: "We're having trouble logging you in right now, please try again later",
|
||||
SERVER_SIGN_IN_USER_NOT_FOUND: "That username and password do not match", //no one with that username
|
||||
SERVER_SIGN_IN_ERROR: "We're having trouble connecting right now. Please try again later",
|
||||
SERVER_SIGN_IN_AUTH: "That username and password do not match", //incorrect password
|
||||
//Get deals
|
||||
SERVER_FILECOIN_NETWORK_DEALS_ERROR:
|
||||
"We're having trouble fetching your deal information right now",
|
||||
SERVER_FILECOIN_NETWORK_ERROR: "We're having trouble fetching your storage information right now",
|
||||
|
||||
//Activity
|
||||
CREATE_ACTIVITY: "We're having issues posting that right now. Please try again",
|
||||
DELETE_ACTIVITY_BY_ID: "We weren't able to delete that. Please try again",
|
||||
GET_ACTIVITY_BY_ID: "We weren't able to fetch that information. Please try again",
|
||||
GET_ACTIVITY_FOR_SLATE_ID: "We weren't able to fetch that information. Please try again",
|
||||
GET_ACTIVITY_FOR_USER_ID: "We weren't able to fetch that user's information. Please try again",
|
||||
SERVER_GET_ACTIVITY_NOT_FOUND: "We're having trouble fetching your feed right now",
|
||||
SERVER_GET_ACTIVITY_USER_NOT_FOUND: "Please login to view your feed",
|
||||
//Sign in
|
||||
SERVER_SIGN_IN_NOT_ALLOWED: "You can only sign in to an account while on slate.host",
|
||||
SERVER_SIGN_IN_NO_USERNAME: "Please provide a username to sign in",
|
||||
SERVER_SIGN_IN_NO_PASSWORD: "Please provide a password to sign in",
|
||||
SERVER_SIGN_IN_USER_NOT_FOUND: "We were unable to locate that account with those credentials",
|
||||
SERVER_SIGN_IN_WRONG_PASSWORD: "We were unable to locate that account with those credentials",
|
||||
|
||||
//Subscription Create
|
||||
CREATE_SUBSCRIPTION: "We weren't able to subscribe you. Please try again later",
|
||||
SERVER_SUBSCRIBE: "Please login to subscribe",
|
||||
SERVER_SUBSCRIBE_USER_NOT_FOUND:
|
||||
"We're having trouble fetching your information. Please try again later",
|
||||
SERVER_SUBSCRIBE_MUST_PROVIDE_SLATE_OR_USER:
|
||||
"No user or slate was provided. Please check your input",
|
||||
SERVER_SUBSCRIBE_CAN_NOT_SUBSCRIBE_TO_YOURSELF: "You cannot subscribe to yourself",
|
||||
SERVER_SUBSCRIBE_TARGET_USER_NOT_FOUND: "That user could not be found",
|
||||
SERVER_SUBSCRIBE_TARGET_SLATE_NOT_FOUND: "That slate could not be found",
|
||||
//Subscribe
|
||||
SERVER_SUBSCRIBE_MUST_PROVIDE_SLATE_OR_USER: "No slate or user to follow specified",
|
||||
SERVER_SUBSCRIBE_CAN_NOT_SUBSCRIBE_TO_YOURSELF: "You cannot follow yourself",
|
||||
SERVER_SUBSCRIBE_TARGET_USER_NOT_FOUND: "We are unable to locate that user",
|
||||
SERVER_SUBSCRIBE_TARGET_SLATE_NOT_FOUND: "We are unable to locate that slate",
|
||||
SERVER_SUBSCRIBE_SUBSCRIPTION_CHECK_ERROR:
|
||||
"We weren't able to subscribe you. Please try again later",
|
||||
SERVER_UNSUBSCRIBE_NOT_FOUND: "We weren't able to unsubscribe you. Please try again",
|
||||
SERVER_UNSUBSCRIBE_ERROR: "We weren't able to unsubscribe you. Please try again",
|
||||
SERVER_SUBSCRIBE_NOT_FOUND: "We weren't able to subscribe you. Please try again",
|
||||
SERVER_SUBSCRIBE_ERROR: "We weren't able to subscribe you. Please try again",
|
||||
|
||||
//Subscription Delete
|
||||
DELETE_SUBSCRIPTION_BY_ID: "We weren't able to unsubscribe you. Please try again later",
|
||||
|
||||
//Subscription Get
|
||||
GET_SUBSCRIBERS_BY_USER_ID:
|
||||
"We weren't able to retrieve that information. Please try again later",
|
||||
GET_SUBSCRIPTION_BY_ID:
|
||||
"We weren't able to retrieve that subscription information. Please try again later",
|
||||
GET_SUBSCRIPTIONS_BY_USER_ID:
|
||||
"We weren't able to retrieve that information. Please try again later",
|
||||
GET_SUBSCRIPTIONS_TO_SLATE_ID:
|
||||
"We weren't able to retrieve that information. Please try again later",
|
||||
GET_SUBSCRIPTIONS_TO_USER_ID:
|
||||
"We weren't able to retrieve that information. Please try again later",
|
||||
|
||||
//Trusted Create
|
||||
CREATE_TRUSTED_RELATIONSHIP:
|
||||
"We weren't able to add this user as trusted. Please try again later",
|
||||
SERVER_TRUST: "Please make sure you are logged in",
|
||||
SERVER_TRUSTED_RELATIONSHIP_USER_NOT_FOUND:
|
||||
"We are having trouble retrieving your information right now",
|
||||
SERVER_TRUSTED_RELATIONSHIP_MUST_PROVIDE_SOMEONE_TO_TRUST: "No user was specified",
|
||||
SERVER_TRUSTED_RELATIONSHIP_CAN_NOT_TRUST_YOURSELF: "You cannot add yourself as trusted",
|
||||
SERVER_TRUSTED_RELATIONSHIP_TARGET_USER_NOT_FOUND:
|
||||
"We could not locate that user. Please try again later",
|
||||
SERVER_TRUSTED_RELATIONSHIP_CHECK_ERROR:
|
||||
"We're having trouble adding this person as your trusted right now",
|
||||
SERVER_TRUSTED_RELATIONSHIP_INVERTED_CHECK_ERROR:
|
||||
"You have already received a trust request from this person. Please accept it instead",
|
||||
SERVER_DELETE_TRUSTED_RELATIONSHIP_NOT_FOUND: "There is no trust relationshp to delete",
|
||||
SERVER_DELETE_TRUSTED_RELATIONSHIP_ERROR:
|
||||
"We're having trouble deleting this trust relationship right now",
|
||||
SERVER_TRUSTED_RELATIONSHIP_NOT_FOUND:
|
||||
"We're having trouble creating this trust relationship right now",
|
||||
SERVER_TRUSTED_RELATIONSHIP_ERROR:
|
||||
"We're having trouble creating this trust relationship right now",
|
||||
|
||||
//Trusted Delete
|
||||
DELETE_TRUSTED_RELATIONSHIP_BY_ID:
|
||||
"We weren't able to remove this user from your trusted. Please try again later",
|
||||
SERVER_TRUST_DELETE: "Please login to manage your trusted",
|
||||
SERVER_TRUST_DELETE_USER_NOT_FOUND: "We weren't able to locate this user. Please try again later",
|
||||
SERVER_TRUST_DELETE_MUST_PROVIDE_ID: "Please check your input",
|
||||
|
||||
//Trusted Get
|
||||
GET_TRUSTED_RELATIONSHIP_BY_ID:
|
||||
"We're having trouble retrieving that information right now. Please try again later",
|
||||
GET_TRUSTED_RELATIONSHIP_BY_IDS:
|
||||
"We're having trouble retrieving that information right now. Please try again later",
|
||||
GET_TRUSTED_RELATIONSHIPS_BY_USER_ID:
|
||||
"We're having trouble retrieving that information right now. Please try again later",
|
||||
|
||||
//Trusted Update
|
||||
UPDATE_TRUSTED_RELATIONSHIP_BY_ID:
|
||||
"We ran into an issue while updating that information. Please try again later",
|
||||
SERVER_TRUST_UPDATE: "Please login to mange your trusted",
|
||||
SERVER_TRUST_UPDATE_USER_NOT_FOUND:
|
||||
"We weren't able to locate your information. Please try again later",
|
||||
SERVER_TRUST_UPDATE_MUST_PROVIDE_SOMEONE_TO_TRUST:
|
||||
"Please check your input. No user was provided",
|
||||
SERVER_TRUST_UPDATE_CAN_NOT_TRUST_YOURSELF: "You cannot add yourself as a trusted peer",
|
||||
SERVER_TRUST_UPDATE_TARGET_USER_NOT_FOUND:
|
||||
"We weren't able to locate that user. Please try again later",
|
||||
SERVER_TRUST_UPDATE_CHECK_ERROR: "This person is already among your trusted peers",
|
||||
|
||||
//API Key Create
|
||||
CREATE_API_KEY_FOR_USER_ID:
|
||||
"We're having trouble creating your API keys right now. Please try again",
|
||||
SERVER_GENERATE_API_KEY_AUTH: "You aren't authorized to create that API key",
|
||||
SERVER_GENERATE_API_KEY_USER_NOT_FOUND:
|
||||
"We ran into issues finding your information while trying to generate that API key",
|
||||
SERVER_GENERATE_API_KEY_TOO_MANY_KEYS: "You have reached the limit for number of API keys",
|
||||
SERVER_GENERATE_API_KEY_ERROR: "We ran into issues while trying to generate that API key",
|
||||
|
||||
//API Key Delete
|
||||
DELETE_API_KEY_BY_ID:
|
||||
"We're having trouble deleting that API key right now. Please try again later",
|
||||
DELETE_API_KEYS_FOR_USER_ID:
|
||||
"We're having trouble deleting your API keys right now. Please try again later",
|
||||
SERVER_DELETE_API_KEY_AUTH: "You aren't authorized to delete that API key",
|
||||
SERVER_DELETE_API_KEY_USER_NOT_FOUND: "No matching API key was found for that user",
|
||||
SERVER_DELETE_API_KEY_NOT_FOUND: "No matching API key was found for that user",
|
||||
SERVER_DELETE_API_KEY_ERROR:
|
||||
"We ran into an issue while trying to delete that API key. Please try again",
|
||||
|
||||
//API Key Get
|
||||
GET_API_KEY_BY_KEY: "We weren't able to fetch that API key. Please try again later",
|
||||
GET_API_KEY: "We weren't able to fetch that API key. Please try again later",
|
||||
GET_API_KEYS_BY_USER_ID:
|
||||
"We're having trouble retrieving your API keys right now. Please try again",
|
||||
|
||||
//Query
|
||||
SERVER_DEEPLINK: "This slate or profile does not exist",
|
||||
SERVER_DEEPLINK_ERROR: "This slate or profile does not exist",
|
||||
SERVER_SEARCH_NO_QUERY: "No query was entered. Please enter a query and try again",
|
||||
|
||||
// Storage Deals
|
||||
SERVER_BUCKET_INIT_FAILURE: "Something went wrong with our infrastructure. Check in later.",
|
||||
STORAGE_DEAL_MAKING_NO_BUCKET: "Something went wrong with our infrastructure. Check in later.",
|
||||
STORAGE_DEAL_MAKING_NO_FILES: "You must provide files before you make a deal.",
|
||||
"We are having trouble editing that subscription right now",
|
||||
SERVER_UNSUBSCRIBE_FAILED: "We were unable to unsubscribe, please try again later",
|
||||
SERVER_SUBSCRIBE_FAILED: "We were unable to subscribe, please try again later",
|
||||
|
||||
//Support
|
||||
SERVER_SUPPORT: "You must be logged in to send a support message. Please try logging in again",
|
||||
SERVER_SUPPORT_USER_NOT_FOUND: "We're having difficulty locating your information right now",
|
||||
SERVER_SUPPORT_NO_DATA_PROVIDED: "We're having trouble transmitting your message right now",
|
||||
SERVER_SUPPORT_MUST_PROVIDE_EMAIL: "Please provide an email where we can contact you",
|
||||
SERVER_SUPPORT_MUST_PROVIDE_MESSAGE: "Please provide a message to send",
|
||||
SERVER_SUPPORT: "We're having trouble sending your support message to the team right now",
|
||||
|
||||
// Unity game download
|
||||
SERVER_SUPPORT_NO_DATA_PROVIDED: "Please include details for us to send a support message",
|
||||
SERVER_SUPPORT_MUST_PROVIDE_EMAIL: "Please include an email where we can reach you",
|
||||
SERVER_SUPPORT_MUST_PROVIDE_MESSAGE: "Please include a support message",
|
||||
SERVER_SUPPORT_NO_USERNAME_PROVIDED: "Please include a username",
|
||||
UNITY_ZIP_DOWNLOAD_FAILED:
|
||||
"We're having trouble downloading you Unity game file. Please try again",
|
||||
|
||||
// Delete tag
|
||||
SERVER_DELETE_TAG_USER_NOT_FOUND:
|
||||
"We're having trouble retrieving that slate's owner right now. Please try again",
|
||||
SERVER_DELETE_TAG_SLATES_NOT_FOUND: "We weren't able to locate the slates for that user",
|
||||
"We're having trouble downloading your Unity game file. Please try again later",
|
||||
};
|
||||
|
@ -88,23 +88,6 @@ export const navigation = [
|
||||
pageTitle: "Slate",
|
||||
ignore: true,
|
||||
},
|
||||
/*
|
||||
{
|
||||
id: "V1_NAVIGATION_LOCAL",
|
||||
decorator: "LOCAL_DATA",
|
||||
name: "Local",
|
||||
pageTitle: "Your local data",
|
||||
children: [],
|
||||
ignore: false,
|
||||
},
|
||||
{
|
||||
id: "V1_NAVIGATION_NETWORK",
|
||||
decorator: "NETWORK",
|
||||
name: "Network",
|
||||
pageTitle: "The Filecoin Network",
|
||||
children: null,
|
||||
},
|
||||
*/
|
||||
{
|
||||
id: "NAV_FILECOIN",
|
||||
decorator: "FILECOIN",
|
||||
|
133
common/schema.js
Normal file
133
common/schema.js
Normal file
@ -0,0 +1,133 @@
|
||||
//NOTE(martina): update this file anytime you add a new item to the user, file, or slate data blobs
|
||||
|
||||
export const userSchema = {
|
||||
id: true,
|
||||
username: true,
|
||||
data: {
|
||||
photo: true,
|
||||
body: true,
|
||||
name: true,
|
||||
tokens: true,
|
||||
settings: true,
|
||||
onboarding: true,
|
||||
status: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const coverImageSchema = {
|
||||
id: true,
|
||||
cid: true,
|
||||
createdAt: true,
|
||||
data: {
|
||||
file: true,
|
||||
name: true,
|
||||
size: true,
|
||||
type: true,
|
||||
blurhash: true,
|
||||
},
|
||||
};
|
||||
|
||||
//add: downloads, tags
|
||||
export const fileSchema = {
|
||||
id: true,
|
||||
ownerId: true,
|
||||
cid: true,
|
||||
isPublic: true,
|
||||
createdAt: true,
|
||||
filename: true,
|
||||
data: {
|
||||
name: true,
|
||||
size: true,
|
||||
type: true,
|
||||
blurhash: true,
|
||||
coverImage: coverImageSchema,
|
||||
body: true,
|
||||
source: true,
|
||||
author: true,
|
||||
unity: {
|
||||
config: true,
|
||||
loader: true,
|
||||
},
|
||||
downloads: true,
|
||||
tags: true,
|
||||
link: true,
|
||||
},
|
||||
};
|
||||
|
||||
//add: tags
|
||||
export const slateSchema = {
|
||||
id: true,
|
||||
slatename: true,
|
||||
ownerId: true,
|
||||
isPublic: true,
|
||||
data: {
|
||||
name: true,
|
||||
body: true,
|
||||
layouts: true,
|
||||
preview: true,
|
||||
tags: true,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
Users
|
||||
[
|
||||
'body',
|
||||
'photo',
|
||||
'tokens',
|
||||
'library',
|
||||
'settings_deals_auto_approve',
|
||||
'allow_automatic_data_storage',
|
||||
'allow_encrypted_data_storage',
|
||||
'allow_filecoin_directory_listing',
|
||||
'name',
|
||||
'status',
|
||||
'onboarding',
|
||||
'description'
|
||||
]
|
||||
[
|
||||
'id', 'cid',
|
||||
'date', 'file',
|
||||
'icon', 'ipfs',
|
||||
'name', 'size',
|
||||
'type', 'decorator',
|
||||
'blurhash', 'unityGameConfig',
|
||||
'unityGameLoader', 'url',
|
||||
'public', 'previewImage',
|
||||
'coverImage', 'settings',
|
||||
'job', 'storage',
|
||||
'networks', 'retrieval'
|
||||
]
|
||||
[
|
||||
'id', 'cid',
|
||||
'url', 'date',
|
||||
'file', 'name',
|
||||
'size', 'type',
|
||||
'blurhash', 'decorator'
|
||||
]
|
||||
|
||||
Slates
|
||||
[
|
||||
'body', 'name',
|
||||
'public', 'objects',
|
||||
'ownerId', 'layouts',
|
||||
'preview'
|
||||
]
|
||||
[
|
||||
'id', 'url',
|
||||
'name', 'type',
|
||||
'title', 'ownerId',
|
||||
'cid', 'body',
|
||||
'author', 'source',
|
||||
'size', 'deeplink',
|
||||
'blurhash', 'unityGameConfig',
|
||||
'unityGameLoader', 'coverImage'
|
||||
]
|
||||
[
|
||||
'id', 'cid',
|
||||
'url', 'date',
|
||||
'file', 'name',
|
||||
'size', 'type',
|
||||
'blurhash', 'decorator'
|
||||
]
|
||||
*/
|
@ -40,7 +40,7 @@ export const zeroPad = (num, places) => {
|
||||
return Array(+(zero > 0 && zero)).join("0") + num;
|
||||
};
|
||||
|
||||
export const getCIDGatewayURL = (cid) => {
|
||||
export const getURLfromCID = (cid) => {
|
||||
return `${Constants.gateways.ipfs}/${cid}`;
|
||||
};
|
||||
|
||||
@ -55,15 +55,15 @@ export const toDateSinceEpoch = (epoch) => {
|
||||
return toDate(new Date().getTime() - epoch);
|
||||
};
|
||||
|
||||
export const getCIDGatewayURLWithExtension = (cid, name) => {
|
||||
const url = getCIDGatewayURL(cid);
|
||||
const extension = getFileExtension(name);
|
||||
if (!isEmpty(extension)) {
|
||||
return `${url}.${getFileExtension(name)}`;
|
||||
}
|
||||
// export const getURLfromCIDWithExtension = (cid, name) => {
|
||||
// const url = getURLfromCID(cid);
|
||||
// const extension = getFileExtension(name);
|
||||
// if (!isEmpty(extension)) {
|
||||
// return `${url}.${getFileExtension(name)}`;
|
||||
// }
|
||||
|
||||
return url;
|
||||
};
|
||||
// return url;
|
||||
// };
|
||||
|
||||
export const getURLFromPath = (path) => {
|
||||
return `${window.location.protocol}//${window.location.hostname}${
|
||||
@ -210,6 +210,7 @@ export const urlToCid = (url) => {
|
||||
return url
|
||||
.replace(`${Constants.gateways.ipfs}/`, "")
|
||||
.replace("https://", "")
|
||||
.replace("undefined", "")
|
||||
.replace(".ipfs.slate.textile.io", "")
|
||||
.replace("hub.textile.io/ipfs/", "");
|
||||
};
|
||||
|
@ -146,7 +146,6 @@ export const formatDroppedFiles = async ({ dataTransfer }) => {
|
||||
|
||||
file = new File(blob, `data-${uuid()}`);
|
||||
file.name = `data-${uuid()}`;
|
||||
console.log(file);
|
||||
} catch (e) {
|
||||
Events.dispatchMessage({
|
||||
message: "File type not supported. Please try a different file",
|
||||
@ -209,30 +208,26 @@ export const uploadImage = async (file, resources, excludeFromLibrary) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await FileUtilities.upload({ file, routes: resources, excludeFromLibrary });
|
||||
const response = await FileUtilities.upload({ file, routes: resources });
|
||||
|
||||
if (Events.hasError(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
delete response.json.data.icon;
|
||||
delete response.json.data.ipfs;
|
||||
|
||||
return response.json;
|
||||
return response;
|
||||
};
|
||||
|
||||
export const deleteFiles = async (fileCids, fileIds = [], noAlert) => {
|
||||
let cids = Array.isArray(fileCids) ? fileCids : [fileCids];
|
||||
export const deleteFiles = async (fileIds = [], noAlert) => {
|
||||
let ids = Array.isArray(fileIds) ? fileIds : [fileIds];
|
||||
|
||||
const response = await Actions.deleteBucketItems({ cids, ids });
|
||||
const response = await Actions.deleteFiles({ ids });
|
||||
|
||||
if (!noAlert) {
|
||||
if (Events.hasError(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Events.dispatchMessage({ message: "Files successfully deleted!", status: "INFO" });
|
||||
Events.dispatchMessage({ message: "Files successfully deleted!", status: "INFO" });
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -251,19 +246,8 @@ export const removeFromSlate = async ({ slate, ids }) => {
|
||||
return response;
|
||||
};
|
||||
|
||||
export const addToSlate = async ({ slate, files, fromSlate }) => {
|
||||
let data = files.map((file) => {
|
||||
return {
|
||||
title: file.name || file.title || file.file,
|
||||
...file,
|
||||
};
|
||||
});
|
||||
|
||||
const addResponse = await Actions.addFileToSlate({
|
||||
slate,
|
||||
data,
|
||||
fromSlate,
|
||||
});
|
||||
export const addToSlate = async ({ slate, files }) => {
|
||||
const addResponse = await Actions.addFileToSlate({ slate, files });
|
||||
|
||||
if (Events.hasError(addResponse)) {
|
||||
return false;
|
||||
@ -275,14 +259,8 @@ export const addToSlate = async ({ slate, files, fromSlate }) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export const addToDataFromSlate = async ({ files }) => {
|
||||
let items = files.map((file) => {
|
||||
return {
|
||||
ownerId: file.ownerId,
|
||||
cid: file.cid,
|
||||
};
|
||||
});
|
||||
let response = await Actions.addCIDToData({ items });
|
||||
export const saveCopy = async ({ files }) => {
|
||||
let response = await Actions.saveCopy({ files });
|
||||
if (Events.hasError(response)) {
|
||||
return false;
|
||||
}
|
||||
@ -292,22 +270,16 @@ export const addToDataFromSlate = async ({ files }) => {
|
||||
};
|
||||
|
||||
export const download = (file) => {
|
||||
const filename = file.file || file.name || file.title;
|
||||
let uri;
|
||||
if (file.url) {
|
||||
uri = file.url;
|
||||
} else {
|
||||
uri = Strings.getCIDGatewayURL(file.cid);
|
||||
}
|
||||
Window.saveAs(uri, filename);
|
||||
let uri = Strings.getURLfromCID(file.cid);
|
||||
Window.saveAs(uri, file.filename);
|
||||
};
|
||||
|
||||
export const downloadZip = async (file) => {
|
||||
try {
|
||||
const { data } = await Actions.getZipFilePaths(file);
|
||||
const filesPaths = data.filesPaths.map((item) => item.replace(`/${file.id}/`, ""));
|
||||
const baseUrl = file.url;
|
||||
const zipFileName = file.file;
|
||||
const baseUrl = Strings.getURLfromCID(file.cid);
|
||||
const zipFileName = file.filename;
|
||||
|
||||
let zip = new JSZip();
|
||||
|
||||
@ -363,8 +335,8 @@ export const compressAndDownloadFiles = async ({ files, name = "slate.zip", reso
|
||||
if (file.type === "application/unity") {
|
||||
const { data } = await Actions.getZipFilePaths(file);
|
||||
const unityFiles = data.filesPaths.map((item) => ({
|
||||
url: item.replace(`/${file.id}/`, `${file.url || Strings.getCIDGatewayURL(file.cid)}/`),
|
||||
name: item.replace(`/${file.id}/`, `/${file.name}/`),
|
||||
url: item.replace(`/${file.id}/`, `${Strings.getURLfromCID(file.cid)}/`),
|
||||
name: item.replace(`/${file.id}/`, `/${file.filename}/`),
|
||||
}));
|
||||
|
||||
downloadFiles = downloadFiles.concat(unityFiles);
|
||||
@ -372,8 +344,8 @@ export const compressAndDownloadFiles = async ({ files, name = "slate.zip", reso
|
||||
}
|
||||
|
||||
downloadFiles.push({
|
||||
name: file.file || file.name,
|
||||
url: file.url || Strings.getCIDGatewayURL(file.cid),
|
||||
name: file.filename,
|
||||
url: Strings.getURLfromCID(file.cid),
|
||||
});
|
||||
}
|
||||
|
||||
@ -395,7 +367,7 @@ export const compressAndDownloadFiles = async ({ files, name = "slate.zip", reso
|
||||
// export const createSlate = async (data) => {
|
||||
// let response = await Actions.createSlate({
|
||||
// name: data.name,
|
||||
// public: data.public,
|
||||
// isPublic: data.isPublic,
|
||||
// body: data.body,
|
||||
// });
|
||||
// return response;
|
||||
|
@ -5,16 +5,16 @@
|
||||
export const getPublicAndPrivateFiles = ({ viewer }) => {
|
||||
let publicFileIds = [];
|
||||
for (let slate of viewer.slates) {
|
||||
if (slate.data.public) {
|
||||
publicFileIds.push(...slate.data.objects.map((obj) => obj.id));
|
||||
if (slate.isPublic) {
|
||||
publicFileIds.push(...slate.objects.map((obj) => obj.id));
|
||||
}
|
||||
}
|
||||
|
||||
let publicFiles = [];
|
||||
let privateFiles = [];
|
||||
let library = viewer.library[0]?.children || [];
|
||||
let library = viewer.library || [];
|
||||
for (let file of library) {
|
||||
if (file.public || publicFileIds.includes(file.id)) {
|
||||
if (file.isPublic || publicFileIds.includes(file.id)) {
|
||||
publicFiles.push(file);
|
||||
} else {
|
||||
privateFiles.push(file);
|
||||
|
@ -149,6 +149,36 @@ export const isPreviewableImage = (type = "") => {
|
||||
return type.startsWith("image/");
|
||||
};
|
||||
|
||||
export const isImageType = (type) => {
|
||||
if (type.startsWith("image/")) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const isAudioType = (type) => {
|
||||
if (type.startsWith("audio/")) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const isVideoType = (type) => {
|
||||
if (type.startsWith("video/")) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const isPdfType = (type) => {
|
||||
if (type.startsWith("application/pdf")) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const isEpubType = (type) => {
|
||||
if (type.startsWith("application/epub")) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const isUnityFile = async (file) => {
|
||||
try {
|
||||
const zip = new JSZip();
|
||||
|
@ -94,7 +94,7 @@ export const isIOS = (userAgent) => {
|
||||
|
||||
export const isMobileBrowser = (userAgent) => {
|
||||
const navigatorAgent = getNavigatorAgent(userAgent);
|
||||
const mobile =
|
||||
const isMobile =
|
||||
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ipad|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
|
||||
navigatorAgent
|
||||
) ||
|
||||
@ -102,7 +102,7 @@ export const isMobileBrowser = (userAgent) => {
|
||||
navigatorAgent.substr(0, 4)
|
||||
);
|
||||
|
||||
return !!mobile;
|
||||
return !!isMobile;
|
||||
};
|
||||
|
||||
//NOTE(toast): this can be switched to regex pattern matching if
|
||||
@ -116,12 +116,13 @@ export const isMac = (userAgent) => {
|
||||
export const debounce = (func, wait) => {
|
||||
let timeout;
|
||||
|
||||
return function passedInFunction(...args) {
|
||||
const later = () => {
|
||||
func(...args);
|
||||
};
|
||||
return function passedInFunction(params) {
|
||||
if (params?.cancel) {
|
||||
clearTimeout(timeout);
|
||||
return;
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
timeout = setTimeout(func, wait);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -115,7 +115,12 @@ export class Alert extends React.Component {
|
||||
|
||||
_handleDismissPrivacyAlert = (e) => {
|
||||
Actions.updateStatus({ status: { hidePrivacyAlert: true } });
|
||||
this.props.onUpdateViewer({ status: { ...this.props.viewer.status, hidePrivacyAlert: true } });
|
||||
this.props.onUpdateViewer({
|
||||
data: {
|
||||
...this.props.viewer.data,
|
||||
status: { ...this.props.viewer.data.status, hidePrivacyAlert: true },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -20,7 +20,6 @@ import SceneFile from "~/scenes/SceneFile";
|
||||
import SceneFilesFolder from "~/scenes/SceneFilesFolder";
|
||||
import SceneSettings from "~/scenes/SceneSettings";
|
||||
import SceneSlates from "~/scenes/SceneSlates";
|
||||
import SceneLocalData from "~/scenes/SceneLocalData";
|
||||
import SceneSettingsDeveloper from "~/scenes/SceneSettingsDeveloper";
|
||||
import SceneSignIn from "~/scenes/SceneSignIn";
|
||||
import SceneSlate from "~/scenes/SceneSlate";
|
||||
@ -87,7 +86,6 @@ const SCENES = {
|
||||
SETTINGS: <SceneEditAccount />,
|
||||
SLATES: <SceneSlates tab={0} />,
|
||||
SLATES_FOLLOWING: <SceneSlates tab={1} />,
|
||||
LOCAL_DATA: <SceneLocalData />,
|
||||
DIRECTORY: <SceneDirectory tab={0} />,
|
||||
DIRECTORY_FOLLOWERS: <SceneDirectory tab={1} />,
|
||||
FILECOIN: <SceneArchive />,
|
||||
@ -105,7 +103,7 @@ export default class ApplicationPage extends React.Component {
|
||||
data: null,
|
||||
sidebar: null,
|
||||
online: null,
|
||||
mobile: this.props.mobile,
|
||||
isMobile: this.props.isMobile,
|
||||
loaded: false,
|
||||
activeUsers: null,
|
||||
};
|
||||
@ -172,10 +170,7 @@ export default class ApplicationPage extends React.Component {
|
||||
const current = NavigationData.getCurrent(page);
|
||||
|
||||
let slate = null;
|
||||
if (
|
||||
current.target.id === "NAV_SLATE" &&
|
||||
this.state.data?.data?.ownerId === this.state.viewer?.id
|
||||
) {
|
||||
if (current.target.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer?.id) {
|
||||
slate = this.state.data;
|
||||
}
|
||||
|
||||
@ -186,7 +181,7 @@ export default class ApplicationPage extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
_handleUpdateViewer = (newViewerState) => {
|
||||
_handleUpdateViewer = (newViewerState, callback) => {
|
||||
// let setAsyncState = (newState) =>
|
||||
// new Promise((resolve) =>
|
||||
// this.setState(
|
||||
@ -207,7 +202,7 @@ export default class ApplicationPage extends React.Component {
|
||||
if (!page || !page.id) {
|
||||
page = { id: "NAV_DATA", scrollTop: 0, data: null };
|
||||
}
|
||||
if (page.id === "NAV_SLATE" && this.state.data?.data?.ownerId === this.state.viewer.id) {
|
||||
if (page.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer.id) {
|
||||
let data = this.state.data;
|
||||
for (let slate of newViewerState.slates) {
|
||||
if (slate.id === data.id) {
|
||||
@ -215,21 +210,39 @@ export default class ApplicationPage extends React.Component {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
viewer: { ...this.state.viewer, ...newViewerState, type: "VIEWER" },
|
||||
data,
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
viewer: { ...this.state.viewer, ...newViewerState },
|
||||
data,
|
||||
},
|
||||
() => {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
viewer: { ...this.state.viewer, ...newViewerState, type: "VIEWER" },
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
viewer: { ...this.state.viewer, ...newViewerState },
|
||||
},
|
||||
() => {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_handleUpdateData = ({ data }) => {
|
||||
_handleUpdateData = (data, callback) => {
|
||||
//TODO(martina): maybe add a default window.history.replacestate where it pushes the new data to browser?
|
||||
this.setState({ data });
|
||||
this.setState({ data }, () => {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_handleSetupWebsocket = async () => {
|
||||
@ -243,7 +256,7 @@ export default class ApplicationPage extends React.Component {
|
||||
console.log("WEBSOCKET: NOT AUTHENTICATED");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("inside handle setup websocket in application.js");
|
||||
wsclient = Websockets.init({
|
||||
resource: this.props.resources.pubsub,
|
||||
viewer: this.state.viewer,
|
||||
@ -269,12 +282,12 @@ export default class ApplicationPage extends React.Component {
|
||||
|
||||
// (1) is Window.isMobileBrowser checks, that one holds.
|
||||
// (2) then if the viewport is smaller than the width
|
||||
let mobile = width > Constants.sizes.mobile ? this.props.mobile : true;
|
||||
let isMobile = width > Constants.sizes.mobile ? this.props.isMobile : true;
|
||||
|
||||
// only change if necessary.
|
||||
if (this.state.mobile !== mobile) {
|
||||
console.log("changing to mobile?", mobile);
|
||||
this.setState({ mobile });
|
||||
if (this.state.isMobile !== isMobile) {
|
||||
console.log("changing to mobile?", isMobile);
|
||||
this.setState({ isMobile });
|
||||
}
|
||||
};
|
||||
|
||||
@ -308,10 +321,7 @@ export default class ApplicationPage extends React.Component {
|
||||
const current = NavigationData.getCurrent(page);
|
||||
|
||||
let slate = null;
|
||||
if (
|
||||
current.target.id === "NAV_SLATE" &&
|
||||
this.state.data?.data?.ownerId === this.state.viewer?.id
|
||||
) {
|
||||
if (current.target.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer?.id) {
|
||||
slate = this.state.data;
|
||||
}
|
||||
|
||||
@ -331,9 +341,10 @@ export default class ApplicationPage extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
_handleUpload = async ({ files, slate, keys, numFailed }) => {
|
||||
_handleUpload = async ({ files, slate, keys, numFailed = 0 }) => {
|
||||
if (!files || !files.length) {
|
||||
return null;
|
||||
this._handleRegisterLoadingFinished({ keys });
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedFiles = [];
|
||||
@ -346,7 +357,7 @@ export default class ApplicationPage extends React.Component {
|
||||
await Window.delay(3000);
|
||||
|
||||
// NOTE(jim): Sends XHR request.
|
||||
let response = null;
|
||||
let response;
|
||||
try {
|
||||
response = await FileUtilities.upload({
|
||||
file: files[i],
|
||||
@ -364,34 +375,53 @@ export default class ApplicationPage extends React.Component {
|
||||
}
|
||||
|
||||
if (!resolvedFiles.length) {
|
||||
this.setState({ fileLoading: {} });
|
||||
return null;
|
||||
}
|
||||
|
||||
let responses = await Promise.allSettled(resolvedFiles);
|
||||
let succeeded = responses
|
||||
.filter((res) => {
|
||||
return res.status === "fulfilled" && res.value && !res.value.error;
|
||||
})
|
||||
.map((res) => res.value);
|
||||
|
||||
if (slate && slate.id) {
|
||||
await FileUtilities.uploadToSlate({ responses: succeeded, slate });
|
||||
}
|
||||
|
||||
let processResponse = await Actions.processPendingFiles();
|
||||
if (Events.hasError(processResponse)) {
|
||||
this._handleRegisterLoadingFinished({ keys });
|
||||
return;
|
||||
}
|
||||
//NOTE(martina): this commented out portion is only for if parallel uploading
|
||||
// let responses = await Promise.allSettled(resolvedFiles);
|
||||
// let succeeded = responses
|
||||
// .filter((res) => {
|
||||
// return res.status === "fulfilled" && res.value && !res.value.error;
|
||||
// })
|
||||
// .map((res) => res.value);
|
||||
|
||||
if (!slate) {
|
||||
const { added, skipped } = processResponse.data;
|
||||
let message = Strings.formatAsUploadMessage(added, skipped + numFailed);
|
||||
Events.dispatchMessage({ message, status: !added ? null : "INFO" });
|
||||
let createResponse = await Actions.createFile({ files: resolvedFiles });
|
||||
|
||||
if (Events.hasError(createResponse)) {
|
||||
this._handleRegisterLoadingFinished({ keys });
|
||||
return;
|
||||
}
|
||||
console.log("no error in actions.createFile");
|
||||
|
||||
console.log(createResponse);
|
||||
let uploadedFiles = createResponse.data;
|
||||
|
||||
let added, skipped;
|
||||
if (slate && slate.id) {
|
||||
console.log(slate);
|
||||
console.log(uploadedFiles);
|
||||
const addResponse = await Actions.addFileToSlate({
|
||||
slate,
|
||||
files: uploadedFiles,
|
||||
});
|
||||
|
||||
if (Events.hasError(addResponse)) {
|
||||
this._handleRegisterLoadingFinished({ keys });
|
||||
return;
|
||||
}
|
||||
|
||||
added = addResponse.added;
|
||||
skipped = addResponse.skipped;
|
||||
} else {
|
||||
added = resolvedFiles.length;
|
||||
skipped = files.length - resolvedFiles.length;
|
||||
}
|
||||
|
||||
let message = Strings.formatAsUploadMessage(added, skipped + numFailed);
|
||||
Events.dispatchMessage({ message, status: !added ? null : "INFO" });
|
||||
|
||||
this._handleRegisterLoadingFinished({ keys });
|
||||
return null;
|
||||
};
|
||||
|
||||
_handleRegisterFileLoading = ({ fileLoading }) => {
|
||||
@ -473,10 +503,11 @@ export default class ApplicationPage extends React.Component {
|
||||
|
||||
let unseenAnnouncements = [];
|
||||
for (let feature of announcements) {
|
||||
if (!Object.keys(viewer.onboarding).includes(feature)) {
|
||||
if (!viewer.data.onboarding || !Object.keys(viewer.data.onboarding).includes(feature)) {
|
||||
unseenAnnouncements.push(feature);
|
||||
}
|
||||
}
|
||||
console.log(unseenAnnouncements);
|
||||
|
||||
if (newAccount || unseenAnnouncements.length) {
|
||||
Events.dispatchCustomEvent({
|
||||
@ -622,6 +653,7 @@ export default class ApplicationPage extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log(this.state.viewer);
|
||||
// NOTE(jim): Not authenticated.
|
||||
if (!this.state.viewer) {
|
||||
return (
|
||||
@ -661,8 +693,8 @@ export default class ApplicationPage extends React.Component {
|
||||
navigation={NavigationData.navigation}
|
||||
activeIds={current.activeIds}
|
||||
onAction={this._handleAction}
|
||||
mobile={this.state.mobile}
|
||||
mac={this.props.mac}
|
||||
isMobile={this.state.isMobile}
|
||||
isMac={this.props.isMac}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -679,8 +711,8 @@ export default class ApplicationPage extends React.Component {
|
||||
onUpdateData: this._handleUpdateData,
|
||||
onUpdateViewer: this._handleUpdateViewer,
|
||||
sceneId: current.target.id,
|
||||
mobile: this.state.mobile,
|
||||
mac: this.props.mac,
|
||||
isMobile: this.state.isMobile,
|
||||
isMac: this.props.isMac,
|
||||
resources: this.props.resources,
|
||||
activeUsers: this.state.activeUsers,
|
||||
userBucketCID: this.state.userBucketCID,
|
||||
@ -737,8 +769,8 @@ export default class ApplicationPage extends React.Component {
|
||||
onDismissSidebar={this._handleDismissSidebar}
|
||||
fileLoading={this.state.fileLoading}
|
||||
filecoin={current.target.filecoin}
|
||||
mobile={this.state.mobile}
|
||||
mac={this.props.mac}
|
||||
isMobile={this.state.isMobile}
|
||||
isMac={this.props.isMac}
|
||||
viewer={this.state.viewer}
|
||||
onUpdateViewer={this._handleUpdateViewer}
|
||||
>
|
||||
@ -748,7 +780,7 @@ export default class ApplicationPage extends React.Component {
|
||||
<SearchModal
|
||||
viewer={this.state.viewer}
|
||||
onAction={this._handleAction}
|
||||
mobile={this.state.mobile}
|
||||
isMobile={this.props.isMobile}
|
||||
resourceURI={this.props.resources.search}
|
||||
/>
|
||||
{!this.state.loaded ? (
|
||||
|
@ -128,7 +128,7 @@ const STYLES_STATIC = css`
|
||||
|
||||
export default class ApplicationHeader extends React.Component {
|
||||
keysPressed = {};
|
||||
searchModKey = this.props.mac ? (
|
||||
searchModKey = this.props.isMac ? (
|
||||
<SVG.MacCommand height="12px" style={{ display: "block", paddingLeft: 8, paddingRight: 8 }} />
|
||||
) : (
|
||||
<span style={{ display: "block", paddingLeft: 8, paddingRight: 8 }}>Ctrl</span>
|
||||
|
@ -113,13 +113,13 @@ export default class ApplicationLayout extends React.Component {
|
||||
|
||||
componentDidMount = () => {
|
||||
this.prevScrollPos = window.pageYOffset;
|
||||
if (this.props.mobile) {
|
||||
if (this.props.isMobile) {
|
||||
window.addEventListener("scroll", this._handleScroll);
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
if (this.props.mobile) {
|
||||
if (this.props.isMobile) {
|
||||
window.removeEventListener("scroll", this._handleScroll);
|
||||
}
|
||||
};
|
||||
@ -166,20 +166,23 @@ export default class ApplicationLayout extends React.Component {
|
||||
{/* <GlobalTooltip elementRef={this._body} allowedTypes={["body"]} /> */}
|
||||
<GlobalTooltip />
|
||||
|
||||
<div css={STYLES_HEADER} style={{ top: this.props.mobile ? this.state.headerTop : null }}>
|
||||
<div
|
||||
css={STYLES_HEADER}
|
||||
style={{ top: this.props.isMobile ? this.state.headerTop : null }}
|
||||
>
|
||||
{this.props.header}
|
||||
</div>
|
||||
|
||||
<Alert
|
||||
noWarning={this.props.viewer.status?.hidePrivacyAlert}
|
||||
noWarning={this.props.viewer.data.status?.hidePrivacyAlert}
|
||||
fileLoading={this.props.fileLoading}
|
||||
onAction={this.props.onAction}
|
||||
filecoin={this.props.filecoin}
|
||||
id={this.props.mobile ? "slate-mobile-alert" : null}
|
||||
id={this.props.isMobile ? "slate-mobile-alert" : null}
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
viewer={this.props.viewer}
|
||||
style={
|
||||
this.props.mobile
|
||||
this.props.isMobile
|
||||
? { top: this.state.headerTop + 56 }
|
||||
: {
|
||||
paddingRight: this.props.sidebar
|
||||
|
921
components/core/CarouselSidebar.js
Normal file
921
components/core/CarouselSidebar.js
Normal file
@ -0,0 +1,921 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as Actions from "~/common/actions";
|
||||
import * as System from "~/components/system";
|
||||
import * as UserBehaviors from "~/common/user-behaviors";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Events from "~/common/custom-events";
|
||||
import * as Window from "~/common/window";
|
||||
|
||||
import { css, withTheme } from "@emotion/react";
|
||||
import { RadioGroup } from "~/components/system/components/RadioGroup";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
import { SlatePicker } from "~/components/core/SlatePicker";
|
||||
import { Input } from "~/components/system/components/Input";
|
||||
import { Toggle } from "~/components/system/components/Toggle";
|
||||
import { Textarea } from "~/components/system/components/Textarea";
|
||||
import { Tag } from "~/components/system/components/Tag";
|
||||
|
||||
import isEqual from "lodash/isEqual";
|
||||
import cloneDeep from "lodash/cloneDeep";
|
||||
import ProcessedText from "~/components/core/ProcessedText";
|
||||
|
||||
const DEFAULT_BOOK =
|
||||
"https://slate.textile.io/ipfs/bafkreibk32sw7arspy5kw3p5gkuidfcwjbwqyjdktd5wkqqxahvkm2qlyi";
|
||||
const DEFAULT_DATA =
|
||||
"https://slate.textile.io/ipfs/bafkreid6bnjxz6fq2deuhehtxkcesjnjsa2itcdgyn754fddc7u72oks2m";
|
||||
const DEFAULT_DOCUMENT =
|
||||
"https://slate.textile.io/ipfs/bafkreiecdiepww52i5q3luvp4ki2n34o6z3qkjmbk7pfhx4q654a4wxeam";
|
||||
const DEFAULT_VIDEO =
|
||||
"https://slate.textile.io/ipfs/bafkreibesdtut4j5arclrxd2hmkfrv4js4cile7ajnndn3dcn5va6wzoaa";
|
||||
const DEFAULT_AUDIO =
|
||||
"https://slate.textile.io/ipfs/bafkreig2hijckpamesp4nawrhd6vlfvrtzt7yau5wad4mzpm3kie5omv4e";
|
||||
|
||||
const STYLES_NO_VISIBLE_SCROLL = css`
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: none;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
display: none;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: ${Constants.system.foreground};
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: ${Constants.system.darkGray};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_BODY = css`
|
||||
font-size: 16px;
|
||||
line-height: 1.225;
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
margin-bottom: 32px;
|
||||
`;
|
||||
|
||||
const STYLES_SIDEBAR_INPUT_LABEL = css`
|
||||
font-size: 16px;
|
||||
font-family: ${Constants.font.semiBold};
|
||||
color: ${Constants.system.darkGray};
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const STYLES_SIDEBAR = css`
|
||||
width: 420px;
|
||||
padding: 48px 24px 0px 24px;
|
||||
flex-shrink: 0;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
background-color: rgba(20, 20, 20, 0.8);
|
||||
${STYLES_NO_VISIBLE_SCROLL}
|
||||
|
||||
@supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) {
|
||||
-webkit-backdrop-filter: blur(75px);
|
||||
backdrop-filter: blur(75px);
|
||||
background-color: rgba(150, 150, 150, 0.2);
|
||||
}
|
||||
|
||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_DISMISS_BOX = css`
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
color: ${Constants.system.darkGray};
|
||||
cursor: pointer;
|
||||
|
||||
:hover {
|
||||
color: ${Constants.system.white};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_HEADING = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
margin-bottom: 32px;
|
||||
`;
|
||||
|
||||
const STYLES_META = css`
|
||||
text-align: start;
|
||||
padding: 14px 0px 8px 0px;
|
||||
overflow-wrap: break-word;
|
||||
`;
|
||||
|
||||
const STYLES_META_TITLE = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
color: ${Constants.system.white};
|
||||
font-size: ${Constants.typescale.lvl2};
|
||||
text-decoration: none;
|
||||
word-break: break-all;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
:hover {
|
||||
color: ${Constants.system.blue};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_TAG = css`
|
||||
margin-right: 24px;
|
||||
padding: 0px 2px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid ${Constants.system.darkGray};
|
||||
`;
|
||||
|
||||
const STYLES_OPTIONS_SECTION = css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 16px 0 16px 0;
|
||||
`;
|
||||
|
||||
const STYLES_META_DETAILS = css`
|
||||
color: ${Constants.system.darkGray};
|
||||
text-transform: uppercase;
|
||||
margin: 24px 0px;
|
||||
font-family: ${Constants.font.medium};
|
||||
font-size: 0.9rem;
|
||||
`;
|
||||
|
||||
const STYLES_SIDEBAR_SECTION = css`
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
const STYLES_ACTIONS = css`
|
||||
color: ${Constants.system.white};
|
||||
border: 1px solid #3c3c3c;
|
||||
border-radius: 4px;
|
||||
background-color: transparent;
|
||||
margin-bottom: 48px;
|
||||
margin-top: 36px;
|
||||
`;
|
||||
|
||||
const STYLES_ACTION = css`
|
||||
cursor: pointer;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #3c3c3c;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:hover {
|
||||
color: ${Constants.system.brand};
|
||||
}
|
||||
|
||||
:last-child {
|
||||
border: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_SECTION_HEADER = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-size: 1.1rem;
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const STYLES_HIDDEN = css`
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
const STYLES_IMAGE_BOX = css`
|
||||
max-width: 100%;
|
||||
max-height: 368px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: ${Constants.system.black};
|
||||
overflow: hidden;
|
||||
${"" /* box-shadow: 0 0 0 1px ${Constants.system.border} inset; */}
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
const STYLES_FILE_HIDDEN = css`
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
`;
|
||||
|
||||
const STYLES_TEXT = css`
|
||||
color: ${Constants.system.darkGray};
|
||||
line-height: 1.5;
|
||||
`;
|
||||
|
||||
const STYLES_INPUT = {
|
||||
marginBottom: 8,
|
||||
backgroundColor: "transparent",
|
||||
boxShadow: "0 0 0 1px #3c3c3c inset",
|
||||
color: Constants.system.white,
|
||||
height: 48,
|
||||
};
|
||||
|
||||
const STYLES_AUTOSAVE = css`
|
||||
font-size: 12px;
|
||||
line-height: 1.225;
|
||||
display: flex;
|
||||
justify-content: baseline;
|
||||
color: ${Constants.system.yellow};
|
||||
opacity: 0;
|
||||
${"" /* margin: 26px 24px; */}
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
left: 16px;
|
||||
|
||||
@keyframes slate-animations-autosave {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
transform: translateX(12px);
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
transform: translateX(12px);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
animation: slate-animations-autosave 4000ms ease;
|
||||
`;
|
||||
|
||||
const STYLES_SPINNER = css`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
`;
|
||||
|
||||
export const FileTypeDefaultPreview = (props) => {
|
||||
if (props.type) {
|
||||
if (Validations.isVideoType(type)) {
|
||||
return DEFAULT_VIDEO;
|
||||
} else if (Validations.isAudioType(type)) {
|
||||
return DEFAULT_AUDIO;
|
||||
} else if (Validations.isPdfType(type)) {
|
||||
return DEFAULT_DOCUMENT;
|
||||
} else if (Validations.isEpubType(type)) {
|
||||
return DEFAULT_BOOK;
|
||||
}
|
||||
}
|
||||
return DEFAULT_DATA;
|
||||
};
|
||||
|
||||
class CarouselSidebar extends React.Component {
|
||||
state = {
|
||||
name: this.props.data.data.name || this.props.data.filename,
|
||||
body: this.props.data.data.body,
|
||||
source: this.props.data.data.source,
|
||||
author: this.props.data.data.author,
|
||||
tags: this.props.data.data.tags || [],
|
||||
suggestions: this.props.viewer?.tags || [],
|
||||
selected: {},
|
||||
isPublic: false,
|
||||
inPublicSlates: 0,
|
||||
isUploading: false,
|
||||
isDownloading: false,
|
||||
showSavedMessage: false,
|
||||
showConnectedSection: false,
|
||||
showFileSection: true,
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
const editingAllowed = !this.props.external && this.props.isOwner && !this.props.isRepost;
|
||||
if (editingAllowed) {
|
||||
this.debounceInstance = Window.debounce(() => this._handleSave(), 3000);
|
||||
this.calculateSelected();
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate = (prevProps, prevState) => {
|
||||
if (!isEqual(prevState.tags, this.state.tags)) {
|
||||
this.updateSuggestions();
|
||||
}
|
||||
};
|
||||
|
||||
updateSuggestions = () => {
|
||||
let newSuggestions = new Set([...this.state.suggestions, ...this.state.tags]);
|
||||
this.setState({ suggestions: Array.from(newSuggestions) });
|
||||
};
|
||||
|
||||
calculateSelected = () => {
|
||||
console.log("calculate selected");
|
||||
let inPublicSlates = 0;
|
||||
let selected = {};
|
||||
const id = this.props.data.id;
|
||||
for (let slate of this.props.viewer.slates) {
|
||||
if (slate.objects.some((obj) => obj.id === id)) {
|
||||
if (slate.isPublic) {
|
||||
inPublicSlates += 1;
|
||||
}
|
||||
selected[slate.id] = true;
|
||||
}
|
||||
}
|
||||
this.setState({ selected, inPublicSlates, isPublic: this.props.data.isPublic });
|
||||
};
|
||||
|
||||
_handleToggleAccordion = (tab) => {
|
||||
this.setState({ [tab]: !this.state[tab] });
|
||||
};
|
||||
|
||||
_handleDarkMode = async (e) => {
|
||||
Events.dispatchCustomEvent({
|
||||
name: "set-slate-theme",
|
||||
detail: { darkmode: e.target.value },
|
||||
});
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
if (this.props.external || !this.props.isOwner) return;
|
||||
this.debounceInstance();
|
||||
this.setState(
|
||||
{
|
||||
[e.target.name]: e.target.value,
|
||||
showSavedMessage: false,
|
||||
},
|
||||
() => {
|
||||
if (e.target.name === "Tags") {
|
||||
this.updateSuggestions();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_handleCapitalization(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
_handleSave = async () => {
|
||||
if (this.props.external || !this.props.isOwner) return;
|
||||
this.props.onUpdateViewer({ tags: this.state.suggestions });
|
||||
const response = await Actions.updateFile({
|
||||
id: this.props.data.id,
|
||||
data: {
|
||||
name: this.state.name,
|
||||
body: this.state.body,
|
||||
source: this.state.source,
|
||||
author: this.state.author,
|
||||
tags: this.state.tags,
|
||||
},
|
||||
});
|
||||
Events.hasError(response);
|
||||
this.setState({ showSavedMessage: true });
|
||||
};
|
||||
|
||||
_handleSaveCopy = async (data) => {
|
||||
this.setState({ loading: "savingCopy" });
|
||||
console.log(data);
|
||||
await UserBehaviors.saveCopy({ files: [data] });
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
||||
_handleUpload = async (e) => {
|
||||
if (this.props.external || !this.props.isOwner) return;
|
||||
e.persist();
|
||||
this.setState({ isUploading: true });
|
||||
let previousCoverId = this.props.data.data.coverImage?.id;
|
||||
if (!e || !e.target) {
|
||||
this.setState({ isUploading: false });
|
||||
return;
|
||||
}
|
||||
let file = await UserBehaviors.uploadImage(e.target.files[0], this.props.resources, true);
|
||||
if (!file) {
|
||||
this.setState({ isUploading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
let coverImage = file;
|
||||
|
||||
//TODO(martina): create an endpoint specifically for cover images instead of this, which will delete original cover image etc
|
||||
|
||||
let updateReponse = await Actions.updateFile({
|
||||
id: this.props.data.id,
|
||||
data: {
|
||||
coverImage,
|
||||
},
|
||||
});
|
||||
|
||||
if (previousCoverId) {
|
||||
if (!this.props.viewer.library.some((obj) => obj.id === previousCoverId)) {
|
||||
await UserBehaviors.deleteFiles(previousCoverId, true);
|
||||
}
|
||||
}
|
||||
|
||||
Events.hasError(updateReponse);
|
||||
this.setState({ isUploading: false });
|
||||
};
|
||||
|
||||
_handleDownload = () => {
|
||||
if (this.props.data.data.type === "application/unity") {
|
||||
this.setState({ isDownloading: true }, async () => {
|
||||
const response = await UserBehaviors.downloadZip(this.props.data);
|
||||
this.setState({ isDownloading: false });
|
||||
Events.hasError(response);
|
||||
});
|
||||
} else {
|
||||
UserBehaviors.download(this.props.data);
|
||||
}
|
||||
};
|
||||
|
||||
_handleCreateSlate = async () => {
|
||||
if (this.props.external) return;
|
||||
this.props.onClose();
|
||||
this.props.onAction({
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_CREATE_SLATE",
|
||||
data: { files: [this.props.data] },
|
||||
});
|
||||
};
|
||||
|
||||
_handleDelete = () => {
|
||||
if (this.props.external || !this.props.isOwner) return;
|
||||
const message =
|
||||
"Are you sure you want to delete this? It will be removed from your slates as well";
|
||||
if (!window.confirm(message)) {
|
||||
return;
|
||||
}
|
||||
const id = this.props.data.id;
|
||||
|
||||
let updatedLibrary = this.props.viewer.library.filter((obj) => obj.id !== id);
|
||||
if (this.props.carouselType === "SLATE") {
|
||||
const slateId = this.props.current.id;
|
||||
let slates = this.props.viewer.slates;
|
||||
for (let slate of slates) {
|
||||
if (slate.id === slateId) {
|
||||
slate.objects = slate.objects.filter((obj) => obj.id !== id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.props.onUpdateViewer({ library: updatedLibrary, slates });
|
||||
} else {
|
||||
this.props.onUpdateViewer({ library: updatedLibrary });
|
||||
}
|
||||
|
||||
UserBehaviors.deleteFiles(id);
|
||||
};
|
||||
|
||||
_handleAdd = async (slate) => {
|
||||
let inPublicSlates = this.state.inPublicSlates;
|
||||
if (this.state.selected[slate.id]) {
|
||||
if (slate.isPublic) {
|
||||
inPublicSlates -= 1;
|
||||
}
|
||||
UserBehaviors.removeFromSlate({ slate, ids: [this.props.data.id] });
|
||||
} else {
|
||||
if (slate.isPublic) {
|
||||
inPublicSlates += 1;
|
||||
}
|
||||
UserBehaviors.addToSlate({
|
||||
slate,
|
||||
files: [this.props.data],
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
selected: {
|
||||
...this.state.selected,
|
||||
[slate.id]: !this.state.selected[slate.id],
|
||||
},
|
||||
inPublicSlates,
|
||||
});
|
||||
};
|
||||
|
||||
_handleRemove = async () => {
|
||||
if (!this.props.carouselType === "SLATE" || this.props.external || !this.props.isOwner) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = this.props.data.id;
|
||||
const slateId = this.props.current.id;
|
||||
let slates = this.props.viewer.slates;
|
||||
for (let slate of slates) {
|
||||
if (slate.id === slateId) {
|
||||
slate.objects = slate.objects.filter((obj) => obj.id !== id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.props.onUpdateViewer({ slates });
|
||||
|
||||
UserBehaviors.removeFromSlate({ slate: this.props.current, ids: [this.props.data.id] });
|
||||
};
|
||||
|
||||
_handleToggleVisibility = async (e) => {
|
||||
if (this.props.external || !this.props.isOwner) return;
|
||||
const isVisible = this.state.isPublic || this.state.inPublicSlates > 0;
|
||||
let selected = cloneDeep(this.state.selected);
|
||||
if (this.state.inPublicSlates) {
|
||||
const slateIds = Object.entries(this.state.selected)
|
||||
.filter((entry) => entry[1])
|
||||
.map((entry) => entry[0]);
|
||||
const publicSlateIds = [];
|
||||
const publicSlateNames = [];
|
||||
for (let slate of this.props.viewer.slates) {
|
||||
if (slate.isPublic && slateIds.includes(slate.id)) {
|
||||
publicSlateNames.push(slate.data.name);
|
||||
publicSlateIds.push(slate.id);
|
||||
selected[slate.id] = false;
|
||||
}
|
||||
}
|
||||
const slateNames = publicSlateNames.join(", ");
|
||||
const message = `Making this file private will remove it from the following public slates: ${slateNames}. Do you wish to continue?`;
|
||||
if (!window.confirm(message)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.carouselType === "SLATE" && this.props.current.isPublic) {
|
||||
const slateId = this.props.current.id;
|
||||
let slates = this.props.viewer.slates;
|
||||
for (let slate of slates) {
|
||||
if (slate.id === slateId) {
|
||||
slate.objects = slate.objects.filter((obj) => obj.id !== id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.props.onUpdateViewer({ slates });
|
||||
}
|
||||
|
||||
let response = await Actions.toggleFilePrivacy(this.props.data);
|
||||
Events.hasError(response);
|
||||
if (isVisible) {
|
||||
this.setState({ inPublicSlates: 0, isPublic: false, selected });
|
||||
} else {
|
||||
this.setState({ isPublic: true });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const isVisible = this.state.isPublic || this.state.inPublicSlates > 0 ? true : false;
|
||||
const file = this.props.data;
|
||||
const { coverImage, type, size } = file.data;
|
||||
const editingAllowed = this.props.isOwner && !this.props.isRepost && !this.props.external;
|
||||
|
||||
const isUnityGame = type === "application/unity";
|
||||
|
||||
const elements = [];
|
||||
if (editingAllowed && !isUnityGame) {
|
||||
elements.push(
|
||||
<div key="sidebar-media-object-info" style={{ marginTop: 8 }}>
|
||||
<Input
|
||||
full
|
||||
value={this.state.name}
|
||||
name="name"
|
||||
onChange={this._handleChange}
|
||||
id={`sidebar-label-name`}
|
||||
style={{
|
||||
fontSize: Constants.typescale.lvl1,
|
||||
...STYLES_INPUT,
|
||||
}}
|
||||
/>
|
||||
<Textarea
|
||||
name="body"
|
||||
placeholder="Add notes or a description..."
|
||||
value={this.state.body}
|
||||
onChange={this._handleChange}
|
||||
style={STYLES_INPUT}
|
||||
/>
|
||||
<Input
|
||||
full
|
||||
value={this.state.source}
|
||||
name="source"
|
||||
placeholder="Source"
|
||||
onChange={this._handleChange}
|
||||
id={`sidebar-label-source`}
|
||||
style={STYLES_INPUT}
|
||||
/>
|
||||
<Input
|
||||
full
|
||||
value={this.state.author}
|
||||
name="author"
|
||||
placeholder="Author"
|
||||
onChange={this._handleChange}
|
||||
id={`sidebar-label-author`}
|
||||
style={{ ...STYLES_INPUT, marginBottom: 12 }}
|
||||
/>
|
||||
<div css={STYLES_OPTIONS_SECTION}>
|
||||
<Tag
|
||||
type="dark"
|
||||
tags={this.state.tags}
|
||||
suggestions={this.state.suggestions}
|
||||
style={{ margin: "0 0 16px" }}
|
||||
inputStyles={{ padding: "16px" }}
|
||||
dropdownStyles={{ top: "50px" }}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const hasName = !Strings.isEmpty(file.data.name || file.filename);
|
||||
const hasBody = !Strings.isEmpty(file.data.body);
|
||||
const hasSource = !Strings.isEmpty(file.data.source);
|
||||
const hasAuthor = !Strings.isEmpty(file.data.author);
|
||||
|
||||
if (hasName) {
|
||||
elements.push(
|
||||
<div key="sidebar-media-info-name" css={STYLES_SIDEBAR_SECTION}>
|
||||
<div css={STYLES_HEADING}>
|
||||
<ProcessedText dark text={file.data.name || file.filename} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (hasBody) {
|
||||
elements.push(
|
||||
<div key="sidebar-media-info-body" css={STYLES_SIDEBAR_SECTION}>
|
||||
<div css={STYLES_BODY}>
|
||||
<ProcessedText dark text={file.data.body} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (hasSource) {
|
||||
elements.push(
|
||||
<div key="sidebar-media-info-source" css={STYLES_SIDEBAR_SECTION}>
|
||||
<div css={STYLES_SIDEBAR_INPUT_LABEL} style={{ position: "relative" }}>
|
||||
Source:
|
||||
</div>
|
||||
<p css={STYLES_BODY} style={{ color: Constants.system.darkGray }}>
|
||||
<ProcessedText dark text={file.data.source} />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (hasAuthor) {
|
||||
elements.push(
|
||||
<div key="sidebar-media-info-author" css={STYLES_SIDEBAR_SECTION}>
|
||||
<div css={STYLES_SIDEBAR_INPUT_LABEL} style={{ position: "relative" }}>
|
||||
Author:
|
||||
</div>
|
||||
<p css={STYLES_BODY} style={{ color: Constants.system.darkGray }}>
|
||||
<ProcessedText dark text={file.data.author} />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let actions = [];
|
||||
|
||||
{
|
||||
this.props.carouselType === "ACTIVITY"
|
||||
? actions.push(
|
||||
<div
|
||||
key="go-to-slate"
|
||||
css={STYLES_ACTION}
|
||||
onClick={() =>
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: "NAV_SLATE",
|
||||
data: file.slate,
|
||||
})
|
||||
}
|
||||
>
|
||||
<SVG.Slate height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>Go to slate</span>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
actions.push(
|
||||
<div key="download" css={STYLES_ACTION} onClick={this._handleDownload}>
|
||||
{this.state.isDownloading ? (
|
||||
<>
|
||||
<LoaderSpinner css={STYLES_SPINNER} />
|
||||
<span style={{ marginLeft: 16 }}>Downloading</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<SVG.Download height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>Download</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (!this.props.external && (!this.props.isOwner || this.props.isRepost)) {
|
||||
actions.push(
|
||||
<div key="save-copy" css={STYLES_ACTION} onClick={() => this._handleSaveCopy(file)}>
|
||||
<SVG.Save height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>
|
||||
{this.state.loading === "savingCopy" ? (
|
||||
<LoaderSpinner style={{ height: 16, width: 16 }} />
|
||||
) : (
|
||||
<span>Save copy</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.carouselType === "SLATE" && !this.props.external && this.props.isOwner) {
|
||||
actions.push(
|
||||
<div key="remove" css={STYLES_ACTION} onClick={this._handleRemove}>
|
||||
<SVG.DismissCircle height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>Remove from slate</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (editingAllowed) {
|
||||
actions.push(
|
||||
<div key="delete" css={STYLES_ACTION} onClick={this._handleDelete}>
|
||||
<SVG.Trash height="24px" />
|
||||
<span style={{ marginLeft: 16 }}>Delete</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let uploadCoverImage;
|
||||
if (editingAllowed && type && !Validations.isPreviewableImage(type)) {
|
||||
uploadCoverImage = (
|
||||
<div style={{ marginBottom: 48 }}>
|
||||
<System.P css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
Preview image
|
||||
</System.P>
|
||||
{coverImage ? (
|
||||
<>
|
||||
<System.P css={STYLES_TEXT}>This is the preview image of your file.</System.P>
|
||||
<div css={STYLES_IMAGE_BOX} style={{ marginTop: 24 }}>
|
||||
<img
|
||||
src={Strings.getURLfromCID(coverImage.cid)}
|
||||
alt=""
|
||||
style={{ maxWidth: "368px", maxHeight: "368px" }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<System.P css={STYLES_TEXT}>Add a preview image for your file.</System.P>
|
||||
)}
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
|
||||
<System.ButtonPrimary full type="label" htmlFor="file" loading={this.state.isUploading}>
|
||||
Upload preview image
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let privacy;
|
||||
if (editingAllowed) {
|
||||
privacy = (
|
||||
<div>
|
||||
<System.P css={STYLES_SECTION_HEADER} style={{ marginBottom: 12 }}>
|
||||
Privacy
|
||||
</System.P>
|
||||
<System.P
|
||||
css={STYLES_TEXT}
|
||||
style={{
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
{isVisible
|
||||
? "This file is currently visible to everyone and searchable within Slate. It may appear in activity feeds and explore."
|
||||
: "This file is currently not visible to others unless they have the link."}
|
||||
</System.P>
|
||||
<RadioGroup
|
||||
name="isPublic"
|
||||
options={[
|
||||
{
|
||||
value: true,
|
||||
label: (
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<SVG.Globe height="16px" style={{ marginRight: 8 }} />
|
||||
Public
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
label: (
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<SVG.SecurityLock height="16px" style={{ marginRight: 8 }} />
|
||||
Private
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
dark={true}
|
||||
style={{ marginTop: 12 }}
|
||||
labelStyle={{ fontFamily: Constants.font.medium }}
|
||||
selected={isVisible}
|
||||
onChange={this._handleToggleVisibility}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div css={STYLES_SIDEBAR} style={{ display: this.props.display }}>
|
||||
{this.state.showSavedMessage && (
|
||||
<div css={STYLES_AUTOSAVE}>
|
||||
<SVG.Check height="14px" style={{ marginRight: 4 }} />
|
||||
Changes saved
|
||||
</div>
|
||||
)}
|
||||
<div key="s-1" css={STYLES_DISMISS_BOX} onClick={this.props.onClose}>
|
||||
<SVG.Dismiss height="24px" />
|
||||
</div>
|
||||
|
||||
<div key="s-2" style={{ marginBottom: 80 }}>
|
||||
{elements}
|
||||
|
||||
{!this.props.external && <div css={STYLES_ACTIONS}>{actions}</div>}
|
||||
{privacy}
|
||||
{uploadCoverImage}
|
||||
{!this.props.external && (
|
||||
<>
|
||||
<div
|
||||
css={STYLES_SECTION_HEADER}
|
||||
style={{ cursor: "pointer", marginTop: 48 }}
|
||||
onClick={() => this._handleToggleAccordion("showConnectedSection")}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
marginRight: 8,
|
||||
transform: this.state.showConnectedSection ? "none" : "rotate(-90deg)",
|
||||
transition: "100ms ease transform",
|
||||
}}
|
||||
>
|
||||
<SVG.ChevronDown height="24px" display="block" />
|
||||
</span>
|
||||
<span>Add to slate</span>
|
||||
</div>
|
||||
{this.state.showConnectedSection && (
|
||||
<div style={{ width: "100%", margin: "24px 0 44px 0" }}>
|
||||
<SlatePicker
|
||||
dark
|
||||
slates={this.props.viewer.slates}
|
||||
onCreateSlate={this._handleCreateSlate}
|
||||
selectedColor={Constants.system.white}
|
||||
files={[this.props.data]}
|
||||
selected={this.state.selected}
|
||||
onAdd={this._handleAdd}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{this.props.data.filename.endsWith(".md") ? (
|
||||
<>
|
||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
Settings
|
||||
</div>
|
||||
<div css={STYLES_OPTIONS_SECTION}>
|
||||
<div css={STYLES_TEXT}>Dark mode</div>
|
||||
<Toggle dark active={this.props?.theme?.darkmode} onChange={this._handleDarkMode} />
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(CarouselSidebar);
|
||||
|
||||
{
|
||||
/* <>
|
||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
Visibility
|
||||
</div>
|
||||
<div css={STYLES_OPTIONS_SECTION}>
|
||||
<div css={STYLES_TEXT}>{isVisible ? "Everyone" : "Link only"}</div>
|
||||
<Toggle dark active={isVisible} onChange={this._handleToggleVisibility} />
|
||||
</div>
|
||||
<div style={{ color: Constants.system.darkGray, marginTop: 8 }}>
|
||||
{isVisible
|
||||
? "This file is currently visible to everyone and searchable within Slate. It may appear in activity feeds and explore."
|
||||
: "This file is currently not visible to others unless they have the link."}
|
||||
</div>
|
||||
</> */
|
||||
}
|
@ -362,7 +362,7 @@ class CarouselSidebarData extends React.Component {
|
||||
|
||||
json.data.url = Strings.getCIDGatewayURL(json.data.cid);
|
||||
|
||||
let updateReponse = await Actions.updateData({
|
||||
let updateReponse = await Actions.updateFile({
|
||||
data: {
|
||||
id: this.props.data.id,
|
||||
coverImage: json.data,
|
||||
|
@ -474,37 +474,29 @@ export default class DataView extends React.Component {
|
||||
this.setState({ checked: {} });
|
||||
};
|
||||
|
||||
_handleDelete = (cid, id) => {
|
||||
_handleDelete = (id) => {
|
||||
const message = `Are you sure you want to delete these files? They will be deleted from your slates as well`;
|
||||
if (!window.confirm(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cids;
|
||||
let ids;
|
||||
if (cid) {
|
||||
cids = [cid];
|
||||
if (id) {
|
||||
ids = [id];
|
||||
} else {
|
||||
cids = Object.keys(this.state.checked).map((id) => {
|
||||
let index = parseInt(id);
|
||||
let item = this.props.viewer.library[0].children[index];
|
||||
return item.cid;
|
||||
});
|
||||
ids = Object.keys(this.state.checked).map((id) => {
|
||||
let index = parseInt(id);
|
||||
let item = this.props.viewer.library[0].children[index];
|
||||
let item = this.props.viewer.library[index];
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
|
||||
let library = this.props.viewer.library;
|
||||
library[0].children = library[0].children.filter(
|
||||
let library = this.props.viewer.library.filter(
|
||||
(obj) => !ids.includes(obj.id) && !cids.includes(obj.cid)
|
||||
);
|
||||
this.props.onUpdateViewer({ library });
|
||||
|
||||
UserBehaviors.deleteFiles(cids, ids);
|
||||
UserBehaviors.deleteFiles(ids);
|
||||
this.setState({ checked: {} });
|
||||
};
|
||||
|
||||
@ -545,7 +537,7 @@ export default class DataView extends React.Component {
|
||||
};
|
||||
|
||||
_handleAddToSlate = (e) => {
|
||||
let userFiles = this.props.viewer.library[0].children;
|
||||
let userFiles = this.props.viewer.library;
|
||||
let files = Object.keys(this.state.checked).map((index) => userFiles[index]);
|
||||
this.props.onAction({
|
||||
type: "SIDEBAR",
|
||||
@ -576,9 +568,9 @@ export default class DataView extends React.Component {
|
||||
};
|
||||
|
||||
_handleDragToDesktop = (e, object) => {
|
||||
const url = Strings.getCIDGatewayURL(object.cid);
|
||||
const title = object.file || object.name;
|
||||
const type = object.type;
|
||||
const url = Strings.getURLfromCID(object.cid);
|
||||
const title = object.filename || object.data.name;
|
||||
const type = object.data.type;
|
||||
console.log(e.dataTransfer, e.dataTransfer.setData);
|
||||
e.dataTransfer.setData("DownloadURL", `${type}:${title}:${url}`);
|
||||
};
|
||||
@ -690,13 +682,15 @@ export default class DataView extends React.Component {
|
||||
>
|
||||
{Strings.pluralize("Download file", numChecked)}
|
||||
</ButtonWarning>
|
||||
<ButtonWarning
|
||||
transparent
|
||||
style={{ marginLeft: 8, color: Constants.system.white }}
|
||||
onClick={() => this._handleDelete()}
|
||||
>
|
||||
{Strings.pluralize("Delete file", numChecked)}
|
||||
</ButtonWarning>
|
||||
{this.props.isOwner && (
|
||||
<ButtonWarning
|
||||
transparent
|
||||
style={{ marginLeft: 8, color: Constants.system.white }}
|
||||
onClick={() => this._handleDelete()}
|
||||
>
|
||||
{Strings.pluralize("Delete file", numChecked)}
|
||||
</ButtonWarning>
|
||||
)}
|
||||
<div
|
||||
css={STYLES_ICON_BOX}
|
||||
onClick={() => {
|
||||
@ -743,13 +737,7 @@ export default class DataView extends React.Component {
|
||||
onMouseLeave={() => this._handleCheckBoxMouseLeave(i)}
|
||||
>
|
||||
<SlateMediaObjectPreview
|
||||
blurhash={each.blurhash}
|
||||
url={Strings.getCIDGatewayURL(each.cid)}
|
||||
title={each.name || each.file}
|
||||
type={each.type}
|
||||
cid={each.cid}
|
||||
coverImage={each.coverImage}
|
||||
dataView={true}
|
||||
file={each}
|
||||
/>
|
||||
<span css={STYLES_MOBILE_HIDDEN} style={{ pointerEvents: "auto" }}>
|
||||
{numChecked || this.state.hover === i || this.state.menu === each.id ? (
|
||||
@ -785,7 +773,7 @@ export default class DataView extends React.Component {
|
||||
{
|
||||
text: "Copy link",
|
||||
onClick: (e) =>
|
||||
this._handleCopy(e, Strings.getCIDGatewayURL(cid)),
|
||||
this._handleCopy(e, Strings.getURLfromCID(cid)),
|
||||
},
|
||||
{
|
||||
text: "Delete",
|
||||
@ -812,7 +800,7 @@ export default class DataView extends React.Component {
|
||||
{
|
||||
text: "Copy link",
|
||||
onClick: (e) =>
|
||||
this._handleCopy(e, Strings.getCIDGatewayURL(cid)),
|
||||
this._handleCopy(e, Strings.getURLfromCID(cid)),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@ -938,12 +926,12 @@ export default class DataView extends React.Component {
|
||||
}}
|
||||
onDragEnd={this._enableDragAndDropUploadEvent}
|
||||
>
|
||||
<FilePreviewBubble url={cid} type={each.type}>
|
||||
<FilePreviewBubble cid={cid} type={each.data.type}>
|
||||
<div css={STYLES_CONTAINER_HOVER} onClick={() => this._handleSelect(index)}>
|
||||
<div css={STYLES_ICON_BOX_HOVER} style={{ paddingLeft: 0, paddingRight: 18 }}>
|
||||
<FileTypeIcon type={each.type} height="24px" />
|
||||
<FileTypeIcon type={each.data.type} height="24px" />
|
||||
</div>
|
||||
<div css={STYLES_LINK}>{each.file || each.name}</div>
|
||||
<div css={STYLES_LINK}>{each.filename || each.data.name}</div>
|
||||
</div>
|
||||
</FilePreviewBubble>
|
||||
</Selectable>
|
||||
@ -979,13 +967,13 @@ export default class DataView extends React.Component {
|
||||
},
|
||||
// {
|
||||
// text: "Copy link",
|
||||
// onClick: (e) => this._handleCopy(e, Strings.getCIDGatewayURL(cid)),
|
||||
// onClick: (e) => this._handleCopy(e, Strings.getURLfromCID(cid)),
|
||||
// },
|
||||
{
|
||||
text: "Delete",
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ menu: null }, () => this._handleDelete(cid, each.id));
|
||||
this.setState({ menu: null }, () => this._handleDelete(each.id));
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
@ -55,7 +55,7 @@ export const FilePreviewBubble = (props) => {
|
||||
</div>
|
||||
{showPreview && (
|
||||
<div css={STYLES_FILE_PREVIEW_BUBBLE}>
|
||||
<img css={STYLES_FILE_PREVIEW} src={Strings.getCIDGatewayURL(props.url)} />
|
||||
<img css={STYLES_FILE_PREVIEW} src={Strings.getURLfromCID(props.cid)} />
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { useFont } from "../hooks";
|
||||
import { useFont } from "~/components/core/FontFrame/hooks";
|
||||
|
||||
const withView = (Component) => (props) => {
|
||||
const ref = React.useRef(null);
|
||||
@ -35,7 +35,7 @@ const STYLES_LETTER = (theme) => css`
|
||||
|
||||
const FontObjectPreview = React.memo(
|
||||
({ url, cid, fallback }) => {
|
||||
const { isFontLoading, error, fontName } = useFont({ url, name: cid }, [cid]);
|
||||
const { isFontLoading, error, fontName } = useFont({ cid }, [cid]);
|
||||
if (error || isFontLoading) {
|
||||
return fallback;
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import * as React from "react";
|
||||
import * as Events from "~/common/custom-events";
|
||||
import * as Content from "./Views/content";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import { generateNumberByStep } from "~/common/utilities";
|
||||
|
||||
export const useFont = ({ url, name }, deps) => {
|
||||
export const useFont = ({ cid }, deps) => {
|
||||
const url = Strings.getURLfromCID(cid);
|
||||
const [fetchState, setFetchState] = React.useState({ loading: false, error: null });
|
||||
const prevName = React.useRef(name);
|
||||
const prevName = React.useRef(cid);
|
||||
|
||||
if (!window.$SLATES_LOADED_FONTS) window.$SLATES_LOADED_FONTS = [];
|
||||
const alreadyLoaded = window.$SLATES_LOADED_FONTS.includes(name);
|
||||
const alreadyLoaded = window.$SLATES_LOADED_FONTS.includes(cid);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (alreadyLoaded) {
|
||||
@ -18,15 +20,15 @@ export const useFont = ({ url, name }, deps) => {
|
||||
}
|
||||
|
||||
setFetchState((prev) => ({ ...prev, error: null, loading: true }));
|
||||
const customFonts = new FontFace(name, `url(${url})`);
|
||||
const customFonts = new FontFace(cid, `url(${url})`);
|
||||
customFonts
|
||||
.load()
|
||||
.then((loadedFont) => {
|
||||
document.fonts.add(loadedFont);
|
||||
prevName.current = name;
|
||||
prevName.current = cid;
|
||||
|
||||
setFetchState((prev) => ({ ...prev, loading: false }));
|
||||
window.$SLATES_LOADED_FONTS.push(name);
|
||||
window.$SLATES_LOADED_FONTS.push(cid);
|
||||
})
|
||||
.catch((err) => {
|
||||
setFetchState({ loading: false, error: err });
|
||||
@ -37,7 +39,7 @@ export const useFont = ({ url, name }, deps) => {
|
||||
isFontLoading: fetchState.loading,
|
||||
error: fetchState.error,
|
||||
// NOTE(Amine): show previous font while we load the new one.
|
||||
fontName: alreadyLoaded ? name : prevName.current,
|
||||
fontName: alreadyLoaded ? cid : prevName.current,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
@ -44,8 +45,9 @@ const FontLoader = () => (
|
||||
<p>loading...</p>
|
||||
</div>
|
||||
);
|
||||
export default function FontFrame({ cid, url, fallback, ...props }) {
|
||||
const { isFontLoading, error, fontName } = useFont({ url, name: cid }, [cid, url]);
|
||||
export default function FontFrame({ cid, fallback, ...props }) {
|
||||
const url = Strings.getURLfromCID(cid);
|
||||
const { isFontLoading, error, fontName } = useFont({ cid }, [cid, url]);
|
||||
|
||||
const [
|
||||
currentState,
|
||||
|
@ -65,7 +65,9 @@ export class OnboardingModal extends React.Component {
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
Actions.updateStatus({ onboarding: this.props.unseenAnnouncements });
|
||||
Actions.updateStatus({
|
||||
onboarding: this.props.unseenAnnouncements,
|
||||
});
|
||||
let slides = [];
|
||||
if (this.props.newAccount) {
|
||||
slides = this.onboardingCopy;
|
||||
|
@ -4,6 +4,7 @@ import * as Strings from "~/common/strings";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Actions from "~/common/actions";
|
||||
import * as Utilities from "~/common/utilities";
|
||||
import * as Events from "~/common/custom-events";
|
||||
import * as Window from "~/common/window";
|
||||
|
||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||
@ -85,8 +86,8 @@ const STYLES_STATUS_INDICATOR = css`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid ${Constants.system.gray50};
|
||||
background-color: ${Constants.system.white};
|
||||
border: 2px solid ${Constants.system.active};
|
||||
background-color: ${Constants.system.active};
|
||||
`;
|
||||
|
||||
const STYLES_NAME = css`
|
||||
@ -211,8 +212,8 @@ const STYLES_DIRECTORY_STATUS_INDICATOR = css`
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
border: 1.2px solid ${Constants.system.gray50};
|
||||
background-color: ${Constants.system.white};
|
||||
border: 1.2px solid ${Constants.system.active};
|
||||
background-color: ${Constants.system.active};
|
||||
`;
|
||||
|
||||
const STYLES_MESSAGE = css`
|
||||
@ -258,15 +259,7 @@ function UserEntry({
|
||||
css={STYLES_DIRECTORY_PROFILE_IMAGE}
|
||||
style={{ backgroundImage: `url(${user.data.photo})` }}
|
||||
>
|
||||
{showStatusIndicator && (
|
||||
<div
|
||||
css={STYLES_DIRECTORY_STATUS_INDICATOR}
|
||||
style={{
|
||||
borderColor: isOnline && `${Constants.system.active}`,
|
||||
backgroundColor: isOnline && `${Constants.system.active}`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showStatusIndicator && isOnline && <div css={STYLES_DIRECTORY_STATUS_INDICATOR} />}
|
||||
</div>
|
||||
<span css={STYLES_DIRECTORY_NAME}>
|
||||
{user.data.name || `@${user.username}`}
|
||||
@ -279,13 +272,7 @@ function UserEntry({
|
||||
css={STYLES_DIRECTORY_PROFILE_IMAGE}
|
||||
style={{ backgroundImage: `url(${user.data.photo})` }}
|
||||
>
|
||||
<div
|
||||
css={STYLES_DIRECTORY_STATUS_INDICATOR}
|
||||
style={{
|
||||
borderColor: isOnline && `${Constants.system.active}`,
|
||||
backgroundColor: isOnline && `${Constants.system.active}`,
|
||||
}}
|
||||
/>
|
||||
{isOnline && <div css={STYLES_DIRECTORY_STATUS_INDICATOR} />}
|
||||
</div>
|
||||
<span css={STYLES_DIRECTORY_NAME}>
|
||||
{user.data.name || `@${user.username}`}
|
||||
@ -307,23 +294,23 @@ export default class Profile extends React.Component {
|
||||
peerTab: 0,
|
||||
// copyValue: "",
|
||||
contextMenu: null,
|
||||
publicFiles: this.props.creator.library[0].children,
|
||||
slates: this.props.creator.slates,
|
||||
slates: this.props.user.slates,
|
||||
subscriptions: [],
|
||||
subscribers: [],
|
||||
isFollowing: this.props.external
|
||||
? false
|
||||
: !!this.props.viewer.subscriptions.filter((entry) => {
|
||||
return entry.target_user_id === this.props.creator.id;
|
||||
}).length,
|
||||
followers: [],
|
||||
following: [],
|
||||
isFollowing:
|
||||
this.props.external || this.props.user.id === this.props.viewer?.id
|
||||
? false
|
||||
: !!this.props.viewer?.following.some((entry) => {
|
||||
return entry.id === this.props.user.id;
|
||||
}),
|
||||
fetched: false,
|
||||
tab: this.props.tab,
|
||||
tab: this.props.tab || 0,
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
console.log(this.props.creator);
|
||||
console.log(this.props.user);
|
||||
this._handleUpdatePage();
|
||||
// this.filterByVisibility();
|
||||
};
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
@ -333,22 +320,30 @@ export default class Profile extends React.Component {
|
||||
};
|
||||
|
||||
fetchSocial = async () => {
|
||||
let query = { userId: this.props.creator.id };
|
||||
const { subscribers, subscriptions } = await Actions.getSocial(query);
|
||||
this.setState({ subscribers: subscribers, subscriptions: subscriptions, fetched: true });
|
||||
if (this.state.fetched) return;
|
||||
let following, followers, subscriptions;
|
||||
if (this.props.user.id === this.props.viewer?.id) {
|
||||
following = this.props.viewer?.following;
|
||||
followers = this.props.viewer?.followers;
|
||||
subscriptions = this.props.viewer?.subscriptions;
|
||||
} else {
|
||||
const query = { id: this.props.user.id };
|
||||
let response = await Actions.getSocial(query);
|
||||
if (Events.hasError(response)) {
|
||||
return;
|
||||
}
|
||||
following = response.following;
|
||||
followers = response.followers;
|
||||
subscriptions = response.subscriptions;
|
||||
}
|
||||
this.setState({
|
||||
following: following,
|
||||
followers: followers,
|
||||
subscriptions: subscriptions,
|
||||
fetched: true,
|
||||
});
|
||||
};
|
||||
|
||||
// filterByVisibility = () => {
|
||||
// let publicFiles = [];
|
||||
// if (this.props.isOwner) {
|
||||
// const res = Utilities.getPublicAndPrivateFiles({ viewer: this.props.creator });
|
||||
// publicFiles = res.publicFiles;
|
||||
// } else {
|
||||
// publicFiles = this.props.creator.library[0].children;
|
||||
// }
|
||||
// this.setState({ publicFiles: publicFiles });
|
||||
// };
|
||||
|
||||
// _handleCopy = (e, value) => {
|
||||
// e.stopPropagation();
|
||||
// this.setState({ copyValue: value }, () => {
|
||||
@ -417,148 +412,88 @@ export default class Profile extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
let tab = typeof this.state.tab === "undefined" || this.state.tab === null ? 1 : this.state.tab;
|
||||
let tab = this.state.tab || 0;
|
||||
let publicFiles = this.props.user.library;
|
||||
let isOwner = this.props.isOwner;
|
||||
let creator = this.props.creator;
|
||||
let username = this.state.slateTab === 0 ? creator.username : null;
|
||||
let subscriptions = this.state.subscriptions;
|
||||
let subscribers = this.state.subscribers;
|
||||
let user = this.props.user;
|
||||
let username = this.state.slateTab === 0 ? user.username : null;
|
||||
let slates = [];
|
||||
if (tab === 1) {
|
||||
if (this.state.slateTab === 0) {
|
||||
slates = isOwner
|
||||
? creator.slates.filter((slate) => slate.data.public === true)
|
||||
: creator.slates;
|
||||
slates = user.slates
|
||||
? isOwner
|
||||
? user.slates.filter((slate) => slate.isPublic === true)
|
||||
: user.slates
|
||||
: null;
|
||||
} else {
|
||||
slates = subscriptions
|
||||
.filter((relation) => {
|
||||
return !!relation.target_slate_id;
|
||||
})
|
||||
.map((relation) => relation.slate);
|
||||
slates = this.state.subscriptions;
|
||||
}
|
||||
}
|
||||
let exploreSlates = this.props.exploreSlates;
|
||||
let peers = [];
|
||||
let peers = this.state.peerTab === 0 ? this.state.following : this.state.followers;
|
||||
if (tab === 2) {
|
||||
if (this.state.peerTab === 0) {
|
||||
peers = subscriptions
|
||||
.filter((relation) => {
|
||||
return !!relation.target_user_id;
|
||||
})
|
||||
.map((relation) => {
|
||||
let button = (
|
||||
<div css={STYLES_ITEM_BOX} onClick={(e) => this._handleClick(e, relation.id)}>
|
||||
<SVG.MoreHorizontal height="24px" />
|
||||
{this.state.contextMenu === relation.id ? (
|
||||
<Boundary
|
||||
captureResize={true}
|
||||
captureScroll={false}
|
||||
enabled
|
||||
onOutsideRectEvent={(e) => this._handleClick(e, relation.id)}
|
||||
>
|
||||
<PopoverNavigation
|
||||
style={{
|
||||
top: "40px",
|
||||
right: "0px",
|
||||
}}
|
||||
navigation={[
|
||||
{
|
||||
text: this.props.viewer?.subscriptions.filter((subscription) => {
|
||||
return subscription.target_user_id === relation.target_user_id;
|
||||
}).length
|
||||
? "Unfollow"
|
||||
: "Follow",
|
||||
onClick: this.props.viewer
|
||||
? (e) => this._handleFollow(e, relation.target_user_id)
|
||||
: () => this.setState({ visible: true }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Boundary>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
peers = peers.map((relation) => {
|
||||
let button = (
|
||||
<div css={STYLES_ITEM_BOX} onClick={(e) => this._handleClick(e, relation.id)}>
|
||||
<SVG.MoreHorizontal height="24px" />
|
||||
{this.state.contextMenu === relation.id ? (
|
||||
<Boundary
|
||||
captureResize={true}
|
||||
captureScroll={false}
|
||||
enabled
|
||||
onOutsideRectEvent={(e) => this._handleClick(e, relation.id)}
|
||||
>
|
||||
<PopoverNavigation
|
||||
style={{
|
||||
top: "40px",
|
||||
right: "0px",
|
||||
}}
|
||||
navigation={[
|
||||
{
|
||||
text: this.props.viewer?.following.some((subscription) => {
|
||||
return subscription.id === relation.id;
|
||||
}).length
|
||||
? "Unfollow"
|
||||
: "Follow",
|
||||
onClick: this.props.viewer
|
||||
? (e) => this._handleFollow(e, relation.id)
|
||||
: () => this.setState({ visible: true }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Boundary>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<UserEntry
|
||||
key={relation.id}
|
||||
user={relation.user}
|
||||
button={button}
|
||||
checkStatus={this.checkStatus}
|
||||
showStatusIndicator={this.props.isAuthenticated}
|
||||
onClick={() => {
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: this.props.sceneId,
|
||||
scene: "PROFILE",
|
||||
data: relation.user,
|
||||
});
|
||||
}}
|
||||
external={this.props.external}
|
||||
url={`/${relation.user.username}`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
peers = subscribers.map((relation) => {
|
||||
let button = (
|
||||
<div css={STYLES_ITEM_BOX} onClick={(e) => this._handleClick(e, relation.id)}>
|
||||
<SVG.MoreHorizontal height="24px" />
|
||||
{this.state.contextMenu === relation.id ? (
|
||||
<Boundary
|
||||
captureResize={true}
|
||||
captureScroll={false}
|
||||
enabled
|
||||
onOutsideRectEvent={(e) => this._handleClick(e, relation.id)}
|
||||
>
|
||||
<PopoverNavigation
|
||||
style={{
|
||||
top: "40px",
|
||||
right: "0px",
|
||||
}}
|
||||
navigation={[
|
||||
{
|
||||
text: this.props.viewer?.subscriptions.filter((subscription) => {
|
||||
return subscription.target_user_id === relation.owner_user_id;
|
||||
}).length
|
||||
? "Unfollow"
|
||||
: "Follow",
|
||||
onClick: this.props.viewer
|
||||
? (e) => this._handleFollow(e, relation.owner_user_id)
|
||||
: () => this.setState({ visible: true }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Boundary>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<UserEntry
|
||||
key={relation.id}
|
||||
user={relation.owner}
|
||||
button={button}
|
||||
checkStatus={this.checkStatus}
|
||||
showStatusIndicator={this.props.isAuthenticated}
|
||||
onClick={() => {
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: this.props.sceneId,
|
||||
scene: "PROFILE",
|
||||
data: relation.owner,
|
||||
});
|
||||
}}
|
||||
external={this.props.external}
|
||||
url={`/${relation.owner.username}`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
return (
|
||||
<UserEntry
|
||||
key={relation.id}
|
||||
user={relation}
|
||||
button={button}
|
||||
checkStatus={this.checkStatus}
|
||||
showStatusIndicator={this.props.isAuthenticated}
|
||||
onClick={() => {
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: this.props.sceneId,
|
||||
scene: "PROFILE",
|
||||
data: relation,
|
||||
});
|
||||
}}
|
||||
external={this.props.external}
|
||||
url={`/${relation.username}`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
let total = creator.slates.reduce((total, slate) => {
|
||||
return total + slate.data?.objects?.length || 0;
|
||||
}, 0);
|
||||
let total = 0;
|
||||
if (user.slates) {
|
||||
total = user.slates.reduce((total, slate) => {
|
||||
return total + slate.data?.objects?.length || 0;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
const showStatusIndicator = this.props.isAuthenticated;
|
||||
|
||||
@ -569,10 +504,10 @@ export default class Profile extends React.Component {
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
resources={this.props.resources}
|
||||
viewer={this.props.viewer}
|
||||
objects={this.state.publicFiles}
|
||||
isOwner={false}
|
||||
objects={publicFiles}
|
||||
isOwner={this.props.isOwner}
|
||||
onAction={this.props.onAction}
|
||||
mobile={this.props.mobile}
|
||||
isMobile={this.props.isMobile}
|
||||
external={this.props.external}
|
||||
/>
|
||||
<div css={STYLES_PROFILE_BACKGROUND}>
|
||||
@ -580,30 +515,22 @@ export default class Profile extends React.Component {
|
||||
<div
|
||||
css={STYLES_PROFILE_IMAGE}
|
||||
style={{
|
||||
backgroundImage: `url('${creator.data.photo}')`,
|
||||
backgroundImage: `url('${user.data.photo}')`,
|
||||
}}
|
||||
>
|
||||
{showStatusIndicator && (
|
||||
<div
|
||||
css={STYLES_STATUS_INDICATOR}
|
||||
style={{
|
||||
borderColor:
|
||||
this.checkStatus({ id: this.props.data?.id }) && `${Constants.system.active}`,
|
||||
backgroundColor:
|
||||
this.checkStatus({ id: this.props.data?.id }) && `${Constants.system.active}`,
|
||||
}}
|
||||
/>
|
||||
{showStatusIndicator && this.checkStatus({ id: user.id }) && (
|
||||
<div css={STYLES_STATUS_INDICATOR} />
|
||||
)}
|
||||
</div>
|
||||
<div css={STYLES_INFO}>
|
||||
<div css={STYLES_NAME}>{Strings.getPresentationName(creator)}</div>
|
||||
<div css={STYLES_NAME}>{Strings.getPresentationName(user)}</div>
|
||||
{!isOwner && (
|
||||
<div css={STYLES_BUTTON}>
|
||||
{this.state.isFollowing ? (
|
||||
<ButtonSecondary
|
||||
onClick={(e) => {
|
||||
this.setState({ isFollowing: false });
|
||||
this._handleFollow(e, this.props.creator.id);
|
||||
this._handleFollow(e, this.props.user.id);
|
||||
}}
|
||||
>
|
||||
Unfollow
|
||||
@ -612,7 +539,7 @@ export default class Profile extends React.Component {
|
||||
<ButtonPrimary
|
||||
onClick={(e) => {
|
||||
this.setState({ isFollowing: true });
|
||||
this._handleFollow(e, this.props.creator.id);
|
||||
this._handleFollow(e, this.props.user.id);
|
||||
}}
|
||||
>
|
||||
Follow
|
||||
@ -620,9 +547,9 @@ export default class Profile extends React.Component {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{creator.data.body ? (
|
||||
{user.data.body ? (
|
||||
<div css={STYLES_DESCRIPTION}>
|
||||
<ProcessedText text={creator.data.body} />
|
||||
<ProcessedText text={user.data.body} />
|
||||
</div>
|
||||
) : null}
|
||||
<div css={STYLES_STATS}>
|
||||
@ -633,7 +560,7 @@ export default class Profile extends React.Component {
|
||||
</div>
|
||||
<div css={STYLES_STAT}>
|
||||
<div style={{ fontFamily: `${Constants.font.text}` }}>
|
||||
{creator.slates.length}{" "}
|
||||
{user.slates?.length || 0}{" "}
|
||||
<span style={{ color: `${Constants.system.darkGray}` }}>Slates</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -649,7 +576,7 @@ export default class Profile extends React.Component {
|
||||
open={this.state.visible}
|
||||
redirectURL={`/_${Strings.createQueryParams({
|
||||
scene: "NAV_PROFILE",
|
||||
user: creator.username,
|
||||
user: user.username,
|
||||
})}`}
|
||||
/>
|
||||
</div>
|
||||
@ -664,7 +591,7 @@ export default class Profile extends React.Component {
|
||||
/>
|
||||
{tab === 0 ? (
|
||||
<div>
|
||||
{this.props.mobile ? null : (
|
||||
{this.props.isMobile ? null : (
|
||||
<div style={{ display: `flex` }}>
|
||||
<SecondaryTabGroup
|
||||
tabs={[
|
||||
@ -677,12 +604,13 @@ export default class Profile extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{this.state.publicFiles.length ? (
|
||||
{publicFiles.length ? (
|
||||
<DataView
|
||||
key="scene-profile"
|
||||
onAction={this.props.onAction}
|
||||
viewer={this.props.viewer}
|
||||
isOwner={isOwner}
|
||||
items={this.state.publicFiles}
|
||||
items={publicFiles}
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
view={this.state.view}
|
||||
resources={this.props.resources}
|
||||
@ -713,7 +641,7 @@ export default class Profile extends React.Component {
|
||||
<SlatePreviewBlocks
|
||||
isOwner={this.state.slateTab === 0 ? isOwner : false}
|
||||
external={this.props.external}
|
||||
slates={slates}
|
||||
slates={slates || []}
|
||||
username={username}
|
||||
onAction={this.props.onAction}
|
||||
/>
|
||||
|
@ -204,26 +204,22 @@ const STYLES_EMPTY_SLATE_PREVIEW = css`
|
||||
|
||||
const SlatePreview = ({ slate, user }) => {
|
||||
let preview;
|
||||
for (let obj of slate.data.objects) {
|
||||
if (obj.type && Validations.isPreviewableImage(obj.type)) {
|
||||
preview = obj;
|
||||
break;
|
||||
if (slate.objects?.length) {
|
||||
for (let obj of slate.objects) {
|
||||
if (obj.type && Validations.isPreviewableImage(obj.type)) {
|
||||
preview = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!preview) {
|
||||
preview = slate.objects[0];
|
||||
}
|
||||
}
|
||||
if (!slate && slate.data.objects && slate.data.objects.length) {
|
||||
preview = slate.data.objects[0];
|
||||
}
|
||||
return (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<div css={STYLES_PREVIEW_IMAGE}>
|
||||
{preview ? (
|
||||
<SlateMediaObjectPreview
|
||||
blurhash={preview.blurhash}
|
||||
url={preview.url}
|
||||
title={preview.title || preview.name}
|
||||
type={preview.type}
|
||||
coverImage={preview.coverImage}
|
||||
/>
|
||||
<SlateMediaObjectPreview file={preview} />
|
||||
) : (
|
||||
<div css={STYLES_EMPTY_SLATE_PREVIEW}>
|
||||
<SVG.Slate height="80px" style={{ color: "#bfbfbf" }} />
|
||||
@ -233,9 +229,11 @@ const SlatePreview = ({ slate, user }) => {
|
||||
{user ? (
|
||||
<div css={STYLES_PREVIEW_TEXT}>Created by: {user.data.name || `@${user.username}`}</div>
|
||||
) : null}
|
||||
<div css={STYLES_PREVIEW_TEXT}>
|
||||
{slate.data.objects.length} File{slate.data.objects.length === 1 ? "" : "s"}
|
||||
</div>
|
||||
{slate.objects && (
|
||||
<div css={STYLES_PREVIEW_TEXT}>
|
||||
{slate.objects.length} File{slate.objects.length === 1 ? "" : "s"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -248,16 +246,10 @@ const FileEntry = ({ file }) => {
|
||||
<FileTypeIcon type={file.type} height="24px" />
|
||||
</div>
|
||||
<div css={STYLES_TEXT_ROWS}>
|
||||
<div css={STYLES_TITLE}>{file.title || file.name || file.file}</div>
|
||||
{file.file ? (
|
||||
<div css={STYLES_SUBTITLE} style={{ textTransform: "uppercase" }}>
|
||||
{Strings.getFileExtension(file.file)}
|
||||
</div>
|
||||
) : file.name ? (
|
||||
<div css={STYLES_SUBTITLE} style={{ textTransform: "uppercase" }}>
|
||||
{Strings.getFileExtension(file.name)}
|
||||
</div>
|
||||
) : null}
|
||||
<div css={STYLES_TITLE}>{file.data.name || file.filename}</div>
|
||||
<div css={STYLES_SUBTITLE} style={{ textTransform: "uppercase" }}>
|
||||
{Strings.getFileExtension(file.filename)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -268,15 +260,7 @@ const FilePreview = ({ file, slate, user, viewerId }) => {
|
||||
return (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<div css={STYLES_PREVIEW_IMAGE}>
|
||||
<SlateMediaObjectPreview
|
||||
previewPanel
|
||||
blurhash={file.blurhash}
|
||||
url={file.url ? file.url : Strings.getCIDGatewayURL(file.cid)}
|
||||
title={file.title || file.name || file.file}
|
||||
type={file.type}
|
||||
cid={file.cide}
|
||||
coverImage={file.coverImage}
|
||||
/>
|
||||
<SlateMediaObjectPreview file={file} previewPanel />
|
||||
</div>
|
||||
{user ? (
|
||||
<div css={STYLES_PREVIEW_TEXT}>Owner: {user.data.name || `@${user.username}`}</div>
|
||||
@ -520,16 +504,6 @@ export class SearchModal extends React.Component {
|
||||
slateIds.push(sub.target_slate_id);
|
||||
}
|
||||
}
|
||||
// for (let sub of this.props.viewer.trusted) {
|
||||
// if (sub.target_user_id) {
|
||||
// networkIds.push(sub.target_user_id);
|
||||
// }
|
||||
// }
|
||||
// for (let sub of this.props.viewer.pendingTrusted) {
|
||||
// if (sub.owner_user_id) {
|
||||
// networkIds.push(sub.owner_user_id);
|
||||
// }
|
||||
// }
|
||||
this.networkIds = networkIds;
|
||||
this.slateIds = slateIds;
|
||||
};
|
||||
@ -545,35 +519,21 @@ export class SearchModal extends React.Component {
|
||||
fuzzy: 0.15,
|
||||
},
|
||||
});
|
||||
let files = this.props.viewer.library[0].children.map((file, i) => {
|
||||
let files = this.props.viewer.library.map((file, i) => {
|
||||
return {
|
||||
...file,
|
||||
type: "DATA_FILE",
|
||||
id: file.id,
|
||||
name: file.title,
|
||||
title: file.file,
|
||||
data: {
|
||||
file: {
|
||||
...file,
|
||||
url: Strings.getCIDGatewayURL(file.cid),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
this.localSearch.addAll(files);
|
||||
let privateSlates = this.props.viewer.slates.filter((slate) => !slate.data.public);
|
||||
let privateSlates = this.props.viewer.slates.filter((slate) => !slate.isPublic);
|
||||
let privateFiles = [];
|
||||
for (let slate of privateSlates) {
|
||||
privateFiles.push(
|
||||
...slate.data.objects.map((file, i) => {
|
||||
...slate.objects.map((file, i) => {
|
||||
return {
|
||||
...file,
|
||||
type: "FILE",
|
||||
id: `${file.id}-${slate.id}`,
|
||||
name: file.name,
|
||||
title: file.title,
|
||||
data: {
|
||||
file,
|
||||
slate,
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
@ -581,8 +541,6 @@ export class SearchModal extends React.Component {
|
||||
privateSlates = privateSlates.map((slate) => {
|
||||
return {
|
||||
...slate,
|
||||
name: slate.slatename,
|
||||
title: slate.data.name,
|
||||
type: "SLATE",
|
||||
};
|
||||
});
|
||||
@ -663,7 +621,7 @@ export class SearchModal extends React.Component {
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
this.debounceInstance(e);
|
||||
this.debounceInstance();
|
||||
this.setState({ inputValue: e.target.value });
|
||||
};
|
||||
|
||||
@ -743,6 +701,7 @@ export class SearchModal extends React.Component {
|
||||
query: this.state.inputValue,
|
||||
type: this.state.typeFilter,
|
||||
});
|
||||
console.log(response);
|
||||
this.setState({ unfilteredResults: response.data.results });
|
||||
res = response.data.results;
|
||||
} else {
|
||||
@ -751,8 +710,8 @@ export class SearchModal extends React.Component {
|
||||
searchResults = this.processResults(res);
|
||||
for (let res of searchResults) {
|
||||
if (res.type === "USER") {
|
||||
let id = res.user.id;
|
||||
if (ids.has(id)) continue;
|
||||
let id = res.user?.id;
|
||||
if (!id || ids.has(id)) continue;
|
||||
ids.add(id);
|
||||
results.push({
|
||||
id,
|
||||
@ -762,8 +721,8 @@ export class SearchModal extends React.Component {
|
||||
preview: <UserPreview user={res.user} />,
|
||||
});
|
||||
} else if (res.type === "SLATE") {
|
||||
let id = res.user.id;
|
||||
if (ids.has(id)) continue;
|
||||
let id = res.slate?.id;
|
||||
if (!id || ids.has(id)) continue;
|
||||
ids.add(id);
|
||||
results.push({
|
||||
id,
|
||||
@ -773,8 +732,8 @@ export class SearchModal extends React.Component {
|
||||
preview: <SlatePreview slate={res.slate} user={res.user} />,
|
||||
});
|
||||
} else if (res.type === "FILE") {
|
||||
let id = res.user.id;
|
||||
if (ids.has(id)) continue;
|
||||
let id = res.file?.id;
|
||||
if (!id || ids.has(id)) continue;
|
||||
ids.add(id);
|
||||
results.push({
|
||||
id,
|
||||
@ -858,26 +817,23 @@ export class SearchModal extends React.Component {
|
||||
value: "NAV_SLATE",
|
||||
data: res.data.slate,
|
||||
});
|
||||
}
|
||||
if (res.type === "USER") {
|
||||
} else if (res.type === "USER") {
|
||||
this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: "NAV_PROFILE",
|
||||
data: res.data.user,
|
||||
});
|
||||
}
|
||||
if (res.type === "DATA_FILE") {
|
||||
} else if (res.type === "DATA_FILE" || res.data.file.ownerId === this.props.viewer.id) {
|
||||
await this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: "NAV_DATA",
|
||||
fileId: res.data.file.id,
|
||||
});
|
||||
}
|
||||
if (res.type === "FILE") {
|
||||
} else if (res.type === "FILE") {
|
||||
await this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: "NAV_SLATE",
|
||||
data: res.data.slate,
|
||||
value: "NAV_PROFILE",
|
||||
data: res.data.user,
|
||||
fileId: res.data.file.id,
|
||||
});
|
||||
}
|
||||
@ -1194,7 +1150,7 @@ export class SearchModal extends React.Component {
|
||||
paddingRight: selectedIndex === i ? "88px" : "4px",
|
||||
}}
|
||||
onClick={() => {
|
||||
selectedIndex === i || this.props.mobile
|
||||
selectedIndex === i || this.props.isMobile
|
||||
? this._handleSelect(each)
|
||||
: this.setState({ selectedIndex: i });
|
||||
}}
|
||||
|
@ -52,7 +52,7 @@ const generateLayout = (items) => {
|
||||
w: SIZE,
|
||||
h: 0,
|
||||
z: 0,
|
||||
id: item.id,
|
||||
id: item.id.replace("data-", ""),
|
||||
};
|
||||
}) || []
|
||||
);
|
||||
@ -60,7 +60,7 @@ const generateLayout = (items) => {
|
||||
|
||||
const preload = (item) =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (!item.type || !Validations.isPreviewableImage(item.type)) {
|
||||
if (!item.data.type || !Validations.isPreviewableImage(item.data.type)) {
|
||||
resolve(200);
|
||||
}
|
||||
const img = new Image();
|
||||
@ -68,7 +68,7 @@ const preload = (item) =>
|
||||
resolve((200 * img.height) / img.width);
|
||||
};
|
||||
img.onerror = reject;
|
||||
const url = item.url;
|
||||
const url = Strings.getURLfromCID(item.cid);
|
||||
img.src = url;
|
||||
});
|
||||
|
||||
@ -431,7 +431,7 @@ export class SlateLayout extends React.Component {
|
||||
let defaultLayout = layouts ? layouts.defaultLayout : this.state.defaultLayout;
|
||||
let fileNames = layouts ? layouts.fileNames : this.state.fileNames;
|
||||
let layout = layouts ? this.cloneLayout(layouts.layout) : this.cloneLayout(this.state.layout);
|
||||
let layoutIds = layout.map((pos) => pos.id);
|
||||
let layoutIds = layout.map((pos) => pos.id.replace("data-", ""));
|
||||
let repairNeeded = false;
|
||||
if (items.length !== layout.length) {
|
||||
repairNeeded = true;
|
||||
@ -444,7 +444,7 @@ export class SlateLayout extends React.Component {
|
||||
}
|
||||
}
|
||||
if (!repairNeeded && items.length === layout.length) {
|
||||
let itemIds = items.map((item) => item.id);
|
||||
let itemIds = items.map((item) => item.id.replace("data-", ""));
|
||||
for (let i = 0; i < itemIds.length; i++) {
|
||||
if (itemIds[i] !== layoutIds[i]) {
|
||||
repairNeeded = true;
|
||||
@ -457,7 +457,7 @@ export class SlateLayout extends React.Component {
|
||||
}
|
||||
let newLayout = new Array(items.length);
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let layoutIndex = layoutIds.indexOf(items[i].id);
|
||||
let layoutIndex = layoutIds.indexOf(items[i].id.replace("data-", ""));
|
||||
if (layoutIndex === -1) {
|
||||
continue;
|
||||
} else if (defaultLayout && layoutIndex >= 5 && !layout[layoutIndex].y) {
|
||||
@ -509,7 +509,7 @@ export class SlateLayout extends React.Component {
|
||||
h: height,
|
||||
w: SIZE,
|
||||
z: 0,
|
||||
id: items[i].id,
|
||||
id: items[i].id.replace("data-", ""),
|
||||
};
|
||||
h += 1;
|
||||
}
|
||||
@ -544,7 +544,7 @@ export class SlateLayout extends React.Component {
|
||||
w: SIZE,
|
||||
h: oldLayout && oldLayout.length > i ? oldLayout[i].h || height : height,
|
||||
z: 0,
|
||||
id: this.state.items[i].id,
|
||||
id: this.state.items[i].id.replace("data-", ""),
|
||||
};
|
||||
}
|
||||
return layout;
|
||||
@ -957,7 +957,7 @@ export class SlateLayout extends React.Component {
|
||||
_handleSetPreview = (e, i) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
let url = this.state.items[i].url;
|
||||
let url = Strings.getURLfromCID(this.state.items[i].cid);
|
||||
if (this.props.preview === url) return;
|
||||
this.props.onSavePreview(url);
|
||||
};
|
||||
@ -982,7 +982,7 @@ export class SlateLayout extends React.Component {
|
||||
items.push(this.state.items[i]);
|
||||
}
|
||||
}
|
||||
UserBehaviors.addToDataFromSlate({ files: items });
|
||||
UserBehaviors.saveCopy({ files: items });
|
||||
};
|
||||
|
||||
_handleAddToSlate = (e, i) => {
|
||||
@ -1009,10 +1009,10 @@ export class SlateLayout extends React.Component {
|
||||
e.preventDefault();
|
||||
let ids = [];
|
||||
if (i !== undefined) {
|
||||
ids = [this.state.items[i].id];
|
||||
ids = [this.state.items[i].id.replace("data-", "")];
|
||||
} else {
|
||||
for (let index of Object.keys(this.state.checked)) {
|
||||
ids.push(this.state.items[index].id);
|
||||
ids.push(this.state.items[index].id.replace("data-", ""));
|
||||
}
|
||||
this.setState({ checked: {} });
|
||||
}
|
||||
@ -1021,7 +1021,7 @@ export class SlateLayout extends React.Component {
|
||||
let slateId = this.props.current.id;
|
||||
for (let slate of slates) {
|
||||
if (slate.id === slateId) {
|
||||
slate.data.objects = slate.data.objects.filter((obj) => !ids.includes(obj.id));
|
||||
slate.objects = slate.objects.filter((obj) => !ids.includes(obj.id.replace("data-", "")));
|
||||
this.props.onUpdateViewer({ slates });
|
||||
break;
|
||||
}
|
||||
@ -1043,9 +1043,9 @@ export class SlateLayout extends React.Component {
|
||||
};
|
||||
|
||||
_handleDragToDesktop = (e, object) => {
|
||||
const url = Strings.getCIDGatewayURL(object.cid);
|
||||
const title = object.file || object.name;
|
||||
const type = object.type;
|
||||
const url = Strings.getURLfromCID(object.cid);
|
||||
const title = object.filename || object.data.name;
|
||||
const type = object.data.type;
|
||||
console.log(e.dataTransfer, e.dataTransfer.setData);
|
||||
e.dataTransfer.setData("DownloadURL", `${type}:${title}:${url}`);
|
||||
};
|
||||
@ -1060,32 +1060,25 @@ export class SlateLayout extends React.Component {
|
||||
e.preventDefault();
|
||||
let ids = [];
|
||||
if (i !== undefined) {
|
||||
ids = [this.state.items[i].id];
|
||||
ids = [this.state.items[i].id.replace("data-", "")];
|
||||
} else {
|
||||
for (let index of Object.keys(this.state.checked)) {
|
||||
ids.push(this.state.items[index].id);
|
||||
ids.push(this.state.items[index].id.replace("data-", ""));
|
||||
}
|
||||
}
|
||||
let cids = [];
|
||||
for (let file of this.props.viewer.library[0].children) {
|
||||
if (ids.includes(file.id)) {
|
||||
cids.push(file.cid);
|
||||
}
|
||||
}
|
||||
|
||||
let slates = this.props.viewer.slates;
|
||||
let slateId = this.props.current.id;
|
||||
for (let slate of slates) {
|
||||
if (slate.id === slateId) {
|
||||
slate.data.objects = slate.data.objects.filter(
|
||||
(obj) => !ids.includes(obj.id) && !cids.includes(obj.cid)
|
||||
slate.objects = slate.objects.filter(
|
||||
(obj) => !ids.includes(obj.id.replace("data-", "")) && !cids.includes(obj.cid)
|
||||
);
|
||||
this.props.onUpdateViewer({ slates });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await UserBehaviors.deleteFiles(cids, ids);
|
||||
await UserBehaviors.deleteFiles(ids);
|
||||
this.setState({ checked: {} });
|
||||
};
|
||||
|
||||
@ -1305,16 +1298,9 @@ export class SlateLayout extends React.Component {
|
||||
}}
|
||||
>
|
||||
<SlateMediaObjectPreview
|
||||
blurhash={this.state.items[i].blurhash}
|
||||
file={this.state.items[i]}
|
||||
iconOnly={this.state.fileNames}
|
||||
charCap={70}
|
||||
type={this.state.items[i].type}
|
||||
url={this.state.items[i].url}
|
||||
cid={this.state.items[i].cid}
|
||||
title={this.state.items[i].title || this.state.items[i].name}
|
||||
coverImage={this.state.items[i].coverImage}
|
||||
height={pos.h * unit}
|
||||
width={pos.w * unit}
|
||||
style={{
|
||||
height: pos.h * unit,
|
||||
width: pos.w * unit,
|
||||
@ -1530,7 +1516,7 @@ export class SlateLayout extends React.Component {
|
||||
: this.state.tooltip === `${i}-download`
|
||||
? "Download"
|
||||
: this.state.tooltip === `${i}-preview`
|
||||
? "Make preview image"
|
||||
? "Make cover image"
|
||||
: "Save copy"}
|
||||
</Tooltip>
|
||||
) : null}
|
||||
@ -1565,34 +1551,6 @@ export class SlateLayout extends React.Component {
|
||||
: "24px",
|
||||
}}
|
||||
>
|
||||
{/* <DynamicIcon
|
||||
onClick={(e) => {
|
||||
this._handleCopy(e, this.state.items[i].url);
|
||||
}}
|
||||
onMouseDown={this._stopProp}
|
||||
onMouseUp={this._stopProp}
|
||||
onMouseEnter={() => this.setState({ tooltip: `${i}-copy` })}
|
||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||
successState={
|
||||
<SVG.CheckBox height="16px" style={{ color: "#4b4a4d" }} />
|
||||
}
|
||||
style={{
|
||||
height: 24,
|
||||
width: 24,
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "rgba(248, 248, 248, 0.6)",
|
||||
color: "#4b4a4d",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
margin: "0 8px",
|
||||
WebkitBackdropFilter: "blur(25px)",
|
||||
backdropFilter: "blur(25px)",
|
||||
}}
|
||||
>
|
||||
<SVG.DeepLink height="16px" style={{ color: "#4b4a4d" }} />
|
||||
</DynamicIcon> */}
|
||||
<div
|
||||
css={STYLES_ICON_CIRCLE}
|
||||
onMouseDown={this._stopProp}
|
||||
@ -1619,22 +1577,27 @@ export class SlateLayout extends React.Component {
|
||||
onClick={
|
||||
this.props.external
|
||||
? this._handleLoginModal
|
||||
: this.state.items[i].type &&
|
||||
Validations.isPreviewableImage(this.state.items[i].type) &&
|
||||
this.state.items[i].size &&
|
||||
this.state.items[i].size < SIZE_LIMIT
|
||||
: this.state.items[i].data.type &&
|
||||
Validations.isPreviewableImage(
|
||||
this.state.items[i].data.type
|
||||
) &&
|
||||
this.state.items[i].data.size &&
|
||||
this.state.items[i].data.size < SIZE_LIMIT
|
||||
? (e) => this._handleSetPreview(e, i)
|
||||
: () => {}
|
||||
}
|
||||
style={
|
||||
this.props.preview === this.state.items[i].url
|
||||
this.props.preview ===
|
||||
Strings.getURLfromCID(this.state.items[i].cid)
|
||||
? {
|
||||
backgroundColor: "rgba(0, 97, 187, 0.75)",
|
||||
}
|
||||
: this.state.items[i].type &&
|
||||
Validations.isPreviewableImage(this.state.items[i].type) &&
|
||||
this.state.items[i].size &&
|
||||
this.state.items[i].size < SIZE_LIMIT
|
||||
: this.state.items[i].data.type &&
|
||||
Validations.isPreviewableImage(
|
||||
this.state.items[i].data.type
|
||||
) &&
|
||||
this.state.items[i].data.size &&
|
||||
this.state.items[i].data.size < SIZE_LIMIT
|
||||
? {}
|
||||
: {
|
||||
color: "#999999",
|
||||
@ -1643,10 +1606,7 @@ export class SlateLayout extends React.Component {
|
||||
}
|
||||
>
|
||||
{this.props.preview ===
|
||||
this.state.items[i].url.replace(
|
||||
"https://undefined",
|
||||
"https://"
|
||||
) ? (
|
||||
Strings.getURLfromCID(this.state.items[i].cid) ? (
|
||||
<SVG.DesktopEye
|
||||
height="16px"
|
||||
style={{
|
||||
@ -1687,10 +1647,10 @@ export class SlateLayout extends React.Component {
|
||||
}}
|
||||
>
|
||||
<span css={STYLES_FILE_NAME}>
|
||||
{this.state.items[i].title || this.state.items[i].name}
|
||||
{this.state.items[i].data.name || this.state.items[i].filename}
|
||||
</span>
|
||||
<span css={STYLES_FILE_TYPE}>
|
||||
{Strings.getFileExtension(this.state.items[i].file)}
|
||||
{Strings.getFileExtension(this.state.items[i].filename)}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -33,14 +33,9 @@ export class SlateLayoutMobile extends React.Component {
|
||||
onClick={() => this.props.onSelect(i)}
|
||||
>
|
||||
<SlateMediaObjectPreview
|
||||
blurhash={item.blurhash}
|
||||
file={item}
|
||||
iconOnly={this.props.fileNames}
|
||||
charCap={70}
|
||||
type={item.type}
|
||||
url={item.url}
|
||||
title={item.title || item.name}
|
||||
cid={item.cid}
|
||||
coverImage={item.coverImage}
|
||||
style={{
|
||||
height: `calc(100vw - 48px)`,
|
||||
width: `calc(100vw - 48px)`,
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as Events from "~/common/custom-events";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import UnityFrame from "~/components/core/UnityFrame";
|
||||
import FontFrame from "~/components/core/FontFrame/index.js";
|
||||
@ -68,35 +69,35 @@ const typeMap = {
|
||||
|
||||
export default class SlateMediaObject extends React.Component {
|
||||
openLink = (url) => {
|
||||
let { isMobile, data } = this.props;
|
||||
const isPDF = data.type && data.type.startsWith("application/pdf");
|
||||
|
||||
if (isPDF && isMobile) {
|
||||
window.open(url, "_blank");
|
||||
Events.dispatchCustomEvent({ name: "slate-global-close-carousel", detail: {} });
|
||||
|
||||
return;
|
||||
}
|
||||
window.open(url, "_blank");
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const url = this.props.data.url;
|
||||
this.openLink(url);
|
||||
if (this.props.isMobile) {
|
||||
const file = this.props.file;
|
||||
if (file.data.type && file.data.type.startsWith("application/pdf")) {
|
||||
const url = Strings.getURLfromCID(file.cid);
|
||||
this.openLink(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const url = this.props.data.url;
|
||||
const type = this.props.data.type ? this.props.data.type : "LEGACY_NO_TYPE";
|
||||
const { file, isMobile } = this.props;
|
||||
const url = Strings.getURLfromCID(file.cid);
|
||||
const type = file.data.type || "";
|
||||
const playType = typeMap[type] ? typeMap[type] : type;
|
||||
|
||||
let { isMobile } = this.props;
|
||||
|
||||
let element = <div css={STYLES_FAILURE}>No Preview</div>;
|
||||
|
||||
if (type.startsWith("application/pdf")) {
|
||||
return (
|
||||
<>
|
||||
{!isMobile && (
|
||||
{isMobile ? (
|
||||
<a href={url} target="_blank">
|
||||
<div css={STYLES_FAILURE}>Tap to open PDF in new tab</div>
|
||||
</a>
|
||||
) : (
|
||||
<object
|
||||
css={STYLES_OBJECT}
|
||||
style={{ width: "calc(100% - 64px)" }}
|
||||
@ -113,12 +114,11 @@ export default class SlateMediaObject extends React.Component {
|
||||
}
|
||||
|
||||
if (type.startsWith("video/")) {
|
||||
const autoPlay = this.props?.data?.settings?.autoPlay || false;
|
||||
return (
|
||||
<video
|
||||
playsInline
|
||||
controls
|
||||
autoPlay={autoPlay}
|
||||
autoPlay={false}
|
||||
name="media"
|
||||
type={playType}
|
||||
css={STYLES_OBJECT}
|
||||
@ -128,7 +128,7 @@ export default class SlateMediaObject extends React.Component {
|
||||
}}
|
||||
>
|
||||
<source src={url} type={playType} />
|
||||
{/** Note(Amine): fallback if video type isn't supported (example .mov) */}
|
||||
{/** NOTE(amine): fallback if video type isn't supported (example .mov) */}
|
||||
<source src={url} type="video/mp4" />
|
||||
</video>
|
||||
);
|
||||
@ -155,12 +155,11 @@ export default class SlateMediaObject extends React.Component {
|
||||
return <iframe src={url} css={STYLES_IFRAME} />;
|
||||
}
|
||||
|
||||
if (endsWithAny([".ttf", ".otf", ".woff", ".woff2"], this.props.data.name)) {
|
||||
if (endsWithAny([".ttf", ".otf", ".woff", ".woff2"], file.filename)) {
|
||||
return (
|
||||
<FontFrame
|
||||
name={this.props.data.file || this.props.data.name}
|
||||
cid={this.props.data.cid}
|
||||
url={url}
|
||||
name={file.data.name || file.filename}
|
||||
cid={file.cid}
|
||||
fallback={element}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@ -169,8 +168,8 @@ export default class SlateMediaObject extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.data.name.endsWith(".md") || type.startsWith("text/plain")) {
|
||||
return <MarkdownFrame date={this.props.data.date} url={this.props.data.url} />;
|
||||
if (file.filename.endsWith(".md") || type.startsWith("text/plain")) {
|
||||
return <MarkdownFrame date={file.data.date} url={url} />;
|
||||
}
|
||||
|
||||
if (Validations.isPreviewableImage(type)) {
|
||||
@ -189,17 +188,9 @@ export default class SlateMediaObject extends React.Component {
|
||||
|
||||
// TODO(jim): We will need to revisit this later.
|
||||
if (type.startsWith("application/unity")) {
|
||||
const unityGameConfig = this.props.data.unityGameConfig;
|
||||
const unityGameLoader = this.props.data.unityGameLoader;
|
||||
const { config, loader } = file.data.unity;
|
||||
|
||||
return (
|
||||
<UnityFrame
|
||||
url={url}
|
||||
unityGameConfig={unityGameConfig}
|
||||
unityGameLoader={unityGameLoader}
|
||||
key={url}
|
||||
/>
|
||||
);
|
||||
return <UnityFrame url={url} unityGameConfig={config} unityGameLoader={loader} key={url} />;
|
||||
}
|
||||
|
||||
return element;
|
||||
|
@ -69,7 +69,7 @@ const STYLES_BLUR_CONTAINER = css`
|
||||
|
||||
export default class SlateMediaObjectPreview extends React.Component {
|
||||
static defaultProps = {
|
||||
charCap: 30,
|
||||
charCap: 70,
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -78,39 +78,48 @@ export default class SlateMediaObjectPreview extends React.Component {
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
if (this.props.type && Validations.isPreviewableImage(this.props.type)) {
|
||||
this.loadImage(this.props.url);
|
||||
} else if (this.props.coverImage) {
|
||||
this.loadImage(this.props.coverImage.url);
|
||||
}
|
||||
this.setImage();
|
||||
};
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
if (prevProps.coverImage?.url !== this.props.coverImage?.url && this.props.coverImage?.url) {
|
||||
this.loadImage(this.props.coverImage.url);
|
||||
if (prevProps.coverImage?.cid !== this.props.coverImage?.cid) {
|
||||
this.setImage();
|
||||
}
|
||||
};
|
||||
|
||||
loadImage = async (url) => {
|
||||
const img = new Image();
|
||||
img.onload = () => this.setState({ showImage: true });
|
||||
img.src = url;
|
||||
setImage = () => {
|
||||
let type = this.props.file.data.type;
|
||||
let coverImage = this.props.file.data.coverImage;
|
||||
let url;
|
||||
if (type && Validations.isPreviewableImage(type)) {
|
||||
url = Strings.getURLfromCID(this.props.file.cid);
|
||||
} else if (coverImage) {
|
||||
url = Strings.getURLfromCID(coverImage.cid);
|
||||
}
|
||||
if (url) {
|
||||
const img = new Image();
|
||||
img.onload = () => this.setState({ showImage: true });
|
||||
img.src = url;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const file = this.props.file;
|
||||
const type = this.props.file.data.type;
|
||||
const coverImage = this.props.file.data.coverImage;
|
||||
let url;
|
||||
if (this.props.type && Validations.isPreviewableImage(this.props.type)) {
|
||||
url = this.props.url;
|
||||
} else if (this.props.coverImage) {
|
||||
url = this.props.coverImage.url;
|
||||
if (type && Validations.isPreviewableImage(type)) {
|
||||
url = Strings.getURLfromCID(this.props.file.cid);
|
||||
} else if (coverImage) {
|
||||
url = Strings.getURLfromCID(coverImage.cid);
|
||||
}
|
||||
|
||||
if (url) {
|
||||
let blurhash =
|
||||
this.props.blurhash && isBlurhashValid(this.props.blurhash)
|
||||
? this.props.blurhash
|
||||
: this.props.coverImage?.blurhash && isBlurhashValid(this.props.coverImage?.blurhash)
|
||||
? this.props.coverImage?.blurhash
|
||||
const blurhash =
|
||||
file.data.blurhash && isBlurhashValid(file.data.blurhash)
|
||||
? file.data.blurhash
|
||||
: coverImage?.data.blurhash && isBlurhashValid(coverImage?.data.blurhash)
|
||||
? coverImage?.data.blurhash
|
||||
: null;
|
||||
if (this.state.error) {
|
||||
return (
|
||||
@ -177,23 +186,20 @@ export default class SlateMediaObjectPreview extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
const title = this.props.title;
|
||||
// this.props.title && this.props.title.length > this.props.charCap
|
||||
// ? this.props.title.substring(0, this.props.charCap) + "..."
|
||||
// : this.props.title;
|
||||
let extension = Strings.getFileExtension(this.props.title);
|
||||
let name = (file.data.name || file.filename).substring(0, this.charCap);
|
||||
let extension = Strings.getFileExtension(file.filename);
|
||||
if (extension && extension.length) {
|
||||
extension = extension.toUpperCase();
|
||||
}
|
||||
let element = (
|
||||
<FileTypeIcon
|
||||
type={this.props.type}
|
||||
type={file.data.type}
|
||||
height={this.props.previewPanel ? "26px" : "20px"}
|
||||
style={{ color: Constants.system.textGray }}
|
||||
/>
|
||||
);
|
||||
|
||||
if (endsWithAny([".ttf", ".otf", ".woff", ".woff2"], this.props.title)) {
|
||||
if (endsWithAny([".ttf", ".otf", ".woff", ".woff2"], file.filename)) {
|
||||
return (
|
||||
<article
|
||||
css={STYLES_ENTITY}
|
||||
@ -253,9 +259,9 @@ export default class SlateMediaObjectPreview extends React.Component {
|
||||
/>
|
||||
<div style={{ position: "absolute" }}>{element}</div>
|
||||
</div>
|
||||
{this.props.title && !this.props.iconOnly && !this.props.previewPanel ? (
|
||||
{!this.props.iconOnly && !this.props.previewPanel ? (
|
||||
<div style={{ position: "absolute", bottom: 16, left: 16, width: "inherit" }}>
|
||||
<div css={STYLES_TITLE}>{title}</div>
|
||||
<div css={STYLES_TITLE}>{name}</div>
|
||||
{extension ? (
|
||||
<div
|
||||
css={STYLES_TITLE}
|
||||
|
@ -173,7 +173,7 @@ export class SlatePicker extends React.Component {
|
||||
{Strings.getPresentationSlateName(slate)}
|
||||
</div>
|
||||
</div>
|
||||
{slate.data.public ? (
|
||||
{slate.isPublic ? (
|
||||
<div style={{ flexShrink: 0, marginLeft: 16 }}>
|
||||
<SVG.Globe height="24px" />
|
||||
</div>
|
||||
|
@ -48,9 +48,10 @@ const STYLES_PLACEHOLDER = css`
|
||||
export class SlatePreviewRow extends React.Component {
|
||||
render() {
|
||||
let numItems = this.props.numItems || 4;
|
||||
let objects;
|
||||
if (this.props.slate.data.objects.length === 0) {
|
||||
objects = [
|
||||
let objects = this.props.slate.objects;
|
||||
let components;
|
||||
if (objects.length === 0) {
|
||||
components = [
|
||||
<div
|
||||
css={STYLES_PLACEHOLDER}
|
||||
style={{
|
||||
@ -61,29 +62,22 @@ export class SlatePreviewRow extends React.Component {
|
||||
];
|
||||
} else {
|
||||
let trimmed =
|
||||
this.props.slate.data.objects.length > numItems
|
||||
? this.props.slate.data.objects.slice(1, numItems)
|
||||
: this.props.slate.data.objects.slice(1, this.props.slate.data.objects.length);
|
||||
objects = trimmed.map((each) => (
|
||||
objects.length > numItems ? objects.slice(1, numItems) : objects.slice(1, objects.length);
|
||||
components = trimmed.map((each) => (
|
||||
<div key={each.id} css={STYLES_ITEM_BOX}>
|
||||
<SlateMediaObjectPreview
|
||||
blurhash={each.blurhash}
|
||||
file={each}
|
||||
charCap={30}
|
||||
centeredImage
|
||||
type={each.type}
|
||||
url={each.url}
|
||||
style={this.props.previewStyle}
|
||||
cid={each.cid}
|
||||
title={each.title || each.name}
|
||||
iconOnly={this.props.small}
|
||||
coverImage={each.coverImage}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<div css={STYLES_IMAGE_ROW} style={{ height: `100%`, ...this.props.containerStyle }}>
|
||||
{objects}
|
||||
{components}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -251,11 +245,11 @@ export class SlatePreviewBlock extends React.Component {
|
||||
|
||||
render() {
|
||||
let count = 0;
|
||||
const { objects } = this.props.slate.data;
|
||||
if (objects.length >= 4) {
|
||||
const set = this.props.slate.data.objects.slice(0, 4);
|
||||
const objects = this.props.slate.objects || [];
|
||||
if (objects && objects.length >= 4) {
|
||||
const set = objects.slice(0, 4);
|
||||
for (let object of set) {
|
||||
if (object.type.startsWith("image/") && !object.type.startsWith("image/svg")) {
|
||||
if (object.data.type.startsWith("image/") && !object.data.type.startsWith("image/svg")) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@ -299,9 +293,9 @@ export class SlatePreviewBlock extends React.Component {
|
||||
<div css={STYLES_TITLE_LINE}>
|
||||
<div css={STYLES_TITLE} style={{ width: "85%" }}>
|
||||
{this.props.slate.data.name}
|
||||
{this.props.isOwner && !this.props.isPublic && (
|
||||
<span style={{ marginLeft: 12, position: "relative", top: 2 }}>
|
||||
<SVG.SecurityLock height="20px" style={{ color: Constants.system.darkGray }} />
|
||||
{this.props.isOwner && !this.props.slate.isPublic && (
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
<SVG.SecurityLock height="20px" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@ -335,15 +329,7 @@ export class SlatePreviewBlock extends React.Component {
|
||||
height: 320,
|
||||
}}
|
||||
>
|
||||
<SlateMediaObjectPreview
|
||||
blurhash={first.blurhash}
|
||||
centeredImage
|
||||
charCap={30}
|
||||
type={first.type}
|
||||
url={first.url}
|
||||
title={first.title || first.name}
|
||||
coverImage={first.coverImage}
|
||||
/>
|
||||
<SlateMediaObjectPreview file={first} centeredImage charCap={30} />
|
||||
</div>
|
||||
) : first ? (
|
||||
<div css={STYLES_PREVIEW}>
|
||||
@ -353,16 +339,7 @@ export class SlatePreviewBlock extends React.Component {
|
||||
height: 320,
|
||||
}}
|
||||
>
|
||||
<SlateMediaObjectPreview
|
||||
blurhash={first.blurhash}
|
||||
centeredImage
|
||||
charCap={30}
|
||||
type={first.type}
|
||||
url={first.url}
|
||||
cid={first.cid}
|
||||
title={first.title || first.name}
|
||||
coverImage={first.coverImage}
|
||||
/>
|
||||
<SlateMediaObjectPreview file={first} centeredImage charCap={30} />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@ -386,11 +363,9 @@ export class SlatePreviewBlock extends React.Component {
|
||||
<span css={STYLES_MOBILE_ONLY}>
|
||||
<div css={STYLES_TITLE_LINE}>
|
||||
<div css={STYLES_TITLE}>{this.props.slate.data.name}</div>
|
||||
{this.props.isOwner && (
|
||||
{this.props.isOwner && !this.props.slate.isPublic && (
|
||||
<div style={{ color: Constants.system.darkGray, margin: `2px 0 0 0` }}>
|
||||
{this.props.isPublic ? null : (
|
||||
<SVG.SecurityLock height="20px" style={{ color: Constants.system.darkGray }} />
|
||||
)}
|
||||
<SVG.SecurityLock height="20px" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -409,13 +384,10 @@ export class SlatePreviewBlock extends React.Component {
|
||||
>
|
||||
{first ? (
|
||||
<SlateMediaObjectPreview
|
||||
file={first}
|
||||
blurhash={first.blurhash}
|
||||
centeredImage
|
||||
charCap={30}
|
||||
type={first.type}
|
||||
url={first.url}
|
||||
title={first.title || first.name}
|
||||
coverImage={first.coverImage}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@ -469,7 +441,6 @@ export default class SlatePreviewBlocks extends React.Component {
|
||||
username={this.props.username}
|
||||
slate={slate}
|
||||
external={this.props.external}
|
||||
isPublic={slate.data.public}
|
||||
/>
|
||||
</a>
|
||||
))
|
||||
@ -489,7 +460,6 @@ export default class SlatePreviewBlocks extends React.Component {
|
||||
username={this.props.username}
|
||||
slate={slate}
|
||||
external={this.props.external}
|
||||
isPublic={slate.data.public}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
@ -30,8 +30,8 @@ const STYLES_BUTTON = css`
|
||||
}
|
||||
`;
|
||||
|
||||
export const CircleButtonGray = (props) => {
|
||||
export const SquareButtonGray = (props) => {
|
||||
return <span css={STYLES_BUTTON} {...props} />;
|
||||
};
|
||||
|
||||
export default CircleButtonGray;
|
||||
export default SquareButtonGray;
|
@ -25,8 +25,8 @@ const STYLES_BUTTON = css`
|
||||
}
|
||||
`;
|
||||
|
||||
export const CircleButtonLight = (props) => {
|
||||
export const SquareButtonLight = (props) => {
|
||||
return <span css={STYLES_BUTTON} {...props} />;
|
||||
};
|
||||
|
||||
export default CircleButtonLight;
|
||||
export default SquareButtonLight;
|
@ -61,7 +61,6 @@ export default class SidebarAddFileToSlate extends React.Component {
|
||||
await UserBehaviors.addToSlate({
|
||||
slate,
|
||||
files: this.props.sidebarData.files,
|
||||
fromSlate: this.props.sidebarData.fromSlate,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -10,8 +10,6 @@ import * as SVG from "~/common/svg";
|
||||
import { RadioGroup } from "~/components/system/components/RadioGroup";
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
const SLATE_LIMIT = 50;
|
||||
|
||||
const STYLES_TEXT = css`
|
||||
color: ${Constants.system.textGray};
|
||||
font-size: ${Constants.typescale.lvl0};
|
||||
@ -33,7 +31,7 @@ const STYLES_GROUPING = css`
|
||||
export default class SidebarCreateSlate extends React.Component {
|
||||
state = {
|
||||
name: "",
|
||||
public: true,
|
||||
isPublic: true,
|
||||
body: "",
|
||||
tags: [],
|
||||
suggestions: this.props.viewer?.tags || [],
|
||||
@ -41,11 +39,6 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
};
|
||||
|
||||
_handleSubmit = async () => {
|
||||
if (this.props.viewer.slates.length >= SLATE_LIMIT) {
|
||||
Events.dispatchMessage({ message: `You have reached the limit of ${SLATE_LIMIT} Slates!` });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
if (!Validations.slatename(this.state.name)) {
|
||||
@ -56,7 +49,7 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
|
||||
const response = await Actions.createSlate({
|
||||
name: this.state.name,
|
||||
public: this.state.public,
|
||||
isPublic: this.state.isPublic,
|
||||
body: this.state.body,
|
||||
tags: this.state.tags,
|
||||
});
|
||||
@ -67,13 +60,9 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
}
|
||||
|
||||
if (this.props.sidebarData && this.props.sidebarData.files) {
|
||||
let data = this.props.sidebarData.files.map((file) => {
|
||||
return { title: file.title || file.name, ...file };
|
||||
});
|
||||
const addResponse = await Actions.addFileToSlate({
|
||||
slate: response.slate,
|
||||
data,
|
||||
fromSlate: this.props.sidebarData.fromSlate,
|
||||
files: this.props.sidebarData.files,
|
||||
});
|
||||
|
||||
if (Events.hasError(addResponse)) {
|
||||
@ -213,7 +202,7 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
on the internet. If you make it private, only you will be able to see it.
|
||||
</System.P>
|
||||
<RadioGroup
|
||||
name="public"
|
||||
name="isPublic"
|
||||
options={[
|
||||
{
|
||||
value: true,
|
||||
@ -236,7 +225,7 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
]}
|
||||
style={{ marginTop: 12 }}
|
||||
labelStyle={{ fontFamily: Constants.font.medium }}
|
||||
selected={this.state.public}
|
||||
selected={this.state.isPublic}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
@ -77,7 +77,7 @@ export default class SidebarEditTags extends React.Component {
|
||||
return { id: this.props.viewer.id, data: objects[checkedIndex] };
|
||||
});
|
||||
|
||||
const response = await Actions.updateData(update);
|
||||
const response = await Actions.updateFile(update);
|
||||
Events.hasError(response);
|
||||
};
|
||||
|
||||
|
@ -41,7 +41,7 @@ export default class SidebarFileStorageDeal extends React.Component {
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
if (!this.props.viewer.settings_deals_auto_approve) {
|
||||
if (!this.props.viewer.settings?.settings_deals_auto_approve) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ const STYLES_GROUPING = css`
|
||||
export default class SidebarSingleSlateSettings extends React.Component {
|
||||
state = {
|
||||
slatename: this.props.data.slatename,
|
||||
public: this.props.data.data.public,
|
||||
isPublic: this.props.data.isPublic,
|
||||
body: this.props.data.data.body,
|
||||
name: this.props.data.data.name,
|
||||
tags: this.props.data.data?.tags || [],
|
||||
@ -68,7 +68,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
for (let slate of slates) {
|
||||
if (slate.id === this.props.data.id) {
|
||||
slate.data.name = this.state.name;
|
||||
slate.data.public = this.state.public;
|
||||
slate.isPublic = this.state.isPublic;
|
||||
slate.data.body = this.state.body;
|
||||
slate.data.tags = this.state.tags;
|
||||
|
||||
@ -82,9 +82,9 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
this.props.onCancel();
|
||||
const response = await Actions.updateSlate({
|
||||
id: this.props.data.id,
|
||||
isPublic: this.state.isPublic,
|
||||
data: {
|
||||
name: this.state.name,
|
||||
public: this.state.public,
|
||||
body: this.state.body,
|
||||
tags: this.state.tags,
|
||||
},
|
||||
@ -100,6 +100,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
console.log("inside single slate settings toggle privacy");
|
||||
this.setState({ [e.target.name]: e.target.value });
|
||||
|
||||
if (e.target.name === "tags") {
|
||||
@ -135,14 +136,14 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
const url = `/${this.props.viewer.username}/${slug}`;
|
||||
let preview = this.props.data.data.preview;
|
||||
if (!preview) {
|
||||
for (let object of this.props.data.data.objects) {
|
||||
for (let object of this.props.data.objects) {
|
||||
if (
|
||||
object.type &&
|
||||
Validations.isPreviewableImage(object.type) &&
|
||||
object.size &&
|
||||
object.size < SIZE_LIMIT
|
||||
object.data.type &&
|
||||
Validations.isPreviewableImage(object.data.type) &&
|
||||
object.data.size &&
|
||||
object.data.size < SIZE_LIMIT
|
||||
) {
|
||||
preview = object.url;
|
||||
preview = Strings.getURLfromCID(object.cid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -228,7 +229,6 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
</System.P>
|
||||
<System.Tag
|
||||
name="tags"
|
||||
placeholder={`Edit tags for ${this.state.slatename}`}
|
||||
tags={this.state.tags}
|
||||
suggestions={this.state.suggestions}
|
||||
style={{ marginTop: 12 }}
|
||||
@ -236,24 +236,23 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{this.state.public ? (
|
||||
<div css={STYLES_GROUPING}>
|
||||
<System.P css={STYLES_HEADER}>Preview image</System.P>
|
||||
<div css={STYLES_GROUPING}>
|
||||
<System.P css={STYLES_HEADER}>Cover image</System.P>
|
||||
|
||||
<System.P
|
||||
css={STYLES_TEXT}
|
||||
style={{
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
This is the image that shows when you share a link to your slate.
|
||||
</System.P>
|
||||
<System.P
|
||||
css={STYLES_TEXT}
|
||||
style={{
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
This is the cover image for your slate. You can select a different cover image using the
|
||||
"Make cover image" button.
|
||||
</System.P>
|
||||
|
||||
<div css={STYLES_IMAGE_BOX} style={{ marginTop: 24 }}>
|
||||
<img src={preview} alt="" style={{ maxWidth: "368px", maxHeight: "368px" }} />
|
||||
</div>
|
||||
<div css={STYLES_IMAGE_BOX} style={{ marginTop: 24 }}>
|
||||
<img src={preview} alt="" style={{ maxWidth: "368px", maxHeight: "368px" }} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div css={STYLES_GROUPING}>
|
||||
<System.P css={STYLES_HEADER} style={{ marginBottom: 12 }}>
|
||||
@ -269,7 +268,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
on the internet. If you make it private, only you will be able to see it.
|
||||
</System.P>
|
||||
<RadioGroup
|
||||
name="public"
|
||||
name="isPublic"
|
||||
options={[
|
||||
{
|
||||
value: true,
|
||||
@ -292,7 +291,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
]}
|
||||
style={{ marginTop: 12 }}
|
||||
labelStyle={{ fontFamily: Constants.font.medium }}
|
||||
selected={this.state.public}
|
||||
selected={this.state.isPublic}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
@ -8,8 +8,7 @@ import * as Events from "~/common/custom-events";
|
||||
import { css } from "@emotion/react";
|
||||
import { Alert } from "~/components/core/Alert";
|
||||
|
||||
import CarouselSidebarSlate from "~/components/core/CarouselSidebarSlate";
|
||||
import CarouselSidebarData from "~/components/core/CarouselSidebarData";
|
||||
import CarouselSidebar from "~/components/core/CarouselSidebar";
|
||||
import SlateMediaObject from "~/components/core/SlateMediaObject";
|
||||
|
||||
const STYLES_ROOT = css`
|
||||
@ -27,7 +26,9 @@ const STYLES_ROOT = css`
|
||||
z-index: ${Constants.zindex.modal};
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
|
||||
// Note(Amine): we're using the blur filter to fix a weird backdrop-filter's bug in chrome
|
||||
${
|
||||
"" /* Note(Amine): we're using the blur filter to fix a weird backdrop-filter's bug in chrome */
|
||||
}
|
||||
filter: blur(0px);
|
||||
@supports ((-webkit-backdrop-filter: blur(15px)) or (backdrop-filter: blur(15px))) {
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
@ -134,8 +135,8 @@ export class GlobalCarousel extends React.Component {
|
||||
|
||||
componentDidUpdate = (prevProps) => {
|
||||
if (
|
||||
!this.props.objects?.length ||
|
||||
this.state.index < 0 ||
|
||||
!this.props.objects ||
|
||||
this.props.objects.length == 0 ||
|
||||
this.state.index >= this.props.objects.length
|
||||
) {
|
||||
this._handleClose();
|
||||
@ -144,15 +145,15 @@ export class GlobalCarousel extends React.Component {
|
||||
|
||||
_handleKeyDown = (e) => {
|
||||
const inputs = document.querySelectorAll("input");
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
if (document.activeElement === inputs[i]) {
|
||||
for (let elem of inputs) {
|
||||
if (document.activeElement === elem) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const textareas = document.querySelectorAll("textarea");
|
||||
for (let i = 0; i < textareas.length; i++) {
|
||||
if (document.activeElement === textareas[i]) {
|
||||
for (let elem of textareas) {
|
||||
if (document.activeElement === elem) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -172,69 +173,55 @@ export class GlobalCarousel extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
setWindowState = (data) => {
|
||||
setWindowState = (data = {}) => {
|
||||
const cid = data?.cid;
|
||||
if (this.props.carouselType === "ACTIVITY") {
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: data && data.cid },
|
||||
{ ...window.history.state, cid: cid },
|
||||
null,
|
||||
data && data.cid
|
||||
? `/${data.owner}/${data.slate.slatename}/cid:${data.cid}`
|
||||
: `/_?scene=NAV_ACTIVITY`
|
||||
cid ? `/${data.owner}/${data.slate.slatename}/cid:${cid}` : `/_?scene=NAV_ACTIVITY`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let baseURL = window.location.pathname.split("/");
|
||||
|
||||
if (this.props.carouselType === "SLATE") {
|
||||
baseURL.length = 3;
|
||||
baseURL = baseURL.join("/");
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: data && data.cid },
|
||||
null,
|
||||
data && data.cid ? `${baseURL}/cid:${data.cid}` : baseURL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.carouselType === "PROFILE") {
|
||||
} else if (this.props.carouselType === "PROFILE") {
|
||||
baseURL.length = 2;
|
||||
baseURL = baseURL.join("/");
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: data && data.cid },
|
||||
null,
|
||||
data && data.cid ? `${baseURL}/cid:${data.cid}` : baseURL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.carouselType === "DATA") {
|
||||
} else if (this.props.carouselType === "DATA") {
|
||||
baseURL.length = 2;
|
||||
if (data && data.cid) {
|
||||
if (cid) {
|
||||
baseURL[1] = this.props.viewer.username;
|
||||
} else {
|
||||
baseURL[1] = "_";
|
||||
baseURL[1] = "_?scene=NAV_DATA";
|
||||
}
|
||||
baseURL = baseURL.join("/");
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: data && data.cid },
|
||||
null,
|
||||
data && data.cid ? `${baseURL}/cid:${data.cid}` : baseURL
|
||||
);
|
||||
return;
|
||||
}
|
||||
baseURL = baseURL.join("/");
|
||||
|
||||
window.history.replaceState(
|
||||
{ ...window.history.state, cid: cid },
|
||||
null,
|
||||
cid ? `${baseURL}/cid:${cid}` : baseURL
|
||||
);
|
||||
};
|
||||
|
||||
_handleOpen = (e) => {
|
||||
if (e.detail.index < 0 || e.detail.index >= this.props.objects.length) {
|
||||
let index = e.detail.index;
|
||||
const objects = this.props.objects;
|
||||
if (e.detail.index === null) {
|
||||
if (e.detail.id !== null) {
|
||||
index = objects.findIndex((obj) => obj.id === e.detail.id);
|
||||
}
|
||||
}
|
||||
if (index === null || index < 0 || index >= objects.length) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
visible: true,
|
||||
index: e.detail.index || 0,
|
||||
index: e.detail.index,
|
||||
});
|
||||
const data = this.props.objects[e.detail.index];
|
||||
const data = objects[e.detail.index];
|
||||
this.setWindowState(data);
|
||||
};
|
||||
|
||||
@ -245,15 +232,17 @@ export class GlobalCarousel extends React.Component {
|
||||
e.preventDefault();
|
||||
}
|
||||
this.setState({ visible: false, index: 0 });
|
||||
|
||||
this.setWindowState();
|
||||
}
|
||||
};
|
||||
|
||||
_handleNext = () => {
|
||||
_handleNext = (e) => {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
let index = this.state.index + 1;
|
||||
if (index >= this.props.objects.length) {
|
||||
index = 0;
|
||||
return;
|
||||
}
|
||||
this.setState({ index });
|
||||
|
||||
@ -261,10 +250,13 @@ export class GlobalCarousel extends React.Component {
|
||||
this.setWindowState(data);
|
||||
};
|
||||
|
||||
_handlePrevious = () => {
|
||||
_handlePrevious = (e) => {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
let index = this.state.index - 1;
|
||||
if (index < 0) {
|
||||
index = this.props.objects.length - 1;
|
||||
return;
|
||||
}
|
||||
this.setState({ index });
|
||||
|
||||
@ -272,51 +264,40 @@ export class GlobalCarousel extends React.Component {
|
||||
this.setWindowState(data);
|
||||
};
|
||||
|
||||
_handleSave = async (details, index) => {
|
||||
let objects = this.props.objects;
|
||||
if (!this.props.isOwner || this.props.external) return;
|
||||
if (this.props.carouselType === "SLATE") {
|
||||
objects[index] = { ...objects[index], ...details };
|
||||
const response = await Actions.updateSlate({
|
||||
id: this.props.current.id,
|
||||
data: { objects },
|
||||
});
|
||||
Events.hasError(response);
|
||||
}
|
||||
if (this.props.carouselType === "DATA") {
|
||||
objects[index] = { ...objects[index], ...details };
|
||||
const response = await Actions.updateData({
|
||||
id: this.props.viewer.id,
|
||||
data: objects[index],
|
||||
});
|
||||
Events.hasError(response);
|
||||
_handleToggleSidebar = (e) => {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
this.setState({ showSidebar: !this.state.showSidebar });
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.visible || !this.props.carouselType || this.state.index < 0) {
|
||||
if (
|
||||
!this.state.visible ||
|
||||
!this.props.carouselType ||
|
||||
this.state.index < 0 ||
|
||||
this.state.index >= this.props.objects.length
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
let data = this.props.objects[this.state.index];
|
||||
let isOwner = this.props.isOwner;
|
||||
let isRepost;
|
||||
let { isMobile, isOwner } = this.props;
|
||||
|
||||
let isRepost = false;
|
||||
if (this.props.carouselType === "SLATE") {
|
||||
isRepost = this.props.external ? false : this.props.current.data.ownerId !== data.ownerId;
|
||||
} else if (this.props.carouselType === "DATA" || this.props.carouselType === "PROFILE") {
|
||||
data.url = Strings.getCIDGatewayURL(data.cid);
|
||||
isRepost = this.props.current?.ownerId !== data.ownerId;
|
||||
}
|
||||
|
||||
let { mobile } = this.props;
|
||||
let slide = <SlateMediaObject data={data} isMobile={mobile} />;
|
||||
let slide = <SlateMediaObject file={data} isMobile={isMobile} />;
|
||||
|
||||
return (
|
||||
<div css={STYLES_ROOT}>
|
||||
<Alert
|
||||
noWarning
|
||||
id={this.props.mobile ? "slate-mobile-alert" : null}
|
||||
id={isMobile ? "slate-mobile-alert" : null}
|
||||
style={
|
||||
this.props.mobile
|
||||
? {}
|
||||
isMobile
|
||||
? null
|
||||
: {
|
||||
bottom: 0,
|
||||
top: "auto",
|
||||
@ -327,26 +308,24 @@ export class GlobalCarousel extends React.Component {
|
||||
}
|
||||
/>
|
||||
<div css={STYLES_ROOT_CONTENT} style={this.props.style} onClick={this._handleClose}>
|
||||
<span
|
||||
css={STYLES_BOX}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
this._handlePrevious(e);
|
||||
}}
|
||||
style={{ top: 0, left: 16, bottom: 0 }}
|
||||
>
|
||||
<SVG.ChevronLeft height="20px" />
|
||||
</span>
|
||||
<span
|
||||
css={STYLES_BOX}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
this._handleNext(e);
|
||||
}}
|
||||
style={{ top: 0, right: 16, bottom: 0 }}
|
||||
>
|
||||
<SVG.ChevronRight height="20px" />
|
||||
</span>
|
||||
{this.state.index > 0 && (
|
||||
<span
|
||||
css={STYLES_BOX}
|
||||
onClick={this._handlePrevious}
|
||||
style={{ top: 0, left: 16, bottom: 0 }}
|
||||
>
|
||||
<SVG.ChevronLeft height="20px" />
|
||||
</span>
|
||||
)}
|
||||
{this.state.index < this.props.objects.length - 1 && (
|
||||
<span
|
||||
css={STYLES_BOX}
|
||||
onClick={this._handleNext}
|
||||
style={{ top: 0, right: 16, bottom: 0 }}
|
||||
>
|
||||
<SVG.ChevronRight height="20px" />
|
||||
</span>
|
||||
)}
|
||||
{slide}
|
||||
<span css={STYLES_MOBILE_ONLY}>
|
||||
<div css={STYLES_DISMISS_BOX} onClick={this._handleClose}>
|
||||
@ -355,24 +334,12 @@ export class GlobalCarousel extends React.Component {
|
||||
</span>
|
||||
<span css={STYLES_MOBILE_HIDDEN}>
|
||||
{this.state.showSidebar ? (
|
||||
<div
|
||||
css={STYLES_EXPANDER}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ showSidebar: !this.state.showSidebar });
|
||||
}}
|
||||
>
|
||||
<div css={STYLES_EXPANDER} onClick={this._handleToggleSidebar}>
|
||||
<SVG.Maximize height="24px" />
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<div
|
||||
css={STYLES_EXPANDER}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ showSidebar: !this.state.showSidebar });
|
||||
}}
|
||||
>
|
||||
<div css={STYLES_EXPANDER} onClick={this._handleToggleSidebar}>
|
||||
<SVG.Minimize height="24px" />
|
||||
</div>
|
||||
<div css={STYLES_DISMISS_BOX} onClick={this._handleClose}>
|
||||
@ -383,42 +350,14 @@ export class GlobalCarousel extends React.Component {
|
||||
</span>
|
||||
</div>
|
||||
<span css={STYLES_MOBILE_HIDDEN}>
|
||||
{this.props.carouselType === "DATA" || this.props.carouselType === "PROFILE" ? (
|
||||
<CarouselSidebarData
|
||||
viewer={this.props.viewer}
|
||||
display={this.state.showSidebar ? "block" : "none"}
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
onClose={this._handleClose}
|
||||
key={data.id}
|
||||
slates={this.props.viewer?.slates || []}
|
||||
onAction={this.props.onAction}
|
||||
resources={this.props.resources}
|
||||
data={data}
|
||||
cid={data.cid}
|
||||
onSave={this._handleSave}
|
||||
external={this.props.external}
|
||||
isOwner={isOwner}
|
||||
index={this.state.index}
|
||||
/>
|
||||
) : (
|
||||
<CarouselSidebarSlate
|
||||
activityView={this.props.carouselType === "ACTIVITY"}
|
||||
display={this.state.showSidebar ? "block" : "none"}
|
||||
viewer={this.props.viewer}
|
||||
onUpdateViewer={this.props.onUpdateViewer}
|
||||
current={this.props.current}
|
||||
key={data.id}
|
||||
slates={this.props.viewer?.slates || []}
|
||||
onClose={this._handleClose}
|
||||
onAction={this.props.onAction}
|
||||
data={data}
|
||||
external={this.props.external}
|
||||
onSave={this._handleSave}
|
||||
isOwner={isOwner}
|
||||
isRepost={isRepost}
|
||||
index={this.state.index}
|
||||
/>
|
||||
)}
|
||||
<CarouselSidebar
|
||||
key={data.id}
|
||||
{...this.props}
|
||||
data={data}
|
||||
display={this.state.showSidebar ? "block" : "none"}
|
||||
onClose={this._handleClose}
|
||||
isRepost={isRepost}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
@ -102,14 +102,25 @@ export class RadioGroup extends React.Component {
|
||||
return (
|
||||
<label
|
||||
css={STYLES_RADIO}
|
||||
style={this.props.containerStyle}
|
||||
style={{
|
||||
...this.props.containerStyle,
|
||||
border: this.props.dark ? "1px solid #3c3c3c" : "inherit",
|
||||
}}
|
||||
key={`radio-${radio.value}`}
|
||||
>
|
||||
<span css={STYLES_RADIO_LABEL} style={this.props.labelStyle}>
|
||||
{radio.label}
|
||||
</span>
|
||||
<span css={STYLES_RADIO_CUSTOM}>
|
||||
<span css={STYLES_RADIO_CUSTOM_SELECTED} style={{ opacity: checked ? 1 : 0 }} />
|
||||
<span
|
||||
css={STYLES_RADIO_CUSTOM}
|
||||
style={this.props.dark ? { backgroundColor: Constants.system.grayBlack } : null}
|
||||
>
|
||||
<span
|
||||
css={STYLES_RADIO_CUSTOM_SELECTED}
|
||||
style={{
|
||||
opacity: checked ? 1 : 0,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<input
|
||||
css={STYLES_RADIO_INPUT}
|
||||
|
@ -231,12 +231,6 @@ const STYLES_INPUT = css`
|
||||
background: ${Constants.system.white};
|
||||
color: ${Constants.system.black};
|
||||
|
||||
:focus {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
box-shadow: 0 0 0 1px ${Constants.system.bgBlue} inset;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
/* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: ${Constants.system.darkGray};
|
||||
@ -255,11 +249,8 @@ const STYLES_INPUT = css`
|
||||
`;
|
||||
|
||||
const STYLES_INPUT_DARK = css`
|
||||
${INPUT_STYLES};
|
||||
${STYLES_INPUT};
|
||||
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 0 0 1px #3c3c3c inset;
|
||||
background: ${Constants.system.blurBlack};
|
||||
color: ${Constants.system.white};
|
||||
@ -480,6 +471,9 @@ export const Tag = ({
|
||||
if (e.key === "Enter" && value.length) {
|
||||
_handleAdd(value);
|
||||
}
|
||||
if (!open) {
|
||||
setOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const _handlePaste = (e) => {
|
||||
|
@ -98,7 +98,6 @@ export class Boundary extends React.PureComponent {
|
||||
};
|
||||
|
||||
_handleOutsideRectEvent = (e) => {
|
||||
// console.log("outside rect event");
|
||||
this.props.onOutsideRectEvent(e);
|
||||
};
|
||||
|
||||
|
@ -81,12 +81,12 @@ export class CreateFilecoinStorageDeal extends React.Component {
|
||||
{this.state.file ? (
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>{this.state.file.name}</div>
|
||||
<div css={STYLES_FOCUS}>{this.state.file.data.name}</div>
|
||||
<div css={STYLES_SUBTEXT}>Name</div>
|
||||
</div>
|
||||
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>{this.state.file.size}</div>
|
||||
<div css={STYLES_FOCUS}>{this.state.file.data.size}</div>
|
||||
<div css={STYLES_SUBTEXT}>File size</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@ import { css } from "@emotion/react";
|
||||
import { Table } from "~/components/system/components/Table";
|
||||
import { StatUpload, StatDownload } from "~/components/system/components/Stat";
|
||||
|
||||
let genericImg = Strings.getCIDGatewayURL(
|
||||
let genericImg = Strings.getURLfromCID(
|
||||
"bafybeiblly23jomdjjiq7ilth667npcfm5llqb5xfstodbbfa5pxtoek7u"
|
||||
);
|
||||
|
||||
|
@ -2,15 +2,28 @@
|
||||
// User postgres queries
|
||||
import createUser from "~/node_common/data/methods/create-user";
|
||||
import updateUserById from "~/node_common/data/methods/update-user-by-id";
|
||||
import deleteUserByUsername from "~/node_common/data/methods/delete-user-by-username";
|
||||
import deleteUserById from "~/node_common/data/methods/delete-user-by-id";
|
||||
import getUserByUsername from "~/node_common/data/methods/get-user-by-username";
|
||||
import getUserById from "~/node_common/data/methods/get-user-by-id";
|
||||
|
||||
//NOTE(martina):
|
||||
// Pending user upload queries
|
||||
import getPendingDataForUserId from "~/node_common/data/methods/get-pending-data-for-user-id";
|
||||
import deletePendingDataByUserId from "~/node_common/data/methods/delete-pending-data-by-user-id";
|
||||
import createPendingData from "~/node_common/data/methods/create-pending-data";
|
||||
// File postgres queries
|
||||
import createFile from "~/node_common/data/methods/create-file";
|
||||
import getFileByCid from "~/node_common/data/methods/get-file-by-cid";
|
||||
import getFilesByCids from "~/node_common/data/methods/get-files-by-cids";
|
||||
import getFileById from "~/node_common/data/methods/get-file-by-id";
|
||||
import getFilesByIds from "~/node_common/data/methods/get-files-by-ids";
|
||||
import getFilesByUserId from "~/node_common/data/methods/get-files-by-user-id";
|
||||
import deleteFilesByIds from "~/node_common/data/methods/delete-files-by-ids";
|
||||
import deleteFilesByUserId from "~/node_common/data/methods/delete-files-by-user-id";
|
||||
import updateFileById from "~/node_common/data/methods/update-file-by-id";
|
||||
import updateFilePrivacy from "~/node_common/data/methods/update-file-privacy";
|
||||
|
||||
//NOTE(martina):
|
||||
// Slate file postgres queries
|
||||
import createSlateFiles from "~/node_common/data/methods/create-slate-files";
|
||||
import deleteSlateFiles from "~/node_common/data/methods/delete-slate-files";
|
||||
import getSlateFilesByCids from "~/node_common/data/methods/get-slate-files-by-cids";
|
||||
|
||||
// NOTE(jim):
|
||||
// Slate postgres queries
|
||||
@ -19,16 +32,15 @@ import getSlateByName from "~/node_common/data/methods/get-slate-by-name";
|
||||
import getSlateById from "~/node_common/data/methods/get-slate-by-id";
|
||||
import getSlatesByUserId from "~/node_common/data/methods/get-slates-by-user-id";
|
||||
import getSlatesByIds from "~/node_common/data/methods/get-slates-by-ids";
|
||||
import getSlateObjectsByCID from "~/node_common/data/methods/get-slate-objects-by-cid";
|
||||
import updateSlateById from "~/node_common/data/methods/update-slate-by-id";
|
||||
import deleteSlatesForUserId from "~/node_common/data/methods/delete-slates-for-user-id";
|
||||
import updateSlatePrivacy from "~/node_common/data/methods/update-slate-privacy";
|
||||
import deleteSlatesByUserId from "~/node_common/data/methods/delete-slates-by-user-id";
|
||||
import deleteSlateById from "~/node_common/data/methods/delete-slate-by-id";
|
||||
|
||||
// NOTE(jim):
|
||||
// API postgres queries
|
||||
import createAPIKeyForUserId from "~/node_common/data/methods/create-api-key-for-user-id";
|
||||
import createAPIKey from "~/node_common/data/methods/create-api-key";
|
||||
import deleteAPIKeyById from "~/node_common/data/methods/delete-api-key-by-id";
|
||||
import deleteAPIKeysForUserId from "~/node_common/data/methods/delete-api-keys-for-user-id";
|
||||
import getAPIKey from "~/node_common/data/methods/get-api-key";
|
||||
import getAPIKeyByKey from "~/node_common/data/methods/get-api-key-by-key";
|
||||
import getAPIKeysByUserId from "~/node_common/data/methods/get-api-keys-by-user-id";
|
||||
@ -36,107 +48,83 @@ import getAPIKeysByUserId from "~/node_common/data/methods/get-api-keys-by-user-
|
||||
// NOTE(jim):
|
||||
// Subscription postgres queries
|
||||
import createSubscription from "~/node_common/data/methods/create-subscription";
|
||||
import getSubscriptionById from "~/node_common/data/methods/get-subscription-by-id";
|
||||
import getSubscription from "~/node_common/data/methods/get-subscription";
|
||||
import getSubscribersBySlateId from "~/node_common/data/methods/get-subscribers-by-slate-id";
|
||||
import getSubscriptionsByUserId from "~/node_common/data/methods/get-subscriptions-by-user-id";
|
||||
import getSubscriptionsToUserId from "~/node_common/data/methods/get-subscriptions-to-user-id";
|
||||
import getSubscriptionsToSlateId from "~/node_common/data/methods/get-subscriptions-to-slate-id";
|
||||
import getSubscribersByUserId from "~/node_common/data/methods/get-subscribers-by-user-id";
|
||||
import getFollowersByUserId from "~/node_common/data/methods/get-followers-by-user-id";
|
||||
import getFollowingByUserId from "~/node_common/data/methods/get-following-by-user-id";
|
||||
import deleteSubscriptionById from "~/node_common/data/methods/delete-subscription-by-id";
|
||||
|
||||
// NOTE(jim):
|
||||
// Trust postgres queries
|
||||
import createTrustedRelationship from "~/node_common/data/methods/create-trusted-relationship";
|
||||
import updateTrustedRelationshipById from "~/node_common/data/methods/update-trusted-relationship-by-id";
|
||||
import getTrustedRelationshipsByUserId from "~/node_common/data/methods/get-trusted-relationships-by-user-id";
|
||||
import getPendingTrustedRelationshipsByUserId from "~/node_common/data/methods/get-pending-trusted-relationships-by-user-id";
|
||||
import getTrustedRelationshipByUserIds from "~/node_common/data/methods/get-trusted-relationship-by-ids";
|
||||
import getTrustedRelationshipById from "~/node_common/data/methods/get-trusted-relationship-by-id";
|
||||
import deleteTrustedRelationshipById from "~/node_common/data/methods/delete-trusted-relationship-by-id";
|
||||
|
||||
// NOTE(jim):
|
||||
// Activity postgres queries
|
||||
import createActivity from "~/node_common/data/methods/create-activity";
|
||||
import getActivityForUserId from "~/node_common/data/methods/get-activity-for-user-id";
|
||||
import getActivityForSlateId from "~/node_common/data/methods/get-activity-for-slate-id";
|
||||
import getActivityById from "~/node_common/data/methods/get-activity-by-id";
|
||||
import deleteActivityById from "~/node_common/data/methods/delete-activity-by-id";
|
||||
import getActivity from "~/node_common/data/methods/get-activity";
|
||||
import getExplore from "~/node_common/data/methods/get-explore";
|
||||
|
||||
// NOTE(jim):
|
||||
// Search postgres queries
|
||||
import querySlates from "~/node_common/data/methods/query-slates";
|
||||
import queryUsers from "~/node_common/data/methods/query-users";
|
||||
import getEverySlate from "~/node_common/data/methods/get-every-slate";
|
||||
import getEveryUser from "~/node_common/data/methods/get-every-user";
|
||||
import getEveryFile from "~/node_common/data/methods/get-every-file";
|
||||
|
||||
// NOTE(jim):
|
||||
// one-offs
|
||||
import getAllStats from "~/node_common/data/methods/get-all-stats";
|
||||
import getAllDeals from "~/node_common/data/methods/get-all-deals";
|
||||
import getAllActivity from "~/node_common/data/methods/get-all-activity";
|
||||
import createOrUpdateStats from "~/node_common/data/methods/create-or-update-stats";
|
||||
import getOrCreateGlobalStats from "~/node_common/data/methods/get-or-create-global-stats";
|
||||
import createOrphan from "~/node_common/data/methods/create-orphan";
|
||||
|
||||
export {
|
||||
// NOTE(jim): One-offs
|
||||
createOrUpdateStats,
|
||||
getOrCreateGlobalStats,
|
||||
createOrphan,
|
||||
getAllDeals,
|
||||
getAllActivity,
|
||||
getAllStats,
|
||||
// NOTE(jim): User operations
|
||||
createUser,
|
||||
updateUserById,
|
||||
deleteUserByUsername,
|
||||
deleteUserById,
|
||||
getUserByUsername,
|
||||
getUserById,
|
||||
// NOTE(martina): Pending user upload operations
|
||||
getPendingDataForUserId,
|
||||
deletePendingDataByUserId,
|
||||
createPendingData,
|
||||
//NOTE(martina): File operations
|
||||
createFile,
|
||||
getFileByCid,
|
||||
getFilesByCids,
|
||||
getFileById,
|
||||
getFilesByIds,
|
||||
getFilesByUserId,
|
||||
deleteFilesByIds,
|
||||
deleteFilesByUserId,
|
||||
updateFileById,
|
||||
updateFilePrivacy,
|
||||
//NOTE(martina): Slate file operations
|
||||
createSlateFiles,
|
||||
deleteSlateFiles,
|
||||
getSlateFilesByCids,
|
||||
// NOTE(jim): Slate operations
|
||||
createSlate,
|
||||
getSlateByName,
|
||||
getSlateById,
|
||||
getSlatesByUserId,
|
||||
getSlatesByIds,
|
||||
getSlateObjectsByCID,
|
||||
updateSlateById,
|
||||
deleteSlatesForUserId,
|
||||
updateSlatePrivacy,
|
||||
deleteSlatesByUserId,
|
||||
deleteSlateById,
|
||||
// NOTE(jim): API key operations
|
||||
createAPIKeyForUserId,
|
||||
createAPIKey,
|
||||
deleteAPIKeyById,
|
||||
deleteAPIKeysForUserId,
|
||||
getAPIKey,
|
||||
getAPIKeyByKey,
|
||||
getAPIKeysByUserId,
|
||||
// NOTE(jim): Subscription operations
|
||||
createSubscription,
|
||||
getSubscriptionById,
|
||||
getSubscription,
|
||||
getSubscribersBySlateId,
|
||||
getSubscriptionsByUserId,
|
||||
getSubscriptionsToSlateId,
|
||||
getSubscriptionsToUserId,
|
||||
getSubscribersByUserId,
|
||||
getFollowersByUserId,
|
||||
getFollowingByUserId,
|
||||
deleteSubscriptionById,
|
||||
// NOTE(jim): Trust operations
|
||||
createTrustedRelationship,
|
||||
updateTrustedRelationshipById,
|
||||
getPendingTrustedRelationshipsByUserId,
|
||||
getTrustedRelationshipsByUserId,
|
||||
getTrustedRelationshipByUserIds,
|
||||
getTrustedRelationshipById,
|
||||
deleteTrustedRelationshipById,
|
||||
// NOTE(jim): Activity operations
|
||||
createActivity,
|
||||
getActivityForUserId,
|
||||
getActivityForSlateId,
|
||||
getActivityById,
|
||||
deleteActivityById,
|
||||
getActivity,
|
||||
getExplore,
|
||||
// NOTE(jim): Search
|
||||
queryUsers,
|
||||
querySlates,
|
||||
getEverySlate,
|
||||
getEveryUser,
|
||||
getEveryFile,
|
||||
};
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ slateId, userId, data }) => {
|
||||
//NOTE(martina): can be single activity item (an object) or multiple activity items (an array of objects)
|
||||
export default async (activityItems) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_ACTIVITY",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.insert({
|
||||
owner_slate_id: slateId,
|
||||
owner_user_id: userId,
|
||||
data,
|
||||
})
|
||||
.into("activity")
|
||||
.returning("*");
|
||||
//TODO(martina): optimization. check for whether something with the same file exists already, so we don't repeat frequently
|
||||
let query = await DB.insert(activityItems).into("activity").returning("*");
|
||||
|
||||
const index = query ? query.pop() : null;
|
||||
index.type = "ACTIVITY";
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
if (!query) {
|
||||
return null;
|
||||
}
|
||||
if (!Array.isArray(activityItems)) {
|
||||
query = query.pop();
|
||||
}
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
|
@ -2,10 +2,10 @@ import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId, key, level = 1 }) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_API_KEY_FOR_USER_ID",
|
||||
label: "CREATE_API_KEY",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.insert({
|
||||
owner_id: userId,
|
||||
ownerId: userId,
|
||||
level,
|
||||
key,
|
||||
})
|
||||
@ -13,13 +13,12 @@ export default async ({ userId, key, level = 1 }) => {
|
||||
.returning("*");
|
||||
|
||||
const index = query ? query.pop() : null;
|
||||
index.type = "API_KEY";
|
||||
return index;
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "CREATE_API_KEY_FOR_USER_ID",
|
||||
decorator: "CREATE_API_KEY",
|
||||
};
|
||||
},
|
||||
});
|
30
node_common/data/methods/create-file.js
Normal file
30
node_common/data/methods/create-file.js
Normal file
@ -0,0 +1,30 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
//NOTE(martina): remember to include an ownerId for the file
|
||||
export default async (files) => {
|
||||
const cleanedFiles = files.map((file) => Serializers.cleanFile(file));
|
||||
|
||||
return await runQuery({
|
||||
label: "CREATE_FILE",
|
||||
queryFn: async (DB) => {
|
||||
let query = await DB.insert(cleanedFiles).into("files").returning("*");
|
||||
|
||||
if (!query) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Array.isArray(files)) {
|
||||
query = query.pop();
|
||||
}
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "CREATE_FILE",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,88 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async (date, data) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_OR_UPDATE_STATS",
|
||||
queryFn: async (DB) => {
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
let end = new Date();
|
||||
end.setHours(23, 59, 59, 999);
|
||||
|
||||
const query = await DB.select("*")
|
||||
.from("stats")
|
||||
.where("created_at", ">=", date)
|
||||
.where("created_at", "<", end)
|
||||
.first();
|
||||
|
||||
if (query && query.id) {
|
||||
let updates = { ...query.data };
|
||||
|
||||
if (data.deals) {
|
||||
updates.deals = updates.deals + data.deals;
|
||||
}
|
||||
|
||||
if (data.users) {
|
||||
updates.users = updates.users + data.users;
|
||||
}
|
||||
|
||||
if (data.slates) {
|
||||
updates.slates = updates.slates + data.slates;
|
||||
}
|
||||
|
||||
if (data.objects) {
|
||||
updates.objects = updates.objects + data.objects;
|
||||
}
|
||||
|
||||
if (data.subscribeUser) {
|
||||
updates.subscribeUsers = updates.subscribeUsers + data.subscribeUser;
|
||||
}
|
||||
|
||||
if (data.subscribeSlate) {
|
||||
updates.subscribeSlates = updates.subscribeSlates + data.subscribeSlate;
|
||||
}
|
||||
|
||||
const changes = await DB.from("stats")
|
||||
.where("id", query.id)
|
||||
.update({ data: updates })
|
||||
.returning("*");
|
||||
|
||||
if (!changes || changes.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const updateIndex = changes ? changes.pop() : null;
|
||||
return updateIndex;
|
||||
}
|
||||
|
||||
const insert = await DB.insert({
|
||||
created_at: date,
|
||||
data: {
|
||||
deals: 0,
|
||||
users: 0,
|
||||
slates: 0,
|
||||
objects: 0,
|
||||
subscribeUsers: 0,
|
||||
subscribeSlates: 0,
|
||||
...data,
|
||||
},
|
||||
})
|
||||
.into("stats")
|
||||
.returning("*");
|
||||
const index = insert ? insert.pop() : null;
|
||||
|
||||
if (!index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log(e);
|
||||
return {
|
||||
error: true,
|
||||
decorator: "CREATE_OR_UPDATE_STATS",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -5,7 +5,7 @@ export default async ({ data }) => {
|
||||
label: "CREATE_ORPHAN",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.insert({
|
||||
created_at: new Date(),
|
||||
createdAt: new Date(),
|
||||
data,
|
||||
})
|
||||
.into("orphans")
|
||||
|
@ -1,24 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ data, owner_user_id }) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_PENDING_DATA",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.insert({
|
||||
data,
|
||||
owner_user_id,
|
||||
})
|
||||
.into("pending")
|
||||
.returning("*");
|
||||
|
||||
const index = query ? query.pop() : null;
|
||||
return index;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "CREATE_PENDING_DATA",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
23
node_common/data/methods/create-slate-files.js
Normal file
23
node_common/data/methods/create-slate-files.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
//NOTE(martina): this is the endpoint used when adding an existing file to a slate. Creates a slate_files entry
|
||||
export default async (slateFiles) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_SLATE_FILES",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.insert(slateFiles).into("slate_files").returning("*");
|
||||
|
||||
if (!query) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "CREATE_SLATE_FILES",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,19 +1,20 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ slatename, data = {} }) => {
|
||||
export default async ({ ownerId, slatename, isPublic, data = {} }) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_SLATE",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.insert({
|
||||
ownerId,
|
||||
slatename,
|
||||
isPublic,
|
||||
data,
|
||||
})
|
||||
.into("slates")
|
||||
.returning("*");
|
||||
|
||||
const index = query ? query.pop() : null;
|
||||
index.type = "SLATE";
|
||||
return index;
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
|
@ -1,19 +1,23 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ subscriberUserId, slateId, userId }) => {
|
||||
export default async ({ ownerId, slateId, userId }) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_SUBSCRIPTION",
|
||||
queryFn: async (DB) => {
|
||||
console.log({
|
||||
ownerId,
|
||||
slateId,
|
||||
userId,
|
||||
});
|
||||
const query = await DB.insert({
|
||||
owner_user_id: subscriberUserId,
|
||||
target_slate_id: slateId,
|
||||
target_user_id: userId,
|
||||
ownerId,
|
||||
slateId,
|
||||
userId,
|
||||
})
|
||||
.into("subscriptions")
|
||||
.returning("*");
|
||||
|
||||
const index = query ? query.pop() : null;
|
||||
index.type = "SUBSCRIPTION";
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
|
@ -1,26 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ ownerUserId, targetUserId }) => {
|
||||
return await runQuery({
|
||||
label: "CREATE_TRUSTED_RELATIONSHIP",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.insert({
|
||||
owner_user_id: ownerUserId,
|
||||
target_user_id: targetUserId,
|
||||
data: { verified: false },
|
||||
})
|
||||
.into("trusted")
|
||||
.returning("*");
|
||||
|
||||
const index = query ? query.pop() : null;
|
||||
index.type = "TRUSTED_RELATIONSHIP";
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "CREATE_TRUSTED_RELATIONSHIP",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -14,8 +14,7 @@ export default async ({ password, username, salt, data = {} }) => {
|
||||
.returning("*");
|
||||
|
||||
const index = query ? query.pop() : null;
|
||||
index.type = "USER";
|
||||
return index;
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_ACTIVITY_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const data = await DB.from("activity").where({ id }).del();
|
||||
|
||||
return 1 === data;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_ACTIVITY_BY_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_API_KEYS_FOR_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const data = await DB.from("keys").where({ owner_id: userId }).del();
|
||||
|
||||
return 1 === data;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_API_KEYS_FOR_USER_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
37
node_common/data/methods/delete-files-by-ids.js
Normal file
37
node_common/data/methods/delete-files-by-ids.js
Normal file
@ -0,0 +1,37 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ ids, ownerId }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_FILES_BY_IDS",
|
||||
queryFn: async (DB) => {
|
||||
// const repostedSlateFiles = await DB.from("slate_files")
|
||||
// .join("slates", "slates.id", "=", "slate_files.slateId")
|
||||
// .whereNot("slates.ownerId", "=", ownerId)
|
||||
// .whereIn("slate_files.fileId", ids)
|
||||
// .update({ "slate_files.fileId": null });
|
||||
const repostedSlateFiles = await DB.from("slate_files")
|
||||
.whereIn("id", function () {
|
||||
this.select("slate_files.id")
|
||||
.from("slate_files")
|
||||
.join("slates", "slates.id", "=", "slate_files.slateId")
|
||||
.whereIn("slate_files.fileId", ids)
|
||||
.whereNot("slates.ownerId", "=", ownerId);
|
||||
})
|
||||
.del();
|
||||
|
||||
const slateFiles = await DB("slate_files").whereIn("fileId", ids).del();
|
||||
|
||||
const activity = await DB("activity").whereIn("fileId", ids).del();
|
||||
|
||||
const files = await DB("files").whereIn("id", ids).del();
|
||||
|
||||
return files === ids.length;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_FILES_BY_IDS",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
48
node_common/data/methods/delete-files-by-user-id.js
Normal file
48
node_common/data/methods/delete-files-by-user-id.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ ownerId }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_FILES_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const selected = await DB.select("id").from("files").where({ ownerId });
|
||||
|
||||
if (!selected || selected.error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let fileIds = selected.map((file) => file.id);
|
||||
|
||||
//NOTE(martina): so that reposted versions of the file can be displayed as "deleted" rather than just disappearing
|
||||
// const repostedSlateFiles = await DB.from("slate_files")
|
||||
// .join("slates", "slates.id", "=", "slate_files.slateId")
|
||||
// .whereNot("slates.ownerId", "=", ownerId)
|
||||
// .whereIn("slate_files.fileId", fileIds)
|
||||
// .update({ "slate_files.fileId": null });
|
||||
//TODO(martina): send out a notification for this instead
|
||||
const repostedSlateFiles = await DB.from("slate_files")
|
||||
.whereIn("id", function () {
|
||||
this.select("slate_files.id")
|
||||
.from("slate_files")
|
||||
.join("slates", "slates.id", "=", "slate_files.slateId")
|
||||
.whereIn("slate_files.fileId", fileIds)
|
||||
.whereNot("slates.ownerId", "=", ownerId);
|
||||
})
|
||||
.del();
|
||||
// .update({ fileId: null });
|
||||
|
||||
const slateFiles = await DB("slate_files").whereIn("fileId", fileIds).del();
|
||||
|
||||
const activity = await DB("activity").whereIn("fileId", fileIds).del();
|
||||
|
||||
const files = await DB("files").whereIn("id", fileIds).del();
|
||||
|
||||
return true;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_FILES_BY_USER_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
import * as Data from "~/node_common/data";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ owner_user_id }) => {
|
||||
return await runQuery({
|
||||
label: "REMOVE_PENDING_DATA_FOR_USER",
|
||||
queryFn: async (DB) => {
|
||||
const pending = await DB.from("pending").where({ owner_user_id }).returning("*").del();
|
||||
|
||||
return pending;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
decorator: "REMOVE_PENDING_DATA_FOR_USER",
|
||||
error: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -4,13 +4,15 @@ export default async ({ id }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_SLATE_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const subscriptions = await DB.from("subscriptions")
|
||||
.where({ target_slate_id: id })
|
||||
.del();
|
||||
const subscriptions = await DB("subscriptions").where({ slateId: id }).del();
|
||||
|
||||
const data = await DB.from("slates").where({ id }).del();
|
||||
const slateFiles = await DB("slate_files").where({ slateId: id }).del();
|
||||
|
||||
return 1 === data;
|
||||
const activity = await DB("activity").where({ slateId: id }).del();
|
||||
|
||||
const slates = await DB("slates").where({ id }).del();
|
||||
|
||||
return 1 === slates;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
|
23
node_common/data/methods/delete-slate-files.js
Normal file
23
node_common/data/methods/delete-slate-files.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ slateId, ids }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_SLATE_FILES",
|
||||
queryFn: async (DB) => {
|
||||
const slateFiles = await DB("slate_files")
|
||||
.where("slateId", slateId)
|
||||
.whereIn("fileId", ids)
|
||||
.del();
|
||||
|
||||
const activity = await DB("activity").whereIn("fileId", ids).where("slateId", slateId).del();
|
||||
|
||||
return true;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_SLATE_FILES",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
32
node_common/data/methods/delete-slates-by-user-id.js
Normal file
32
node_common/data/methods/delete-slates-by-user-id.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ ownerId }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_SLATES_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const selected = await DB.select("id").from("slates").where({ ownerId });
|
||||
|
||||
if (!selected || selected.error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let slateIds = selected.map((slate) => slate.id);
|
||||
|
||||
const subscriptions = await DB("subscriptions").whereIn("slateId", slateIds).del();
|
||||
|
||||
const slateFiles = await DB("slate_files").whereIn("slateId", slateIds).del();
|
||||
|
||||
const activity = await DB("activity").whereIn("slateId", slateIds).del();
|
||||
|
||||
const slates = await DB("slates").whereIn("id", slateIds).del();
|
||||
|
||||
return true;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_SLATES_BY_USER_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_SLATES_FOR_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const hasUser = (id) =>
|
||||
DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ ownerId: id })]);
|
||||
|
||||
const slates = await DB.select("id")
|
||||
.from("slates")
|
||||
.where(hasUser(userId));
|
||||
|
||||
if (!slates || slates.error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let slateIds = slates.map((slate) => slate.id);
|
||||
|
||||
const subscriptions = await DB.from("subscriptions")
|
||||
.whereIn("target_slate_id", slateIds)
|
||||
.del();
|
||||
|
||||
const data = await DB.from("slates").where(hasUser(userId)).del();
|
||||
|
||||
return 1 === data;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_SLATES_FOR_USER_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_TRUSTED_RELATIONSHIP_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const data = await DB.from("trusted").where({ id }).del();
|
||||
|
||||
return 1 === data;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
decorator: "DELETE_TRUSTED_RELATIONSHIP_BY_ID",
|
||||
error: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
30
node_common/data/methods/delete-user-by-id.js
Normal file
30
node_common/data/methods/delete-user-by-id.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_USER_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const keys = await DB.from("keys").where({ ownerId: id }).del();
|
||||
|
||||
const subscriptions = await DB.from("subscriptions")
|
||||
.where({ ownerId: id })
|
||||
.orWhere({ userId: id })
|
||||
.del();
|
||||
|
||||
const activity = await DB.from("activity")
|
||||
.where({ ownerId: id })
|
||||
.orWhere({ userId: id })
|
||||
.del();
|
||||
|
||||
const data = await DB.from("users").where({ id }).del();
|
||||
|
||||
return 1 === data;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_USER_BY_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,38 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ username }) => {
|
||||
return await runQuery({
|
||||
label: "DELETE_USER_BY_USERNAME",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("id")
|
||||
.from("users")
|
||||
.where({ username })
|
||||
.first();
|
||||
|
||||
if (!query || query.error || !query.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const id = query.id;
|
||||
|
||||
const deletedSubscriptions = await DB.from("subscriptions")
|
||||
.where({ owner_user_id: id })
|
||||
.orWhere({ target_user_id: id })
|
||||
.del();
|
||||
const deletedTrusted = await DB.from("trusted")
|
||||
.where({ owner_user_id: id })
|
||||
.orWhere({ target_user_id: id })
|
||||
.del();
|
||||
|
||||
const data = await DB.from("users").where({ username }).del();
|
||||
|
||||
return 1 === data;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "DELETE_USER_BY_USERNAME",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id }) => {
|
||||
return await runQuery({
|
||||
label: "GET_ACTIVITY_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*").from("activity").where({ id }).first();
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.id) {
|
||||
query.type = "ACTIVITY";
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_ACTIVITY_BY_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ slateId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_ACTIVITY_FOR_SLATE_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*")
|
||||
.from("activity")
|
||||
.where({ owner_slate_id: slateId })
|
||||
.orderBy("created_at", "desc");
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_ACTIVITY_FOR_SLATE_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,56 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId, earliestTimestamp, latestTimestamp }) => {
|
||||
return await runQuery({
|
||||
label: "GET_ACTIVITY_FOR_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
let query;
|
||||
if (earliestTimestamp) {
|
||||
//NOTE(martina): for pagination, fetching the "next 100" results
|
||||
let date = new Date(earliestTimestamp);
|
||||
let s = date.getSeconds();
|
||||
if (s < 0) {
|
||||
s = 60 + s;
|
||||
}
|
||||
date.setSeconds(s - 1);
|
||||
query = await DB.select("*")
|
||||
.from("activity")
|
||||
.where({ owner_user_id: userId })
|
||||
.where("created_at", "<", date.toISOString())
|
||||
.orderBy("created_at", "desc")
|
||||
.limit(100);
|
||||
} else if (latestTimestamp) {
|
||||
//NOTE(martina): for fetching new updates since the last time they loaded
|
||||
let date = new Date(latestTimestamp);
|
||||
date.setSeconds(date.getSeconds() + 1);
|
||||
query = await DB.select("*")
|
||||
.from("activity")
|
||||
.where({ owner_user_id: userId })
|
||||
.where("created_at", ">", date.toISOString())
|
||||
.orderBy("created_at", "desc")
|
||||
.limit(100);
|
||||
} else {
|
||||
//NOTE(martina): for the first fetch they make, when they have not loaded any explore events yet
|
||||
query = await DB.select("*")
|
||||
.from("activity")
|
||||
.where({ owner_user_id: userId })
|
||||
.orderBy("created_at", "desc")
|
||||
.limit(100);
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_ACTIVITY_FOR_USER_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
103
node_common/data/methods/get-activity.js
Normal file
103
node_common/data/methods/get-activity.js
Normal file
@ -0,0 +1,103 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({
|
||||
following = [],
|
||||
subscriptions = [],
|
||||
earliestTimestamp,
|
||||
latestTimestamp,
|
||||
}) => {
|
||||
return await runQuery({
|
||||
label: "GET_ACTIVITY_FOR_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const users = () => DB.raw("row_to_json(??) as ??", ["users", "owner"]);
|
||||
|
||||
const slates = () => DB.raw("row_to_json(??) as ??", ["slates", "slate"]);
|
||||
|
||||
const files = () => DB.raw("row_to_json(??) as ??", ["files", "file"]);
|
||||
|
||||
let query;
|
||||
if (earliestTimestamp) {
|
||||
//NOTE(martina): for pagination, fetching the "next 100" results
|
||||
let date = new Date(earliestTimestamp);
|
||||
let s = date.getSeconds();
|
||||
if (s < 0) {
|
||||
s = 60 + s;
|
||||
}
|
||||
date.setSeconds(s - 1);
|
||||
query = await DB.select(
|
||||
"activity.id",
|
||||
"activity.type",
|
||||
"activity.createdAt",
|
||||
users(),
|
||||
slates(),
|
||||
files()
|
||||
)
|
||||
.from("activity")
|
||||
.join("users", "users.id", "=", "activity.ownerId")
|
||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||
.where("activity.createdAt", "<", date.toISOString())
|
||||
.whereIn("activity.ownerId", following)
|
||||
.orWhereIn("activity.slateId", subscriptions)
|
||||
.orderBy("activity.createdAt", "desc")
|
||||
.limit(100);
|
||||
} else if (latestTimestamp) {
|
||||
//NOTE(martina): for fetching new updates since the last time they loaded
|
||||
let date = new Date(latestTimestamp);
|
||||
date.setSeconds(date.getSeconds() + 1);
|
||||
query = await DB.select(
|
||||
"activity.id",
|
||||
"activity.type",
|
||||
"activity.createdAt",
|
||||
users(),
|
||||
slates(),
|
||||
files()
|
||||
)
|
||||
.from("activity")
|
||||
.join("users", "users.id", "=", "activity.ownerId")
|
||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||
.where("activity.createdAt", ">", date.toISOString())
|
||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||
.whereIn("activity.ownerId", following)
|
||||
.orWhereIn("activity.slateId", subscriptions)
|
||||
.orderBy("activity.createdAt", "desc")
|
||||
.limit(100);
|
||||
} else {
|
||||
//NOTE(martina): for the first fetch they make, when they have not loaded any explore events yet
|
||||
query = await DB.select(
|
||||
"activity.id",
|
||||
"activity.type",
|
||||
"activity.createdAt",
|
||||
users(),
|
||||
slates(),
|
||||
files()
|
||||
)
|
||||
.from("activity")
|
||||
.join("users", "users.id", "=", "activity.ownerId")
|
||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||
.whereIn("activity.ownerId", following)
|
||||
.orWhereIn("activity.slateId", subscriptions)
|
||||
.orderBy("activity.createdAt", "desc")
|
||||
.limit(100);
|
||||
}
|
||||
|
||||
if (!query) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_ACTIVITY_FOR_USER_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async () => {
|
||||
return await runQuery({
|
||||
label: "GET_ALL_ACTIVITY",
|
||||
queryFn: async (DB) => {
|
||||
const r = await DB.select("*").from("activity");
|
||||
|
||||
if (!r || r.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(r));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_ALL_ACTIVITY",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async () => {
|
||||
return await runQuery({
|
||||
label: "GET_ALL_DEALS",
|
||||
queryFn: async (DB) => {
|
||||
const r = await DB.select("*").from("deals");
|
||||
|
||||
if (!r || r.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(r));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_ALL_DEALS",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async () => {
|
||||
return await runQuery({
|
||||
label: "GET_ALL_STATS",
|
||||
queryFn: async (DB) => {
|
||||
const r = await DB.select("*").from("stats").orderBy("created_at", "desc");
|
||||
|
||||
if (!r || r.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(r));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_ALL_STATS",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -4,9 +4,7 @@ export default async ({ userId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_API_KEYS_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*")
|
||||
.from("keys")
|
||||
.where({ owner_id: userId });
|
||||
const query = await DB.select("*").from("keys").where({ ownerId: userId });
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
|
47
node_common/data/methods/get-every-file.js
Normal file
47
node_common/data/methods/get-every-file.js
Normal file
@ -0,0 +1,47 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ sanitize = false, publicOnly = false } = {}) => {
|
||||
return await runQuery({
|
||||
label: "GET_EVERY_FILE",
|
||||
queryFn: async (DB) => {
|
||||
let files;
|
||||
if (publicOnly) {
|
||||
files = await DB.select(
|
||||
"files.id",
|
||||
"files.ownerId",
|
||||
"files.cid",
|
||||
"files.isPublic",
|
||||
"files.data"
|
||||
)
|
||||
.from("files")
|
||||
.leftJoin("slate_files", "slate_files.fileId", "files.id")
|
||||
.leftJoin("slates", "slate_files.slateId", "slates.id")
|
||||
.where("slates.isPublic", true)
|
||||
.orWhere("files.isPublic", true)
|
||||
.groupBy("files.id");
|
||||
} else {
|
||||
files = await DB.select("*").from("files");
|
||||
}
|
||||
|
||||
if (!files || files.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (sanitize) {
|
||||
files = files.map((file) => Serializers.sanitizeFile(file));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(files));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_EVERY_FILE",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -2,24 +2,68 @@ import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async (sanitize = true) => {
|
||||
export default async ({ sanitize = false, includeFiles = false, publicOnly = false } = {}) => {
|
||||
return await runQuery({
|
||||
label: "GET_EVERY_SLATE",
|
||||
queryFn: async (DB) => {
|
||||
const r = await DB.select("id", "slatename", "data").from("slates");
|
||||
// const slateFiles = () =>
|
||||
// DB.raw("json_agg(?? order by ?? asc) as ??", ["files", "slate_files.createdAt", "objects"]);
|
||||
|
||||
if (!r || r.error) {
|
||||
const slateFiles = () =>
|
||||
DB.raw("coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??", [
|
||||
"files",
|
||||
"slate_files.createdAt",
|
||||
"files.id",
|
||||
"objects",
|
||||
]);
|
||||
|
||||
let slates;
|
||||
if (publicOnly) {
|
||||
if (includeFiles) {
|
||||
slates = await DB.select(
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
"slates.ownerId",
|
||||
"slates.isPublic",
|
||||
slateFiles()
|
||||
)
|
||||
.from("slates")
|
||||
.leftJoin("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.leftJoin("files", "slate_files.fileId", "=", "files.id")
|
||||
.where("slates.isPublic", true)
|
||||
.groupBy("slates.id");
|
||||
} else {
|
||||
slates = await DB.select("*").from("slates").where("isPublic", true);
|
||||
}
|
||||
} else {
|
||||
if (includeFiles) {
|
||||
slates = await DB.select(
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
"slates.ownerId",
|
||||
"slates.isPublic",
|
||||
slateFiles()
|
||||
)
|
||||
.from("slates")
|
||||
.leftJoin("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.leftJoin("files", "slate_files.fileId", "=", "files.id")
|
||||
.groupBy("slates.id");
|
||||
} else {
|
||||
slates = await DB.select("*").from("slates");
|
||||
}
|
||||
}
|
||||
|
||||
if (!slates || slates.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (sanitize) {
|
||||
const sanitized = r
|
||||
.filter((each) => !each.data.public)
|
||||
.map((each) => Serializers.slate(each));
|
||||
return JSON.parse(JSON.stringify(sanitized));
|
||||
slates = slates.map((slate) => Serializers.sanitizeSlate(slate));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(r));
|
||||
return JSON.parse(JSON.stringify(slates));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
|
@ -2,22 +2,37 @@ import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async (sanitize = true) => {
|
||||
export default async ({ sanitize = false, includeFiles = false } = {}) => {
|
||||
return await runQuery({
|
||||
label: "GET_EVERY_USER",
|
||||
queryFn: async (DB) => {
|
||||
const r = await DB.select("id", "username", "data").from("users");
|
||||
// const userFiles = () =>
|
||||
// DB.raw("json_agg(?? order by ?? desc) as ??", ["files", "files.createdAt", "library"]);
|
||||
|
||||
if (!r || r.error) {
|
||||
const userFiles = () =>
|
||||
DB.raw(
|
||||
"coalesce(json_agg(?? order by ?? desc) filter (where ?? is not null), '[]') as ??",
|
||||
["files", "files.createdAt", "files.id", "library"]
|
||||
);
|
||||
|
||||
let users;
|
||||
if (includeFiles) {
|
||||
users = await DB.select("users.id", "users.username", "users.data", userFiles())
|
||||
.from("users")
|
||||
.leftJoin("files", "files.ownerId", "users.id");
|
||||
} else {
|
||||
users = await DB.select("*").from("users");
|
||||
}
|
||||
|
||||
if (!users || users.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (sanitize) {
|
||||
const sanitized = r.map((each) => Serializers.user(each));
|
||||
return JSON.parse(JSON.stringify(sanitized));
|
||||
users = users.map((user) => Serializers.sanitizeUser(user));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(r));
|
||||
return JSON.parse(JSON.stringify(users));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
|
92
node_common/data/methods/get-explore.js
Normal file
92
node_common/data/methods/get-explore.js
Normal file
@ -0,0 +1,92 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ earliestTimestamp, latestTimestamp }) => {
|
||||
return await runQuery({
|
||||
label: "GET_EXPLORE",
|
||||
queryFn: async (DB) => {
|
||||
const users = () => DB.raw("row_to_json(??) as ??", ["users", "owner"]);
|
||||
|
||||
const slates = () => DB.raw("row_to_json(??) as ??", ["slates", "slate"]);
|
||||
|
||||
const files = () => DB.raw("row_to_json(??) as ??", ["files", "file"]);
|
||||
|
||||
let query;
|
||||
if (earliestTimestamp) {
|
||||
//NOTE(martina): for pagination, fetching the "next 100" results
|
||||
let date = new Date(earliestTimestamp);
|
||||
let s = date.getSeconds();
|
||||
if (s < 0) {
|
||||
s = 60 + s;
|
||||
}
|
||||
date.setSeconds(s - 1);
|
||||
query = await DB.select(
|
||||
"activity.id",
|
||||
"activity.type",
|
||||
"activity.createdAt",
|
||||
users(),
|
||||
slates(),
|
||||
files()
|
||||
)
|
||||
.from("activity")
|
||||
.join("users", "users.id", "=", "activity.ownerId")
|
||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||
.where("activity.createdAt", "<", date.toISOString())
|
||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||
.orderBy("activity.createdAt", "desc")
|
||||
.limit(100);
|
||||
} else if (latestTimestamp) {
|
||||
//NOTE(martina): for fetching new updates since the last time they loaded
|
||||
let date = new Date(latestTimestamp);
|
||||
date.setSeconds(date.getSeconds() + 1);
|
||||
query = await DB.select(
|
||||
"activity.id",
|
||||
"activity.type",
|
||||
"activity.createdAt",
|
||||
users(),
|
||||
slates(),
|
||||
files()
|
||||
)
|
||||
.from("activity")
|
||||
.join("users", "users.id", "=", "activity.ownerId")
|
||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||
.where("activity.createdAt", ">", date.toISOString())
|
||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||
.orderBy("activity.createdAt", "desc")
|
||||
.limit(100);
|
||||
} else {
|
||||
//NOTE(martina): for the first fetch they make, when they have not loaded any explore events yet
|
||||
query = await DB.select(
|
||||
"activity.id",
|
||||
"activity.type",
|
||||
"activity.createdAt",
|
||||
users(),
|
||||
slates(),
|
||||
files()
|
||||
)
|
||||
.from("activity")
|
||||
.join("users", "users.id", "=", "activity.ownerId")
|
||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||
.orderBy("activity.createdAt", "desc")
|
||||
.limit(100);
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_EXPLORE",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
35
node_common/data/methods/get-file-by-cid.js
Normal file
35
node_common/data/methods/get-file-by-cid.js
Normal file
@ -0,0 +1,35 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
//NOTE(martina): if you include an ownerId, this will return a single entry since there are no duplicate cids within a user's files
|
||||
//if you don't include an ownerId, this will return an array of entries since multiple users can have the same file
|
||||
export default async ({ ownerId, cid, sanitize = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_FILE_BY_CID",
|
||||
queryFn: async (DB) => {
|
||||
let query;
|
||||
if (ownerId) {
|
||||
query = await DB.select("*").from("files").where({ ownerId, cid }).first();
|
||||
} else {
|
||||
query = await DB.select("*").from("files").where({ cid });
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sanitize) {
|
||||
query = Serializers.sanitizeFile(query);
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_FILE_BY_CID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
28
node_common/data/methods/get-file-by-id.js
Normal file
28
node_common/data/methods/get-file-by-id.js
Normal file
@ -0,0 +1,28 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id, sanitize = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_FILE_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*").from("files").where({ id }).first();
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sanitize) {
|
||||
query = Serializers.sanitizeFile(query);
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_FILE_BY_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
37
node_common/data/methods/get-files-by-cids.js
Normal file
37
node_common/data/methods/get-files-by-cids.js
Normal file
@ -0,0 +1,37 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ ownerId, cids, sanitize = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_FILES_BY_CIDS",
|
||||
queryFn: async (DB) => {
|
||||
let query;
|
||||
if (ownerId) {
|
||||
query = await DB.select("*").from("files").where("ownerId", ownerId).whereIn("cid", cids);
|
||||
} else {
|
||||
query = await DB.select("*").from("files").whereIn("cid", cids);
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let serialized = [];
|
||||
if (sanitize) {
|
||||
for (let file of query) {
|
||||
serialized.push(Serializers.sanitizeFile(file));
|
||||
}
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_FILES_BY_CIDS",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
48
node_common/data/methods/get-files-by-ids.js
Normal file
48
node_common/data/methods/get-files-by-ids.js
Normal file
@ -0,0 +1,48 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ ids, sanitize = false, publicOnly = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_FILES_BY_IDS",
|
||||
queryFn: async (DB) => {
|
||||
let query;
|
||||
if (publicOnly) {
|
||||
query = await DB.select(
|
||||
"files.id",
|
||||
"files.ownerId",
|
||||
"files.cid",
|
||||
"files.isPublic",
|
||||
"files.filename",
|
||||
"files.data"
|
||||
)
|
||||
.from("files")
|
||||
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
||||
.leftJoin("slates", "slates.id", "=", "slate_files.slateId")
|
||||
.whereIn("files.id", ids)
|
||||
.where("files.isPublic", true)
|
||||
.orWhereIn("files.id", ids)
|
||||
.andWhere("slates.isPublic", true)
|
||||
.groupBy("files.id");
|
||||
} else {
|
||||
query = await DB.select("*").from("files").whereIn("id", ids);
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sanitize) {
|
||||
query = query.map((file) => Serializers.sanitizeFile(file));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_FILES_BY_IDS",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
54
node_common/data/methods/get-files-by-user-id.js
Normal file
54
node_common/data/methods/get-files-by-user-id.js
Normal file
@ -0,0 +1,54 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id, sanitize = false, publicOnly = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_FILES_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
let query;
|
||||
if (publicOnly) {
|
||||
query = await DB.select(
|
||||
"files.id",
|
||||
"files.ownerId",
|
||||
"files.cid",
|
||||
"files.isPublic",
|
||||
"files.filename",
|
||||
"files.data"
|
||||
)
|
||||
.from("files")
|
||||
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
||||
.leftJoin("slates", "slate_files.slateId", "=", "slates.id")
|
||||
.where({ "files.ownerId": id, "slates.isPublic": true })
|
||||
.orWhere({ "files.ownerId": id, "files.isPublic": true })
|
||||
.orderBy("files.createdAt", "desc")
|
||||
.groupBy("files.id");
|
||||
} else {
|
||||
query = await DB.select("*")
|
||||
.from("files")
|
||||
.where("ownerId", id)
|
||||
.orderBy("createdAt", "desc");
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let serialized = [];
|
||||
if (sanitize) {
|
||||
for (let file of query) {
|
||||
serialized.push(Serializers.sanitizeFile(file));
|
||||
}
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_FILES_BY_USER_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
34
node_common/data/methods/get-followers-by-user-id.js
Normal file
34
node_common/data/methods/get-followers-by-user-id.js
Normal file
@ -0,0 +1,34 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
export default async ({ userId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_FOLLOWERS_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("users.id", "users.username", "users.data")
|
||||
.from("users")
|
||||
.join("subscriptions", "subscriptions.ownerId", "=", "users.id")
|
||||
.where({ "subscriptions.userId": userId })
|
||||
.orderBy("subscriptions.createdAt", "desc");
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let serialized = [];
|
||||
for (let user of query) {
|
||||
serialized.push(Serializers.sanitizeUser(user));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_FOLLOWERS_BY_USER_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
35
node_common/data/methods/get-following-by-user-id.js
Normal file
35
node_common/data/methods/get-following-by-user-id.js
Normal file
@ -0,0 +1,35 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
export default async ({ ownerId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_FOLLOWING_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("users.id", "users.username", "users.data")
|
||||
.from("users")
|
||||
.join("subscriptions", "subscriptions.userId", "=", "users.id")
|
||||
.where({ "subscriptions.ownerId": ownerId })
|
||||
.orderBy("subscriptions.createdAt", "desc");
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let serialized = [];
|
||||
for (let user of query) {
|
||||
serialized.push(Serializers.sanitizeUser(user));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_FOLLOWING_BY_USER_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,45 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async (date, generateDataFn) => {
|
||||
return await runQuery({
|
||||
label: "GET_OR_CREATE_GLOBAL_STATS",
|
||||
queryFn: async (DB) => {
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
let end = new Date();
|
||||
end.setHours(23, 59, 59, 999);
|
||||
|
||||
const query = await DB.select("*")
|
||||
.from("global")
|
||||
.where("created_at", ">=", date)
|
||||
.where("created_at", "<", end)
|
||||
.first();
|
||||
|
||||
if (query && query.id) {
|
||||
return query;
|
||||
}
|
||||
|
||||
const insert = await DB.insert({
|
||||
created_at: date,
|
||||
data: await generateDataFn(),
|
||||
})
|
||||
.into("global")
|
||||
.returning("*");
|
||||
|
||||
const index = insert ? insert.pop() : null;
|
||||
|
||||
if (!index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(index));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log(e);
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_OR_CREATE_GLOBAL_STATS",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_PENDING_DATA_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*")
|
||||
.from("pending")
|
||||
.where({ owner_user_id: userId });
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_PENDING_DATA_BY_USER_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_PENDING_TRUSTED_RELATIONSHIPS_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*")
|
||||
.from("trusted")
|
||||
.where({ target_user_id: userId });
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_PENDING_TRUSTED_RELATIONSHIPS_BY_USER_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,20 +1,54 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id }) => {
|
||||
export default async ({ id, sanitize = false, includeFiles = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SLATE_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*").from("slates").where({ id }).first();
|
||||
// const slateFiles = () =>
|
||||
// DB.raw("json_agg(?? order by ?? asc) as ??", ["files", "slate_files.createdAt", "objects"]);
|
||||
const slateFiles = () =>
|
||||
DB.raw("coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??", [
|
||||
"files",
|
||||
"slate_files.createdAt",
|
||||
"files.id",
|
||||
"objects",
|
||||
]);
|
||||
|
||||
let query;
|
||||
|
||||
if (includeFiles) {
|
||||
query = await DB.select(
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
"slates.ownerId",
|
||||
"slates.isPublic",
|
||||
slateFiles()
|
||||
)
|
||||
.from("slates")
|
||||
.leftJoin("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.leftJoin("files", "slate_files.fileId", "=", "files.id")
|
||||
.where({ "slates.id": id })
|
||||
.groupBy("slates.id")
|
||||
.first();
|
||||
} else {
|
||||
query = await DB.select("id", "slatename", "data", "ownerId", "isPublic")
|
||||
.from("slates")
|
||||
.where({ id })
|
||||
.first();
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.id) {
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
if (sanitize) {
|
||||
query = Serializers.sanitizeSlate(query);
|
||||
}
|
||||
|
||||
return null;
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
|
@ -1,15 +1,14 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ slatename, ownerId, username }) => {
|
||||
export default async ({ slatename, ownerId, username, sanitize = false, includeFiles = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SLATE_BY_NAME",
|
||||
queryFn: async (DB) => {
|
||||
let id = ownerId;
|
||||
if (username && !ownerId) {
|
||||
const user = await DB.select("*")
|
||||
.from("users")
|
||||
.where({ username })
|
||||
.first();
|
||||
const user = await DB.select("id").from("users").where({ username }).first();
|
||||
|
||||
if (!user || user.error) {
|
||||
return null;
|
||||
@ -18,24 +17,50 @@ export default async ({ slatename, ownerId, username }) => {
|
||||
id = user.id;
|
||||
}
|
||||
|
||||
const query = await DB.select("*").from("slates").where({ slatename });
|
||||
// const slateFiles = () =>
|
||||
// DB.raw("json_agg(?? order by ?? asc) as ??", ["files", "slate_files.createdAt", "objects"]);
|
||||
|
||||
const slateFiles = () =>
|
||||
DB.raw("coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??", [
|
||||
"files",
|
||||
"slate_files.createdAt",
|
||||
"files.id",
|
||||
"objects",
|
||||
]);
|
||||
|
||||
let query;
|
||||
|
||||
if (includeFiles) {
|
||||
query = await DB.select(
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
"slates.ownerId",
|
||||
"slates.isPublic",
|
||||
slateFiles()
|
||||
)
|
||||
.from("slates")
|
||||
.leftJoin("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.leftJoin("files", "slate_files.fileId", "=", "files.id")
|
||||
.where({ "slates.slatename": slatename, "slates.ownerId": id })
|
||||
.groupBy("slates.id")
|
||||
.first();
|
||||
} else {
|
||||
query = await DB.select("id", "slatename", "data", "ownerId", "isPublic")
|
||||
.from("slates")
|
||||
.where({ slatename, ownerId: id })
|
||||
.first();
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let foundSlate;
|
||||
for (let slate of query) {
|
||||
if (slate.data.ownerId && slate.data.ownerId === id) {
|
||||
foundSlate = slate;
|
||||
}
|
||||
if (sanitize) {
|
||||
query = Serializers.sanitizeSlate(query);
|
||||
}
|
||||
|
||||
if (foundSlate && foundSlate.id) {
|
||||
return JSON.parse(JSON.stringify(foundSlate));
|
||||
}
|
||||
|
||||
return null;
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
|
38
node_common/data/methods/get-slate-files-by-cids.js
Normal file
38
node_common/data/methods/get-slate-files-by-cids.js
Normal file
@ -0,0 +1,38 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
/**
|
||||
* Gets the files in the slate with id SLATEID that have a cid in CIDS. Used to check for
|
||||
* duplicate cid files in a slate before adding a file to a slate
|
||||
*/
|
||||
export default async ({ slateId, cids }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SLATE_FILES_BY_CID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select(
|
||||
"files.id",
|
||||
"files.ownerId",
|
||||
"files.cid",
|
||||
"files.isPublic",
|
||||
"files.filename",
|
||||
"files.data"
|
||||
)
|
||||
.from("files")
|
||||
.join("slate_files", "slate_files.fileId", "=", "files.id")
|
||||
.where("slate_files.slateId", slateId)
|
||||
.whereIn("files.cid", cids)
|
||||
.groupBy("files.id");
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_SLATE_FILES_BY_CID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,77 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
// NOTE(jim): Passing URL and CID because right now
|
||||
// the CID only lives in the url property of the object.
|
||||
export default async ({ cid, url, ownerId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SLATE_OBJECT_BY_CID",
|
||||
queryFn: async (DB) => {
|
||||
let hasObjectURL;
|
||||
if (ownerId) {
|
||||
hasObjectURL = (textileURL) =>
|
||||
DB.raw(`?? @> ?::jsonb`, [
|
||||
"data",
|
||||
JSON.stringify({ objects: [{ url: textileURL, ownerId }] }),
|
||||
]);
|
||||
} else {
|
||||
hasObjectURL = (textileURL) =>
|
||||
DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ objects: [{ url: textileURL }] })]);
|
||||
}
|
||||
|
||||
/*
|
||||
const hasObjectCID = (id) =>
|
||||
DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ objects: [{ cid: id }] })]);
|
||||
|
||||
const query = await DB.select("*")
|
||||
.from("slates")
|
||||
.where(hasObjectCID(cid));
|
||||
*/
|
||||
|
||||
const query = await DB.select("*")
|
||||
.from("slates")
|
||||
.where(hasObjectURL(url));
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return query;
|
||||
|
||||
// let solution = [];
|
||||
// query.forEach((each) => {
|
||||
// // TODO(martina): Change this when you fix props.
|
||||
// const object = each.data.objects.find((object) => object.url === url);
|
||||
|
||||
// if (!object) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (!each.data.public) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// object.slate = {
|
||||
// id: each.id,
|
||||
// created_at: each.created_at,
|
||||
// updated_at: each.updated_at,
|
||||
// slatename: each.slatename,
|
||||
// data: {
|
||||
// ownerId: each.data.ownerId,
|
||||
// },
|
||||
// };
|
||||
|
||||
// solution.push(object);
|
||||
// });
|
||||
|
||||
// return solution;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_SLATE_OBJECT_BY_CID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,14 +1,54 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ ids }) => {
|
||||
export default async ({ ids, sanitize = false, includeFiles = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SLATES_BY_IDS",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*").from("slates").whereIn("id", ids);
|
||||
// const slateFiles = () =>
|
||||
// DB.raw("json_agg(?? order by ?? asc) as ??", ["files", "slate_files.createdAt", "objects"]);
|
||||
|
||||
const slateFiles = () =>
|
||||
DB.raw("coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??", [
|
||||
"files",
|
||||
"slate_files.createdAt",
|
||||
"files.id",
|
||||
"objects",
|
||||
]);
|
||||
|
||||
if (includeFiles) {
|
||||
query = await DB.select(
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
"slates.ownerId",
|
||||
"slates.isPublic",
|
||||
slateFiles()
|
||||
)
|
||||
.from("slates")
|
||||
.leftJoin("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.leftJoin("files", "slate_files.fileId", "=", "files.id")
|
||||
.whereIn("slates.id", ids)
|
||||
.groupBy("slates.id");
|
||||
} else {
|
||||
query = await DB.select("id", "slatename", "data", "ownerId", "isPublic")
|
||||
.from("slates")
|
||||
.whereIn("id", ids);
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let serialized = [];
|
||||
if (sanitize) {
|
||||
for (let slate of query) {
|
||||
serialized.push(Serializers.sanitizeSlate(slate));
|
||||
}
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
|
@ -1,32 +1,88 @@
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId, publicOnly = false }) => {
|
||||
export default async ({ ownerId, sanitize = false, includeFiles = false, publicOnly = false }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SLATES_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const hasUser = (id) =>
|
||||
DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ ownerId: id })]);
|
||||
const isPublic = () =>
|
||||
DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ public: true })]);
|
||||
// const slateFiles = () =>
|
||||
// DB.raw("json_agg(?? order by ?? asc) as ??", ["files", "slate_files.createdAt", "objects"]);
|
||||
|
||||
// const slateFiles = () =>
|
||||
// DB.raw("coalesce(json_agg(?? order by ?? asc), '[]'::json) as ??", [
|
||||
// "files",
|
||||
// "slate_files.createdAt",
|
||||
// "objects",
|
||||
// ]);
|
||||
const slateFiles = () =>
|
||||
DB.raw("coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??", [
|
||||
"files",
|
||||
"slate_files.createdAt",
|
||||
"files.id",
|
||||
"objects",
|
||||
]);
|
||||
|
||||
let query;
|
||||
if (publicOnly) {
|
||||
query = await DB.select("*")
|
||||
.from("slates")
|
||||
.where(hasUser(userId))
|
||||
.where(isPublic())
|
||||
.orderBy("updated_at", "desc");
|
||||
|
||||
if (includeFiles) {
|
||||
if (publicOnly) {
|
||||
query = await DB.select(
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
"slates.ownerId",
|
||||
"slates.isPublic",
|
||||
slateFiles()
|
||||
)
|
||||
.from("slates")
|
||||
.leftJoin("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.leftJoin("files", "slate_files.fileId", "=", "files.id")
|
||||
.where({ "slates.ownerId": ownerId, "slates.isPublic": true })
|
||||
.groupBy("slates.id")
|
||||
.orderBy("slates.updatedAt", "desc");
|
||||
} else {
|
||||
query = await DB.select(
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
"slates.ownerId",
|
||||
"slates.isPublic",
|
||||
slateFiles()
|
||||
)
|
||||
.from("slates")
|
||||
.leftJoin("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.leftJoin("files", "slate_files.fileId", "=", "files.id")
|
||||
.where({ "slates.ownerId": ownerId })
|
||||
.groupBy("slates.id")
|
||||
.orderBy("slates.updatedAt", "desc");
|
||||
}
|
||||
} else {
|
||||
query = await DB.select("*")
|
||||
.from("slates")
|
||||
.where(hasUser(userId))
|
||||
.orderBy("updated_at", "desc");
|
||||
if (publicOnly) {
|
||||
query = await DB.select("id", "slatename", "data", "ownerId", "isPublic")
|
||||
.from("slates")
|
||||
.where({ "slates.ownerId": ownerId, "slates.isPublic": true })
|
||||
.orderBy("updatedAt", "desc");
|
||||
} else {
|
||||
query = await DB.select("id", "slatename", "data", "ownerId", "isPublic")
|
||||
.from("slates")
|
||||
.where({ "slates.ownerId": ownerId })
|
||||
.orderBy("updatedAt", "desc");
|
||||
}
|
||||
}
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let serialized = [];
|
||||
if (sanitize) {
|
||||
for (let slate of query) {
|
||||
serialized.push(Serializers.sanitizeSlate(slate));
|
||||
}
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
|
35
node_common/data/methods/get-subscribers-by-slate-id.js
Normal file
35
node_common/data/methods/get-subscribers-by-slate-id.js
Normal file
@ -0,0 +1,35 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
|
||||
export default async ({ slateId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SUBSCRIBERS_BY_SLATE_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("users.id", "users.username", "users.data")
|
||||
.from("users")
|
||||
.join("subscriptions", "subscriptions.ownerId", "=", "users.id")
|
||||
.where({ "subscriptions.slateId": slateId })
|
||||
.orderBy("subscriptions.createdAt", "desc");
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let serialized = [];
|
||||
for (let user of query) {
|
||||
serialized.push(Serializers.sanitizeUser(user));
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_SUBSCRIBERS_BY_SLATE_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ userId }) => {
|
||||
return await runQuery({
|
||||
label: "GET_SUBSCRIBERS_BY_USER_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*")
|
||||
.from("subscriptions")
|
||||
.where({ target_user_id: userId });
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
console.log({
|
||||
error: true,
|
||||
decorator: "GET_SUBSCRIBERS_BY_USER_ID",
|
||||
});
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
};
|
@ -1,39 +0,0 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ subscriberUserId, slateId, userId }) => {
|
||||
const whereQuery = { owner_user_id: subscriberUserId };
|
||||
|
||||
if (slateId) {
|
||||
whereQuery.target_slate_id = slateId;
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
whereQuery.target_user_id = userId;
|
||||
}
|
||||
|
||||
return await runQuery({
|
||||
label: "GET_SUBSCRIPTION_BY_ID",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*")
|
||||
.from("subscriptions")
|
||||
.where(whereQuery)
|
||||
.first();
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.id) {
|
||||
return JSON.parse(JSON.stringify(query));
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_SUBSCRIPTION_BY_ID",
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,10 +1,18 @@
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
export default async ({ id }) => {
|
||||
export default async ({ ownerId, slateId, userId }) => {
|
||||
let whereQuery;
|
||||
|
||||
if (slateId) {
|
||||
whereQuery = { ownerId, slateId };
|
||||
} else if (userId) {
|
||||
whereQuery = { ownerId, userId };
|
||||
}
|
||||
|
||||
return await runQuery({
|
||||
label: "GET_TRUSTED_RELATIONSHIP_BY_ID",
|
||||
label: "GET_SUBSCRIPTION",
|
||||
queryFn: async (DB) => {
|
||||
const query = await DB.select("*").from("trusted").where({ id }).first();
|
||||
const query = await DB.select("*").from("subscriptions").where(whereQuery).first();
|
||||
|
||||
if (!query || query.error) {
|
||||
return null;
|
||||
@ -19,7 +27,7 @@ export default async ({ id }) => {
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
error: true,
|
||||
decorator: "GET_TRUSTED_RELATIONSHIP_BY_ID",
|
||||
decorator: "GET_SUBSCRIPTION",
|
||||
};
|
||||
},
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user