Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/backend/src/couchers/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"host_request_too_short2_one": "Host request cannot be shorter than {{count}} character.",
"host_request_too_short2_other": "Host request cannot be shorter than {{count}} characters.",
"hosting_status_required": "Hosting status is required.",
"incomplete_profile_create_discussion": "You have to complete your profile before you can start a discussion.",
"incomplete_profile_create_event": "You have to complete your profile before you can create an event.",
"incomplete_profile_create_public_trip": "You have to complete your profile before you can create a public trip.",
"incomplete_profile_send_friend_request": "You have to complete your profile before you can send a friend request.",
Expand Down
5 changes: 5 additions & 0 deletions app/backend/src/couchers/servicers/discussions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from couchers.context import CouchersContext, make_notification_user_context
from couchers.db import can_moderate_node, session_scope
from couchers.event_log import log_event
from couchers.helpers.completed_profile import has_completed_profile
from couchers.jobs.enqueue import queue_job
from couchers.models import Cluster, ClusterSubscription, Discussion, ModerationObjectType, Thread, User
from couchers.models.discussions import ContentChangeType, DiscussionVersion
Expand Down Expand Up @@ -119,6 +120,10 @@ def CreateDiscussion(
if not cluster.small_community_features_enabled:
context.abort_with_error_code(grpc.StatusCode.FAILED_PRECONDITION, "cannot_create_discussion")

user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()
if not has_completed_profile(session, user):
context.abort_with_error_code(grpc.StatusCode.FAILED_PRECONDITION, "incomplete_profile_create_discussion")

thread = Thread()
session.add(thread)
session.flush()
Expand Down
20 changes: 20 additions & 0 deletions app/backend/src/tests/test_discussions.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,3 +861,23 @@ def test_admin_delete_discussion_creates_version_record(db):
assert v.old_title == "Admin will delete this"
assert v.new_title is None
assert v.editor_user_id == admin.id


def test_create_discussion_incomplete_profile(db):
user, token = generate_user(complete_profile=False)
user2, token2 = generate_user()

with session_scope() as session:
community_id = create_community(session, 0, 1, "Testing Community", [user2], [], None).id

with discussions_session(token) as api:
with pytest.raises(grpc.RpcError) as e:
api.CreateDiscussion(
discussions_pb2.CreateDiscussionReq(
title="dummy title",
content="dummy content",
owner_community_id=community_id,
)
)
assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
assert e.value.details() == "You have to complete your profile before you can start a discussion."
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ interface ProfileIncompleteDialogProps {
| "send_message"
| "send_request"
| "create_public_trip"
| "send_friend_request";
| "send_friend_request"
| "create_discussion";
}

export default function ProfileIncompleteDialog({
Expand Down Expand Up @@ -70,9 +71,6 @@ export default function ProfileIncompleteDialog({
<Button component={Link} href={routeToEditProfile()}>
{t("profile:complete_profile_dialog.edit_profile_button")}
</Button>
<Button component={Link} href={routeToEditProfile()}>
{t("dashboard:complete_profile_dialog.edit_profile_button")}
</Button>
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated
</DialogActions>
</Dialog>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import Alert from "components/Alert";
import Button from "components/Button";
import CenteredSpinner from "components/CenteredSpinner/CenteredSpinner";
import { EmailIcon } from "components/Icons";
import ProfileIncompleteDialog from "components/ProfileIncompleteDialog/ProfileIncompleteDialog";
import TextBody from "components/TextBody";
import useAccountInfo from "features/auth/useAccountInfo";
import { SectionTitle } from "features/communities/CommunityPage";
import { useListDiscussions } from "features/communities/hooks";
import { useTranslation } from "i18n";
import { COMMUNITIES } from "i18n/namespaces";
import { Community } from "proto/communities_pb";
import { useState } from "react";
import { useEffect, useState } from "react";
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated
import { theme } from "theme";
import hasAtLeastOnePage from "utils/hasAtLeastOnePage";

Expand Down Expand Up @@ -62,10 +64,29 @@ export default function DiscussionsListPage({
}) {
const { t } = useTranslation([COMMUNITIES]);

const { data: accountInfo } = useAccountInfo();
const hash = typeof window !== "undefined" ? window.location.hash : "";
const [isCreatingNewPost, setIsCreatingNewPost] = useState(
hash.includes("new"),
);
const [profileDialogOpen, setProfileDialogOpen] = useState(false);
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated

// If #new hash auto-opened the form but profile is incomplete, show dialog instead
useEffect(() => {
if (accountInfo && !accountInfo.profileComplete && isCreatingNewPost) {
setIsCreatingNewPost(false);
setProfileDialogOpen(true);
}
}, [accountInfo, isCreatingNewPost]);
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated

const handleNewPostClick = () => {
if (accountInfo !== undefined && !accountInfo.profileComplete) {
setProfileDialogOpen(true);
} else {
setIsCreatingNewPost(true);
}
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated
};

const {
isLoading: isDiscussionsLoading,
isFetching: isDiscussionsFetching,
Expand All @@ -80,6 +101,11 @@ export default function DiscussionsListPage({

return (
<>
<ProfileIncompleteDialog
open={profileDialogOpen}
onClose={() => setProfileDialogOpen(false)}
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated
attempted_action="create_discussion"
/>
<StyledDiscussionsHeader>
<SectionTitle icon={<EmailIcon />}>
{t("communities:discussions_title")}
Expand All @@ -90,9 +116,7 @@ export default function DiscussionsListPage({
)}
<Collapse in={!isCreatingNewPost}>
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated
<StyledNewPostButtonContainer>
<StyledCreateResourceButton
onClick={() => setIsCreatingNewPost(true)}
>
<StyledCreateResourceButton onClick={handleNewPostClick}>
Comment thread
kevinortiz43 marked this conversation as resolved.
Outdated
{t("communities:new_post_label")}
</StyledCreateResourceButton>
{isRefetching && <CenteredSpinner />}
Expand Down
3 changes: 2 additions & 1 deletion app/web/features/profile/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@
"send_message": "send a message",
"send_friend_request": "send a friend request",
"create_event": "create an event",
"create_public_trip": "create a public trip"
"create_public_trip": "create a public trip",
"create_discussion": "create a discussion"
},
"edit_profile_button": "Edit your profile now",
"cancel_button": "Never mind"
Expand Down
Loading