fixed filtering bug and cleaned up code

This commit is contained in:
Martina 2021-03-07 12:53:54 -08:00
parent f2543c6a1a
commit 55d724771a
212 changed files with 7141 additions and 6147 deletions

View File

@ -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 }),
});

View File

@ -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));
});

View File

@ -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;
};

View File

@ -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",
};

View File

@ -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
View 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'
]
*/

View File

@ -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/", "");
};

View File

@ -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;

View File

@ -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);

View 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();

View File

@ -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);
};
};

View File

@ -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() {

View File

@ -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 ? (

View File

@ -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>

View File

@ -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

View 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>
</> */
}

View File

@ -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,

View File

@ -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));
},
},
]}

View File

@ -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>

View File

@ -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;
}

View File

@ -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,
};
};

View File

@ -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,

View File

@ -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;

View File

@ -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}
/>

View File

@ -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 });
}}

View File

@ -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}

View File

@ -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)`,

View File

@ -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;

View File

@ -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}

View File

@ -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>

View File

@ -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>
))}

View File

@ -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;

View File

@ -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;

View File

@ -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,
});
}
};

View File

@ -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>

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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>

View File

@ -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>
);

View File

@ -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}

View File

@ -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) => {

View File

@ -98,7 +98,6 @@ export class Boundary extends React.PureComponent {
};
_handleOutsideRectEvent = (e) => {
// console.log("outside rect event");
this.props.onOutsideRectEvent(e);
};

View File

@ -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>

View File

@ -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"
);

View File

@ -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,
};

View File

@ -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 {

View File

@ -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",
};
},
});

View 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",
};
},
});
};

View 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",
};
},
});
};

View File

@ -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")

View File

@ -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",
};
},
});
};

View 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",
};
},
});
};

View File

@ -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 {

View File

@ -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) => {

View File

@ -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",
};
},
});
};

View File

@ -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 {

View File

@ -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",
};
},
});
};

View File

@ -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",
};
},
});
};

View 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",
};
},
});
};

View 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",
};
},
});
};

View File

@ -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,
};
},
});
};

View File

@ -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 {

View 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",
};
},
});
};

View 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",
};
},
});
};

View File

@ -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",
};
},
});
};

View File

@ -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,
};
},
});
};

View 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",
};
},
});
};

View File

@ -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",
};
},
});
};

View File

@ -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",
};
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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 [];
},
});
};

View 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 [];
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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 [];

View 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 [];
},
});
};

View File

@ -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({

View File

@ -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({

View 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 [];
},
});
};

View 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",
};
},
});
};

View 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",
};
},
});
};

View 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",
};
},
});
};

View 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",
};
},
});
};

View 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",
};
},
});
};

View 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 [];
},
});
};

View 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 [];
},
});
};

View File

@ -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",
};
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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 {

View File

@ -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 {

View 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",
};
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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) => {

View File

@ -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) => {

View 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 [];
},
});
};

View File

@ -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 [];
},
});
};

View File

@ -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",
};
},
});
};

View File

@ -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