Allow users to leave groups if not owner
This commit is contained in:
@@ -94,7 +94,8 @@ service cloud.firestore {
|
|||||||
return [requiredFields, allFields];
|
return [requiredFields, allFields];
|
||||||
}
|
}
|
||||||
|
|
||||||
allow read, delete: if isSignedIn() && (isSignedInUser() || getGroupRole(groupId) == "owner" || isAdmin()); // is current user's data or is owner of group or is admin
|
allow read: if isSignedIn() && (isSignedInUser() || getGroupRole(groupId) == "owner" || isAdmin()); // is current user's data or is owner of group or is admin
|
||||||
|
allow delete: if isSignedIn() && ((isSignedInUser() && getGroupRole(groupId) != "owner") || (!isSignedInUser() && getGroupRole(groupId) == "owner") || isAdmin())
|
||||||
allow create: if isSignedIn() && isSignedInUser() && (getRequestField("role", "") == "member" || (isAdmin() && verifyGroupFieldTypes())) && verifyCreateFields(getPossibleGroupFields());
|
allow create: if isSignedIn() && isSignedInUser() && (getRequestField("role", "") == "member" || (isAdmin() && verifyGroupFieldTypes())) && verifyCreateFields(getPossibleGroupFields());
|
||||||
allow update: if isSignedIn() &&
|
allow update: if isSignedIn() &&
|
||||||
(getGroupRole(groupId) == "owner" || isAdmin()) &&
|
(getGroupRole(groupId) == "owner" || isAdmin()) &&
|
||||||
|
|||||||
353
src/GroupPage.js
353
src/GroupPage.js
@@ -19,7 +19,8 @@ import "./css/GroupPage.css";
|
|||||||
import "./css/ConfirmationDialog.css";
|
import "./css/ConfirmationDialog.css";
|
||||||
import "./css/OptionsListOverlay.css";
|
import "./css/OptionsListOverlay.css";
|
||||||
|
|
||||||
export default withRouter(class GroupPage extends Component {
|
export default withRouter(
|
||||||
|
class GroupPage extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -35,7 +36,7 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
link: "/",
|
link: "/",
|
||||||
icon: <HomeRoundedIcon />,
|
icon: <HomeRoundedIcon />,
|
||||||
hideTextMobile: true,
|
hideTextMobile: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
role: null,
|
role: null,
|
||||||
groupName: "",
|
groupName: "",
|
||||||
@@ -52,18 +53,20 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
editingUser: null,
|
editingUser: null,
|
||||||
showDeleteGroup: false,
|
showDeleteGroup: false,
|
||||||
deleteGroupLoading: false,
|
deleteGroupLoading: false,
|
||||||
|
showLeaveGroup: false,
|
||||||
|
leaveGroupLoading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
Object.defineProperty(this, "isMounted", {
|
Object.defineProperty(this, "isMounted", {
|
||||||
get: () => isMounted,
|
get: () => isMounted,
|
||||||
set: (value) => isMounted = value,
|
set: (value) => (isMounted = value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setState = (state, callback = null) => {
|
setState = (state, callback = null) => {
|
||||||
if (this.isMounted) super.setState(state, callback);
|
if (this.isMounted) super.setState(state, callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
let promises = [];
|
let promises = [];
|
||||||
@@ -92,7 +95,7 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
{
|
{
|
||||||
displayName: this.state.user.displayName,
|
displayName: this.state.user.displayName,
|
||||||
uid: this.state.user.uid,
|
uid: this.state.user.uid,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
contributors: [],
|
contributors: [],
|
||||||
members: [],
|
members: [],
|
||||||
@@ -116,8 +119,10 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
.doc(this.props.match.params.groupId)
|
.doc(this.props.match.params.groupId)
|
||||||
.get()
|
.get()
|
||||||
.then(async (groupDoc) => {
|
.then(async (groupDoc) => {
|
||||||
await Promise.all(groupDoc.data().sets.map((setId) => {
|
await Promise.all(
|
||||||
return this.state.db.collection("sets")
|
groupDoc.data().sets.map((setId) => {
|
||||||
|
return this.state.db
|
||||||
|
.collection("sets")
|
||||||
.doc(setId)
|
.doc(setId)
|
||||||
.get()
|
.get()
|
||||||
.then((doc) => {
|
.then((doc) => {
|
||||||
@@ -126,10 +131,12 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return groupDoc.data();
|
return groupDoc.data();
|
||||||
}).catch((error) => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
console.log(`Can't access group: ${error}`);
|
console.log(`Can't access group: ${error}`);
|
||||||
return {
|
return {
|
||||||
display_name: "",
|
display_name: "",
|
||||||
@@ -146,7 +153,9 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
newState.role = completedPromises[0].role;
|
newState.role = completedPromises[0].role;
|
||||||
newState.groupName = completedPromises[1].display_name;
|
newState.groupName = completedPromises[1].display_name;
|
||||||
newState.originalGroupName = completedPromises[1].display_name;
|
newState.originalGroupName = completedPromises[1].display_name;
|
||||||
newState.memberCount = Object.keys(completedPromises[1].users).length + (Object.keys(completedPromises[1].users).includes(this.state.user.uid) ? 0 : 1);
|
newState.memberCount =
|
||||||
|
Object.keys(completedPromises[1].users).length +
|
||||||
|
(Object.keys(completedPromises[1].users).includes(this.state.user.uid) ? 0 : 1);
|
||||||
newState.joinCode = completedPromises[0].role === "owner" ? completedPromises[1].join_code : "";
|
newState.joinCode = completedPromises[0].role === "owner" ? completedPromises[1].join_code : "";
|
||||||
|
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
@@ -164,16 +173,19 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
editGroupName = () => {
|
editGroupName = () => {
|
||||||
this.setState({
|
this.setState(
|
||||||
|
{
|
||||||
editGroupName: true,
|
editGroupName: true,
|
||||||
}, () => this.groupNameInput.focus());
|
},
|
||||||
}
|
() => this.groupNameInput.focus()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
handleGroupNameChange = (event) => {
|
handleGroupNameChange = (event) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
groupName: event.target.value,
|
groupName: event.target.value,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
handleInputKeypress = (event) => {
|
handleInputKeypress = (event) => {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
@@ -181,21 +193,21 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
} else if (event.key === "Escape") {
|
} else if (event.key === "Escape") {
|
||||||
this.cancelGroupRename();
|
this.cancelGroupRename();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
stopLoading = () => {
|
stopLoading = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
editGroupName: false,
|
editGroupName: false,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
cancelGroupRename = () => {
|
cancelGroupRename = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
editGroupName: false,
|
editGroupName: false,
|
||||||
groupName: this.state.originalGroupName,
|
groupName: this.state.originalGroupName,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
renameGroup = () => {
|
renameGroup = () => {
|
||||||
if (!this.state.loading && this.state.groupName.replace(" ", "") !== "") {
|
if (!this.state.loading && this.state.groupName.replace(" ", "") !== "") {
|
||||||
@@ -206,13 +218,16 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
loading: true,
|
loading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.state.db.collection("groups")
|
this.state.db
|
||||||
|
.collection("groups")
|
||||||
.doc(this.props.match.params.groupId)
|
.doc(this.props.match.params.groupId)
|
||||||
.update({
|
.update({
|
||||||
display_name: this.state.groupName.trim(),
|
display_name: this.state.groupName.trim(),
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
this.stopLoading();
|
this.stopLoading();
|
||||||
}).catch((error) => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
console.log(`Couldn't update group name: ${error}`);
|
console.log(`Couldn't update group name: ${error}`);
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -222,7 +237,7 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
removeSet = (setId) => {
|
removeSet = (setId) => {
|
||||||
let newLoadingState = {
|
let newLoadingState = {
|
||||||
@@ -231,21 +246,24 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
newLoadingState.sets[setId].loading = true;
|
newLoadingState.sets[setId].loading = true;
|
||||||
this.setState(newLoadingState);
|
this.setState(newLoadingState);
|
||||||
|
|
||||||
this.state.functions.removeSetFromGroup({
|
this.state.functions
|
||||||
|
.removeSetFromGroup({
|
||||||
groupId: this.props.match.params.groupId,
|
groupId: this.props.match.params.groupId,
|
||||||
setId: setId,
|
setId: setId,
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
let newState = {
|
let newState = {
|
||||||
sets: this.state.sets,
|
sets: this.state.sets,
|
||||||
};
|
};
|
||||||
delete newState.sets[setId];
|
delete newState.sets[setId];
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}).catch((error) => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
console.log(`Can't remove set from group: ${error}`);
|
console.log(`Can't remove set from group: ${error}`);
|
||||||
newLoadingState.sets[setId].loading = false;
|
newLoadingState.sets[setId].loading = false;
|
||||||
this.setState(newLoadingState);
|
this.setState(newLoadingState);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
showEditUserRole = (role, index) => {
|
showEditUserRole = (role, index) => {
|
||||||
let user;
|
let user;
|
||||||
@@ -263,13 +281,13 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
index: index,
|
index: index,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
hideEditUserRole = () => {
|
hideEditUserRole = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
editingUser: null,
|
editingUser: null,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
editUserRole = (role) => {
|
editUserRole = (role) => {
|
||||||
if (role === this.state.editingUser.role) {
|
if (role === this.state.editingUser.role) {
|
||||||
@@ -278,7 +296,8 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (role === "remove") {
|
if (role === "remove") {
|
||||||
this.state.db.collection("users")
|
this.state.db
|
||||||
|
.collection("users")
|
||||||
.doc(this.state.editingUser.uid)
|
.doc(this.state.editingUser.uid)
|
||||||
.collection("groups")
|
.collection("groups")
|
||||||
.doc(this.props.match.params.groupId)
|
.doc(this.props.match.params.groupId)
|
||||||
@@ -296,20 +315,23 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
editingUser: null,
|
editingUser: null,
|
||||||
groupUsers: groupUsers,
|
groupUsers: groupUsers,
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
editingUser: null,
|
editingUser: null,
|
||||||
});
|
});
|
||||||
console.log(`Couldn't change user role: ${error}`)
|
console.log(`Couldn't change user role: ${error}`);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.db.collection("users")
|
this.state.db
|
||||||
|
.collection("users")
|
||||||
.doc(this.state.editingUser.uid)
|
.doc(this.state.editingUser.uid)
|
||||||
.collection("groups")
|
.collection("groups")
|
||||||
.doc(this.props.match.params.groupId)
|
.doc(this.props.match.params.groupId)
|
||||||
.update({
|
.update({
|
||||||
role: role,
|
role: role,
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
let groupUsers = this.state.groupUsers;
|
let groupUsers = this.state.groupUsers;
|
||||||
let userData;
|
let userData;
|
||||||
if (this.state.editingUser.role === "owner") {
|
if (this.state.editingUser.role === "owner") {
|
||||||
@@ -330,68 +352,101 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
editingUser: null,
|
editingUser: null,
|
||||||
groupUsers: groupUsers,
|
groupUsers: groupUsers,
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
editingUser: null,
|
editingUser: null,
|
||||||
});
|
});
|
||||||
console.log(`Couldn't change user role: ${error}`)
|
console.log(`Couldn't change user role: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
showDeleteGroup = () => {
|
showDeleteGroup = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showDeleteGroup: true,
|
showDeleteGroup: true,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
hideDeleteGroup = () => {
|
hideDeleteGroup = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showDeleteGroup: false,
|
showDeleteGroup: false,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
deleteGroup = () => {
|
deleteGroup = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
deleteGroupLoading: true,
|
deleteGroupLoading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.state.db.collection("groups")
|
this.state.db
|
||||||
|
.collection("groups")
|
||||||
.doc(this.props.match.params.groupId)
|
.doc(this.props.match.params.groupId)
|
||||||
.delete()
|
.delete()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.props.history.push("/groups");
|
this.props.history.push("/groups");
|
||||||
}).catch((error) => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
console.log(`Couldn't delete group: ${error}`);
|
console.log(`Couldn't delete group: ${error}`);
|
||||||
this.setState({
|
this.setState({
|
||||||
deleteGroupLoading: false,
|
deleteGroupLoading: false,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
showLeaveGroup = () => {
|
||||||
|
this.setState({
|
||||||
|
showLeaveGroup: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
hideLeaveGroup = () => {
|
||||||
|
this.setState({
|
||||||
|
showLeaveGroup: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
leaveGroup = () => {
|
||||||
|
this.setState({
|
||||||
|
leaveGroupLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.state.db
|
||||||
|
.collection("users")
|
||||||
|
.doc(this.props.user.uid)
|
||||||
|
.collection("groups")
|
||||||
|
.doc(this.props.match.params.groupId)
|
||||||
|
.delete()
|
||||||
|
.then(() => {
|
||||||
|
this.props.history.push("/groups");
|
||||||
})
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
|
console.log(`Couldn't leave group: ${error}`);
|
||||||
|
this.setState({
|
||||||
|
leaveGroupLoading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return this.state.role === "none" ? (
|
||||||
(this.state.role === "none") ?
|
|
||||||
<Error404 />
|
<Error404 />
|
||||||
:
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<NavBar items={this.state.navbarItems} />
|
<NavBar items={this.state.navbarItems} />
|
||||||
<main>
|
<main>
|
||||||
{
|
{!(this.state.role === null) && (
|
||||||
!(this.state.role === null) &&
|
|
||||||
<>
|
<>
|
||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
{
|
{this.state.editGroupName && this.state.role === "owner" ? (
|
||||||
this.state.editGroupName && this.state.role === "owner"
|
|
||||||
?
|
|
||||||
<h1 className="group-name-header-input-container">
|
<h1 className="group-name-header-input-container">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
onChange={this.handleGroupNameChange}
|
onChange={this.handleGroupNameChange}
|
||||||
value={this.state.groupName}
|
value={this.state.groupName}
|
||||||
onKeyDown={this.handleInputKeypress}
|
onKeyDown={this.handleInputKeypress}
|
||||||
ref={inputEl => (this.groupNameInput = inputEl)}
|
ref={(inputEl) => (this.groupNameInput = inputEl)}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@@ -402,19 +457,17 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
loading={this.state.loading}
|
loading={this.state.loading}
|
||||||
></Button>
|
></Button>
|
||||||
</h1>
|
</h1>
|
||||||
:
|
) : (
|
||||||
<h1 onClick={this.state.role === "owner" ? this.editGroupName : () => {}}>
|
<h1 onClick={this.state.role === "owner" ? this.editGroupName : () => {}}>
|
||||||
{this.state.groupName}
|
{this.state.groupName}
|
||||||
{
|
{this.state.role === "owner" && (
|
||||||
this.state.role === "owner" &&
|
|
||||||
<span className="group-edit-icon">
|
<span className="group-edit-icon">
|
||||||
<EditRoundedIcon />
|
<EditRoundedIcon />
|
||||||
</span>
|
</span>
|
||||||
}
|
)}
|
||||||
</h1>
|
</h1>
|
||||||
}
|
)}
|
||||||
{
|
{this.state.role === "owner" ? (
|
||||||
this.state.role === "owner" &&
|
|
||||||
<div className="button-container">
|
<div className="button-container">
|
||||||
<LinkButton
|
<LinkButton
|
||||||
to={`/groups/${this.props.match.params.groupId}/stats`}
|
to={`/groups/${this.props.match.params.groupId}/stats`}
|
||||||
@@ -429,17 +482,24 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
title="Delete group"
|
title="Delete group"
|
||||||
></Button>
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
) : (
|
||||||
|
<div className="button-container">
|
||||||
|
<Button
|
||||||
|
onClick={this.showLeaveGroup}
|
||||||
|
icon={<GroupRemoveRoundedIcon />}
|
||||||
|
className="button--round"
|
||||||
|
title="Leave group"
|
||||||
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
{
|
)}
|
||||||
this.state.joinCode &&
|
</div>
|
||||||
|
{this.state.joinCode && (
|
||||||
<div className="stat-row stat-row--inline">
|
<div className="stat-row stat-row--inline">
|
||||||
<p>Join code</p>
|
<p>Join code</p>
|
||||||
<h2>{this.state.joinCode}</h2>
|
<h2>{this.state.joinCode}</h2>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
{
|
{this.state.memberCount && (
|
||||||
this.state.memberCount &&
|
|
||||||
<div className="stat-row stat-row--inline">
|
<div className="stat-row stat-row--inline">
|
||||||
<h2>{this.state.memberCount}</h2>
|
<h2>{this.state.memberCount}</h2>
|
||||||
<p>
|
<p>
|
||||||
@@ -447,16 +507,13 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
{this.state.memberCount !== 1 && "s"}
|
{this.state.memberCount !== 1 && "s"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2>Sets</h2>
|
<h2>Sets</h2>
|
||||||
{
|
{Object.keys(this.state.sets).length > 0 ? (
|
||||||
Object.keys(this.state.sets).length > 0
|
|
||||||
?
|
|
||||||
<div className="group-links-container">
|
<div className="group-links-container">
|
||||||
{
|
{Object.keys(this.state.sets)
|
||||||
Object.keys(this.state.sets)
|
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (this.state.sets[a].displayName < this.state.sets[b].displayName) {
|
if (this.state.sets[a].displayName < this.state.sets[b].displayName) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -466,15 +523,10 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
})
|
})
|
||||||
.map((setId) =>
|
.map((setId) => (
|
||||||
<div key={setId} className="group-set-link">
|
<div key={setId} className="group-set-link">
|
||||||
<Link
|
<Link to={`/sets/${setId}`}>{this.state.sets[setId].displayName}</Link>
|
||||||
to={`/sets/${setId}`}
|
{this.state.role === "owner" && (
|
||||||
>
|
|
||||||
{this.state.sets[setId].displayName}
|
|
||||||
</Link>
|
|
||||||
{
|
|
||||||
this.state.role === "owner" &&
|
|
||||||
<Button
|
<Button
|
||||||
className="button--no-background"
|
className="button--no-background"
|
||||||
onClick={() => this.removeSet(setId)}
|
onClick={() => this.removeSet(setId)}
|
||||||
@@ -483,144 +535,143 @@ export default withRouter(class GroupPage extends Component {
|
|||||||
disabled={this.state.sets[setId].loading}
|
disabled={this.state.sets[setId].loading}
|
||||||
title="Remove set"
|
title="Remove set"
|
||||||
></Button>
|
></Button>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
))}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
:
|
) : (
|
||||||
<p>
|
<p>This group doesn't have any sets yet!</p>
|
||||||
This group doesn't have any sets yet!
|
)}
|
||||||
</p>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
{
|
{this.state.role === "owner" && (
|
||||||
this.state.role === "owner" &&
|
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<h2>Members</h2>
|
<h2>Members</h2>
|
||||||
{
|
{this.state.groupUsers && this.state.groupUsers.owners.length > 0 && (
|
||||||
this.state.groupUsers && this.state.groupUsers.owners.length > 0 &&
|
|
||||||
<>
|
<>
|
||||||
<h3 className="group-role-header">Owners</h3>
|
<h3 className="group-role-header">Owners</h3>
|
||||||
<div className="group-links-container">
|
<div className="group-links-container">
|
||||||
{
|
{this.state.groupUsers.owners.map((user, index) => (
|
||||||
this.state.groupUsers.owners.map((user, index) =>
|
|
||||||
<p
|
<p
|
||||||
key={user.uid}
|
key={user.uid}
|
||||||
className={`group-set-link ${user.uid !== this.state.user.uid ? "group-set-link--enabled" : ""}`}
|
className={`group-set-link ${
|
||||||
onClick={user.uid === this.state.user.uid ? () => { } : () => this.showEditUserRole("owner", index)}
|
user.uid !== this.state.user.uid ? "group-set-link--enabled" : ""
|
||||||
>
|
}`}
|
||||||
{
|
onClick={
|
||||||
user.uid === this.state.user.uid
|
user.uid === this.state.user.uid
|
||||||
?
|
? () => {}
|
||||||
|
: () => this.showEditUserRole("owner", index)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{user.uid === this.state.user.uid ? (
|
||||||
"You"
|
"You"
|
||||||
:
|
) : (
|
||||||
<>
|
<>
|
||||||
{user.displayName}
|
{user.displayName}
|
||||||
<EditRoundedIcon />
|
<EditRoundedIcon />
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</p>
|
</p>
|
||||||
)
|
))}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
{
|
{this.state.groupUsers && this.state.groupUsers.contributors.length > 0 && (
|
||||||
this.state.groupUsers && this.state.groupUsers.contributors.length > 0 &&
|
|
||||||
<>
|
<>
|
||||||
<h3 className="group-role-header">Contributors</h3>
|
<h3 className="group-role-header">Contributors</h3>
|
||||||
<div className="group-links-container">
|
<div className="group-links-container">
|
||||||
{
|
{this.state.groupUsers.contributors.map((user, index) => (
|
||||||
this.state.groupUsers.contributors.map((user, index) =>
|
|
||||||
<p
|
<p
|
||||||
key={user.uid}
|
key={user.uid}
|
||||||
className={`group-set-link ${user.uid !== this.state.user.uid ? "group-set-link--enabled" : ""}`}
|
className={`group-set-link ${
|
||||||
onClick={user.uid === this.state.user.uid ? () => { } : () => this.showEditUserRole("contributor", index)}
|
user.uid !== this.state.user.uid ? "group-set-link--enabled" : ""
|
||||||
>
|
}`}
|
||||||
{
|
onClick={
|
||||||
user.uid === this.state.user.uid
|
user.uid === this.state.user.uid
|
||||||
?
|
? () => {}
|
||||||
|
: () => this.showEditUserRole("contributor", index)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{user.uid === this.state.user.uid ? (
|
||||||
"You"
|
"You"
|
||||||
:
|
) : (
|
||||||
<>
|
<>
|
||||||
{user.displayName}
|
{user.displayName}
|
||||||
<EditRoundedIcon />
|
<EditRoundedIcon />
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</p>
|
</p>
|
||||||
)
|
))}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
{
|
{this.state.groupUsers && this.state.groupUsers.members.length > 0 && (
|
||||||
this.state.groupUsers && this.state.groupUsers.members.length > 0 &&
|
|
||||||
<>
|
<>
|
||||||
<h3 className="group-role-header">Members</h3>
|
<h3 className="group-role-header">Members</h3>
|
||||||
<div className="group-links-container">
|
<div className="group-links-container">
|
||||||
{
|
{this.state.groupUsers.members.map((user, index) => (
|
||||||
this.state.groupUsers.members.map((user, index) =>
|
|
||||||
<p
|
<p
|
||||||
key={user.uid}
|
key={user.uid}
|
||||||
className={`group-set-link ${user.uid !== this.state.user.uid ? "group-set-link--enabled" : ""}`}
|
className={`group-set-link ${
|
||||||
onClick={user.uid === this.state.user.uid ? () => { } : () => this.showEditUserRole("member", index)}
|
user.uid !== this.state.user.uid ? "group-set-link--enabled" : ""
|
||||||
>
|
}`}
|
||||||
{
|
onClick={
|
||||||
user.uid === this.state.user.uid
|
user.uid === this.state.user.uid
|
||||||
?
|
? () => {}
|
||||||
|
: () => this.showEditUserRole("member", index)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{user.uid === this.state.user.uid ? (
|
||||||
"You"
|
"You"
|
||||||
:
|
) : (
|
||||||
<>
|
<>
|
||||||
{user.displayName}
|
{user.displayName}
|
||||||
<EditRoundedIcon />
|
<EditRoundedIcon />
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</p>
|
</p>
|
||||||
)
|
))}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{
|
{this.state.editingUser && (
|
||||||
this.state.editingUser &&
|
|
||||||
<>
|
<>
|
||||||
<div className="overlay" onClick={this.hideEditUserRole}></div>
|
<div className="overlay" onClick={this.hideEditUserRole}></div>
|
||||||
<div className="overlay-content options-list-overlay-content">
|
<div className="overlay-content options-list-overlay-content">
|
||||||
{
|
{["Owner", "Contributor", "Member", "Remove"].map((role) => (
|
||||||
["Owner", "Contributor", "Member", "Remove"].map((role) =>
|
<h3 key={role} onClick={() => this.editUserRole(role.toLowerCase())}>
|
||||||
<h3
|
|
||||||
key={role}
|
|
||||||
onClick={() => this.editUserRole(role.toLowerCase())}
|
|
||||||
>
|
|
||||||
{role}
|
{role}
|
||||||
</h3>
|
</h3>
|
||||||
)
|
))}
|
||||||
}
|
|
||||||
|
|
||||||
<div onClick={this.hideEditUserRole}>
|
<div onClick={this.hideEditUserRole}>Cancel</div>
|
||||||
Cancel
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
{
|
{this.state.showDeleteGroup && (
|
||||||
this.state.showDeleteGroup &&
|
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
yesFunction={this.deleteGroup}
|
yesFunction={this.deleteGroup}
|
||||||
noFunction={this.hideDeleteGroup}
|
noFunction={this.hideDeleteGroup}
|
||||||
message="Are you sure you want to delete this group?"
|
message="Are you sure you want to delete this group?"
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
|
{this.state.showLeaveGroup && (
|
||||||
|
<ConfirmationDialog
|
||||||
|
yesFunction={this.leaveGroup}
|
||||||
|
noFunction={this.hideLeaveGroup}
|
||||||
|
message="Are you sure you want to leave this group?"
|
||||||
|
loading={this.state.loading}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -117,18 +117,42 @@ describe("Parandum Firestore database", () => {
|
|||||||
await firebase.assertSucceeds(testDoc.get());
|
await firebase.assertSucceeds(testDoc.get());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can delete current user's groups", async () => {
|
it("Can delete current user's groups when not group owner", async () => {
|
||||||
|
const admin = getAdminFirestore();
|
||||||
|
await admin.collection("users").doc(myId).collection("groups").doc(groupOne).set({ role: "member" });
|
||||||
|
|
||||||
const db = getFirestore(myAuth);
|
const db = getFirestore(myAuth);
|
||||||
const testDoc = db.collection("users").doc(myId).collection("groups").doc(groupOne);
|
const testDoc = db.collection("users").doc(myId).collection("groups").doc(groupOne);
|
||||||
await firebase.assertSucceeds(testDoc.delete());
|
await firebase.assertSucceeds(testDoc.delete());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can't delete other users' groups", async () => {
|
it("Can't delete current user's groups when group owner", async () => {
|
||||||
|
const admin = getAdminFirestore();
|
||||||
|
await admin.collection("users").doc(myId).collection("groups").doc(groupOne).set({ role: "owner" });
|
||||||
|
|
||||||
|
const db = getFirestore(myAuth);
|
||||||
|
const testDoc = db.collection("users").doc(myId).collection("groups").doc(groupOne);
|
||||||
|
await firebase.assertFails(testDoc.delete());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Can't delete other users' groups when not group owner", async () => {
|
||||||
|
const admin = getAdminFirestore();
|
||||||
|
await admin.collection("users").doc(myId).collection("groups").doc(groupOne).set({ role: "member" });
|
||||||
|
|
||||||
const db = getFirestore(myAuth);
|
const db = getFirestore(myAuth);
|
||||||
const testDoc = db.collection("users").doc(theirId).collection("groups").doc(groupOne);
|
const testDoc = db.collection("users").doc(theirId).collection("groups").doc(groupOne);
|
||||||
await firebase.assertFails(testDoc.delete());
|
await firebase.assertFails(testDoc.delete());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Can delete other users' groups when group owner", async () => {
|
||||||
|
const admin = getAdminFirestore();
|
||||||
|
await admin.collection("users").doc(myId).collection("groups").doc(groupOne).set({ role: "owner" });
|
||||||
|
|
||||||
|
const db = getFirestore(myAuth);
|
||||||
|
const testDoc = db.collection("users").doc(theirId).collection("groups").doc(groupOne);
|
||||||
|
await firebase.assertSucceeds(testDoc.delete());
|
||||||
|
});
|
||||||
|
|
||||||
it("Can delete other users' groups when admin", async () => {
|
it("Can delete other users' groups when admin", async () => {
|
||||||
const db = getFirestore(myAdminAuth);
|
const db = getFirestore(myAdminAuth);
|
||||||
const testDoc = db.collection("users").doc(theirId).collection("groups").doc(groupOne);
|
const testDoc = db.collection("users").doc(theirId).collection("groups").doc(groupOne);
|
||||||
|
|||||||
Reference in New Issue
Block a user