mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-23 05:54:49 +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) => {
|
export const deleteBucketItems = async (data) => {
|
||||||
return await returnJSON(`/api/data/remove`, {
|
return await returnJSON(`/api/data/remove`, {
|
||||||
...DEFAULT_OPTIONS,
|
...DEFAULT_OPTIONS,
|
||||||
|
@ -15,6 +15,7 @@ import { LoaderSpinner } from "~/components/system/components/Loaders";
|
|||||||
import { SlatePicker } from "~/components/core/SlatePicker";
|
import { SlatePicker } from "~/components/core/SlatePicker";
|
||||||
import { Input } from "~/components/system/components/Input";
|
import { Input } from "~/components/system/components/Input";
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
|
import { Toggle } from "~/components/system/components/Toggle";
|
||||||
|
|
||||||
const DEFAULT_BOOK =
|
const DEFAULT_BOOK =
|
||||||
"https://slate.textile.io/ipfs/bafkreibk32sw7arspy5kw3p5gkuidfcwjbwqyjdktd5wkqqxahvkm2qlyi";
|
"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`
|
const STYLES_META = css`
|
||||||
padding: 14px 0px 8px 0px;
|
padding: 14px 0px 8px 0px;
|
||||||
overflow-wrap: break-word;
|
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,
|
name: Strings.isEmpty(this.props.data.name) ? "" : this.props.data.name,
|
||||||
selected: {},
|
selected: {},
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
|
inPublicSlates: false,
|
||||||
copyValue: "",
|
copyValue: "",
|
||||||
loading: false,
|
loading: false,
|
||||||
changingPreview: false,
|
changingPreview: false,
|
||||||
@ -249,18 +246,18 @@ export default class CarouselSidebarData extends React.Component {
|
|||||||
this.setState({ unsavedChanges: true });
|
this.setState({ unsavedChanges: true });
|
||||||
if (this.props.isOwner && !this.props.external) {
|
if (this.props.isOwner && !this.props.external) {
|
||||||
this.debounceInstance = Window.debounce(() => this._handleSave(), 3000);
|
this.debounceInstance = Window.debounce(() => this._handleSave(), 3000);
|
||||||
let isPublic = false;
|
let inPublicSlates = false;
|
||||||
let selected = {};
|
let selected = {};
|
||||||
const id = this.props.data.id;
|
const id = this.props.data.id;
|
||||||
for (let slate of this.props.slates) {
|
for (let slate of this.props.slates) {
|
||||||
if (slate.data.objects.some((o) => o.id === id)) {
|
if (slate.data.objects.some((o) => o.id === id)) {
|
||||||
if (slate.data.public) {
|
if (slate.data.public) {
|
||||||
isPublic = true;
|
inPublicSlates = true;
|
||||||
}
|
}
|
||||||
selected[slate.id] = 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() {
|
render() {
|
||||||
|
const isVisible = this.state.inPublicSlates || this.state.isPublic;
|
||||||
const { cid, file, name, coverImage, type, size, url, blurhash } = this.props.data;
|
const { cid, file, name, coverImage, type, size, url, blurhash } = this.props.data;
|
||||||
const elements = [];
|
const elements = [];
|
||||||
if (this.props.onClose) {
|
if (this.props.onClose) {
|
||||||
@ -395,7 +431,7 @@ export default class CarouselSidebarData extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
elements.push(
|
elements.push(
|
||||||
<div key="s-2" css={STYLES_CONTAINER}>
|
<div key="s-2" style={{ marginBottom: 80 }}>
|
||||||
<div css={STYLES_META}>
|
<div css={STYLES_META}>
|
||||||
{this.state.isEditing ? (
|
{this.state.isEditing ? (
|
||||||
<Boundary enabled onOutsideRectEvent={this._handleEditFilename}>
|
<Boundary enabled onOutsideRectEvent={this._handleEditFilename}>
|
||||||
@ -499,12 +535,26 @@ export default class CarouselSidebarData extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||||
Privacy
|
Visibility
|
||||||
</div>
|
</div>
|
||||||
<div style={{ color: Constants.system.darkGray, lineHeight: "1.5", marginBottom: 104 }}>
|
<div
|
||||||
{this.state.isPublic
|
style={{
|
||||||
? "Public. This file is currently visible to others and searchable within Slate through public slates."
|
display: "flex",
|
||||||
: "Private. This file is currently not visible to others unless they have the link."}
|
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>
|
</div>
|
||||||
<input
|
<input
|
||||||
css={STYLES_HIDDEN}
|
css={STYLES_HIDDEN}
|
||||||
|
@ -50,12 +50,19 @@ export class Toggle extends React.Component {
|
|||||||
css={STYLES_TOGGLE}
|
css={STYLES_TOGGLE}
|
||||||
onClick={this._handleChange}
|
onClick={this._handleChange}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: this.props.active ? Constants.system.brand : null,
|
backgroundColor: this.props.active
|
||||||
|
? Constants.system.brand
|
||||||
|
: this.props.dark
|
||||||
|
? Constants.system.grayBlack
|
||||||
|
: null,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<figure
|
<figure
|
||||||
css={STYLES_DIAL}
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,8 +125,9 @@ export const editItem = ({ user, update }) => {
|
|||||||
if (library[0].children[i].id === update.data.id) {
|
if (library[0].children[i].id === update.data.id) {
|
||||||
library[0].children[i] = {
|
library[0].children[i] = {
|
||||||
...library[0].children[i],
|
...library[0].children[i],
|
||||||
coverImage: update.data?.coverImage,
|
...update.data,
|
||||||
name: update.data?.name,
|
// coverImage: update.data?.coverImage,
|
||||||
|
// name: update.data?.name,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ export const getById = async ({ id }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jim): You can serialize this last because you will have all the information
|
// 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 });
|
let activity = await Data.getActivityForUserId({ userId: id });
|
||||||
const slates = await Data.getSlatesByUserId({ userId: id });
|
const slates = await Data.getSlatesByUserId({ userId: id });
|
||||||
const keys = await Data.getAPIKeysByUserId({ userId: id });
|
const keys = await Data.getAPIKeysByUserId({ userId: id });
|
||||||
|
@ -35,6 +35,7 @@ export default class ProfilePage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
console.log(this.props.creator);
|
||||||
const title = this.props.creator ? `${this.props.creator.username}` : "404";
|
const title = this.props.creator ? `${this.props.creator.username}` : "404";
|
||||||
const url = `https://slate.host/${title}`;
|
const url = `https://slate.host/${title}`;
|
||||||
const description = this.props.creator.data.body;
|
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,
|
error: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let library = user.data.library;
|
||||||
|
|
||||||
user = Serializers.user(user);
|
user = Serializers.user(user);
|
||||||
|
|
||||||
let slates = await Data.getSlatesByUserId({
|
let slates = await Data.getSlatesByUserId({
|
||||||
@ -29,10 +31,48 @@ export default async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let publicFileIds = [];
|
||||||
user.slates = [];
|
user.slates = [];
|
||||||
for (let slate of slates) {
|
for (let slate of slates) {
|
||||||
user.slates.push(Serializers.slate(slate));
|
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({
|
return res.status(200).send({
|
||||||
decorator: "SERIALIZED_USER",
|
decorator: "SERIALIZED_USER",
|
||||||
|
@ -77,6 +77,7 @@ export default class SceneProfile extends React.Component {
|
|||||||
|
|
||||||
targetUser = response.data;
|
targetUser = response.data;
|
||||||
}
|
}
|
||||||
|
console.log(targetUser);
|
||||||
|
|
||||||
window.history.replaceState(window.history.state, "A slate user", `/${targetUser.username}`);
|
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,
|
username: req.params.username,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -236,11 +236,56 @@ app.prepare().then(async () => {
|
|||||||
return res.redirect("/404");
|
return res.redirect("/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let library = creator.data.library;
|
||||||
|
|
||||||
|
creator = Serializers.user(creator);
|
||||||
|
|
||||||
const slates = await Data.getSlatesByUserId({
|
const slates = await Data.getSlatesByUserId({
|
||||||
userId: creator.id,
|
userId: creator.id,
|
||||||
publicOnly: true,
|
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 = [];
|
let exploreSlates = [];
|
||||||
|
|
||||||
if (Environment.IS_PRODUCTION) {
|
if (Environment.IS_PRODUCTION) {
|
||||||
@ -281,7 +326,7 @@ app.prepare().then(async () => {
|
|||||||
|
|
||||||
return app.render(req, res, "/_/profile", {
|
return app.render(req, res, "/_/profile", {
|
||||||
viewer,
|
viewer,
|
||||||
creator: Serializers.user({ ...creator, slates }),
|
creator,
|
||||||
mobile,
|
mobile,
|
||||||
resources: EXTERNAL_RESOURCES,
|
resources: EXTERNAL_RESOURCES,
|
||||||
exploreSlates,
|
exploreSlates,
|
||||||
@ -324,9 +369,6 @@ app.prepare().then(async () => {
|
|||||||
return res.redirect("/404");
|
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) {
|
if (!slate.data.public && slate.data.ownerId !== id) {
|
||||||
return res.redirect("/403");
|
return res.redirect("/403");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user