mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-22 21:45:56 +03:00
added public files
This commit is contained in:
parent
cd3a62833c
commit
5fcf748f20
@ -266,6 +266,13 @@ export const updateData = async (data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const toggleFilePrivacy = async (data) => {
|
||||
return await returnJSON(`/api/data/toggle-privacy`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteBucketItems = async (data) => {
|
||||
return await returnJSON(`/api/data/remove`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
|
@ -15,6 +15,7 @@ import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
import { SlatePicker } from "~/components/core/SlatePicker";
|
||||
import { Input } from "~/components/system/components/Input";
|
||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||
import { Toggle } from "~/components/system/components/Toggle";
|
||||
|
||||
const DEFAULT_BOOK =
|
||||
"https://slate.textile.io/ipfs/bafkreibk32sw7arspy5kw3p5gkuidfcwjbwqyjdktd5wkqqxahvkm2qlyi";
|
||||
@ -80,11 +81,6 @@ const STYLES_DISMISS_BOX = css`
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_CONTAINER = css`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const STYLES_META = css`
|
||||
padding: 14px 0px 8px 0px;
|
||||
overflow-wrap: break-word;
|
||||
@ -238,6 +234,7 @@ export default class CarouselSidebarData extends React.Component {
|
||||
name: Strings.isEmpty(this.props.data.name) ? "" : this.props.data.name,
|
||||
selected: {},
|
||||
isPublic: false,
|
||||
inPublicSlates: false,
|
||||
copyValue: "",
|
||||
loading: false,
|
||||
changingPreview: false,
|
||||
@ -249,18 +246,18 @@ export default class CarouselSidebarData extends React.Component {
|
||||
this.setState({ unsavedChanges: true });
|
||||
if (this.props.isOwner && !this.props.external) {
|
||||
this.debounceInstance = Window.debounce(() => this._handleSave(), 3000);
|
||||
let isPublic = false;
|
||||
let inPublicSlates = false;
|
||||
let selected = {};
|
||||
const id = this.props.data.id;
|
||||
for (let slate of this.props.slates) {
|
||||
if (slate.data.objects.some((o) => o.id === id)) {
|
||||
if (slate.data.public) {
|
||||
isPublic = true;
|
||||
inPublicSlates = true;
|
||||
}
|
||||
selected[slate.id] = true;
|
||||
}
|
||||
}
|
||||
this.setState({ selected, isPublic });
|
||||
this.setState({ selected, inPublicSlates, isPublic: this.props.data.public });
|
||||
}
|
||||
};
|
||||
|
||||
@ -383,7 +380,46 @@ export default class CarouselSidebarData extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
_handleToggleVisibility = async (e) => {
|
||||
const isVisible = this.state.inPublicSlates || this.state.isPublic;
|
||||
let selected = this.state.selected;
|
||||
if (this.state.inPublicSlates) {
|
||||
console.log("in public slates");
|
||||
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.slates) {
|
||||
if (slate.data.public && slateIds.includes(slate.id)) {
|
||||
publicSlateNames.push(slate.data.name);
|
||||
publicSlateIds.push(slate.id);
|
||||
selected[slate.id] = false;
|
||||
}
|
||||
}
|
||||
const message = `Making this file private will remove it from the following public slates: ${publicSlateNames.join(
|
||||
", "
|
||||
)}. Do you wish to continue?`;
|
||||
if (!window.confirm(message)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let response = await Actions.toggleFilePrivacy({
|
||||
data: {
|
||||
id: this.props.data.id,
|
||||
public: !isVisible,
|
||||
},
|
||||
});
|
||||
console.log(response);
|
||||
if (isVisible) {
|
||||
this.setState({ inPublicSlates: false, isPublic: false, selected });
|
||||
} else {
|
||||
this.setState({ isPublic: true });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const isVisible = this.state.inPublicSlates || this.state.isPublic;
|
||||
const { cid, file, name, coverImage, type, size, url, blurhash } = this.props.data;
|
||||
const elements = [];
|
||||
if (this.props.onClose) {
|
||||
@ -395,7 +431,7 @@ export default class CarouselSidebarData extends React.Component {
|
||||
}
|
||||
|
||||
elements.push(
|
||||
<div key="s-2" css={STYLES_CONTAINER}>
|
||||
<div key="s-2" style={{ marginBottom: 80 }}>
|
||||
<div css={STYLES_META}>
|
||||
{this.state.isEditing ? (
|
||||
<Boundary enabled onOutsideRectEvent={this._handleEditFilename}>
|
||||
@ -499,12 +535,26 @@ export default class CarouselSidebarData extends React.Component {
|
||||
</div>
|
||||
)}
|
||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||
Privacy
|
||||
Visibility
|
||||
</div>
|
||||
<div style={{ color: Constants.system.darkGray, lineHeight: "1.5", marginBottom: 104 }}>
|
||||
{this.state.isPublic
|
||||
? "Public. This file is currently visible to others and searchable within Slate through public slates."
|
||||
: "Private. This file is currently not visible to others unless they have the link."}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
margin: "16px 0 16px 0",
|
||||
}}
|
||||
>
|
||||
<div style={{ color: Constants.system.darkGray, lineHeight: "1.5" }}>
|
||||
{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 through public slates."
|
||||
: "This file is currently not visible to others unless they have the link."}
|
||||
</div>
|
||||
<input
|
||||
css={STYLES_HIDDEN}
|
||||
|
@ -50,12 +50,19 @@ export class Toggle extends React.Component {
|
||||
css={STYLES_TOGGLE}
|
||||
onClick={this._handleChange}
|
||||
style={{
|
||||
backgroundColor: this.props.active ? Constants.system.brand : null,
|
||||
backgroundColor: this.props.active
|
||||
? Constants.system.brand
|
||||
: this.props.dark
|
||||
? Constants.system.grayBlack
|
||||
: null,
|
||||
}}
|
||||
>
|
||||
<figure
|
||||
css={STYLES_DIAL}
|
||||
style={{ transform: this.props.active ? `translateX(28px)` : null }}
|
||||
style={{
|
||||
transform: this.props.active ? `translateX(28px)` : null,
|
||||
background: this.props.dark ? Constants.system.border : null,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -125,8 +125,9 @@ export const editItem = ({ user, update }) => {
|
||||
if (library[0].children[i].id === update.data.id) {
|
||||
library[0].children[i] = {
|
||||
...library[0].children[i],
|
||||
coverImage: update.data?.coverImage,
|
||||
name: update.data?.name,
|
||||
...update.data,
|
||||
// coverImage: update.data?.coverImage,
|
||||
// name: update.data?.name,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ export const getById = async ({ id }) => {
|
||||
}
|
||||
|
||||
// TODO(jim): You can serialize this last because you will have all the information
|
||||
// from subscriptionsed, trusted, and pendingTrusted most likely.
|
||||
// from subscriptions, trusted, and pendingTrusted most likely.
|
||||
let activity = await Data.getActivityForUserId({ userId: id });
|
||||
const slates = await Data.getSlatesByUserId({ userId: id });
|
||||
const keys = await Data.getAPIKeysByUserId({ userId: id });
|
||||
|
@ -35,6 +35,7 @@ export default class ProfilePage extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log(this.props.creator);
|
||||
const title = this.props.creator ? `${this.props.creator.username}` : "404";
|
||||
const url = `https://slate.host/${title}`;
|
||||
const description = this.props.creator.data.body;
|
||||
|
82
pages/api/data/toggle-privacy.js
Normal file
82
pages/api/data/toggle-privacy.js
Normal file
@ -0,0 +1,82 @@
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as LibraryManager from "~/node_common/managers/library";
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
import * as SearchManager from "~/node_common/managers/search";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
if (!id) {
|
||||
return res.status(500).send({ decorator: "SERVER_EDIT_DATA", error: true });
|
||||
}
|
||||
|
||||
const user = await Data.getUserById({ id });
|
||||
if (!user || user.error) {
|
||||
return res.status(403).send({ decorator: "SERVER_EDIT_DATA_USER_NOT_FOUND", error: true });
|
||||
}
|
||||
|
||||
let newUserData = LibraryManager.editItem({ user, update: req.body.data });
|
||||
let response = await Data.updateUserById({
|
||||
id: user.id,
|
||||
data: newUserData,
|
||||
});
|
||||
if (!response || response.error) {
|
||||
return res.status(500).send({ decorator: "SERVER_EDIT_DATA_NOT_UPDATED", error: true });
|
||||
}
|
||||
|
||||
let slates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (!slates || slates.error) {
|
||||
return res.status(500).send({ decorator: "SERVER_SLATES_NOT_FOUND", error: true });
|
||||
}
|
||||
if (!req.body.data.data.public) {
|
||||
//NOTE(martina): if toggling a file to private, must remove it from any public slates as well
|
||||
let slatesChanged = false;
|
||||
for (let slate of slates) {
|
||||
let edited = false;
|
||||
if (!slate.data.public) {
|
||||
continue;
|
||||
}
|
||||
let objects = slate.data.objects.filter((obj) => {
|
||||
if (obj.id === req.body.data.data.id) {
|
||||
edited = true;
|
||||
slatesChanged = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (edited) {
|
||||
await Data.updateSlateById({
|
||||
id: slate.id,
|
||||
updated_at: new Date(),
|
||||
data: {
|
||||
...slate.data,
|
||||
objects,
|
||||
},
|
||||
});
|
||||
SearchManager.updateSlate(slate, "EDIT");
|
||||
}
|
||||
}
|
||||
|
||||
if (slatesChanged) {
|
||||
let userSlates = await Data.getSlatesByUserId({ userId: id });
|
||||
if (!userSlates || userSlates.error) {
|
||||
return res.status(500).send({ decorator: "SERVER_SLATES_NOT_FOUND", error: true });
|
||||
}
|
||||
ViewerManager.hydratePartialSlates(userSlates, id);
|
||||
} else {
|
||||
//remove it from the search index as an individual file
|
||||
}
|
||||
} else {
|
||||
//add to search index as an individual file
|
||||
}
|
||||
|
||||
if (newUserData && newUserData.library) {
|
||||
ViewerManager.hydratePartialLibrary(newUserData.library, id);
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_EDIT_DATA",
|
||||
data: {},
|
||||
});
|
||||
};
|
@ -15,6 +15,8 @@ export default async (req, res) => {
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
let library = user.data.library;
|
||||
|
||||
user = Serializers.user(user);
|
||||
|
||||
let slates = await Data.getSlatesByUserId({
|
||||
@ -29,11 +31,49 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
let publicFileIds = [];
|
||||
user.slates = [];
|
||||
for (let slate of slates) {
|
||||
user.slates.push(Serializers.slate(slate));
|
||||
if (slate.data.public) {
|
||||
publicFileIds.push(...slate.data.objects.map((obj) => obj.id));
|
||||
}
|
||||
}
|
||||
|
||||
if (library && library.length) {
|
||||
library[0].children = library[0].children.filter((file) => {
|
||||
return file.public || publicFileIds.includes(file.id);
|
||||
});
|
||||
}
|
||||
user.library = library;
|
||||
|
||||
const subscriptions = await Data.getSubscriptionsByUserId({ userId: req.body.data.id });
|
||||
const subscribers = await Data.getSubscribersByUserId({ userId: req.body.data.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,
|
||||
});
|
||||
|
||||
user.subscriptions = r1.serializedSubscriptions;
|
||||
|
||||
const r2 = await Serializers.doSubscribers({
|
||||
users: [],
|
||||
slates: [],
|
||||
subscribers,
|
||||
serializedUsersMap: r1.serializedUsersMap,
|
||||
serializedSlatesMap: r1.serializedSlatesMap,
|
||||
});
|
||||
|
||||
user.subscribers = r2.serializedSubscribers;
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERIALIZED_USER",
|
||||
data: user,
|
||||
|
@ -77,6 +77,7 @@ export default class SceneProfile extends React.Component {
|
||||
|
||||
targetUser = response.data;
|
||||
}
|
||||
console.log(targetUser);
|
||||
|
||||
window.history.replaceState(window.history.state, "A slate user", `/${targetUser.username}`);
|
||||
|
||||
|
52
server.js
52
server.js
@ -224,7 +224,7 @@ app.prepare().then(async () => {
|
||||
});
|
||||
}
|
||||
|
||||
const creator = await Data.getUserByUsername({
|
||||
let creator = await Data.getUserByUsername({
|
||||
username: req.params.username,
|
||||
});
|
||||
|
||||
@ -236,11 +236,56 @@ app.prepare().then(async () => {
|
||||
return res.redirect("/404");
|
||||
}
|
||||
|
||||
let library = creator.data.library;
|
||||
|
||||
creator = Serializers.user(creator);
|
||||
|
||||
const slates = await Data.getSlatesByUserId({
|
||||
userId: creator.id,
|
||||
publicOnly: true,
|
||||
});
|
||||
|
||||
let publicFileIds = [];
|
||||
for (let slate of slates) {
|
||||
publicFileIds.push(...slate.data.objects.map((obj) => obj.id));
|
||||
}
|
||||
|
||||
creator.slates = slates;
|
||||
|
||||
if (library && library.length) {
|
||||
library[0].children = library[0].children.filter((file) => {
|
||||
return file.public || publicFileIds.includes(file.id);
|
||||
});
|
||||
}
|
||||
creator.library = library;
|
||||
|
||||
const subscriptions = await Data.getSubscriptionsByUserId({ userId: creator.id });
|
||||
const subscribers = await Data.getSubscribersByUserId({ userId: creator.id });
|
||||
|
||||
let serializedUsersMap = { [creator.id]: creator };
|
||||
let serializedSlatesMap = {};
|
||||
|
||||
// NOTE(jim): The most expensive call first.
|
||||
const r1 = await Serializers.doSubscriptions({
|
||||
users: [],
|
||||
slates: [],
|
||||
subscriptions,
|
||||
serializedUsersMap,
|
||||
serializedSlatesMap,
|
||||
});
|
||||
|
||||
creator.subscriptions = r1.serializedSubscriptions;
|
||||
|
||||
const r2 = await Serializers.doSubscribers({
|
||||
users: [],
|
||||
slates: [],
|
||||
subscribers,
|
||||
serializedUsersMap: r1.serializedUsersMap,
|
||||
serializedSlatesMap: r1.serializedSlatesMap,
|
||||
});
|
||||
|
||||
creator.subscribers = r2.serializedSubscribers;
|
||||
|
||||
let exploreSlates = [];
|
||||
|
||||
if (Environment.IS_PRODUCTION) {
|
||||
@ -281,7 +326,7 @@ app.prepare().then(async () => {
|
||||
|
||||
return app.render(req, res, "/_/profile", {
|
||||
viewer,
|
||||
creator: Serializers.user({ ...creator, slates }),
|
||||
creator,
|
||||
mobile,
|
||||
resources: EXTERNAL_RESOURCES,
|
||||
exploreSlates,
|
||||
@ -324,9 +369,6 @@ app.prepare().then(async () => {
|
||||
return res.redirect("/404");
|
||||
}
|
||||
|
||||
console.log(slate.data.public);
|
||||
console.log(slate.data.ownerId);
|
||||
console.log(id);
|
||||
if (!slate.data.public && slate.data.ownerId !== id) {
|
||||
return res.redirect("/403");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user