mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-10 08:17:59 -05:00
Add pace privacy controls & edit modal support
[backend] add hide_pace column to user_privacy_settings table [backend] add hide_pace column to activities table [backend] add logic for hide_pace column [backend] add logic to only return activity values hidden if user_id = token_user_id [frontend] add AlertComponent [frontend] add pace column [frontend] change US translations from "Velocity" to "Speed" [frontend] added ability to change activity privacy settings on edit modal
This commit is contained in:
@@ -197,6 +197,7 @@ def get_user_activities_with_pagination(
|
||||
name_search: str | None = None,
|
||||
sort_by: str | None = None,
|
||||
sort_order: str | None = None,
|
||||
user_is_owner: bool = False,
|
||||
) -> list[activities_schema.Activity] | None:
|
||||
try:
|
||||
# Mapping from frontend sort keys to database model fields
|
||||
@@ -311,6 +312,13 @@ def get_user_activities_with_pagination(
|
||||
serialized_activities = []
|
||||
if activities:
|
||||
for activity in activities:
|
||||
if not user_is_owner:
|
||||
if activity.hide_start_time:
|
||||
activity.start_time = None
|
||||
if activity.hide_location:
|
||||
activity.city = None
|
||||
activity.town = None
|
||||
activity.country = None
|
||||
serialized_activities.append(
|
||||
activities_utils.serialize_activity(activity)
|
||||
)
|
||||
@@ -364,6 +372,7 @@ def get_user_activities_per_timeframe(
|
||||
start: datetime,
|
||||
end: datetime,
|
||||
db: Session,
|
||||
user_is_owner: bool = False,
|
||||
):
|
||||
try:
|
||||
# Get the activities from the database
|
||||
@@ -384,6 +393,14 @@ def get_user_activities_per_timeframe(
|
||||
for activity in activities:
|
||||
activity = activities_utils.serialize_activity(activity)
|
||||
|
||||
if not user_is_owner:
|
||||
if activity.hide_start_time:
|
||||
activity.start_time = None
|
||||
if activity.hide_location:
|
||||
activity.city = None
|
||||
activity.town = None
|
||||
activity.country = None
|
||||
|
||||
# Return the activities
|
||||
return activities
|
||||
|
||||
@@ -426,6 +443,12 @@ def get_user_following_activities_per_timeframe(
|
||||
|
||||
for activity in activities:
|
||||
activity = activities_utils.serialize_activity(activity)
|
||||
if activity.hide_start_time:
|
||||
activity.start_time = None
|
||||
if activity.hide_location:
|
||||
activity.city = None
|
||||
activity.town = None
|
||||
activity.country = None
|
||||
|
||||
# Return the activities
|
||||
return activities
|
||||
@@ -477,6 +500,12 @@ def get_user_following_activities_with_pagination(
|
||||
# Iterate and format the dates
|
||||
for activity in activities:
|
||||
activity = activities_utils.serialize_activity(activity)
|
||||
if activity.hide_start_time:
|
||||
activity.start_time = None
|
||||
if activity.hide_location:
|
||||
activity.city = None
|
||||
activity.town = None
|
||||
activity.country = None
|
||||
|
||||
# Return the activities
|
||||
return activities
|
||||
|
||||
@@ -171,6 +171,11 @@ class Activity(Base):
|
||||
nullable=False,
|
||||
comment="Hide activity speed",
|
||||
)
|
||||
hide_pace = Column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
comment="Hide activity pace",
|
||||
)
|
||||
|
||||
# Define a relationship to the User model
|
||||
user = relationship("User", back_populates="activities")
|
||||
|
||||
@@ -63,7 +63,7 @@ async def read_activities_user_activities_week(
|
||||
if user_id == token_user_id:
|
||||
# Get all user activities for the requested week if the user is the owner of the token
|
||||
activities = activities_crud.get_user_activities_per_timeframe(
|
||||
user_id, start_of_week, end_of_week, db
|
||||
user_id, start_of_week, end_of_week, db, True
|
||||
)
|
||||
else:
|
||||
# Get user following activities for the requested week if the user is not the owner of the token
|
||||
@@ -107,7 +107,7 @@ async def read_activities_user_activities_this_week_distances(
|
||||
if user_id == token_user_id:
|
||||
# Get all user activities for the requested week if the user is the owner of the token
|
||||
activities = activities_crud.get_user_activities_per_timeframe(
|
||||
user_id, start_of_week, end_of_week, db
|
||||
user_id, start_of_week, end_of_week, db, True
|
||||
)
|
||||
else:
|
||||
# Get user following activities for the requested week if the user is not the owner of the token
|
||||
@@ -148,7 +148,7 @@ async def read_activities_user_activities_this_month_distances(
|
||||
if user_id == token_user_id:
|
||||
# Get all user activities for the requested month if the user is the owner of the token
|
||||
activities = activities_crud.get_user_activities_per_timeframe(
|
||||
user_id, start_of_month, end_of_month, db
|
||||
user_id, start_of_month, end_of_month, db, True
|
||||
)
|
||||
else:
|
||||
# Get user following activities for the requested month if the user is not the owner of the token
|
||||
@@ -193,7 +193,7 @@ async def read_activities_user_activities_this_month_number(
|
||||
if user_id == token_user_id:
|
||||
# Get all user activities for the requested month if the user is the owner of the token
|
||||
activities = activities_crud.get_user_activities_per_timeframe(
|
||||
user_id, start_of_month, end_of_month, db
|
||||
user_id, start_of_month, end_of_month, db, True
|
||||
)
|
||||
else:
|
||||
# Get user following activities for the requested month if the user is not the owner of the token
|
||||
@@ -313,6 +313,10 @@ async def read_activities_user_activities_pagination(
|
||||
check_scopes: Annotated[
|
||||
Callable, Security(session_security.check_scopes, scopes=["activities:read"])
|
||||
],
|
||||
token_user_id: Annotated[
|
||||
int,
|
||||
Depends(session_security.get_user_id_from_access_token),
|
||||
],
|
||||
db: Annotated[
|
||||
Session,
|
||||
Depends(core_database.get_db),
|
||||
@@ -335,6 +339,9 @@ async def read_activities_user_activities_pagination(
|
||||
sort_by: str | None = Query(None),
|
||||
sort_order: str | None = Query(None),
|
||||
):
|
||||
user_is_owner = True
|
||||
if token_user_id != user_id:
|
||||
user_is_owner = False
|
||||
# Get and return the activities for the user with pagination and filters
|
||||
return activities_crud.get_user_activities_with_pagination(
|
||||
user_id=user_id,
|
||||
@@ -347,6 +354,7 @@ async def read_activities_user_activities_pagination(
|
||||
name_search=name_search,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
user_is_owner=user_is_owner,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class Activity(BaseModel):
|
||||
hide_cadence: bool | None = None
|
||||
hide_elevation: bool | None = None
|
||||
hide_speed: bool | None = None
|
||||
hide_pace: bool | None = None
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
@@ -223,6 +223,7 @@ def transform_schema_activity_to_model_activity(
|
||||
hide_cadence=activity.hide_cadence,
|
||||
hide_elevation=activity.hide_elevation,
|
||||
hide_speed=activity.hide_speed,
|
||||
hide_pace=activity.hide_pace,
|
||||
)
|
||||
|
||||
return new_activity
|
||||
|
||||
@@ -84,6 +84,12 @@ def upgrade() -> None:
|
||||
nullable=False,
|
||||
comment="Hide activity speed",
|
||||
),
|
||||
sa.Column(
|
||||
"hide_activity_pace",
|
||||
sa.Boolean(),
|
||||
nullable=False,
|
||||
comment="Hide activity pace",
|
||||
),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
@@ -108,7 +114,8 @@ def upgrade() -> None:
|
||||
hide_activity_power,
|
||||
hide_activity_cadence,
|
||||
hide_activity_elevation,
|
||||
hide_activity_speed
|
||||
hide_activity_speed,
|
||||
hide_activity_pace
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
@@ -120,7 +127,8 @@ def upgrade() -> None:
|
||||
FALSE,
|
||||
FALSE,
|
||||
FALSE,
|
||||
FALSE
|
||||
FALSE,
|
||||
FALSE
|
||||
FROM users;
|
||||
"""
|
||||
)
|
||||
@@ -195,6 +203,12 @@ def upgrade() -> None:
|
||||
"hide_speed", sa.Boolean(), nullable=True, comment="Hide activity speed"
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"activities",
|
||||
sa.Column(
|
||||
"hide_pace", sa.Boolean(), nullable=True, comment="Hide activity pace"
|
||||
),
|
||||
)
|
||||
|
||||
# Update all existing activities to set these columns to FALSE
|
||||
connection.execute(
|
||||
@@ -209,6 +223,7 @@ def upgrade() -> None:
|
||||
hide_cadence = FALSE,
|
||||
hide_elevation = FALSE,
|
||||
hide_speed = FALSE
|
||||
hide_pace = FALSE
|
||||
WHERE hide_start_time IS NULL;
|
||||
"""
|
||||
)
|
||||
@@ -223,11 +238,13 @@ def upgrade() -> None:
|
||||
op.alter_column("activities", "hide_cadence", nullable=False)
|
||||
op.alter_column("activities", "hide_elevation", nullable=False)
|
||||
op.alter_column("activities", "hide_speed", nullable=False)
|
||||
op.alter_column("activities", "hide_pace", nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("activities", "hide_pace")
|
||||
op.drop_column("activities", "hide_speed")
|
||||
op.drop_column("activities", "hide_elevation")
|
||||
op.drop_column("activities", "hide_cadence")
|
||||
|
||||
@@ -178,6 +178,7 @@ def create_activity_objects(
|
||||
hide_elevation=user_privacy_settings.hide_activity_elevation
|
||||
or False,
|
||||
hide_speed=user_privacy_settings.hide_activity_speed or False,
|
||||
hide_pace=user_privacy_settings.hide_activity_pace or False,
|
||||
),
|
||||
"is_elevation_set": session_record["is_elevation_set"],
|
||||
"ele_waypoints": session_record["ele_waypoints"],
|
||||
|
||||
@@ -330,6 +330,7 @@ def parse_gpx_file(
|
||||
hide_elevation=user_privacy_settings.hide_activity_elevation
|
||||
or False,
|
||||
hide_speed=user_privacy_settings.hide_activity_speed or False,
|
||||
hide_pace=user_privacy_settings.hide_activity_pace or False,
|
||||
)
|
||||
|
||||
# Generate activity laps
|
||||
|
||||
@@ -338,6 +338,7 @@ def parse_activity(
|
||||
hide_elevation=user_privacy_settings.hide_activity_elevation
|
||||
or False,
|
||||
hide_speed=user_privacy_settings.hide_activity_speed or False,
|
||||
hide_pace=user_privacy_settings.hide_activity_pace or False,
|
||||
)
|
||||
|
||||
# Fetch and process activity laps
|
||||
|
||||
@@ -51,6 +51,7 @@ class UserMe(User):
|
||||
hide_activity_cadence: bool | None = None
|
||||
hide_activity_elevation: bool | None = None
|
||||
hide_activity_speed: bool | None = None
|
||||
hide_activity_pace: bool | None = None
|
||||
|
||||
|
||||
class UserEditPassword(BaseModel):
|
||||
|
||||
@@ -56,6 +56,7 @@ def create_user_privacy_settings(user_id: int, db: Session):
|
||||
hide_activity_cadence=False,
|
||||
hide_activity_elevation=False,
|
||||
hide_activity_speed=False,
|
||||
hide_activity_pace=False,
|
||||
)
|
||||
|
||||
# Add the user privacy settings to the database
|
||||
|
||||
@@ -73,6 +73,12 @@ class UsersPrivacySettings(Base):
|
||||
default=False,
|
||||
comment="Hide activity speed",
|
||||
)
|
||||
hide_activity_pace = Column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=False,
|
||||
comment="Hide activity pace",
|
||||
)
|
||||
|
||||
|
||||
# Define a relationship to the User model
|
||||
|
||||
@@ -13,6 +13,7 @@ class UsersPrivacySettings(BaseModel):
|
||||
hide_activity_cadence: bool | None = None
|
||||
hide_activity_elevation: bool | None = None
|
||||
hide_activity_speed: bool | None = None
|
||||
hide_activity_pace: bool | None = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
@@ -1,72 +1,191 @@
|
||||
<template>
|
||||
<!-- Modal edit activity -->
|
||||
<div class="modal fade" id="editActivityModal" tabindex="-1" aria-labelledby="editActivityModalComponent" aria-hidden="true">
|
||||
<div class="modal fade" id="editActivityModal" tabindex="-1" aria-labelledby="editActivityModalComponent"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="editActivityModal">{{ $t("editActivityModalComponent.modalEditActivityTitle") }}</h1>
|
||||
<h1 class="modal-title fs-5" id="editActivityModal">{{
|
||||
$t("editActivityModalComponent.modalEditActivityTitle") }}</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form @submit.prevent="submitEditActivityForm">
|
||||
<form @submit.prevent="submitEditActivityForm">
|
||||
<div class="modal-body">
|
||||
<!-- name fields -->
|
||||
<label for="activityNameEdit"><b>* {{ $t("editActivityModalComponent.modalEditActivityNameLabel") }}</b></label>
|
||||
<input class="form-control" type="text" name="activityNameEdit" :placeholder='$t("editActivityModalComponent.modalEditActivityNamePlaceholder")' maxlength="45" v-model="editActivityName" required>
|
||||
<label for="activityNameEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityNameLabel") }}</b></label>
|
||||
<input class="form-control" type="text" name="activityNameEdit"
|
||||
:placeholder='$t("editActivityModalComponent.modalEditActivityNamePlaceholder")'
|
||||
maxlength="45" v-model="editActivityName" required>
|
||||
<!-- description fields -->
|
||||
<label for="activityDescriptionEdit"><b>{{ $t("editActivityModalComponent.modalEditActivityDescriptionLabel") }}</b></label>
|
||||
<input class="form-control" type="text" name="activityDescriptionEdit" :placeholder='$t("editActivityModalComponent.modalEditActivityDescriptionPlaceholder")' maxlength="2500" v-model="editActivityDescription">
|
||||
<label for="activityDescriptionEdit"><b>{{
|
||||
$t("editActivityModalComponent.modalEditActivityDescriptionLabel") }}</b></label>
|
||||
<input class="form-control" type="text" name="activityDescriptionEdit"
|
||||
:placeholder='$t("editActivityModalComponent.modalEditActivityDescriptionPlaceholder")'
|
||||
maxlength="2500" v-model="editActivityDescription">
|
||||
<!-- type fields -->
|
||||
<label for="activityTypeEdit"><b>* {{ $t("editActivityModalComponent.modalEditActivityTypeLabel") }}</b></label>
|
||||
<label for="activityTypeEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityTypeLabel") }}</b></label>
|
||||
<select class="form-select" name="activityTypeEdit" v-model="editActivityType" required>
|
||||
<option value="1">{{ $t("editActivityModalComponent.modalEditActivityTypeOption1") }}</option>
|
||||
<option value="2">{{ $t("editActivityModalComponent.modalEditActivityTypeOption2") }}</option>
|
||||
<option value="3">{{ $t("editActivityModalComponent.modalEditActivityTypeOption3") }}</option>
|
||||
<option value="4">{{ $t("editActivityModalComponent.modalEditActivityTypeOption4") }}</option>
|
||||
<option value="1">{{ $t("editActivityModalComponent.modalEditActivityTypeOption1") }}
|
||||
</option>
|
||||
<option value="2">{{ $t("editActivityModalComponent.modalEditActivityTypeOption2") }}
|
||||
</option>
|
||||
<option value="3">{{ $t("editActivityModalComponent.modalEditActivityTypeOption3") }}
|
||||
</option>
|
||||
<option value="4">{{ $t("editActivityModalComponent.modalEditActivityTypeOption4") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="5">{{ $t("editActivityModalComponent.modalEditActivityTypeOption5") }}</option>
|
||||
<option value="6">{{ $t("editActivityModalComponent.modalEditActivityTypeOption6") }}</option>
|
||||
<option value="7">{{ $t("editActivityModalComponent.modalEditActivityTypeOption7") }}</option>
|
||||
<option value="27">{{ $t("editActivityModalComponent.modalEditActivityTypeOption27") }}</option>
|
||||
<option value="5">{{ $t("editActivityModalComponent.modalEditActivityTypeOption5") }}
|
||||
</option>
|
||||
<option value="6">{{ $t("editActivityModalComponent.modalEditActivityTypeOption6") }}
|
||||
</option>
|
||||
<option value="7">{{ $t("editActivityModalComponent.modalEditActivityTypeOption7") }}
|
||||
</option>
|
||||
<option value="27">{{ $t("editActivityModalComponent.modalEditActivityTypeOption27") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="8">{{ $t("editActivityModalComponent.modalEditActivityTypeOption8") }}</option>
|
||||
<option value="9">{{ $t("editActivityModalComponent.modalEditActivityTypeOption9") }}</option>
|
||||
<option value="8">{{ $t("editActivityModalComponent.modalEditActivityTypeOption8") }}
|
||||
</option>
|
||||
<option value="9">{{ $t("editActivityModalComponent.modalEditActivityTypeOption9") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="18">{{ $t("editActivityModalComponent.modalEditActivityTypeOption18") }}</option>
|
||||
<option value="18">{{ $t("editActivityModalComponent.modalEditActivityTypeOption18") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="10">{{ $t("editActivityModalComponent.modalEditActivityTypeOption10") }}</option>
|
||||
<option value="19">{{ $t("editActivityModalComponent.modalEditActivityTypeOption19") }}</option>
|
||||
<option value="20">{{ $t("editActivityModalComponent.modalEditActivityTypeOption20") }}</option>
|
||||
<option value="10">{{ $t("editActivityModalComponent.modalEditActivityTypeOption10") }}
|
||||
</option>
|
||||
<option value="19">{{ $t("editActivityModalComponent.modalEditActivityTypeOption19") }}
|
||||
</option>
|
||||
<option value="20">{{ $t("editActivityModalComponent.modalEditActivityTypeOption20") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="11">{{ $t("editActivityModalComponent.modalEditActivityTypeOption11") }}</option>
|
||||
<option value="12">{{ $t("editActivityModalComponent.modalEditActivityTypeOption12") }}</option>
|
||||
<option value="11">{{ $t("editActivityModalComponent.modalEditActivityTypeOption11") }}
|
||||
</option>
|
||||
<option value="12">{{ $t("editActivityModalComponent.modalEditActivityTypeOption12") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="13">{{ $t("editActivityModalComponent.modalEditActivityTypeOption13") }}</option>
|
||||
<option value="13">{{ $t("editActivityModalComponent.modalEditActivityTypeOption13") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="14">{{ $t("editActivityModalComponent.modalEditActivityTypeOption14") }}</option>
|
||||
<option value="14">{{ $t("editActivityModalComponent.modalEditActivityTypeOption14") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="15">{{ $t("editActivityModalComponent.modalEditActivityTypeOption15") }}</option>
|
||||
<option value="16">{{ $t("editActivityModalComponent.modalEditActivityTypeOption16") }}</option>
|
||||
<option value="17">{{ $t("editActivityModalComponent.modalEditActivityTypeOption17") }}</option>
|
||||
<option value="15">{{ $t("editActivityModalComponent.modalEditActivityTypeOption15") }}
|
||||
</option>
|
||||
<option value="16">{{ $t("editActivityModalComponent.modalEditActivityTypeOption16") }}
|
||||
</option>
|
||||
<option value="17">{{ $t("editActivityModalComponent.modalEditActivityTypeOption17") }}
|
||||
</option>
|
||||
<hr>
|
||||
<option value="21">{{ $t("editActivityModalComponent.modalEditActivityTypeOption21") }}</option>
|
||||
<option value="22">{{ $t("editActivityModalComponent.modalEditActivityTypeOption22") }}</option>
|
||||
<option value="23">{{ $t("editActivityModalComponent.modalEditActivityTypeOption23") }}</option>
|
||||
<option value="24">{{ $t("editActivityModalComponent.modalEditActivityTypeOption24") }}</option>
|
||||
<option value="25">{{ $t("editActivityModalComponent.modalEditActivityTypeOption25") }}</option>
|
||||
<option value="26">{{ $t("editActivityModalComponent.modalEditActivityTypeOption26") }}</option>
|
||||
<option value="21">{{ $t("editActivityModalComponent.modalEditActivityTypeOption21") }}
|
||||
</option>
|
||||
<option value="22">{{ $t("editActivityModalComponent.modalEditActivityTypeOption22") }}
|
||||
</option>
|
||||
<option value="23">{{ $t("editActivityModalComponent.modalEditActivityTypeOption23") }}
|
||||
</option>
|
||||
<option value="24">{{ $t("editActivityModalComponent.modalEditActivityTypeOption24") }}
|
||||
</option>
|
||||
<option value="25">{{ $t("editActivityModalComponent.modalEditActivityTypeOption25") }}
|
||||
</option>
|
||||
<option value="26">{{ $t("editActivityModalComponent.modalEditActivityTypeOption26") }}
|
||||
</option>
|
||||
</select>
|
||||
<!-- visibility fields -->
|
||||
<label for="activityVisibilityEdit"><b>* {{ $t("editActivityModalComponent.modalEditActivityVisibilityLabel") }}</b></label>
|
||||
<select class="form-select" name="activityVisibilityEdit" v-model="editActivityVisibility" required>
|
||||
<option value="0">{{ $t("editActivityModalComponent.modalEditActivityVisibilityOption0") }}</option>
|
||||
<option value="1">{{ $t("editActivityModalComponent.modalEditActivityVisibilityOption1") }}</option>
|
||||
<option value="2">{{ $t("editActivityModalComponent.modalEditActivityVisibilityOption2") }}</option>
|
||||
<label for="activityVisibilityEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityVisibilityLabel") }}</b></label>
|
||||
<select class="form-select" name="activityVisibilityEdit" v-model="editActivityVisibility"
|
||||
required>
|
||||
<option value="0">{{ $t("editActivityModalComponent.modalEditActivityVisibilityOption0") }}
|
||||
</option>
|
||||
<option value="1">{{ $t("editActivityModalComponent.modalEditActivityVisibilityOption1") }}
|
||||
</option>
|
||||
<option value="2">{{ $t("editActivityModalComponent.modalEditActivityVisibilityOption2") }}
|
||||
</option>
|
||||
</select>
|
||||
<!-- hide start time fields -->
|
||||
<label for="activityHideStartTimeEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHideStartTimeLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHideStartTimeEdit" v-model="editActivityHideStartTime"
|
||||
required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
<!-- hide location fields -->
|
||||
<label for="activityHideLocationEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHideLocationLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHideLocationEdit" v-model="editActivityHideLocation"
|
||||
required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
<!-- hide map fields -->
|
||||
<label for="activityHideMapEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHideMapLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHideMapEdit" v-model="editActivityHideMap" required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
<!-- hide HR fields -->
|
||||
<label for="activityHideHrEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHideHrLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHideHrEdit" v-model="editActivityHideHr" required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
<!-- hide power fields -->
|
||||
<label for="activityHidePowerEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHidePowerLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHidePowerEdit" v-model="editActivityHidePower"
|
||||
required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
<!-- hide cadence fields -->
|
||||
<label for="activityHideCadenceEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHideCadenceLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHideCadenceEdit" v-model="editActivityHideCadence"
|
||||
required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
<!-- hide elevation fields -->
|
||||
<div v-if="!activityTypeIsSwimming(activity)">
|
||||
<label for="activityHideElevationEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHideElevationLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHideElevationEdit" v-model="editActivityHideElevation"
|
||||
required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- hide speed fields -->
|
||||
<div v-if="activityTypeIsCycling(activity)">
|
||||
<label for="activityHideSpeedEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHideSpeedLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHideSpeedEdit" v-model="editActivityHideSpeed"
|
||||
required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- hide pace fields -->
|
||||
<div v-if="activityTypeIsRunning(activity) || activityTypeIsWalking(activity) || activityTypeIsSwimming(activity)">
|
||||
<label for="activityHidePaceEdit"><b>* {{
|
||||
$t("editActivityModalComponent.modalEditActivityHidePaceLabel") }}</b></label>
|
||||
<select class="form-select" name="activityHidePaceEdit" v-model="editActivityHidePace"
|
||||
required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<p>* {{ $t("generalItems.requiredField") }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("generalItems.buttonClose") }}</button>
|
||||
<button type="submit" class="btn btn-success" data-bs-dismiss="modal">{{ $t("editActivityModalComponent.modalEditActivityTitle") }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{
|
||||
$t("generalItems.buttonClose") }}</button>
|
||||
<button type="submit" class="btn btn-success" data-bs-dismiss="modal">{{
|
||||
$t("editActivityModalComponent.modalEditActivityTitle") }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -81,56 +200,89 @@ import { useI18n } from "vue-i18n";
|
||||
import { push } from "notivue";
|
||||
// Importing the services
|
||||
import { activities } from "@/services/activitiesService";
|
||||
// Importing the utils
|
||||
import { activityTypeIsRunning, activityTypeIsCycling, activityTypeIsWalking, activityTypeIsSwimming } from "@/utils/activityUtils";
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
props: {
|
||||
activity: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["activityEditedFields"],
|
||||
setup(props, { emit }) {
|
||||
const { t } = useI18n();
|
||||
const editActivityDescription = ref(props.activity.description);
|
||||
const editActivityName = ref(props.activity.name);
|
||||
const editActivityType = ref(props.activity.activity_type);
|
||||
const editActivityVisibility = ref(props.activity.visibility);
|
||||
components: {},
|
||||
props: {
|
||||
activity: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["activityEditedFields"],
|
||||
setup(props, { emit }) {
|
||||
const { t } = useI18n();
|
||||
const editActivityDescription = ref(props.activity.description);
|
||||
const editActivityName = ref(props.activity.name);
|
||||
const editActivityType = ref(props.activity.activity_type);
|
||||
const editActivityVisibility = ref(props.activity.visibility);
|
||||
const editActivityHideStartTime = ref(props.activity.hide_start_time);
|
||||
const editActivityHideLocation = ref(props.activity.hide_location);
|
||||
const editActivityHideMap = ref(props.activity.hide_map);
|
||||
const editActivityHideHr = ref(props.activity.hide_hr);
|
||||
const editActivityHidePower = ref(props.activity.hide_power);
|
||||
const editActivityHideCadence = ref(props.activity.hide_cadence);
|
||||
const editActivityHideElevation = ref(props.activity.hide_elevation);
|
||||
const editActivityHideSpeed = ref(props.activity.hide_speed);
|
||||
const editActivityHidePace = ref(props.activity.hide_pace);
|
||||
|
||||
async function submitEditActivityForm() {
|
||||
try {
|
||||
const data = {
|
||||
id: props.activity.id,
|
||||
description: editActivityDescription.value,
|
||||
name: editActivityName.value,
|
||||
activity_type: editActivityType.value,
|
||||
visibility: editActivityVisibility.value,
|
||||
};
|
||||
async function submitEditActivityForm() {
|
||||
try {
|
||||
const data = {
|
||||
id: props.activity.id,
|
||||
description: editActivityDescription.value,
|
||||
name: editActivityName.value,
|
||||
activity_type: editActivityType.value,
|
||||
visibility: editActivityVisibility.value,
|
||||
hide_start_time: editActivityHideStartTime.value,
|
||||
hide_location: editActivityHideLocation.value,
|
||||
hide_map: editActivityHideMap.value,
|
||||
hide_hr: editActivityHideHr.value,
|
||||
hide_power: editActivityHidePower.value,
|
||||
hide_cadence: editActivityHideCadence.value,
|
||||
hide_elevation: editActivityHideElevation.value,
|
||||
hide_speed: editActivityHideSpeed.value,
|
||||
hide_pace: editActivityHidePace.value,
|
||||
};
|
||||
|
||||
// Call the service to edit the activity
|
||||
await activities.editActivity(data);
|
||||
// Call the service to edit the activity
|
||||
await activities.editActivity(data);
|
||||
|
||||
// show success toast
|
||||
push.success(t("editActivityModalComponent.successActivityEdit"));
|
||||
// show success toast
|
||||
push.success(t("editActivityModalComponent.successActivityEdit"));
|
||||
|
||||
// Emit the activityEditedFields event to the parent component
|
||||
emit("activityEditedFields", data);
|
||||
} catch (error) {
|
||||
// If there is an error, set the error message and show the error alert.
|
||||
push.error(
|
||||
`${t("editActivityModalComponent.errorActivityEdit")} - ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Emit the activityEditedFields event to the parent component
|
||||
emit("activityEditedFields", data);
|
||||
} catch (error) {
|
||||
// If there is an error, set the error message and show the error alert.
|
||||
push.error(
|
||||
`${t("editActivityModalComponent.errorActivityEdit")} - ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
editActivityDescription,
|
||||
editActivityName,
|
||||
editActivityType,
|
||||
editActivityVisibility,
|
||||
submitEditActivityForm,
|
||||
};
|
||||
},
|
||||
return {
|
||||
editActivityDescription,
|
||||
editActivityName,
|
||||
editActivityType,
|
||||
editActivityVisibility,
|
||||
editActivityHideStartTime,
|
||||
editActivityHideLocation,
|
||||
editActivityHideMap,
|
||||
editActivityHideHr,
|
||||
editActivityHidePower,
|
||||
editActivityHideCadence,
|
||||
editActivityHideElevation,
|
||||
editActivityHideSpeed,
|
||||
editActivityHidePace,
|
||||
submitEditActivityForm,
|
||||
activityTypeIsCycling,
|
||||
activityTypeIsRunning,
|
||||
activityTypeIsWalking,
|
||||
activityTypeIsSwimming,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="alert alert-dismissible fade show" :class="`alert-${type}`" role="alert">
|
||||
<font-awesome-icon :icon="['fas', `${symbol}`]" class="me-1"/>
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" v-if="dismissible"></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'info'
|
||||
},
|
||||
symbol: {
|
||||
type: String,
|
||||
default: 'circle-info'
|
||||
},
|
||||
dismissible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -287,6 +287,13 @@
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
</form>
|
||||
<form>
|
||||
<label for="activityPace">{{ $t("settingsUserProfileZone.defaultActivityPace") }}</label>
|
||||
<select class="form-select" name="activityPace" v-model="activityPace" required>
|
||||
<option :value="true">{{ $t("generalItems.yes") }}</option>
|
||||
<option :value="false">{{ $t("generalItems.no") }}</option>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Edit profile section -->
|
||||
@@ -361,6 +368,7 @@ const activityPower = ref(authStore.user.hide_activity_power);
|
||||
const activityCadence = ref(authStore.user.hide_activity_cadence);
|
||||
const activityElevation = ref(authStore.user.hide_activity_elevation);
|
||||
const activitySpeed = ref(authStore.user.hide_activity_speed);
|
||||
const activityPace = ref(authStore.user.hide_activity_pace);
|
||||
|
||||
async function submitDeleteUserPhoto() {
|
||||
try {
|
||||
@@ -438,6 +446,7 @@ async function updateUserPrivacySettings() {
|
||||
hide_activity_cadence: activityCadence.value,
|
||||
hide_activity_elevation: activityElevation.value,
|
||||
hide_activity_speed: activitySpeed.value,
|
||||
hide_activity_pace: activityPace.value,
|
||||
};
|
||||
try {
|
||||
// Update the user privacy settings in the DB
|
||||
@@ -453,6 +462,7 @@ async function updateUserPrivacySettings() {
|
||||
authStore.user.hide_activity_cadence = activityCadence.value;
|
||||
authStore.user.hide_activity_elevation = activityElevation.value;
|
||||
authStore.user.hide_activity_speed = activitySpeed.value;
|
||||
authStore.user.hide_activity_pace = activityPace.value;
|
||||
|
||||
push.success(t("settingsUserProfileZone.successUpdateUserPrivacySettings"));
|
||||
} catch (error) {
|
||||
@@ -536,6 +546,7 @@ watch(
|
||||
activityCadence,
|
||||
activityElevation,
|
||||
activitySpeed,
|
||||
activityPace,
|
||||
],
|
||||
async () => {
|
||||
if (!isMounted.value || isLoading.value) return;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"labelGraphPower": "Power",
|
||||
"labelGraphCadence": "Cadence",
|
||||
"labelGraphElevation": "Elevation",
|
||||
"labelGraphVelocity": "Velocity",
|
||||
"labelGraphVelocity": "Speed",
|
||||
"labelGraphPace": "Pace",
|
||||
"labelDownsampling": "Data downsampled to ~200 points",
|
||||
"errorMessageProcessingActivityStreams": "Error processing activity streams",
|
||||
|
||||
@@ -7,5 +7,7 @@
|
||||
"successMessageGearAdded": "Gear added to activity",
|
||||
"successMessageGearDeleted": "Gear deleted from activity",
|
||||
"errorMessageDeleteGear": "Error deleting gear from activity",
|
||||
"errorMessageActivityNotFound": "Activity not found"
|
||||
"errorMessageActivityNotFound": "Activity not found",
|
||||
"alertPrivacyMessage": "You have hidden information in this activity. You can see it, but others cannot."
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
"labelGraphPower": "Power",
|
||||
"labelGraphCadence": "Cadence",
|
||||
"labelGraphElevation": "Elevation",
|
||||
"labelGraphVelocity": "Velocity",
|
||||
"labelGraphVelocity": "Speed",
|
||||
"labelGraphPace": "Pace",
|
||||
"labelDownsampling": "Data downsampled to ~200 points",
|
||||
"errorMessageProcessingActivityStreams": "Error processing activity streams",
|
||||
|
||||
@@ -36,6 +36,15 @@
|
||||
"modalEditActivityVisibilityOption0": "Public",
|
||||
"modalEditActivityVisibilityOption1": "Followers",
|
||||
"modalEditActivityVisibilityOption2": "Private",
|
||||
"modalEditActivityHideStartTimeLabel": "Hide start time",
|
||||
"modalEditActivityHideLocationLabel": "Hide location",
|
||||
"modalEditActivityHideMapLabel": "Hide map",
|
||||
"modalEditActivityHideHrLabel": "Hide heart rate",
|
||||
"modalEditActivityHidePowerLabel": "Hide power",
|
||||
"modalEditActivityHideCadenceLabel": "Hide cadence",
|
||||
"modalEditActivityHideElevationLabel": "Hide elevation",
|
||||
"modalEditActivityHideSpeedLabel": "Hide speed",
|
||||
"modalEditActivityHidePaceLabel": "Hide pace",
|
||||
"successActivityEdit": "Activity edited successfully",
|
||||
"errorActivityEdit": "Error editing activity"
|
||||
}
|
||||
@@ -41,8 +41,8 @@
|
||||
"unitsSpm": "spm",
|
||||
"labelElevationInMeters": "Elevation in meters",
|
||||
"labelElevationInFeet": "Elevation in feet",
|
||||
"labelVelocityInKmH": "Velocity in km/h",
|
||||
"labelVelocityInMph": "Velocity in mph",
|
||||
"labelVelocityInKmH": "Speed in km/h",
|
||||
"labelVelocityInMph": "Speed in mph",
|
||||
"labelPaceInMinKm": "Pace in min/km",
|
||||
"labelPaceInMin100m": "Pace in min/100m",
|
||||
"labelPaceInMinMile": "Pace in min/mile",
|
||||
|
||||
@@ -31,6 +31,7 @@ export const useAuthStore = defineStore('auth', {
|
||||
hide_activity_cadence: false,
|
||||
hide_activity_elevation: false,
|
||||
hide_activity_speed: false,
|
||||
hide_activity_pace: false,
|
||||
},
|
||||
isAuthenticated: false,
|
||||
user_websocket: null,
|
||||
@@ -93,6 +94,7 @@ export const useAuthStore = defineStore('auth', {
|
||||
hide_activity_cadence: false,
|
||||
hide_activity_elevation: false,
|
||||
hide_activity_speed: false,
|
||||
hide_activity_pace: false,
|
||||
};
|
||||
if (this.user_websocket && this.user_websocket.readyState === WebSocket.OPEN) {
|
||||
this.user_websocket.close();
|
||||
|
||||
@@ -137,6 +137,36 @@ export function activityTypeIsSwimming(activity) {
|
||||
return activity.activity_type === 8 || activity.activity_type === 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the activity type is a running activity.
|
||||
*
|
||||
* @param {object} activity - The activity object.
|
||||
* @returns {boolean} True if the type of the activity is running, false otherwise.
|
||||
*/
|
||||
export function activityTypeIsRunning(activity) {
|
||||
return activity.activity_type === 1 || activity.activity_type === 2 || activity.activity_type === 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the activity type is a running activity.
|
||||
*
|
||||
* @param {object} activity - The activity object.
|
||||
* @returns {boolean} True if the type of the activity is cycling, false otherwise.
|
||||
*/
|
||||
export function activityTypeIsCycling(activity) {
|
||||
return activity.activity_type === 4 || activity.activity_type === 5 || activity.activity_type === 6 || activity.activity_type === 7 || activity.activity_type === 27;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the activity type is a running activity.
|
||||
*
|
||||
* @param {object} activity - The activity object.
|
||||
* @returns {boolean} True if the type of the activity is walking, false otherwise.
|
||||
*/
|
||||
export function activityTypeIsWalking(activity) {
|
||||
return activity.activity_type === 11 || activity.activity_type === 12;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the pace of an activity based on its type and the specified unit system.
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<div v-else>
|
||||
<ActivitySummaryComponent v-if="activity" :activity="activity" :source="'activity'" @activityEditedFields="updateActivityFieldsOnEdit" :units="units" />
|
||||
<AlertComponent v-if="activity && activity.user_id === authStore.user.id" :message="alertPrivacyMessage" :dismissible="true" class="mt-2"/>
|
||||
</div>
|
||||
|
||||
<!-- map zone -->
|
||||
@@ -106,6 +107,7 @@ import LoadingComponent from "@/components/GeneralComponents/LoadingComponent.vu
|
||||
import BackButtonComponent from "@/components/GeneralComponents/BackButtonComponent.vue";
|
||||
import ModalComponent from "@/components/Modals/ModalComponent.vue";
|
||||
import AddGearToActivityModalComponent from "@/components/Activities/Modals/AddGearToActivityModalComponent.vue";
|
||||
import AlertComponent from "@/components/GeneralComponents/AlertComponent.vue";
|
||||
// Importing the services
|
||||
import { gears } from "@/services/gearsService";
|
||||
import { activities } from "@/services/activitiesService";
|
||||
@@ -125,6 +127,7 @@ export default {
|
||||
BackButtonComponent,
|
||||
ModalComponent,
|
||||
AddGearToActivityModalComponent,
|
||||
AlertComponent,
|
||||
},
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
@@ -143,6 +146,7 @@ export default {
|
||||
const units = ref(1);
|
||||
const activityActivityExerciseTitles = ref([]);
|
||||
const activityActivitySets = ref([]);
|
||||
const alertPrivacyMessage = ref(null);
|
||||
|
||||
async function submitDeleteGearFromActivity() {
|
||||
try {
|
||||
@@ -176,6 +180,15 @@ export default {
|
||||
activity.value.description = data.description;
|
||||
activity.value.activity_type = data.activity_type;
|
||||
activity.value.visibility = data.visibility;
|
||||
activity,value.hide_start_time = data.hide_start_time;
|
||||
activity,value.location = data.location;
|
||||
activity,value.hide_map = data.hide_map;
|
||||
activity,value.hide_hr = data.hide_hr;
|
||||
activity.value.hide_power = data.hide_power;
|
||||
activity.value.hide_cadence = data.hide_cadence;
|
||||
activity.value.hide_elevation = data.hide_elevation;
|
||||
activity.value.hide_speed = data.hide_speed;
|
||||
activity.value.hide_pace = data.hide_pace;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -327,6 +340,10 @@ export default {
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
console.log("Activity loaded:", activity.value);
|
||||
if (authStore.user.id === activity.value.user_id) {
|
||||
alertPrivacyMessage.value = t("activityView.alertPrivacyMessage");
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -346,6 +363,7 @@ export default {
|
||||
activityActivityWorkoutSteps,
|
||||
activityActivityExerciseTitles,
|
||||
activityActivitySets,
|
||||
alertPrivacyMessage,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user