mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-18 22:51:32 +03:00
Merge pull request #398 from filecoin-project/@martinalong/search-v2
Replaced rehydrate with webhooks
This commit is contained in:
commit
71fd7f4ae7
@ -77,6 +77,7 @@ PUBSUB_SECRET=pKLO4lbzdMrhAFKwPo9bnmq03bxQrtu3
|
||||
RESOURCE_URI_UPLOAD=http://localhost:4242
|
||||
RESOURCE_URI_STORAGE_UPLOAD=http://localhost:4242
|
||||
RESOURCE_URI_PUBSUB=ws://localhost:6464
|
||||
RESOURCE_URI_SEARCH=http://localhost:1313
|
||||
```
|
||||
|
||||
### Setup pubsub server
|
||||
|
@ -13,6 +13,12 @@ const DEFAULT_OPTIONS = {
|
||||
credentials: "include",
|
||||
};
|
||||
|
||||
const CORS_OPTIONS = {
|
||||
method: "POST",
|
||||
headers: REQUEST_HEADERS,
|
||||
credentials: "omit",
|
||||
};
|
||||
|
||||
const returnJSON = async (route, options) => {
|
||||
const response = await fetch(route, options);
|
||||
const json = await response.json();
|
||||
@ -120,8 +126,8 @@ export const search = async (data) => {
|
||||
return { decorator: "NO_SERVER_TRIP", data: { results: [] } };
|
||||
}
|
||||
|
||||
return await returnJSON(`/api/search/${data.query}`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
return await returnJSON(`http://localhost:1313/${data.query}`, {
|
||||
...CORS_OPTIONS,
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ export const init = ({ resource = "", viewer, onUpdate }) => {
|
||||
}, 30000 + 1000);
|
||||
});
|
||||
|
||||
client.addEventListener("message", function(event) {
|
||||
client.addEventListener("message", function (event) {
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import * as Actions from "~/common/actions";
|
||||
import * as Store from "~/common/store";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Credentials from "~/common/credentials";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import { dispatchCustomEvent } from "~/common/custom-events";
|
||||
import { encode } from "blurhash";
|
||||
@ -212,12 +213,7 @@ export const uploadToSlate = async ({ responses, slate }) => {
|
||||
skipped = addResponse.skipped;
|
||||
}
|
||||
}
|
||||
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded to slate. `;
|
||||
if (skipped) {
|
||||
message += `${skipped || 0} duplicate / existing file${
|
||||
added !== 1 ? "s were" : " was"
|
||||
} skipped.`;
|
||||
}
|
||||
let message = Strings.formatAsUploadMessage(added, skipped, true);
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
|
@ -103,6 +103,7 @@ export const error = {
|
||||
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",
|
||||
|
||||
//Data CID Status
|
||||
NO_CIDS_TO_CHECK: "There are no CIDs to check",
|
||||
|
@ -87,6 +87,16 @@ export const getCIDFromIPFS = (url) => {
|
||||
return cid;
|
||||
};
|
||||
|
||||
export const formatAsUploadMessage = (added, skipped, slate = false) => {
|
||||
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded${slate ? " to slate" : ""}. `;
|
||||
if (skipped) {
|
||||
message += `${skipped || 0} duplicate / existing file${
|
||||
added !== 1 ? "s were" : " was"
|
||||
} skipped.`;
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
export const formatAsFilecoinConversion = (number) => {
|
||||
const filecoinNumber = new FilecoinNumber(`${number}`, "attofil");
|
||||
//const inAttoFil = filecoinNumber.toAttoFil();
|
||||
|
@ -68,13 +68,8 @@ const STYLES_MESSAGE = css`
|
||||
`;
|
||||
|
||||
const STYLES_TEXT = css`
|
||||
border-bottom: 1px solid ${Constants.system.white};
|
||||
max-width: ${Constants.sizes.mobile}px;
|
||||
width: 100%;
|
||||
|
||||
@supports ((-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))) {
|
||||
border-bottom: 1px solid ${Constants.system.brand};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_MESSAGE_BOX = css`
|
||||
|
@ -180,9 +180,10 @@ export default class ApplicationPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
//make this a function that can handle different types of inputs and updates state accordingly (rather than ahving a bunch of different functions for each type)
|
||||
_handleUpdateViewer = (newViewerState) => {
|
||||
console.log("update viewer");
|
||||
console.log({ newViewerState });
|
||||
|
||||
if (this.state.viewer && newViewerState.id && newViewerState.id === this.state.viewer.id) {
|
||||
this.setState({
|
||||
viewer: { ...this.state.viewer, ...newViewerState, type: "VIEWER" },
|
||||
@ -425,39 +426,11 @@ export default class ApplicationPage extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(jim): Remove this once the viewer is being broadcasted for
|
||||
// this case.
|
||||
await this.rehydrate({ resetFiles: true });
|
||||
|
||||
// //NOTE(martina): to update the carousel to include the new file if you're on the data view page
|
||||
// dispatchCustomEvent({
|
||||
// name: "remote-update-carousel",
|
||||
// detail: {},
|
||||
// });
|
||||
|
||||
//NOTE(martina): to update the slate carousel to include the new file if you're on a slate page
|
||||
// const navigation = NavigationData.generate(this.state.viewer);
|
||||
// const next = this.state.history[this.state.currentIndex];
|
||||
// const current = NavigationData.getCurrentById(navigation, next.id);
|
||||
// if (
|
||||
// current.target &&
|
||||
// current.target.slateId &&
|
||||
// this.state.viewer.slates.map((slate) => slate.id).includes(current.target.slateId)
|
||||
// ) {
|
||||
// dispatchCustomEvent({
|
||||
// name: "remote-update-slate-screen",
|
||||
// detail: {},
|
||||
// });
|
||||
// }
|
||||
this.setState({ sidebar: null });
|
||||
|
||||
if (!slate) {
|
||||
const { added, skipped } = processResponse.data;
|
||||
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded. `;
|
||||
if (skipped) {
|
||||
message += `${skipped || 0} duplicate / existing file${
|
||||
added !== 1 ? "s were" : " was"
|
||||
} skipped.`;
|
||||
}
|
||||
let message = Strings.formatAsUploadMessage(added, skipped);
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
@ -527,6 +500,7 @@ export default class ApplicationPage extends React.Component {
|
||||
|
||||
_handleSidebarLoading = (sidebarLoading) => this.setState({ sidebarLoading });
|
||||
|
||||
//change the naem to hydrate. and use this only upon initial sign in (should be the only time you need it)
|
||||
rehydrate = async (options) => {
|
||||
const response = await Actions.hydrateAuthenticatedUser();
|
||||
|
||||
@ -587,10 +561,6 @@ export default class ApplicationPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(jim): Remove this once the viewer is being broadcasted for
|
||||
// this case.
|
||||
await this.rehydrate();
|
||||
|
||||
this._handleDismissSidebar();
|
||||
|
||||
return response;
|
||||
@ -666,8 +636,6 @@ export default class ApplicationPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(jim): Remove this once the viewer is being broadcasted for
|
||||
// this case.
|
||||
await this.rehydrate();
|
||||
|
||||
let wsclient = Websockets.getClient();
|
||||
@ -893,7 +861,6 @@ export default class ApplicationPage extends React.Component {
|
||||
</WebsitePrototypeWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE(jim): Authenticated.
|
||||
const navigation = NavigationData.generate(this.state.viewer);
|
||||
const next = this.state.history[this.state.currentIndex];
|
||||
@ -924,7 +891,6 @@ export default class ApplicationPage extends React.Component {
|
||||
currentIndex={this.state.currentIndex}
|
||||
history={this.state.history}
|
||||
onAction={this._handleAction}
|
||||
onRehydrate={this.rehydrate}
|
||||
onBack={this._handleBack}
|
||||
onForward={this._handleForward}
|
||||
onSignOut={this._handleSignOut}
|
||||
@ -944,7 +910,6 @@ export default class ApplicationPage extends React.Component {
|
||||
onUpload: this._handleUploadFiles,
|
||||
onBack: this._handleBack,
|
||||
onForward: this._handleForward,
|
||||
onRehydrate: this.rehydrate,
|
||||
sceneId: current.target.id,
|
||||
mobile: this.state.mobile,
|
||||
resources: this.props.resources,
|
||||
@ -967,7 +932,6 @@ export default class ApplicationPage extends React.Component {
|
||||
onUpload: this._handleUploadFiles,
|
||||
onSidebarLoading: this._handleSidebarLoading,
|
||||
onAction: this._handleAction,
|
||||
onRehydrate: this.rehydrate,
|
||||
resources: this.props.resources,
|
||||
});
|
||||
}
|
||||
@ -1002,7 +966,6 @@ export default class ApplicationPage extends React.Component {
|
||||
? current.target
|
||||
: this.state.data //NOTE(martina): for slates that are not your own
|
||||
}
|
||||
onRehydrate={this.rehydrate}
|
||||
slates={this.state.viewer.slates}
|
||||
onAction={this._handleAction}
|
||||
mobile={this.props.mobile}
|
||||
|
@ -140,14 +140,7 @@ export default class ApplicationHeader extends React.Component {
|
||||
_handleCreateSearch = (e) => {
|
||||
dispatchCustomEvent({
|
||||
name: "create-modal",
|
||||
detail: { modal: <SearchModal onAction={this.props.onAction} /> },
|
||||
});
|
||||
};
|
||||
|
||||
_handleRehydrate = (e) => {
|
||||
this.setState({ isRefreshing: true }, async () => {
|
||||
await this.props.onRehydrate();
|
||||
this.setState({ isRefreshing: false });
|
||||
detail: { modal: <SearchModal viewer={this.props.viewer} onAction={this.props.onAction} /> },
|
||||
});
|
||||
};
|
||||
|
||||
@ -193,16 +186,6 @@ export default class ApplicationHeader extends React.Component {
|
||||
<SVG.NavigationArrow height="24px" />
|
||||
</span>
|
||||
</span>
|
||||
<span css={STYLES_MOBILE_HIDDEN}>
|
||||
<span
|
||||
css={this.state.isRefreshing ? STYLES_ROTATION : STYLES_STATIC}
|
||||
style={{ marginLeft: 24 }}
|
||||
>
|
||||
<span css={STYLES_ICON_ELEMENT} onClick={this._handleRehydrate}>
|
||||
<SVG.Refresh height="20px" />
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span css={STYLES_MOBILE_HIDDEN}>
|
||||
<span
|
||||
css={STYLES_ICON_ELEMENT}
|
||||
|
@ -278,7 +278,6 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
selected: { ...this.state.selected, [slate.id]: true },
|
||||
});
|
||||
}
|
||||
this.props.onRehydrate();
|
||||
};
|
||||
|
||||
_handleDownload = () => {
|
||||
@ -326,7 +325,13 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.props.onRehydrate();
|
||||
let message = Strings.formatAsUploadMessage(response.data.added, response.data.skipped);
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: { message, status: !response.data.added ? null : "INFO" },
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
||||
@ -363,8 +368,8 @@ export default class CarouselSidebarSlate extends React.Component {
|
||||
this.setState({ loading: false });
|
||||
return;
|
||||
}
|
||||
await this.props.onRehydrate();
|
||||
this.setState({ loading: false });
|
||||
console.log("got here to end of handle delete");
|
||||
};
|
||||
|
||||
_toggleAccordion = (tab) => {
|
||||
|
@ -243,7 +243,6 @@ export default class DataView extends React.Component {
|
||||
if (windowBottom >= docHeight - 600) {
|
||||
this.setState({ viewLimit: this.state.viewLimit + 30 });
|
||||
}
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
_handleCheckScroll = Window.debounce(this._handleScroll, 200);
|
||||
@ -300,7 +299,6 @@ export default class DataView extends React.Component {
|
||||
dispatchCustomEvent({ name: "state-global-carousel-loading", detail: { loading: false } });
|
||||
return;
|
||||
}
|
||||
await this.props.onRehydrate();
|
||||
this._handleLoading({ cids });
|
||||
this.setState({ checked: {} });
|
||||
dispatchCustomEvent({
|
||||
@ -357,12 +355,7 @@ export default class DataView extends React.Component {
|
||||
}
|
||||
|
||||
const { added, skipped } = addResponse;
|
||||
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded. `;
|
||||
if (skipped) {
|
||||
message += `${skipped || 0} duplicate / existing file${
|
||||
added !== 1 ? "s were" : " was"
|
||||
} skipped.`;
|
||||
}
|
||||
let message = Strings.formatAsUploadMessage(added, skipped, true);
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
@ -370,7 +363,6 @@ export default class DataView extends React.Component {
|
||||
},
|
||||
});
|
||||
|
||||
await this.props.onRehydrate();
|
||||
System.dispatchCustomEvent({
|
||||
name: "state-global-carousel-loading",
|
||||
detail: { loading: false },
|
||||
@ -422,8 +414,6 @@ export default class DataView extends React.Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
await this.props.onRehydrate();
|
||||
|
||||
System.dispatchCustomEvent({
|
||||
name: "state-global-carousel-loading",
|
||||
detail: { loading: false },
|
||||
|
@ -70,10 +70,7 @@ const UserEntry = ({ item }) => {
|
||||
return (
|
||||
<div css={STYLES_ENTRY}>
|
||||
<div css={STYLES_USER_ENTRY_CONTAINER}>
|
||||
<div
|
||||
style={{ backgroundImage: `url(${item.data.photo})` }}
|
||||
css={STYLES_PROFILE_IMAGE}
|
||||
/>
|
||||
<div style={{ backgroundImage: `url(${item.data.photo})` }} css={STYLES_PROFILE_IMAGE} />
|
||||
{item.data.name ? <div css={STYLES_TITLE}>{item.data.name}</div> : null}
|
||||
<div css={STYLES_TITLE}>@{item.username}</div>
|
||||
</div>
|
||||
@ -132,18 +129,14 @@ const FileEntry = ({ item }) => {
|
||||
<SVG.Folder height="16px" />
|
||||
</div>
|
||||
<div css={STYLES_TITLE}>
|
||||
{item.data.file.title || item.data.file.name}
|
||||
{item.data.file.title || item.data.file.name || item.data.file.file}
|
||||
</div>
|
||||
{item.data.slate.owner && item.data.slate.owner.username ? (
|
||||
{item.data.slate && item.data.slate.owner && item.data.slate.owner.username ? (
|
||||
<div css={STYLES_TITLE}>@{item.data.slate.owner.username}</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div css={STYLES_SLATE_IMAGES_CONTAINER}>
|
||||
<SlatePreviewRow
|
||||
numItems={1}
|
||||
slate={{ data: { objects: [item.data.file] } }}
|
||||
small
|
||||
/>
|
||||
<SlatePreviewRow numItems={1} slate={{ data: { objects: [item.data.file] } }} small />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -154,10 +147,12 @@ export class SearchModal extends React.Component {
|
||||
loading: true,
|
||||
results: [],
|
||||
inputValue: "",
|
||||
filters: { slates: true },
|
||||
};
|
||||
|
||||
componentDidMount = async () => {
|
||||
await this.fillDirectory();
|
||||
// await this.fillPersonalDirectory();
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
||||
@ -165,19 +160,9 @@ export class SearchModal extends React.Component {
|
||||
const response = await Actions.getNetworkDirectory();
|
||||
this.miniSearch = new MiniSearch({
|
||||
fields: ["slatename", "data.name", "username", "filename"],
|
||||
storeFields: [
|
||||
"type",
|
||||
"slatename",
|
||||
"username",
|
||||
"data",
|
||||
"id",
|
||||
"slates",
|
||||
"owner",
|
||||
],
|
||||
storeFields: ["type", "slatename", "username", "data", "id", "slates", "owner"],
|
||||
extractField: (entry, fieldName) => {
|
||||
return fieldName
|
||||
.split(".")
|
||||
.reduce((doc, key) => doc && doc[key], entry);
|
||||
return fieldName.split(".").reduce((doc, key) => doc && doc[key], entry);
|
||||
},
|
||||
searchOptions: {
|
||||
fuzzy: 0.15,
|
||||
@ -199,16 +184,72 @@ export class SearchModal extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
this.users = response.data.users;
|
||||
this.slates = response.data.slates;
|
||||
this.miniSearch.addAll(response.data.users);
|
||||
this.miniSearch.addAll(response.data.slates);
|
||||
this.miniSearch.addAll(files);
|
||||
}
|
||||
};
|
||||
|
||||
fillPersonalDirectory = () => {
|
||||
this.personalSearch = new MiniSearch({
|
||||
fields: ["slatename", "data.name", "username", "filename"],
|
||||
storeFields: ["type", "slatename", "username", "data", "id", "slates", "owner"],
|
||||
extractField: (entry, fieldName) => {
|
||||
return fieldName.split(".").reduce((doc, key) => doc && doc[key], entry);
|
||||
},
|
||||
searchOptions: {
|
||||
fuzzy: 0.15,
|
||||
},
|
||||
});
|
||||
let files = this.props.viewer.library[0].children.map((file, i) => {
|
||||
return {
|
||||
type: "DATA_FILE",
|
||||
id: file.id,
|
||||
filename: file.name || file.file,
|
||||
data: {
|
||||
file: {
|
||||
...file,
|
||||
url: `${Constants.gateways.ipfs}/${file.cid || file.ipfs.replace("/ipfs/", "")}`,
|
||||
},
|
||||
index: i,
|
||||
},
|
||||
};
|
||||
});
|
||||
let slates = this.props.viewer.slates.map((slate) => {
|
||||
return { ...slate, type: "SLATE", owner: this.props.viewer };
|
||||
});
|
||||
this.personalSearch.addAll(slates);
|
||||
let userIds = [];
|
||||
this.personalSearch.addAll(this.props.viewer.pendingTrusted.map((trust) => trust.owner));
|
||||
userIds.push(...this.props.viewer.pendingTrusted.map((trust) => trust.owner.id));
|
||||
this.personalSearch.addAll(
|
||||
this.props.viewer.trusted
|
||||
.filter((trust) => !userIds.includes(trust.user.id))
|
||||
.map((trust) => trust.user)
|
||||
);
|
||||
userIds.push(...this.props.viewer.trusted.map((trust) => trust.user.id));
|
||||
this.personalSearch.addAll(
|
||||
this.props.viewer.subscriptions
|
||||
.filter((sub) => sub.target_user_id && !userIds.includes(sub.user.id))
|
||||
.map((sub) => sub.user)
|
||||
);
|
||||
this.personalSearch.addAll(files);
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
this.setState({ inputValue: e.target.value });
|
||||
//interpret the thing with handleInterpret (and change shownValue accordingly)
|
||||
};
|
||||
|
||||
//converts input to search filter options
|
||||
_handleInterpret = (query) => {
|
||||
if (query.contains("files:")) {
|
||||
} else if (query.contains("users:")) {
|
||||
} else if (query.contains("slates:")) {
|
||||
} else if (query.contains("tags:")) {
|
||||
} else if (query.contains("#")) {
|
||||
} else if (query.contains("@")) {
|
||||
}
|
||||
};
|
||||
|
||||
_handleSearch = () => {
|
||||
@ -230,6 +271,63 @@ export class SearchModal extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
// _handleSearch = async () => {
|
||||
// if (this.state.loading) return;
|
||||
|
||||
// if (this.state.filters["my"]) {
|
||||
// let miniSearch = this.personalSearch;
|
||||
// let filter;
|
||||
// if (this.state.filters["files"]) {
|
||||
// filter = {
|
||||
// filter: (result) => {
|
||||
// return result.type === "FILE" || result.type === "DATA_FILE";
|
||||
// },
|
||||
// };
|
||||
// } else if (this.state.filters["users"]) {
|
||||
// filter = {
|
||||
// filter: (result) => result.type === "USER",
|
||||
// };
|
||||
// } else if (this.state.filters["slates"]) {
|
||||
// filter = {
|
||||
// filter: (result) => result.type === "SLATE",
|
||||
// };
|
||||
// }
|
||||
// let searchResults;
|
||||
// if (filter) {
|
||||
// searchResults = miniSearch.search(this.state.inputValue, filter);
|
||||
// } else {
|
||||
// searchResults = miniSearch.search(this.state.inputValue);
|
||||
// }
|
||||
// let ids = new Set();
|
||||
// for (let result of searchResults) {
|
||||
// ids.add(result.id);
|
||||
// }
|
||||
// let autofill = miniSearch.autoSuggest(this.state.inputValue);
|
||||
// for (let i = 0; i < autofill.length; i++) {
|
||||
// let result;
|
||||
// if (Object.keys(this.state.filter).length) {
|
||||
// result = miniSearch.search(autofill[i].suggestion, this.state.filter);
|
||||
// } else {
|
||||
// result = miniSearch.search(autofill[i].suggestion);
|
||||
// }
|
||||
// if (result && result.length) {
|
||||
// result = result[0];
|
||||
// if (!ids.has(result.id)) {
|
||||
// ids.add(result.id);
|
||||
// searchResults.push(result);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// this.setState({ results: searchResults });
|
||||
// } else {
|
||||
// const results = await Actions.search({
|
||||
// query: this.state.inputValue,
|
||||
// filter: this.state.filters,
|
||||
// });
|
||||
// this.setState({ results: results.data.results });
|
||||
// }
|
||||
// };
|
||||
|
||||
_handleSelect = async (value) => {
|
||||
if (value.type === "SLATE") {
|
||||
this.props.onAction({
|
||||
@ -245,6 +343,16 @@ export class SearchModal extends React.Component {
|
||||
data: value.data,
|
||||
});
|
||||
}
|
||||
if (value.type === "DATA_FILE") {
|
||||
await this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
value: "data",
|
||||
});
|
||||
dispatchCustomEvent({
|
||||
name: "slate-global-open-carousel",
|
||||
detail: { index: value.data.data.index },
|
||||
});
|
||||
}
|
||||
if (value.type === "FILE") {
|
||||
await this.props.onAction({
|
||||
type: "NAVIGATE",
|
||||
@ -281,10 +389,10 @@ export class SearchModal extends React.Component {
|
||||
},
|
||||
component: <SlateEntry item={item} />,
|
||||
});
|
||||
} else if (item.type === "FILE") {
|
||||
} else if (item.type === "FILE" || item.type === "DATA_FILE") {
|
||||
results.push({
|
||||
value: {
|
||||
type: "FILE",
|
||||
type: item.type,
|
||||
data: item,
|
||||
},
|
||||
component: <FileEntry item={item} />,
|
||||
@ -301,7 +409,7 @@ export class SearchModal extends React.Component {
|
||||
onSelect={this._handleSelect}
|
||||
onChange={this._handleChange}
|
||||
onSearch={this._handleSearch}
|
||||
inputValue={this.state.inputValue}
|
||||
inputValue={this.state.shownValue}
|
||||
style={STYLES_SEARCH_DROPDOWN}
|
||||
/>
|
||||
</div>
|
||||
|
@ -89,12 +89,7 @@ export default class SidebarAddFileToSlate extends React.Component {
|
||||
}
|
||||
|
||||
const { added, skipped } = addResponse;
|
||||
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded. `;
|
||||
if (skipped) {
|
||||
message += `${skipped || 0} duplicate / existing file${
|
||||
added !== 1 ? "s were" : " was"
|
||||
} skipped.`;
|
||||
}
|
||||
let message = Strings.formatAsUploadMessage(added, skipped, true);
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
@ -102,7 +97,6 @@ export default class SidebarAddFileToSlate extends React.Component {
|
||||
},
|
||||
});
|
||||
}
|
||||
await this.props.onRehydrate();
|
||||
this.props.onCancel();
|
||||
};
|
||||
|
||||
|
@ -117,12 +117,7 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
}
|
||||
|
||||
const { added, skipped } = addResponse;
|
||||
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded. `;
|
||||
if (skipped) {
|
||||
message += `${skipped || 0} duplicate / existing file${
|
||||
added !== 1 ? "s were" : " was"
|
||||
} skipped.`;
|
||||
}
|
||||
let message = Strings.formatAsUploadMessage(added, skipped, true);
|
||||
if (message) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
@ -131,8 +126,6 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await this.props.onRehydrate();
|
||||
}
|
||||
|
||||
this.setState({ loading: false });
|
||||
|
@ -65,15 +65,13 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
body: this.state.body,
|
||||
},
|
||||
});
|
||||
console.log(response);
|
||||
|
||||
if (!response) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message:
|
||||
"We're having trouble connecting right now. Please try again later",
|
||||
message: "We're having trouble connecting right now. Please try again later",
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -87,8 +85,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
});
|
||||
return this.setState({ loading: false });
|
||||
}
|
||||
|
||||
await this.props.onSubmit({});
|
||||
this.props.onCancel();
|
||||
};
|
||||
|
||||
_handleCancel = () => {
|
||||
@ -103,9 +100,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
this.setState({ loading: true });
|
||||
|
||||
if (
|
||||
!window.confirm(
|
||||
"Are you sure you want to delete this Slate? This action is irreversible."
|
||||
)
|
||||
!window.confirm("Are you sure you want to delete this Slate? This action is irreversible.")
|
||||
) {
|
||||
return this.setState({ loading: false });
|
||||
}
|
||||
@ -119,8 +114,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message:
|
||||
"We're having trouble connecting right now. Please try again later",
|
||||
message: "We're having trouble connecting right now. Please try again later",
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -139,8 +133,6 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
type: "NAVIGATE",
|
||||
value: "V1_NAVIGATION_SLATES",
|
||||
});
|
||||
|
||||
return await this.props.onRehydrate();
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -182,19 +174,14 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
Changing the slatename will change your public slate URL. Your slate
|
||||
URL is:{" "}
|
||||
Changing the slatename will change your public slate URL. Your slate URL is:{" "}
|
||||
</System.P>
|
||||
<System.P
|
||||
style={{
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
style={{ color: Constants.system.brand }}
|
||||
>
|
||||
<a href={url} target="_blank" style={{ color: Constants.system.brand }}>
|
||||
https://slate.host{url}
|
||||
</a>
|
||||
</System.P>
|
||||
@ -236,11 +223,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
</System.P>
|
||||
|
||||
<div css={STYLES_IMAGE_BOX} style={{ marginTop: 24 }}>
|
||||
<img
|
||||
src={preview}
|
||||
alt=""
|
||||
style={{ maxWidth: "368px", maxHeight: "368px" }}
|
||||
/>
|
||||
<img src={preview} alt="" style={{ maxWidth: "368px", maxHeight: "368px" }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -252,20 +235,12 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
? "Public. Anyone can search for and view this slate."
|
||||
: "Private. Only you can view this slate."}
|
||||
</System.P>
|
||||
<System.Toggle
|
||||
name="public"
|
||||
onChange={this._handleChange}
|
||||
active={this.state.public}
|
||||
/>
|
||||
<System.Toggle name="public" onChange={this._handleChange} active={this.state.public} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 40 }}>
|
||||
<System.ButtonPrimary
|
||||
full
|
||||
onClick={this._handleSubmit}
|
||||
loading={this.state.loading}
|
||||
>
|
||||
<System.ButtonPrimary full onClick={this._handleSubmit} loading={this.state.loading}>
|
||||
Save changes
|
||||
</System.ButtonPrimary>
|
||||
|
||||
@ -284,11 +259,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
|
||||
{!this.state.loading ? (
|
||||
<div style={{ marginTop: 48 }}>
|
||||
<System.ButtonWarning
|
||||
full
|
||||
onClick={this._handleDelete}
|
||||
style={{ overflow: "hidden" }}
|
||||
>
|
||||
<System.ButtonWarning full onClick={this._handleDelete} style={{ overflow: "hidden" }}>
|
||||
Delete{" "}
|
||||
{this.props.current.data && this.props.current.data.name
|
||||
? this.props.current.data.name
|
||||
|
@ -302,7 +302,6 @@ export class GlobalCarousel extends React.Component {
|
||||
detail: { alert: { decorator: response.decorator } },
|
||||
});
|
||||
}
|
||||
this.props.onRehydrate();
|
||||
this.setState({ loading: false, saving: false });
|
||||
};
|
||||
|
||||
@ -347,7 +346,7 @@ export class GlobalCarousel extends React.Component {
|
||||
this.state.index < this.props.viewer.library[0].children.length
|
||||
) {
|
||||
data = this.props.viewer.library[0].children[this.state.index];
|
||||
data.url = `${Constants.gateways.ipfs}/${data.cid || Strings.getCIDFromIPFS(data.ipfs)}`;
|
||||
data.url = `${Constants.gateways.ipfs}/${data.cid || data.ipfs.replace("/ipfs/", "")}`;
|
||||
}
|
||||
if (!data) {
|
||||
this._handleClose();
|
||||
@ -398,7 +397,6 @@ export class GlobalCarousel extends React.Component {
|
||||
saving={this.state.saving}
|
||||
loading={this.state.loading}
|
||||
slates={this.props.slates}
|
||||
onRehydrate={this.props.onRehydrate}
|
||||
onAction={this.props.onAction}
|
||||
data={data}
|
||||
cid={data.cid}
|
||||
@ -411,7 +409,6 @@ export class GlobalCarousel extends React.Component {
|
||||
loading={this.state.loading}
|
||||
slates={this.props.slates}
|
||||
onClose={this._handleClose}
|
||||
onRehydrate={this.props.onRehydrate}
|
||||
onAction={this.props.onAction}
|
||||
data={data}
|
||||
external={this.props.external}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as LibraryManager from "~/node_common/managers/library";
|
||||
|
||||
import { runQuery } from "~/node_common/data/utilities";
|
||||
|
||||
@ -7,32 +6,9 @@ export default async ({ owner_user_id }) => {
|
||||
return await runQuery({
|
||||
label: "REMOVE_PENDING_DATA_FOR_USER",
|
||||
queryFn: async (DB) => {
|
||||
const user = await Data.getUserById({
|
||||
id: owner_user_id,
|
||||
});
|
||||
const pending = await DB.from("pending")
|
||||
.where({ owner_user_id })
|
||||
.returning("*")
|
||||
.del();
|
||||
let cids = [];
|
||||
let noRepeats = [];
|
||||
for (let entry of pending) {
|
||||
if (cids.includes(entry.data.ipfs)) continue;
|
||||
cids.push(entry.data.ipfs);
|
||||
noRepeats.push(entry.data);
|
||||
}
|
||||
const pending = await DB.from("pending").where({ owner_user_id }).returning("*").del();
|
||||
|
||||
const { updatedUserDataFields, added, skipped } = LibraryManager.addData({
|
||||
user,
|
||||
files: noRepeats,
|
||||
});
|
||||
|
||||
await Data.updateUserById({
|
||||
id: user.id,
|
||||
data: updatedUserDataFields,
|
||||
});
|
||||
|
||||
return { added, skipped };
|
||||
return pending;
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
return {
|
||||
|
@ -41,3 +41,4 @@ export const TEXTILE_SLACK_WEBHOOK_KEY = process.env.TEXTILE_SLACK_WEBHOOK_KEY;
|
||||
export const RESOURCE_URI_UPLOAD = process.env.RESOURCE_URI_UPLOAD;
|
||||
export const RESOURCE_URI_STORAGE_UPLOAD = process.env.RESOURCE_URI_STORAGE_UPLOAD;
|
||||
export const RESOURCE_URI_PUBSUB = process.env.RESOURCE_URI_PUBSUB;
|
||||
export const RESOURCE_URI_SEARCH = process.env.RESOURCE_URI_SEARCH;
|
@ -21,9 +21,37 @@ const delay = async (waitMs) => {
|
||||
|
||||
Websocket.create();
|
||||
|
||||
//NOTE(martina): type = "UPDATE" will be processed by slate and lens. type = "SEARCH" will only be processed by lens
|
||||
const websocketSend = async (type, data) => {
|
||||
if (Strings.isEmpty(Environment.PUBSUB_SECRET)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ws = Websocket.get();
|
||||
const encryptedData = await Utilities.encryptWithSecret(
|
||||
JSON.stringify(data),
|
||||
Environment.PUBSUB_SECRET
|
||||
);
|
||||
|
||||
// NOTE(jim): Only allow this to be passed around encrypted.
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type,
|
||||
iv: encryptedData.iv,
|
||||
data: encryptedData.hex,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const hydratePartialViewer = async (user) => {
|
||||
const data = {
|
||||
...Serializers.user(user),
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
data: {
|
||||
name: user.data.name ? user.data.name : "",
|
||||
photo: user.data.photo ? user.data.photo : "",
|
||||
body: user.data.body ? user.data.body : "",
|
||||
},
|
||||
type: "PARTIAL_VIEWER",
|
||||
library: user.data.library,
|
||||
onboarding: user.data.onboarding || {},
|
||||
@ -40,24 +68,237 @@ export const hydratePartialViewer = async (user) => {
|
||||
: null,
|
||||
};
|
||||
|
||||
if (Strings.isEmpty(Environment.PUBSUB_SECRET)) {
|
||||
return;
|
||||
websocketSend("UPDATE", data);
|
||||
};
|
||||
|
||||
export const hydratePartialOnboarding = async (onboarding, userId) => {
|
||||
const data = {
|
||||
id: userId,
|
||||
onboarding,
|
||||
};
|
||||
websocketSend("UPDATE", data);
|
||||
};
|
||||
|
||||
export const hydratePartialSubscriptions = async (updated, userId) => {
|
||||
const data = {
|
||||
id: userId,
|
||||
};
|
||||
const user = await Data.getUserById({ id: userId });
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ws = Websocket.get();
|
||||
const encryptedData = await Utilities.encryptWithSecret(
|
||||
JSON.stringify(data),
|
||||
Environment.PUBSUB_SECRET
|
||||
);
|
||||
if (user.error) {
|
||||
return null;
|
||||
}
|
||||
let mostRecent;
|
||||
if (updated.subscriptions) {
|
||||
const subscriptions = await Data.getSubscriptionsByUserId({ userId });
|
||||
let r1 = await Serializers.doSubscriptions({
|
||||
users: [],
|
||||
slates: [],
|
||||
subscriptions,
|
||||
serializedUsersMap: { [user.id]: Serializers.user(user) },
|
||||
serializedSlatesMap: {},
|
||||
});
|
||||
data.subscriptions = r1.serializedSubscriptions;
|
||||
mostRecent = r1;
|
||||
}
|
||||
if (updated.subscribers) {
|
||||
const subscribers = await Data.getSubscribersByUserId({ userId });
|
||||
let r2 = await Serializers.doSubscribers({
|
||||
users: [],
|
||||
slates: [],
|
||||
subscribers,
|
||||
serializedUsersMap: mostRecent
|
||||
? mostRecent.serializedUsersMap
|
||||
: { [user.id]: Serializers.user(user) },
|
||||
serializedSlatesMap: mostRecent ? mostRecent.serializedSlatesMap : {},
|
||||
});
|
||||
data.subscribers = r2.serializedSubscribers;
|
||||
mostRecent = r2;
|
||||
}
|
||||
if (updated.trusted) {
|
||||
console.log("update trusted");
|
||||
const trusted = await Data.getTrustedRelationshipsByUserId({ userId });
|
||||
let r3 = await Serializers.doTrusted({
|
||||
users: [],
|
||||
trusted,
|
||||
serializedUsersMap: mostRecent
|
||||
? mostRecent.serializedUsersMap
|
||||
: { [user.id]: Serializers.user(user) },
|
||||
serializedSlatesMap: mostRecent ? mostRecent.serializedSlatesMap : {},
|
||||
});
|
||||
data.trusted = r3.serializedTrusted;
|
||||
mostRecent = r3;
|
||||
}
|
||||
if (updated.pendingTrusted) {
|
||||
console.log("update pending trusted");
|
||||
const pendingTrusted = await Data.getPendingTrustedRelationshipsByUserId({
|
||||
userId,
|
||||
});
|
||||
let r4 = await Serializers.doPendingTrusted({
|
||||
users: [userId],
|
||||
pendingTrusted,
|
||||
serializedUsersMap: mostRecent
|
||||
? mostRecent.serializedUsersMap
|
||||
: { [user.id]: Serializers.user(user) },
|
||||
serializedSlatesMap: mostRecent ? mostRecent.serializedSlatesMap : {},
|
||||
});
|
||||
data.pendingTrusted = r4.serializedPendingTrusted;
|
||||
}
|
||||
websocketSend("UPDATE", data);
|
||||
};
|
||||
|
||||
// NOTE(jim): Only allow this to be passed around encrypted.
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "UPDATE",
|
||||
iv: encryptedData.iv,
|
||||
data: encryptedData.hex,
|
||||
})
|
||||
);
|
||||
export const hydratePartialKeys = async (keys, userId) => {
|
||||
const data = {
|
||||
id: userId,
|
||||
keys,
|
||||
};
|
||||
websocketSend("UPDATE", data);
|
||||
};
|
||||
|
||||
export const hydratePartialLibrary = async (library, userId) => {
|
||||
const data = {
|
||||
id: userId,
|
||||
library,
|
||||
};
|
||||
websocketSend("UPDATE", data);
|
||||
};
|
||||
|
||||
export const hydratePartialSlates = async (slates, userId) => {
|
||||
console.log("hydrate slates");
|
||||
const data = {
|
||||
id: userId,
|
||||
slates,
|
||||
};
|
||||
websocketSend("UPDATE", data);
|
||||
};
|
||||
|
||||
// export const hydratePartialActivity = async (activity, userId) => {
|
||||
// this one will need to be more complex like what is in hydrate
|
||||
// websocketSend("UPDATE", data);
|
||||
// };
|
||||
|
||||
export const hydrate = async (id) => {
|
||||
const user = await Data.getUserById({
|
||||
id,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO(jim): You can serialize this last because you will have all the information
|
||||
// from subscriptions, trusted, and pendingTrusted most likely.
|
||||
const activity = await Data.getActivityForUserId({ userId: id });
|
||||
const slates = await Data.getSlatesByUserId({ userId: id });
|
||||
const keys = await Data.getAPIKeysByUserId({ userId: id });
|
||||
const subscriptions = await Data.getSubscriptionsByUserId({ userId: id });
|
||||
const subscribers = await Data.getSubscribersByUserId({ userId: id });
|
||||
|
||||
let serializedUsersMap = { [user.id]: Serializers.user(user) };
|
||||
let serializedSlatesMap = {};
|
||||
|
||||
// NOTE(jim): The most expensive call first.
|
||||
const r1 = await Serializers.doSubscriptions({
|
||||
users: [],
|
||||
slates: [],
|
||||
subscriptions,
|
||||
serializedUsersMap,
|
||||
serializedSlatesMap,
|
||||
});
|
||||
|
||||
const r2 = await Serializers.doSubscribers({
|
||||
users: [],
|
||||
slates: [],
|
||||
subscribers,
|
||||
serializedUsersMap: r1.serializedUsersMap,
|
||||
serializedSlatesMap: r1.serializedSlatesMap,
|
||||
});
|
||||
|
||||
// NOTE(jim): If any trusted users are subscription users, this ends up being cheaper.
|
||||
const trusted = await Data.getTrustedRelationshipsByUserId({ userId: id });
|
||||
const r3 = await Serializers.doTrusted({
|
||||
users: [],
|
||||
trusted,
|
||||
serializedUsersMap: r2.serializedUsersMap,
|
||||
serializedSlatesMap: r2.serializedSlatesMap,
|
||||
});
|
||||
|
||||
// NOTE(jim): This should be the cheapest call.
|
||||
const pendingTrusted = await Data.getPendingTrustedRelationshipsByUserId({
|
||||
userId: id,
|
||||
});
|
||||
const r4 = await Serializers.doPendingTrusted({
|
||||
users: [id],
|
||||
pendingTrusted,
|
||||
serializedUsersMap: r3.serializedUsersMap,
|
||||
serializedSlatesMap: r3.serializedSlatesMap,
|
||||
});
|
||||
|
||||
let bytes = 0;
|
||||
let imageBytes = 0;
|
||||
let videoBytes = 0;
|
||||
let audioBytes = 0;
|
||||
let epubBytes = 0;
|
||||
let pdfBytes = 0;
|
||||
user.data.library[0].children.forEach((each) => {
|
||||
if (each.type && each.type.startsWith("image/")) {
|
||||
imageBytes += each.size;
|
||||
} else if (each.type && each.type.startsWith("video/")) {
|
||||
videoBytes += each.size;
|
||||
} else if (each.type && each.type.startsWith("audio/")) {
|
||||
audioBytes += each.size;
|
||||
} else if (each.type && each.type.startsWith("application/epub")) {
|
||||
epubBytes += each.size;
|
||||
} else if (each.type && each.type.startsWith("application/pdf")) {
|
||||
pdfBytes += each.size;
|
||||
}
|
||||
bytes += each.size;
|
||||
});
|
||||
|
||||
let data = {
|
||||
...Serializers.user(user),
|
||||
type: "VIEWER",
|
||||
library: user.data.library,
|
||||
onboarding: user.data.onboarding || {},
|
||||
|
||||
// TODO(jim): Move this elsewhere.
|
||||
allow_filecoin_directory_listing: user.data.allow_filecoin_directory_listing
|
||||
? user.data.allow_filecoin_directory_listing
|
||||
: null,
|
||||
allow_automatic_data_storage: user.data.allow_automatic_data_storage
|
||||
? user.data.allow_automatic_data_storage
|
||||
: null,
|
||||
allow_encrypted_data_storage: user.data.allow_encrypted_data_storage
|
||||
? user.data.allow_encrypted_data_storage
|
||||
: null,
|
||||
|
||||
// NOTE(jim): Remaining data.
|
||||
stats: {
|
||||
bytes,
|
||||
maximumBytes: Constants.TEXTILE_ACCOUNT_BYTE_LIMIT,
|
||||
imageBytes,
|
||||
videoBytes,
|
||||
audioBytes,
|
||||
epubBytes,
|
||||
pdfBytes,
|
||||
},
|
||||
keys,
|
||||
activity,
|
||||
slates,
|
||||
subscriptions: r1.serializedSubscriptions,
|
||||
subscribers: r2.serializedSubscribers,
|
||||
trusted: r3.serializedTrusted,
|
||||
pendingTrusted: r4.serializedPendingTrusted,
|
||||
};
|
||||
websocketSend("UPDATE", data);
|
||||
};
|
||||
|
||||
// TODO(jim): Work on better serialization when adoption starts occuring.
|
||||
|
@ -59,6 +59,7 @@
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"express-rate-limit": "^5.1.3",
|
||||
"fs": "0.0.1-security",
|
||||
"fs-extra": "^9.0.1",
|
||||
"heic2any": "0.0.3",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
|
@ -46,7 +46,12 @@ export default class IntegrationPage extends React.Component {
|
||||
|
||||
_handleChange = async (e) => {
|
||||
const query = e.target.value;
|
||||
const results = await Actions.search({ query });
|
||||
const results = await Actions.search({
|
||||
query,
|
||||
filter: {
|
||||
slates: true,
|
||||
},
|
||||
});
|
||||
|
||||
this.setState({ results: results.data.results });
|
||||
};
|
||||
@ -59,8 +64,7 @@ export default class IntegrationPage extends React.Component {
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message:
|
||||
"We're having trouble connecting right now. Please try again later",
|
||||
message: "We're having trouble connecting right now. Please try again later",
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -135,15 +139,11 @@ export default class IntegrationPage extends React.Component {
|
||||
{JSON.stringify(each, null, 1)}{" "}
|
||||
<div>
|
||||
{!each.data.verified ? (
|
||||
<button onClick={() => this._handleAccept(each)}>
|
||||
Accept
|
||||
</button>
|
||||
<button onClick={() => this._handleAccept(each)}>Accept</button>
|
||||
) : null}
|
||||
|
||||
{each.data.verified ? (
|
||||
<button onClick={() => this._handleDelete(each)}>
|
||||
Delete
|
||||
</button>
|
||||
<button onClick={() => this._handleDelete(each)}>Delete</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@ -162,9 +162,7 @@ export default class IntegrationPage extends React.Component {
|
||||
) : null}
|
||||
|
||||
{each.data.verified ? (
|
||||
<button onClick={() => this._handleDelete(each)}>
|
||||
Delete
|
||||
</button>
|
||||
<button onClick={() => this._handleDelete(each)}>Delete</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@ -172,15 +170,9 @@ export default class IntegrationPage extends React.Component {
|
||||
})}
|
||||
</div>
|
||||
<div css={STYLES_COLUMN}>
|
||||
<button onClick={this._handleUpdate}>
|
||||
Update {this.state.viewer.username}
|
||||
</button>
|
||||
<button onClick={this._handleUpdate}>Update {this.state.viewer.username}</button>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
onChange={this._handleChange}
|
||||
placeholder="type anything to search"
|
||||
/>
|
||||
<input type="text" onChange={this._handleChange} placeholder="type anything to search" />
|
||||
|
||||
{this.state.results.map((each) => {
|
||||
if (!each) {
|
||||
@ -192,13 +184,9 @@ export default class IntegrationPage extends React.Component {
|
||||
{JSON.stringify(each, null, 1)}{" "}
|
||||
<div>
|
||||
{each.type === "USER" ? (
|
||||
<button onClick={() => this._handleTrust(each)}>
|
||||
Trust
|
||||
</button>
|
||||
<button onClick={() => this._handleTrust(each)}>Trust</button>
|
||||
) : null}
|
||||
<button onClick={() => this._handleSubscribe(each)}>
|
||||
Subscribe
|
||||
</button>
|
||||
<button onClick={() => this._handleSubscribe(each)}>Subscribe</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -210,9 +198,7 @@ export default class IntegrationPage extends React.Component {
|
||||
<div css={STYLES_ITEM} key={each.id}>
|
||||
{JSON.stringify(each, null, 1)}{" "}
|
||||
<div>
|
||||
<button onClick={() => this._handleSubscribe(each)}>
|
||||
Unsubscribe
|
||||
</button>
|
||||
<button onClick={() => this._handleSubscribe(each)}>Unsubscribe</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as Upload from "~/node_common/upload";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as LibraryManager from "~/node_common/managers/library";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
import { Buckets } from "@textile/hub";
|
||||
|
||||
@ -13,17 +14,10 @@ export default async (req, res) => {
|
||||
});
|
||||
|
||||
if (!user || user.error) {
|
||||
return res
|
||||
.status(403)
|
||||
.send({ decorator: "SERVER_ADD_TO_SLATE_USER_NOT_FOUND", error: true });
|
||||
return res.status(403).send({ decorator: "SERVER_ADD_TO_SLATE_USER_NOT_FOUND", error: true });
|
||||
}
|
||||
|
||||
let {
|
||||
buckets,
|
||||
bucketKey,
|
||||
bucketRoot,
|
||||
bucketName,
|
||||
} = await Utilities.getBucketAPIFromUserToken({
|
||||
let { buckets, bucketKey, bucketRoot, bucketName } = await Utilities.getBucketAPIFromUserToken({
|
||||
user,
|
||||
});
|
||||
|
||||
@ -41,9 +35,7 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
let userCIDs = user.data.library[0].children.map((file) =>
|
||||
file.ipfs.replace("/ipfs/", "")
|
||||
);
|
||||
let userCIDs = user.data.library[0].children.map((file) => file.ipfs.replace("/ipfs/", ""));
|
||||
let newFiles = [];
|
||||
for (let item of req.body.data.items) {
|
||||
let cid = item.cid;
|
||||
@ -71,7 +63,7 @@ export default async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
const { updatedUserDataFields } = LibraryManager.addData({
|
||||
const { updatedUserDataFields, added, skipped } = LibraryManager.addData({
|
||||
user,
|
||||
files: newFiles,
|
||||
});
|
||||
@ -79,9 +71,12 @@ export default async (req, res) => {
|
||||
id: user.id,
|
||||
data: updatedUserDataFields,
|
||||
});
|
||||
if (updatedUserDataFields && updatedUserDataFields.library) {
|
||||
ViewerManager.hydratePartialLibrary(updatedUserDataFields.library, id);
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_ADD_EXISTING_CID_TO_DATA",
|
||||
data: true,
|
||||
data: { added, skipped: skipped + req.body.data.items.length - newFiles.length },
|
||||
});
|
||||
};
|
||||
|
@ -1,32 +1,68 @@
|
||||
import * as Upload from "~/node_common/upload";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as LibraryManager from "~/node_common/managers/library";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
|
||||
if (!id) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "PROCESS_PENDING_ERROR", error: true });
|
||||
return res.status(500).send({ decorator: "PROCESS_PENDING_ERROR", error: true });
|
||||
}
|
||||
|
||||
const response = await Data.deletePendingDataByUserId({ owner_user_id: id });
|
||||
const user = await Data.getUserById({
|
||||
id,
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ decorator: "PROCESS_PENDING_ERROR", error: true });
|
||||
if (!user) {
|
||||
return res.status(404).send({
|
||||
decorator: "PROCESS_PENDING_USER_NOT_FOUND",
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: response.decorator, error: response.error });
|
||||
if (user.error) {
|
||||
return res.status(500).send({
|
||||
decorator: "PROCESS_PENDING_USER_NOT_FOUND",
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
|
||||
const pending = await Data.deletePendingDataByUserId({ owner_user_id: id });
|
||||
|
||||
if (!pending) {
|
||||
return res.status(404).send({ decorator: "PROCESS_PENDING_ERROR", error: true });
|
||||
}
|
||||
|
||||
if (pending.error) {
|
||||
return res.status(500).send({ decorator: response.decorator, error: response.error });
|
||||
}
|
||||
|
||||
let cids = [];
|
||||
let noRepeats = [];
|
||||
for (let entry of pending) {
|
||||
if (cids.includes(entry.data.ipfs)) continue;
|
||||
cids.push(entry.data.ipfs);
|
||||
noRepeats.push(entry.data);
|
||||
}
|
||||
|
||||
const { updatedUserDataFields, added, skipped } = LibraryManager.addData({
|
||||
user,
|
||||
files: noRepeats,
|
||||
});
|
||||
|
||||
await Data.updateUserById({
|
||||
id,
|
||||
data: updatedUserDataFields,
|
||||
});
|
||||
|
||||
if (added && updatedUserDataFields && updatedUserDataFields.library) {
|
||||
ViewerManager.hydratePartialLibrary(updatedUserDataFields.library, id);
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "PROCESS_PENDING_DATA",
|
||||
data: response,
|
||||
data: { added, skipped: skipped + pending.length - noRepeats.length },
|
||||
});
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Social from "~/node_common/social";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
if (!req.body.data || !req.body.data.cids || !req.body.data.cids.length) {
|
||||
@ -99,7 +100,8 @@ export default async (req, res) => {
|
||||
|
||||
// NOTE(jim):
|
||||
// Goes through all of your slates and removes all data references.
|
||||
const slates = await Data.getSlatesByUserId({ userId: id });
|
||||
let refreshSlates = false;
|
||||
let slates = await Data.getSlatesByUserId({ userId: id });
|
||||
for (let i = 0; i < slates.length; i++) {
|
||||
let slate = slates[i];
|
||||
|
||||
@ -108,6 +110,7 @@ export default async (req, res) => {
|
||||
for (let cid of req.body.data.cids) {
|
||||
if (o.url.includes(cid)) {
|
||||
removal = true;
|
||||
refreshSlates = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -115,7 +118,7 @@ export default async (req, res) => {
|
||||
});
|
||||
|
||||
if (removal) {
|
||||
let layouts = await Data.updateSlateById({
|
||||
await Data.updateSlateById({
|
||||
id: slate.id,
|
||||
updated_at: new Date(),
|
||||
data: {
|
||||
@ -126,6 +129,11 @@ export default async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshSlates) {
|
||||
slates = await Data.getSlatesByUserId({ userId: id });
|
||||
ViewerManager.hydratePartialSlates(slates, id);
|
||||
}
|
||||
|
||||
// NOTE(martina):
|
||||
// Removes the reposted file from other people's slates
|
||||
// for (let cid of req.body.data.cids) {
|
||||
@ -134,7 +142,7 @@ export default async (req, res) => {
|
||||
|
||||
// NOTE(jim):
|
||||
// Removes the file reference from your library
|
||||
const response = await Data.updateUserById({
|
||||
const unsafeResponse = await Data.updateUserById({
|
||||
id: user.id,
|
||||
data: {
|
||||
...user.data,
|
||||
@ -154,6 +162,10 @@ export default async (req, res) => {
|
||||
},
|
||||
});
|
||||
|
||||
if (unsafeResponse) {
|
||||
ViewerManager.hydratePartialViewer(unsafeResponse);
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_REMOVE_DATA",
|
||||
success: true,
|
||||
|
@ -1,13 +1,12 @@
|
||||
import * as Environment from "~/node_common/environment";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
if (!id) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_DELETE_API_KEY_AUTH", error: true });
|
||||
return res.status(500).send({ decorator: "SERVER_DELETE_API_KEY_AUTH", error: true });
|
||||
}
|
||||
|
||||
const user = await Data.getUserById({
|
||||
@ -30,7 +29,7 @@ export default async (req, res) => {
|
||||
|
||||
const key = await Data.getAPIKey({ id: req.body.data.id });
|
||||
|
||||
if (key.owner_id !== user.id) {
|
||||
if (!key || key.owner_id !== user.id) {
|
||||
return res.status(403).send({
|
||||
decorator: "SERVER_DELETE_API_KEY_NOT_FOUND",
|
||||
error: true,
|
||||
@ -67,5 +66,8 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
let keys = await Data.getAPIKeysByUserId({ userId: user.id });
|
||||
ViewerManager.hydratePartialKeys(keys, user.id);
|
||||
|
||||
return res.status(200).send({ decorator: "SERVER_DELETE_API_KEY" });
|
||||
};
|
||||
|
@ -1,15 +1,14 @@
|
||||
import * as Environment from "~/node_common/environment";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
if (!id) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_GENERATE_API_KEY_AUTH", error: true });
|
||||
return res.status(500).send({ decorator: "SERVER_GENERATE_API_KEY_AUTH", error: true });
|
||||
}
|
||||
|
||||
const user = await Data.getUserById({
|
||||
@ -30,7 +29,7 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const keys = await Data.getAPIKeysByUserId({ userId: user.id });
|
||||
let keys = await Data.getAPIKeysByUserId({ userId: user.id });
|
||||
|
||||
if (keys.length > 9) {
|
||||
return res.status(404).send({
|
||||
@ -58,5 +57,8 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
keys = await Data.getAPIKeysByUserId({ userId: user.id });
|
||||
ViewerManager.hydratePartialKeys(keys, user.id);
|
||||
|
||||
return res.status(200).send({ decorator: "SERVER_GENERATE_API_KEY", key });
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import * as Constants from "~/node_common/constants";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -120,6 +121,11 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const slates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (slates) {
|
||||
ViewerManager.hydratePartialSlates(slates, id);
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_SLATE_ADD_TO_SLATE",
|
||||
added: addlObjects.length,
|
||||
|
@ -2,6 +2,7 @@ import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Social from "~/node_common/social";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
const SLATE_LIMIT = 50;
|
||||
|
||||
@ -40,7 +41,7 @@ export default async (req, res) => {
|
||||
return res.status(500).send({ decorator: "SERVER_EXISTING_SLATE", error: true });
|
||||
}
|
||||
|
||||
const slates = await Data.getSlatesByUserId({ userId: id });
|
||||
let slates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (slates.length >= SLATE_LIMIT) {
|
||||
return res.status(500).send({ decorator: "SERVER_SLATE_LIMIT", error: true });
|
||||
}
|
||||
@ -64,6 +65,11 @@ export default async (req, res) => {
|
||||
return res.status(500).send({ decorator: "SERVER_CREATE_SLATE", error: true });
|
||||
}
|
||||
|
||||
slates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (slates) {
|
||||
ViewerManager.hydratePartialSlates(slates, id);
|
||||
}
|
||||
|
||||
const userProfileURL = `https://slate.host/${user.username}`;
|
||||
const userURL = `<${userProfileURL}|${user.username}>`;
|
||||
Social.sendSlackMessage(
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -46,5 +47,10 @@ export default async (req, res) => {
|
||||
return res.status(500).send({ decorator: "SERVER_DELETE_SLATE", error: true });
|
||||
}
|
||||
|
||||
let slates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (slates) {
|
||||
ViewerManager.hydratePartialSlates(slates, id);
|
||||
}
|
||||
|
||||
return res.status(200).send({ decorator: "SERVER_DELETE_SLATE", error: false });
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import * as Constants from "~/node_common/constants";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -79,6 +80,11 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
let slates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (slates) {
|
||||
ViewerManager.hydratePartialSlates(slates, id);
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_SLATE_REMOVE_FROM_SLATE",
|
||||
slate,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -10,9 +11,7 @@ export default async (req, res) => {
|
||||
|
||||
if (!layoutOnly) {
|
||||
if (!id) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_FIND_USER_UPDATE_SLATE", error: true });
|
||||
return res.status(500).send({ decorator: "SERVER_FIND_USER_UPDATE_SLATE", error: true });
|
||||
}
|
||||
|
||||
user = await Data.getUserById({
|
||||
@ -37,15 +36,11 @@ export default async (req, res) => {
|
||||
const response = await Data.getSlateById({ id: req.body.data.id });
|
||||
|
||||
if (!response) {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ decorator: "SERVER_UPDATE_SLATE_NOT_FOUND", error: true });
|
||||
return res.status(404).send({ decorator: "SERVER_UPDATE_SLATE_NOT_FOUND", error: true });
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_UPDATE_SLATE_NOT_FOUND", error: true });
|
||||
return res.status(500).send({ decorator: "SERVER_UPDATE_SLATE_NOT_FOUND", error: true });
|
||||
}
|
||||
|
||||
if (!req.body.data) {
|
||||
@ -87,8 +82,7 @@ export default async (req, res) => {
|
||||
slatename: req.body.data.data.name
|
||||
? Strings.createSlug(req.body.data.data.name)
|
||||
: response.slatename,
|
||||
updated_at:
|
||||
layoutOnly || req.body.data.autoSave ? response.updated_at : new Date(),
|
||||
updated_at: layoutOnly || req.body.data.autoSave ? response.updated_at : new Date(),
|
||||
data: {
|
||||
...response.data,
|
||||
...req.body.data.data,
|
||||
@ -97,15 +91,16 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
if (!slate) {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ decorator: "SERVER_UPDATE_SLATE", error: true });
|
||||
return res.status(404).send({ decorator: "SERVER_UPDATE_SLATE", error: true });
|
||||
}
|
||||
|
||||
if (slate.error) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_UPDATE_SLATE", error: true });
|
||||
return res.status(500).send({ decorator: "SERVER_UPDATE_SLATE", error: true });
|
||||
}
|
||||
|
||||
let slates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (slates) {
|
||||
ViewerManager.hydratePartialSlates(slates, id);
|
||||
}
|
||||
|
||||
return res.status(200).send({ decorator: "SERVER_UPDATE_SLATE", slate });
|
||||
|
@ -2,6 +2,7 @@ import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -102,6 +103,8 @@ export default async (req, res) => {
|
||||
return res.status(500).send({ decorator: "SERVER_UNSUBSCRIBE_ERROR", error: true });
|
||||
}
|
||||
|
||||
ViewerManager.hydratePartialSubscriptions({ subscriptions: true }, user.id);
|
||||
|
||||
return res.status(200).send({ decorator: "SERVER_UNSUBSCRIBE", data: unsubscribeResponse });
|
||||
}
|
||||
|
||||
@ -119,6 +122,8 @@ export default async (req, res) => {
|
||||
return res.status(500).send({ decorator: "SERVER_SUBSCRIBE_ERROR", error: true });
|
||||
}
|
||||
|
||||
ViewerManager.hydratePartialSubscriptions({ subscriptions: true }, user.id);
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_SUBSCRIBE",
|
||||
data: {
|
||||
|
@ -3,6 +3,7 @@ import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -48,6 +49,8 @@ export default async (req, res) => {
|
||||
data: { ...user.data, onboarding },
|
||||
});
|
||||
|
||||
ViewerManager.hydratePartialOnboarding(onboarding, user.id);
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_ONBOARDING_UPDATE",
|
||||
data: updateResponse,
|
||||
|
@ -2,6 +2,7 @@ import * as Environment from "~/node_common/environment";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -38,5 +39,7 @@ export default async (req, res) => {
|
||||
id: req.body.data.id,
|
||||
});
|
||||
|
||||
ViewerManager.hydratePartialSubscriptions({ trusted: true, pendingTrusted: true }, id);
|
||||
|
||||
return res.status(200).send({ decorator: "SERVER_TRUST_UPDATE", data: response });
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -84,6 +85,8 @@ export default async (req, res) => {
|
||||
data: { ...existingResponse.data, verified: true },
|
||||
});
|
||||
|
||||
ViewerManager.hydratePartialSubscriptions({ trusted: true, pendingTrusted: true }, id);
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_TRUST_UPDATE",
|
||||
data: {
|
||||
|
@ -3,6 +3,7 @@ import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
@ -105,6 +106,8 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
ViewerManager.hydratePartialSubscriptions({ trusted: true, pendingTrusted: true }, id);
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_DELETE_TRUSTED_RELATIONSHIP",
|
||||
data: deleteRelationshipResponse,
|
||||
@ -127,6 +130,8 @@ export default async (req, res) => {
|
||||
return res.status(500).send({ decorator: "SERVER_TRUSTED_RELATIONSHIP_ERROR", error: true });
|
||||
}
|
||||
|
||||
ViewerManager.hydratePartialSubscriptions({ trusted: true, pendingTrusted: true }, id);
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_TRUSTED_RELATIONSHIP",
|
||||
data: {
|
||||
|
@ -138,7 +138,6 @@ export default class SceneDirectory extends React.Component {
|
||||
|
||||
state = {
|
||||
copyValue: "",
|
||||
loading: false,
|
||||
tab: 0,
|
||||
contextMenu: null,
|
||||
};
|
||||
@ -168,28 +167,25 @@ export default class SceneDirectory extends React.Component {
|
||||
_handleDelete = async (e, id) => {
|
||||
this._handleHide();
|
||||
e.stopPropagation();
|
||||
const response = await Actions.deleteTrustRelationship({
|
||||
await Actions.deleteTrustRelationship({
|
||||
id: id,
|
||||
});
|
||||
await this.props.onRehydrate();
|
||||
};
|
||||
|
||||
_handleAccept = async (e, id) => {
|
||||
this._handleHide();
|
||||
e.stopPropagation();
|
||||
const response = await Actions.updateTrustRelationship({
|
||||
await Actions.updateTrustRelationship({
|
||||
userId: id,
|
||||
});
|
||||
await this.props.onRehydrate();
|
||||
};
|
||||
|
||||
_handleFollow = async (e, id) => {
|
||||
this._handleHide();
|
||||
e.stopPropagation();
|
||||
const response = await Actions.createSubscription({
|
||||
await Actions.createSubscription({
|
||||
userId: id,
|
||||
});
|
||||
await this.props.onRehydrate();
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -50,7 +50,6 @@ export default class SceneFilesFolder extends React.Component {
|
||||
onAction={this.props.onAction}
|
||||
viewer={this.props.viewer}
|
||||
items={this.props.viewer.library[0].children}
|
||||
onRehydrate={this.props.onRehydrate}
|
||||
/>
|
||||
) : (
|
||||
<EmptyState>
|
||||
|
@ -58,7 +58,6 @@ export default class SceneHome extends React.Component {
|
||||
viewer={this.props.viewer}
|
||||
items={this.props.viewer.library[0].children}
|
||||
onAction={this.props.onAction}
|
||||
onRehydrate={this.props.onRehydrate}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
@ -5,10 +5,7 @@ import * as Constants from "~/common/constants";
|
||||
import * as SVG from "~/common/svg";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import {
|
||||
ButtonPrimary,
|
||||
ButtonSecondary,
|
||||
} from "~/components/system/components/Buttons";
|
||||
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
||||
import { dispatchCustomEvent } from "~/common/custom-events";
|
||||
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
@ -28,165 +25,87 @@ const STATUS_BUTTON_MAP = {
|
||||
};
|
||||
|
||||
export default class SceneProfile extends React.Component {
|
||||
state = {
|
||||
loading: false,
|
||||
trustStatus: "untrusted",
|
||||
followStatus: false,
|
||||
followLoading: false,
|
||||
trustLoading: false,
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
this.setStatus(this.props.viewer);
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.data.username !== this.props.data.username) {
|
||||
this.setStatus(this.props.viewer);
|
||||
_handleTrust = async (trustStatus, trustId) => {
|
||||
if (trustStatus === "untrusted" || trustStatus === "sent") {
|
||||
await Actions.createTrustRelationship({
|
||||
userId: this.props.data.id,
|
||||
});
|
||||
} else if (trustStatus === "received") {
|
||||
await Actions.updateTrustRelationship({
|
||||
userId: this.props.data.id,
|
||||
});
|
||||
} else {
|
||||
await Actions.deleteTrustRelationship({
|
||||
id: trustId,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setStatus = (viewer) => {
|
||||
let newState = {
|
||||
trustStatus: "untrusted",
|
||||
followStatus: false,
|
||||
followLoading: false,
|
||||
trustLoading: false,
|
||||
};
|
||||
_handleFollow = async () => {
|
||||
await Actions.createSubscription({
|
||||
userId: this.props.data.id,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
let trustId, followStatus, relation;
|
||||
let trustStatus = "untrusted";
|
||||
let viewer = this.props.viewer;
|
||||
let trust = viewer.trusted.filter((entry) => {
|
||||
return entry.target_user_id === this.props.data.id;
|
||||
});
|
||||
if (trust.length) {
|
||||
let relation = trust[0];
|
||||
newState.trustId = relation.id;
|
||||
relation = trust[0];
|
||||
trustId = relation.id;
|
||||
if (relation.data.verified) {
|
||||
newState.trustStatus = "trusted";
|
||||
trustStatus = "trusted";
|
||||
} else {
|
||||
newState.trustStatus = "sent";
|
||||
trustStatus = "sent";
|
||||
}
|
||||
}
|
||||
let pendingTrust = viewer.pendingTrusted.filter((entry) => {
|
||||
return entry.owner_user_id === this.props.data.id;
|
||||
});
|
||||
if (pendingTrust.length) {
|
||||
let relation = pendingTrust[0];
|
||||
newState.trustId = relation.id;
|
||||
relation = pendingTrust[0];
|
||||
trustId = relation.id;
|
||||
if (pendingTrust[0].data.verified) {
|
||||
newState.trustStatus = "trusted";
|
||||
trustStatus = "trusted";
|
||||
} else {
|
||||
newState.trustStatus = "received";
|
||||
trustStatus = "received";
|
||||
}
|
||||
}
|
||||
if (
|
||||
viewer.subscriptions.filter((entry) => {
|
||||
return entry.target_user_id === this.props.data.id;
|
||||
}).length
|
||||
) {
|
||||
newState.followStatus = true;
|
||||
}
|
||||
this.setState(newState);
|
||||
};
|
||||
followStatus = !!viewer.subscriptions.filter((entry) => {
|
||||
return entry.target_user_id === this.props.data.id;
|
||||
}).length;
|
||||
|
||||
_handleUpdate = async (e) => {
|
||||
let response = await this.props.onRehydrate();
|
||||
if (!response) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message:
|
||||
"We're having trouble connecting right now. Please try again later",
|
||||
},
|
||||
},
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: { alert: { decorator: response.decorator } },
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
let viewer = response.data;
|
||||
|
||||
this.setStatus(viewer);
|
||||
};
|
||||
|
||||
_handleTrust = () => {
|
||||
this.setState({ trustLoading: true }, async () => {
|
||||
let response;
|
||||
if (
|
||||
this.state.trustStatus === "untrusted" ||
|
||||
this.state.trustStatus === "sent"
|
||||
) {
|
||||
response = await Actions.createTrustRelationship({
|
||||
userId: this.props.data.id,
|
||||
});
|
||||
console.log(response);
|
||||
} else if (this.state.trustStatus === "received") {
|
||||
response = await Actions.updateTrustRelationship({
|
||||
userId: this.props.data.id,
|
||||
});
|
||||
console.log(response);
|
||||
} else {
|
||||
response = await Actions.deleteTrustRelationship({
|
||||
id: this.state.trustId,
|
||||
});
|
||||
console.log(response);
|
||||
}
|
||||
await this._handleUpdate();
|
||||
});
|
||||
};
|
||||
|
||||
_handleFollow = () => {
|
||||
this.setState({ followLoading: true }, async () => {
|
||||
let response = await Actions.createSubscription({
|
||||
userId: this.props.data.id,
|
||||
});
|
||||
console.log(response);
|
||||
await this._handleUpdate();
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
let buttons = (
|
||||
<div css={STYLES_BUTTONS}>
|
||||
{this.state.followStatus ? (
|
||||
{followStatus ? (
|
||||
<ButtonSecondary
|
||||
loading={this.state.followLoading}
|
||||
style={{ margin: "0px 8px", minWidth: 152 }}
|
||||
onClick={this._handleFollow}
|
||||
>
|
||||
Unfollow
|
||||
</ButtonSecondary>
|
||||
) : (
|
||||
<ButtonPrimary
|
||||
loading={this.state.followLoading}
|
||||
style={{ margin: "0px 8px", minWidth: 152 }}
|
||||
onClick={this._handleFollow}
|
||||
>
|
||||
<ButtonPrimary style={{ margin: "0px 8px", minWidth: 152 }} onClick={this._handleFollow}>
|
||||
Follow
|
||||
</ButtonPrimary>
|
||||
)}
|
||||
{this.state.trustStatus === "untrusted" ||
|
||||
this.state.trustStatus === "received" ? (
|
||||
{trustStatus === "untrusted" || trustStatus === "received" ? (
|
||||
<ButtonPrimary
|
||||
loading={this.state.trustLoading}
|
||||
style={{ margin: "0px 8px", minWidth: 152 }}
|
||||
onClick={this._handleTrust}
|
||||
onClick={() => this._handleTrust(trustStatus, trustId)}
|
||||
>
|
||||
{STATUS_BUTTON_MAP[this.state.trustStatus]}
|
||||
{STATUS_BUTTON_MAP[trustStatus]}
|
||||
</ButtonPrimary>
|
||||
) : (
|
||||
<ButtonSecondary
|
||||
loading={this.state.trustLoading}
|
||||
style={{ margin: "0px 8px", minWidth: 152 }}
|
||||
onClick={this._handleTrust}
|
||||
onClick={() => this._handleTrust(trustStatus, trustId)}
|
||||
>
|
||||
{STATUS_BUTTON_MAP[this.state.trustStatus]}
|
||||
{STATUS_BUTTON_MAP[trustStatus]}
|
||||
</ButtonSecondary>
|
||||
)}
|
||||
</div>
|
||||
@ -198,11 +117,7 @@ export default class SceneProfile extends React.Component {
|
||||
onAction={this.props.onAction}
|
||||
creator={this.props.data}
|
||||
sceneId={this.props.sceneId}
|
||||
buttons={
|
||||
this.props.viewer.username === this.props.data.username
|
||||
? null
|
||||
: buttons
|
||||
}
|
||||
buttons={this.props.viewer.username === this.props.data.username ? null : buttons}
|
||||
isOwner={this.props.viewer.username === this.props.data.username}
|
||||
/>
|
||||
</ScenePage>
|
||||
|
@ -118,8 +118,6 @@ export default class SceneSettings extends React.Component {
|
||||
},
|
||||
});
|
||||
|
||||
await this.props.onRehydrate();
|
||||
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
||||
|
@ -211,8 +211,6 @@ export default class SceneSettingsDeveloper extends React.Component {
|
||||
return this.setState({ loading: false });
|
||||
}
|
||||
|
||||
await this.props.onRehydrate();
|
||||
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
||||
@ -244,6 +242,7 @@ export default class SceneSettingsDeveloper extends React.Component {
|
||||
});
|
||||
return this.setState({ loading: false });
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
|
@ -57,7 +57,6 @@ export default class SceneSlate extends React.Component {
|
||||
saving: "IDLE",
|
||||
isOwner: this.props.current.data.ownerId === this.props.viewer.id,
|
||||
editing: false,
|
||||
followLoading: false,
|
||||
};
|
||||
|
||||
// NOTE(jim):
|
||||
@ -108,12 +107,8 @@ export default class SceneSlate extends React.Component {
|
||||
}
|
||||
|
||||
_handleFollow = () => {
|
||||
this.setState({ followLoading: true }, async () => {
|
||||
let response = await Actions.createSubscription({
|
||||
slateId: this.props.current.id,
|
||||
});
|
||||
await this.props.onRehydrate();
|
||||
this.setState({ followLoading: false });
|
||||
Actions.createSubscription({
|
||||
slateId: this.props.current.id,
|
||||
});
|
||||
};
|
||||
|
||||
@ -165,10 +160,6 @@ export default class SceneSlate extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.isOwner) {
|
||||
await this.props.onRehydrate();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
saving: "SAVED",
|
||||
});
|
||||
@ -224,7 +215,6 @@ export default class SceneSlate extends React.Component {
|
||||
});
|
||||
return;
|
||||
}
|
||||
await this.props.onRehydrate();
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
@ -277,8 +267,6 @@ export default class SceneSlate extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.props.onRehydrate();
|
||||
|
||||
System.dispatchCustomEvent({
|
||||
name: "state-global-carousel-loading",
|
||||
detail: { loading: false },
|
||||
@ -323,8 +311,14 @@ export default class SceneSlate extends React.Component {
|
||||
});
|
||||
return;
|
||||
}
|
||||
let message = Strings.formatAsUploadMessage(response.data.added, response.data.skipped);
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: { message, status: !response.data.added ? null : "INFO" },
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false, saving: "SAVED" });
|
||||
this.props.onRehydrate();
|
||||
};
|
||||
|
||||
_handleShowSettings = () => {
|
||||
@ -336,7 +330,6 @@ export default class SceneSlate extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log(this.props);
|
||||
const { user, data } = this.props.current;
|
||||
const { body = "", preview } = data;
|
||||
let objects = this.props.current.data.objects;
|
||||
@ -375,19 +368,11 @@ export default class SceneSlate extends React.Component {
|
||||
) : (
|
||||
<div onClick={this._handleFollow}>
|
||||
{following ? (
|
||||
<ButtonSecondary
|
||||
transparent
|
||||
style={{ minWidth: 120, paddingLeft: 0 }}
|
||||
loading={this.state.followLoading}
|
||||
>
|
||||
<ButtonSecondary transparent style={{ minWidth: 120, paddingLeft: 0 }}>
|
||||
Unfollow
|
||||
</ButtonSecondary>
|
||||
) : (
|
||||
<ButtonPrimary
|
||||
transparent
|
||||
style={{ minWidth: 120, paddingLeft: 0 }}
|
||||
loading={this.state.followLoading}
|
||||
>
|
||||
<ButtonPrimary transparent style={{ minWidth: 120, paddingLeft: 0 }}>
|
||||
Follow
|
||||
</ButtonPrimary>
|
||||
)}
|
||||
|
@ -35,13 +35,12 @@ export default class SceneSlates extends React.Component {
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_CREATE_SLATE",
|
||||
});
|
||||
this.props.onRehydrate();
|
||||
};
|
||||
|
||||
_handleSearch = () => {
|
||||
dispatchCustomEvent({
|
||||
name: "create-modal",
|
||||
detail: { modal: <SearchModal onAction={this.props.onAction} /> },
|
||||
detail: { modal: <SearchModal viewer={this.props.viewer} onAction={this.props.onAction} /> },
|
||||
});
|
||||
};
|
||||
|
||||
@ -72,10 +71,7 @@ export default class SceneSlates extends React.Component {
|
||||
title="Slates"
|
||||
actions={
|
||||
this.state.tab === 0 ? (
|
||||
<CircleButtonGray
|
||||
onClick={this._handleAdd}
|
||||
style={{ marginLeft: 12 }}
|
||||
>
|
||||
<CircleButtonGray onClick={this._handleAdd} style={{ marginLeft: 12 }}>
|
||||
<SVG.Plus height="16px" />
|
||||
</CircleButtonGray>
|
||||
) : null
|
||||
@ -106,13 +102,9 @@ export default class SceneSlates extends React.Component {
|
||||
<SVG.Video height="24px" style={{ margin: "0 16px" }} />
|
||||
</div>
|
||||
<div style={{ marginTop: 24 }}>
|
||||
Use slates to create mood boards, share files, and organize
|
||||
research.
|
||||
Use slates to create mood boards, share files, and organize research.
|
||||
</div>
|
||||
<ButtonSecondary
|
||||
onClick={this._handleAdd}
|
||||
style={{ marginTop: 32 }}
|
||||
>
|
||||
<ButtonSecondary onClick={this._handleAdd} style={{ marginTop: 32 }}>
|
||||
Create slate
|
||||
</ButtonSecondary>
|
||||
</EmptyState>
|
||||
@ -146,10 +138,7 @@ export default class SceneSlates extends React.Component {
|
||||
) : (
|
||||
<EmptyState>
|
||||
You can follow any public slates on the network.
|
||||
<ButtonSecondary
|
||||
onClick={this._handleSearch}
|
||||
style={{ marginTop: 32 }}
|
||||
>
|
||||
<ButtonSecondary onClick={this._handleSearch} style={{ marginTop: 32 }}>
|
||||
Browse slates
|
||||
</ButtonSecondary>
|
||||
</EmptyState>
|
||||
|
@ -45,7 +45,6 @@ export default class SceneTara extends React.Component {
|
||||
onAction={this.props.onAction}
|
||||
viewer={this.props.viewer}
|
||||
items={this.props.viewer.library[0].children}
|
||||
onRehydrate={this.props.onRehydrate}
|
||||
/>
|
||||
) : (
|
||||
<EmptyState>
|
||||
|
Loading…
Reference in New Issue
Block a user