From 499a25ddcbda2051a2084897dcafb367aa95ba08 Mon Sep 17 00:00:00 2001
From: Tom
Date: Tue, 3 Sep 2024 23:17:50 -0700
Subject: [PATCH 1/7] Add collective moderation to mobile
---
ios/Podfile.lock | 10 +
locales/en.json | 34 ++-
locales/es.json | 34 ++-
package.json | 1 +
.../FlagGroupContent/FlagGroupContent.js | 257 ++++++++++++++++++
src/components/FlagGroupContent/index.js | 3 +
.../ImageAttachments/ImageAttachments.js | 1 +
.../ModerationList/ModerationList.js | 120 ++++++++
src/components/ModerationList/index.js | 3 +
.../ModerationListItem/ModerationListItem.js | 210 ++++++++++++++
src/components/ModerationListItem/index.js | 3 +
src/components/MultiSelect/MultiSelect.js | 103 +++++++
src/components/MultiSelect/index.js | 3 +
src/components/PostCard/PostBody/PostBody.js | 10 +-
.../PostBodyProposal/PostBodyProposal.js | 3 +-
src/components/PostCard/PostCard.js | 24 +-
src/components/PostCard/PostCard.styles.js | 39 ++-
src/components/PostCard/PostCardForDetails.js | 20 +-
.../PostCard/PostHeader/PostHeader.js | 30 +-
.../PostCard/PostHeader/PostHeader.styles.js | 7 +-
src/components/PostListRow/PostListRow.js | 247 +++++++++++++++++
src/components/PostListRow/index.js | 3 +
src/components/StreamList/PostRow/PostRow.js | 1 +
src/components/StreamList/StreamList.js | 14 +-
src/graphql/fragments/postFieldsFragment.js | 2 +
src/graphql/queries/MeQuery.js | 8 +
src/navigation/AuthRootNavigator.js | 2 +
src/navigation/HomeNavigator.js | 4 +-
src/navigation/linking/index.js | 1 +
.../GroupNavigation/GroupNavigation.js | 6 +
src/screens/PostDetails/PostDetails.js | 1 +
src/screens/Stream/Stream.js | 69 +++--
src/store/actions/fetchPlatformAgreements.js | 20 ++
src/store/actions/moderationActions.js | 132 +++++++++
src/store/constants.js | 8 +
src/store/models/ModerationAction.js | 19 ++
src/store/models/PlatformAgreement.js | 17 ++
src/store/models/index.js | 4 +
src/store/presenters/presentPost.js | 1 +
src/store/reducers/ormReducer/index.js | 33 ++-
src/store/selectors/getModerationActions.js | 24 ++
src/store/selectors/getPlatformAgreements.js | 11 +
src/style/colors.js | 3 +
yarn.lock | 15 +
44 files changed, 1517 insertions(+), 43 deletions(-)
create mode 100644 src/components/FlagGroupContent/FlagGroupContent.js
create mode 100644 src/components/FlagGroupContent/index.js
create mode 100644 src/components/ModerationList/ModerationList.js
create mode 100644 src/components/ModerationList/index.js
create mode 100644 src/components/ModerationListItem/ModerationListItem.js
create mode 100644 src/components/ModerationListItem/index.js
create mode 100644 src/components/MultiSelect/MultiSelect.js
create mode 100644 src/components/MultiSelect/index.js
create mode 100644 src/components/PostListRow/PostListRow.js
create mode 100644 src/components/PostListRow/index.js
create mode 100644 src/store/actions/fetchPlatformAgreements.js
create mode 100644 src/store/actions/moderationActions.js
create mode 100644 src/store/models/ModerationAction.js
create mode 100644 src/store/models/PlatformAgreement.js
create mode 100644 src/store/selectors/getModerationActions.js
create mode 100644 src/store/selectors/getPlatformAgreements.js
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 2a1cc6b9b..77ae7f6df 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -5,6 +5,7 @@ PODS:
- AppAuth/Core (1.7.5)
- AppAuth/ExternalUserAgent (1.7.5):
- AppAuth/Core
+ - BEMCheckBox (1.4.1)
- boost (1.83.0)
- BVLinearGradient (2.8.3):
- React-Core
@@ -1317,6 +1318,9 @@ PODS:
- React-Core
- RNCAsyncStorage (1.24.0):
- React-Core
+ - RNCCheckbox (0.5.17):
+ - BEMCheckBox (~> 1.4)
+ - React-Core
- RNCClipboard (1.14.1):
- React-Core
- RNCPicker (2.7.7):
@@ -1490,6 +1494,7 @@ DEPENDENCIES:
- "RNAppleAuthentication (from `../node_modules/@invertase/react-native-apple-authentication`)"
- RNBootSplash (from `../node_modules/react-native-bootsplash`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
+ - "RNCCheckbox (from `../node_modules/@react-native-community/checkbox`)"
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
- "RNCPicker (from `../node_modules/@react-native-picker/picker`)"
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
@@ -1507,6 +1512,7 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- AppAuth
+ - BEMCheckBox
- GoogleSignIn
- GTMAppAuth
- GTMSessionFetcher
@@ -1663,6 +1669,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-bootsplash"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
+ RNCCheckbox:
+ :path: "../node_modules/@react-native-community/checkbox"
RNCClipboard:
:path: "../node_modules/@react-native-clipboard/clipboard"
RNCPicker:
@@ -1692,6 +1700,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
+ BEMCheckBox: 5ba6e37ade3d3657b36caecc35c8b75c6c2b1a4e
boost: d3f49c53809116a5d38da093a8aa78bf551aed09
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
@@ -1770,6 +1779,7 @@ SPEC CHECKSUMS:
RNAppleAuthentication: e99eaf3c4c01ad8ecb6125dd6f0cfd98871685b5
RNBootSplash: 7abfdb0e67dd2316dde619a54d5ef68b13e39b5f
RNCAsyncStorage: ec53e44dc3e75b44aa2a9f37618a49c3bc080a7a
+ RNCCheckbox: a3ca9978cb0846b981d28da4e9914bd437403d77
RNCClipboard: 0a720adef5ec193aa0e3de24c3977222c7e52a37
RNCPicker: b7873ba797dc586bfaf3307d737cbdc620a9ff3e
RNDeviceInfo: 59344c19152c4b2b32283005f9737c5c64b42fba
diff --git a/locales/en.json b/locales/en.json
index 61757ddc8..a3404b70b 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -1,10 +1,10 @@
{
" (explanation required)": " (explanation required)",
+ "(Moderators will see your name)": "(Moderators will see your name)",
"1 other": "1 other",
"< Back": "< Back",
"A link to reset your password has been sent to you at {{email}}": "A link to reset your password has been sent to you at {{email}}",
"ABOUT ME": "ABOUT ME",
- "announcementExplainer": "This means that all members of this group will receive an instant email and push notification about this Post. \n (This feature is available to stewards only.)",
"Accept all agreements": "Accept all agreements",
"Add Participant": "Add Participant",
"Add a description": "Add a description",
@@ -19,6 +19,8 @@
"An account may already exist for this email address, Login or try resetting your password": "An account may already exist for this email address, Login or try resetting your password",
"Announcement?": "Announcement?",
"Announcements": "Announcements",
+ "Anonymous": "Anonymous",
+ "Anonymous (moderators will see your name)": "Anonymous (moderators will see your name)",
"Are you sure you want to block {{name}}?": "Are you sure you want to block {{name}}?",
"Are you sure you want to delete this comment?": "Are you sure you want to delete this comment?",
"Are you sure you want to delete this post?": "Are you sure you want to delete this post?",
@@ -44,10 +46,12 @@
"Clear Search": "Clear Search",
"Click on a user name or user the search bar": "Click on a user name or user the search bar",
"Comments": "Comments",
+ "Complaint": "Complaint",
"Confirm Delete": "Confirm Delete",
"Confirm Password": "Confirm Password",
"Confirm Removal": "Confirm Removal",
"Contact your coordinator for another one": "Contact your coordinator for another one",
+ "Contents obscured": "Contents obscured",
"Continue": "Continue",
"Continue Editing": "Continue Editing",
"Contribute": "Contribute",
@@ -58,6 +62,7 @@
"Create": "Create",
"Create a post": "Create a post",
"Current Selection": "Current Selection",
+ "Decisions": "Decisions",
"Delete": "Delete",
"Deleted User": "Deleted User",
"Description": "Description",
@@ -82,6 +87,7 @@
"Exit": "Exit",
"Exit this Group & Return Home": "Exit this Group & Return Home",
"Expired or invalid code": "Expired or invalid code",
+ "Explanation for Flagging": "Explanation for Flagging",
"Explore": "Explore",
"FLAG THIS": "FLAG THIS",
"Files": "Files",
@@ -91,9 +97,11 @@
"Forgot your password?": "Forgot your password?",
"Go Back": "Go Back",
"Going": "Going",
+ "Governance": "Governance",
"Group": "Group",
- "Group_plural": "Groups",
+ "Group Agreements broken": "Group Agreements broken",
"Group Purpose": "Group Purpose",
+ "Group_plural": "Groups",
"Groups": "Groups",
"Hi {{firstName}}, press here to post": "Hi {{firstName}}, press here",
"Hi {{firstName}}, want to create an event?": "Hi {{firstName}}, want to create an event?",
@@ -122,12 +130,13 @@
"Lets Do This!": "Let's Do This!",
"Lets do this!": "Let's do this!",
"Lets get started!": "Let's get started!",
+ "Link to group agreements": "Link to group agreements",
+ "Link to platform agreements": "Link to platform agreements",
"Location": "Location",
"Log In": "Log In",
"Log in now": "Log in now",
"Log in to Hylo": "Log in to Hylo",
"Logout": "Logout",
- "makeAnAnnouncement": "MAKE AN ANNOUNCEMENT",
"MY SKILLS": "MY SKILLS",
"Make Public": "Make Public:",
"Manage Notifications": "Manage Notifications",
@@ -141,6 +150,7 @@
"Membership Requested": "Membership Requested",
"Mentions": "Mentions",
"Missing message recipient!": "Missing message recipient!",
+ "Moderation": "Moderation",
"Moderator: Confirm Delete": "Moderator: Confirm Delete",
"Multiple people are typing": "Multiple people are typing...",
"My Home": "My Home",
@@ -158,6 +168,9 @@
"No topics match your search": "No topics match your search",
"Not Going": "Not Going",
"Not a Member": "Not a Member",
+ "Not permitted anywhere on the platform": "Not permitted anywhere on the platform",
+ "Not permitted in Public Spaces": "Not permitted in Public Spaces",
+ "Not permitted in {{groupName}}": "Not permitted in {{groupName}}",
"Nothing new for you!": "Nothing new for you!",
"Notifications": "Notifications",
"Offensive": "Offensive",
@@ -180,12 +193,14 @@
"People": "People",
"Pick a Topic": "Pick a Topic",
"Pin": "Pin",
+ "Platform Agreements broken": "Platform Agreements broken",
"Please answer all the join questions to continue": "Please answer all the join questions to continue",
"Please select below:": "Please select below:",
"Popular": "Popular",
"Post": "Post",
"Post Date": "Post Date",
"Post In": "Post In",
+ "Post flagged": "- Post flagged -",
"Post in Groups": "Post in Groups",
"Posted In": "Posted In:",
"Posts": "Posts",
@@ -209,6 +224,8 @@
"Remove image": "Remove image",
"Reply": "Reply",
"Replying to": "Replying to",
+ "Reported by": "Reported by",
+ "Reported content": "Reported content",
"Request": "Request",
"Requests": "Requests",
"Resend code": "Resend code",
@@ -231,6 +248,7 @@
"Settings": "Settings",
"Share": "Share",
"Short Description": "Short Description",
+ "Show more": "Show more",
"Sign in with Google": "Sign in with Google",
"Sign up now": "Sign up now",
"Sign up with Google": "Sign up with Google",
@@ -264,8 +282,10 @@
"Upcoming Events": "Upcoming Events",
"Upload a Photo": "Upload a Photo",
"Upvotes": "Upvotes",
+ "View post": "View post",
"View project management tool": "View project management tool",
"View tasks": "View tasks",
+ "Violations of platform agreements": "Violations of platform agreements",
"Voting ended": "Voting ended",
"Voting open": "Voting open",
"Welcome to": "Welcome to",
@@ -279,6 +299,7 @@
"What is your event called?": "What is your event called?",
"What is your proposal?": "What is your proposal?",
"What resource is available?": "What resource is available?",
+ "What was wrong?": "What was wrong?",
"What would you like to call your project?": "What would you like to call your project?",
"Whats the URL of your group?": "What's the URL of your group?",
"Whats the address for your group": "What's the address for your group?",
@@ -306,22 +327,27 @@
"Your data is safe with Hylo By clicking the Sign Up button above you are agreeing to these terms:": "Your data is safe with Hylo. By clicking the \"Sign Up\" button above you are agreeing to these terms:",
"Your purpose statement is a concise summary of why your group": "Your purpose statement is a concise summary of why your group exists and what you hope to accomplish. A clear and specific purpose helps align members of your group to coordinate effectively to achieve your goals.",
"and": "and",
+ "announcementExplainer": "This means that all members of this group will receive an instant email and push notification about this Post. \n (This feature is available to stewards only.)",
"are a member": "are a member",
"are attending": "are attending",
"are members": "are members",
"attending": "attending",
"by": "by",
"characters max": "characters max",
+ "clickthroughExplainer": "This post may not align with group or platform agreements.",
"comment": "comment",
"commented": "commented",
"discussion": "discussion",
"edit": "edit",
"event": "event",
+ "flaggingExplainer": "Reporting a post will blur it for all group members. Members will be able to see the report and its details. Moderators can clear reports or take other actions if necessary.",
+ "flagsNeedACategory": "All reports need to be linked to at least one agreement (group or platform)",
"forgotPasswordDescription": "Enter your email address below and we'll send you an \n email message with a link for resetting your password.",
"is a member": "is a member",
"is attending": "is attending",
"is typing": "is typing",
"loading": "loading",
+ "makeAnAnnouncement": "MAKE AN ANNOUNCEMENT",
"manyCommentersInTheFooter": "{{firstPerson}}, {{secondPerson}} and {{count}} other",
"manyCommentersInTheFooter_plural": "{{firstPerson}}, {{secondPerson}} and {{count}} others",
"offer": "offer",
@@ -336,6 +362,8 @@
"shareDialogTitle": "Share \"{{title}}\" by {{name}}",
"shareMessage": "\"{{title}}\" by {{name}} on hylo.com: {{url}}",
"shareSubject": "\"{{title}}\" by {{name}} on hylo.com",
+ "status-active": "Status: Active",
+ "status-cleared": "Status: Cleared by Moderator",
"we just need to know your name and password and youre account will be created": "we just need to know your name and password and you're account will be created",
"welcome page backup text": "Please take a moment to explore and see what’s alive in our group. \n \n Introduce yourself by clicking Create, and posting a Discussion to share who you are and what you brings you here. \n \n Don’t forget to fill our your profile, so likeminded folks can connect with you",
"{{childGroupsLength}} Group(s) are a part of {{currentGroupName}}": "{{childGroupsLength}} Group(s) are a part of {{currentGroupName}}",
diff --git a/locales/es.json b/locales/es.json
index c65a59598..5923e0bfb 100644
--- a/locales/es.json
+++ b/locales/es.json
@@ -1,10 +1,10 @@
{
" (explanation required)": "(explicación requerida)",
+ "(Moderators will see your name)": "(Los moderadores verán tu nombre)",
"1 other": "1 más",
"< Back": "< Atrás",
"A link to reset your password has been sent to you at {{email}}": "Se le ha enviado un enlace para restablecer su contraseña a {{email}}",
"ABOUT ME": "ACERCA DE MÍ",
- "announcementExplainer": "Esto significa que todos los miembros de este grupo recibirán un correo electrónico instantáneo y una notificación automática sobre esta publicación.\n \n(Esta función está disponible solo para moderadores).",
"Accept all agreements": "Aceptar todos los acuerdos",
"Add Participant": "Añada participante",
"Add a description": "Agregar una descripción",
@@ -19,6 +19,8 @@
"An account may already exist for this email address, Login or try resetting your password": "Es posible que ya exista una cuenta para esta dirección de correo electrónico. Inicie sesión o intente restablecer su contraseña.",
"Announcement?": "¿Anuncio?",
"Announcements": "Anuncios",
+ "Anonymous": "Anónimo",
+ "Anonymous (moderators will see your name)": "Anónimo (los moderadores verán tu nombre)",
"Are you sure you want to block {{name}}?": "¿Estás seguro de que quieres bloquear a {{name}}?",
"Are you sure you want to delete this comment?": "¿Estás seguro de que quieres eliminar este comentario?",
"Are you sure you want to delete this post?": "¿Estás seguro de que deseas eliminar esta publicación?",
@@ -44,10 +46,12 @@
"Clear Search": "Borrar búsqueda",
"Click on a user name or user the search bar": "Haga clic en un nombre de usuario o usuario en la barra de búsqueda.",
"Comments": "Comentarios",
+ "Complaint": "Queja",
"Confirm Delete": "Confirmar eliminación",
"Confirm Password": "confirmar Contraseña",
"Confirm Removal": "Confirmar eliminación",
"Contact your coordinator for another one": "Contacta a tu moderador para otro",
+ "Contents obscured": "Contenidos oscurecidos",
"Continue": "Continuar",
"Continue Editing": "Continua editando",
"Contribute": "Contribuir",
@@ -58,6 +62,7 @@
"Create": "Crear",
"Create a post": "Crear una publicación",
"Current Selection": "Selección actual",
+ "Decisions": "Decisiones",
"Delete": "Eliminar",
"Deleted User": "Usuario eliminado",
"Description": "Descripción",
@@ -82,6 +87,7 @@
"Exit": "Salida",
"Exit this Group & Return Home": "Salir de este grupo",
"Expired or invalid code": "Código caducado o no válido",
+ "Explanation for Flagging": "Explicación para marcar",
"Explore": "Explorar",
"FLAG THIS": "MARCAR ESTO",
"Files": "Archivos",
@@ -91,9 +97,11 @@
"Forgot your password?": "¿Olvidaste tu contraseña?",
"Go Back": "Regresa",
"Going": "Yendo",
+ "Governance": "Gobernancia",
"Group": "Grupo",
- "Group_plural": "Grupos",
+ "Group Agreements broken": "Acuerdos de grupo rotos",
"Group Purpose": "Propósito del grupo",
+ "Group_plural": "Grupos",
"Groups": "Grupos",
"Hi {{firstName}}, press here to post": "Hola {{firstName}}, presiona aquí",
"Hi {{firstName}}, want to create an event?": "Hola {{firstName}}, ¿quieres crear un evento?",
@@ -122,12 +130,13 @@
"Lets Do This!": "¡Hagámoslo!",
"Lets do this!": "¡Hagámoslo!",
"Lets get started!": "¡Empecemos!",
+ "Link to group agreements": "Enlace a acuerdos de grupo",
+ "Link to platform agreements": "Enlace a acuerdos de plataforma",
"Location": "Ubicación",
"Log In": "Acceso",
"Log in now": "Inicia sesión ahora",
"Log in to Hylo": "Iniciar sesión en Hylo",
"Logout": "Cerrar sesión",
- "makeAnAnnouncement": "HACER UN ANUNCIO",
"MY SKILLS": "MIS HABILIDADES",
"Make Public": "Hacer Público:",
"Manage Notifications": "Administrar notificaciones",
@@ -141,6 +150,7 @@
"Membership Requested": "Membresía solicitada",
"Mentions": "Menciones",
"Missing message recipient!": "¡Falta destinatario del mensaje!",
+ "Moderation": "Moderación",
"Moderator: Confirm Delete": "Moderador: Confirmar eliminación",
"Multiple people are typing": "Varias personas están escribiendo...",
"My Home": "Mi hogar",
@@ -158,6 +168,9 @@
"No topics match your search": "Ningún tema coincide con tu búsqueda",
"Not Going": "No voy",
"Not a Member": "No es un miembro",
+ "Not permitted anywhere on the platform": "No permitido en ningún lugar de la plataforma.",
+ "Not permitted in Public Spaces": "No permitido en espacios públicos.",
+ "Not permitted in {{groupName}}": "No permitido en {{groupName}}",
"Nothing new for you!": "¡Nada nuevo para ti!",
"Notifications": "Notificaciones",
"Offensive": "Ofensivo",
@@ -180,12 +193,14 @@
"People": "Gente",
"Pick a Topic": "Elige un tema",
"Pin": "Alfiler",
+ "Platform Agreements broken": "Acuerdos de plataforma rotos",
"Please answer all the join questions to continue": "Por favor responda todas las preguntas sobre cómo unirse para continuar.",
"Please select below:": "Por favor seleccione a continuación:",
"Popular": "Popular",
"Post": "Publicacion",
"Post Date": "Fecha de publicación",
"Post In": "Publicar",
+ "Post flagged": "- Publicación marcada -",
"Post in Groups": "Publicar en grupos",
"Posted In": "Publicado en:",
"Posts": "Publicaciones",
@@ -209,6 +224,8 @@
"Remove image": "Quita la imagen",
"Reply": "Responder",
"Replying to": "Respondiendo a",
+ "Reported by": "Reportado por",
+ "Reported content": "Contenido reportado",
"Request": "Pedido",
"Requests": "Peticiones",
"Resend code": "Reenviar codigo",
@@ -231,6 +248,7 @@
"Settings": "Ajustes",
"Share": "Compartir",
"Short Description": "Breve descripción",
+ "Show more": "Mostrar más",
"Sign in with Google": "Inicia sesión con Google",
"Sign up now": "Regístrate ahora",
"Sign up with Google": "Regístrate con Google",
@@ -264,8 +282,10 @@
"Upcoming Events": "Próximos Eventos",
"Upload a Photo": "Sube una foto",
"Upvotes": "Votos a favor",
+ "View post": "Ver publicación",
"View project management tool": "Ver herramienta de gestión de proyectos",
"View tasks": "Ver tareas",
+ "Violations of platform agreements": "Violaciones de acuerdos de plataforma.",
"Voting ended": "La votación terminó",
"Voting open": "Votación abierta",
"Welcome to": "Bienvenido a",
@@ -279,6 +299,7 @@
"What is your event called?": "¿Cómo se llama tu evento?",
"What is your proposal?": "Cual es tu propuesta?",
"What resource is available?": "¿Qué recurso está disponible?",
+ "What was wrong?": "¿Qué pasó?",
"What would you like to call your project?": "¿Cómo te gustaría llamar tu proyecto?",
"Whats the URL of your group?": "¿Cuál es la URL de tu grupo?",
"Whats the address for your group": "¿Cuál es la dirección de su grupo?",
@@ -306,22 +327,27 @@
"Your data is safe with Hylo By clicking the Sign Up button above you are agreeing to these terms:": "Tus datos están seguros con Hylo. Al hacer clic en el botón \"Registrarse\" arriba, acepta estos términos:",
"Your purpose statement is a concise summary of why your group": "Su declaración de propósito es un resumen conciso de por qué existe su grupo y qué espera lograr. \nUn propósito claro y específico ayuda a alinear a los miembros de su grupo para coordinarse de manera efectiva y lograr sus objetivos.",
"and": "y",
+ "announcementExplainer": "Esto significa que todos los miembros de este grupo recibirán un correo electrónico instantáneo y una notificación automática sobre esta publicación.\n \n(Esta función está disponible solo para moderadores).",
"are a member": "eres miembro",
"are attending": "estan asistiendo",
"are members": "son miembros",
"attending": "asistiendo",
"by": "por",
"characters max": "caracteres máximo",
+ "clickthroughExplainer": "Es posible que esta publicación no se ajuste a los acuerdos del grupo o la plataforma.",
"comment": "comentario",
"commented": "comentó",
"discussion": "discusión",
"edit": "editar",
"event": "evento",
+ "flaggingExplainer": "Informar una publicación la borrará para todos los miembros del grupo. \nLos miembros podrán ver el informe y sus detalles. \nLos moderadores pueden borrar informes o tomar otras acciones si es necesario.",
+ "flagsNeedACategory": "Todos los informes deben estar vinculados al menos a un acuerdo (grupo o plataforma)",
"forgotPasswordDescription": "Ingrese su dirección de correo electrónico a continuación y \n le enviaremos un mensaje de correo electrónico con un enlace para restablecer su contraseña.",
"is a member": "es miembro",
"is attending": "asiste",
"is typing": "esta escribiendo",
"loading": "cargando",
+ "makeAnAnnouncement": "HACER UN ANUNCIO",
"manyCommentersInTheFooter": "{{firstPerson}}, {{secondPerson}} y {{count}} otro",
"manyCommentersInTheFooter_plural": "{{firstPerson}}, {{secondPerson}} y {{count}} otros",
"offer": "oferta",
@@ -336,6 +362,8 @@
"shareDialogTitle": "Compartir \"{{title}}\" por {{name}}",
"shareMessage": "\"{{title}}\" de {{name}} en hylo.com: {{url}}",
"shareSubject": "\"{{title}}\" de {{name}} en hylo.com",
+ "status-active": "Estado: Activo",
+ "status-cleared": "Estado: Borrado por el moderador",
"we just need to know your name and password and youre account will be created": "sólo necesitamos saber su nombre y contraseña y se creará su cuenta",
"welcome page backup text": "Tómese un momento para explorar y ver qué hay vivo en nuestro grupo.\n \n Preséntate haciendo clic en Crear y publicando una discusión para compartir quién eres y qué te trae aquí.\n \n No olvide completar nuestro perfil para que personas con ideas afines puedan conectarse con usted.",
"{{childGroupsLength}} Group(s) are a part of {{currentGroupName}}": "Los grupos {{childGroupsLength}} son parte de {{currentGroupName}}",
diff --git a/package.json b/package.json
index 1673d2980..c5873ac6d 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"@native-html/iframe-plugin": "^2.6.1",
"@react-native-async-storage/async-storage": "^1.24.0",
"@react-native-clipboard/clipboard": "^1.14.1",
+ "@react-native-community/checkbox": "^0.5.17",
"@react-native-community/hooks": "^2.8.0",
"@react-native-google-signin/google-signin": "^8.0.1",
"@react-native-picker/picker": "^2.7.7",
diff --git a/src/components/FlagGroupContent/FlagGroupContent.js b/src/components/FlagGroupContent/FlagGroupContent.js
new file mode 100644
index 000000000..6d68aa284
--- /dev/null
+++ b/src/components/FlagGroupContent/FlagGroupContent.js
@@ -0,0 +1,257 @@
+import React, { useState } from 'react'
+import { View, Text, TouchableOpacity, ScrollView, Linking, Modal, TextInput } from 'react-native'
+import { regentGray, mangoOrange } from 'style/colors'
+import { useDispatch, useSelector } from 'react-redux'
+import { useTranslation } from 'react-i18next'
+import Icon from 'components/Icon'
+import Button from 'components/Button'
+import CheckBox from '@react-native-community/checkbox'
+import MultiSelect from 'components/MultiSelect/MultiSelect'
+import { createModerationAction } from 'store/actions/moderationActions'
+import getGroup from 'store/selectors/getGroup'
+import { agreementsURL } from 'store/constants'
+import presentGroup from 'store/presenters/presentGroup'
+import getPlatformAgreements from 'store/selectors/getPlatformAgreements'
+import { groupUrl } from 'util/navigation'
+import { isEmpty } from 'lodash'
+
+const FlagGroupContent = ({ onClose, linkData, type = 'content' }) => {
+ const { t } = useTranslation()
+ const dispatch = useDispatch()
+ const { id, slug } = linkData || {}
+
+ const platformAgreements = useSelector(getPlatformAgreements)
+ const currentGroup = useSelector(state => getGroup(state, { slug }))
+ console.log(currentGroup, 'jjahahahahahahggggg', Object.keys(currentGroup))
+ const group = presentGroup(currentGroup)
+
+ const agreements = group?.agreements || []
+ const groupAgreementsUrl = group ? groupUrl(group.slug) + `/group/${group.slug}` : ''
+ const [anonymous, setAnonymous] = useState(false)
+ const [explanation, setExplanation] = useState('')
+ const [subtitle, setSubtitle] = useState(t('What was wrong?'))
+ const [agreementsSelected, setAgreementsSelected] = useState([])
+ const [platformAgreementsSelected, setPlatformAgreementsSelected] = useState([])
+
+ const isValid = () => {
+ if (isEmpty(agreementsSelected) && isEmpty(platformAgreementsSelected)) return false
+ if (explanation.length < 5) return false
+ return true
+ }
+
+ const closeModal = () => {
+ if (onClose) {
+ onClose()
+ }
+ }
+
+ const handleAgreementsSelect = (selected) => {
+ if (agreementsSelected.includes(selected)) {
+ setAgreementsSelected(agreementsSelected.filter(ag => ag !== selected))
+ } else {
+ setAgreementsSelected([...agreementsSelected, selected])
+ }
+ }
+
+ const handlePlatformAgreementsSelect = (selected) => {
+ if (platformAgreementsSelected.includes(selected)) {
+ setPlatformAgreementsSelected(platformAgreementsSelected.filter(ag => ag !== selected))
+ } else {
+ setPlatformAgreementsSelected([...platformAgreementsSelected, selected])
+ }
+ }
+
+ const submit = () => {
+ dispatch(createModerationAction({ text: explanation, postId: id, groupId: group.id, agreements: agreementsSelected, platformAgreements: platformAgreementsSelected, anonymous }))
+ closeModal()
+ return true
+ }
+
+ return (
+
+
+
+
+ {t('Explanation for Flagging')}
+
+
+
+
+
+ {t('flaggingExplainer')}
+ {t('flagsNeedACategory')}
+
+
+
+ {group && agreements.length > 0 && (
+ <>
+ {t('Not permitted in {{groupName}}', { groupName: group?.name })}
+ Linking.openURL(groupAgreementsUrl)}>
+ {t('Link to group agreements')}
+
+
+ >
+ )}
+
+ {t('Violations of platform agreements')}
+ Linking.openURL(agreementsURL)}>
+ {t('Link to platform agreements')}
+
+
+ {t('Not permitted in Public Spaces')}
+ ag.type !== 'anywhere')}
+ selected={platformAgreementsSelected}
+ handleSelect={handlePlatformAgreementsSelect}
+ />
+
+ {t('Not permitted anywhere on the platform')}
+ ag.type === 'anywhere')}
+ selected={platformAgreementsSelected}
+ handleSelect={handlePlatformAgreementsSelect}
+ />
+
+
+
+ setAnonymous(value)}
+ labelStyle={styles.anonLabel}
+ />
+ {t('Anonymous')}
+
+ {t('(Moderators will see your name)')}
+
+
+
+
+
+
+
+ )
+}
+
+const styles = {
+ popup: {
+ flex: 1,
+ backgroundColor: 'rgba(0,0,0,0.5)',
+ justifyContent: 'center',
+ alignItems: 'center'
+ },
+ popupInner: {
+ width: '90%',
+ maxWidth: 500,
+ height: '87%',
+ backgroundColor: 'white',
+ borderRadius: 10,
+ overflow: 'hidden'
+ },
+ scrollContent: {
+ padding: 20
+ },
+ title: {
+ fontSize: 24,
+ color: '#2C405A',
+ fontWeight: 'normal',
+ fontFamily: 'Circular Medium',
+ textAlign: 'center',
+ marginTop: 28,
+ marginBottom: 16
+ },
+ closeButton: {
+ position: 'absolute',
+ top: 15,
+ right: 15,
+ padding: 10
+ },
+ closeIcon: {
+ fontSize: 26
+ },
+ content: {
+ flex: 1
+ },
+ explainer: {
+ color: '#666',
+ fontSize: 14,
+ marginBottom: 5
+ },
+ reasonRequired: {
+ color: 'red'
+ },
+ explanationTextbox: {
+ borderWidth: 1,
+ borderColor: '#CCD1D7',
+ borderRadius: 2,
+ padding: 14,
+ marginTop: 20,
+ fontFamily: 'Circular Book',
+ fontSize: 14
+ },
+ sectionTitle: {
+ fontSize: 20,
+ color: '#2C405A',
+ fontWeight: 'normal',
+ fontFamily: 'Circular Medium',
+ marginTop: 20,
+ marginBottom: 8
+ },
+ subSectionTitle: {
+ fontSize: 18,
+ color: '#2C405A',
+ fontWeight: 'normal',
+ fontFamily: 'Circular Medium',
+ marginTop: 15,
+ marginBottom: 5
+ },
+ link: {
+ color: '#2C405A',
+ fontSize: 14,
+ textDecorationLine: 'underline',
+ marginTop: 10,
+ marginBottom: 10
+ },
+ submission: {
+ color: 'black',
+ marginTop: 20,
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ gap: 6
+ },
+ submissionAnon: {
+ flexDirection: 'row',
+ gap: 8,
+ justifyContent: 'space-between',
+ alignItems: 'center'
+ },
+ anonLabel: {
+ color: '#666',
+ fontSize: 10,
+ marginRight: 10
+ },
+ submitButton: {
+ marginTop: 21,
+ disabledColor: mangoOrange
+ }
+}
+
+export default FlagGroupContent
diff --git a/src/components/FlagGroupContent/index.js b/src/components/FlagGroupContent/index.js
new file mode 100644
index 000000000..e4f911a04
--- /dev/null
+++ b/src/components/FlagGroupContent/index.js
@@ -0,0 +1,3 @@
+import component from './FlagGroupContent'
+
+export default component
diff --git a/src/components/ImageAttachments/ImageAttachments.js b/src/components/ImageAttachments/ImageAttachments.js
index ce08941b1..6778078e3 100644
--- a/src/components/ImageAttachments/ImageAttachments.js
+++ b/src/components/ImageAttachments/ImageAttachments.js
@@ -8,6 +8,7 @@ export default function ImageAttachments ({
children,
creator,
firstImageStyle,
+ isFlagged,
images,
onlyLongPress,
onPress,
diff --git a/src/components/ModerationList/ModerationList.js b/src/components/ModerationList/ModerationList.js
new file mode 100644
index 000000000..c8eaf0ea3
--- /dev/null
+++ b/src/components/ModerationList/ModerationList.js
@@ -0,0 +1,120 @@
+import React, { useCallback, useMemo, useEffect } from 'react'
+import { View, FlatList, StyleSheet } from 'react-native'
+import { useSelector, useDispatch } from 'react-redux'
+import { createSelector } from 'reselect'
+import { useIsFocused, useNavigation } from '@react-navigation/native'
+
+import { fetchModerationActions, clearModerationAction } from 'store/actions/moderationActions'
+import { getHasMoreModerationActions, getModerationActions } from 'store/selectors/getModerationActions'
+import Loading from 'components/Loading'
+import ModerationListItem from 'components/ModerationListItem'
+import ListControl from 'components/ListControl'
+import { DECISIONS_OPTIONS } from 'components/StreamList/StreamList'
+
+export default function ModerationList ({ forGroup, header, scrollRef, streamType }) {
+ const dispatch = useDispatch()
+ const isFocused = useIsFocused()
+ const navigation = useNavigation()
+ const { navigate } = navigation
+
+ const fetchModerationActionParam = useMemo(() => ({
+ slug: forGroup?.slug,
+ groupId: forGroup?.id
+ }), [forGroup?.slug, forGroup?.id])
+
+ const selectModerationActions = useMemo(
+ () => createSelector(
+ state => state,
+ state => getModerationActions(state, fetchModerationActionParam)
+ ),
+ [fetchModerationActionParam]
+ )
+
+ const selectHasMoreModerationActions = useMemo(
+ () => createSelector(
+ state => state,
+ state => getHasMoreModerationActions(state, fetchModerationActionParam)
+ ),
+ [fetchModerationActionParam]
+ )
+
+ const moderationActions = useSelector(selectModerationActions)
+ const hasMoreModerationActions = useSelector(selectHasMoreModerationActions)
+ const pending = useSelector(state => state.pending.FETCH_MODERATION_ACTIONS)
+
+ const refreshModerationActions = useCallback(() => {
+ if (fetchModerationActionParam) {
+ dispatch(fetchModerationActions(fetchModerationActionParam, { reset: true }))
+ }
+ }, [fetchModerationActionParam, dispatch])
+
+ const fetchMoreModerationActions = useCallback(() => {
+ if (fetchModerationActionParam && hasMoreModerationActions && !pending) {
+ dispatch(fetchModerationActions({
+ ...fetchModerationActionParam,
+ offset: moderationActions.length
+ }))
+ }
+ }, [fetchModerationActionParam, hasMoreModerationActions, pending, moderationActions.length, dispatch])
+
+ const handleClearModerationAction = useCallback(({ postId, moderationActionId, groupId }) => {
+ dispatch(clearModerationAction({ postId, moderationActionId, groupId }))
+ }, [dispatch])
+
+ const renderModerationItem = useCallback(({ item }) => (
+
+ ), [forGroup, handleClearModerationAction])
+
+ useEffect(() => {
+ if (isFocused && fetchModerationActionParam) {
+ dispatch(fetchModerationActions(fetchModerationActionParam, { reset: true }))
+ }
+ }, [isFocused, fetchModerationActionParam, dispatch])
+
+ if (!fetchModerationActionParam) return null
+ return (
+
+ `moderation-action-${item.id}`}
+ onEndReached={fetchMoreModerationActions}
+ ListHeaderComponent={(
+
+ {header}
+
+ navigate('Decisions')} options={DECISIONS_OPTIONS} />
+
+
+ )}
+ ListFooterComponent={pending ? : null}
+ />
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'white'
+ },
+ loading: {
+ marginVertical: 20
+ },
+ listControls: {
+ paddingTop: 4,
+ paddingBottom: 4,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 10,
+ marginRight: 6,
+ marginLeft: 6
+ },
+})
diff --git a/src/components/ModerationList/index.js b/src/components/ModerationList/index.js
new file mode 100644
index 000000000..3075868e2
--- /dev/null
+++ b/src/components/ModerationList/index.js
@@ -0,0 +1,3 @@
+import component from './ModerationList'
+
+export default component
diff --git a/src/components/ModerationListItem/ModerationListItem.js b/src/components/ModerationListItem/ModerationListItem.js
new file mode 100644
index 000000000..2a59cf6ec
--- /dev/null
+++ b/src/components/ModerationListItem/ModerationListItem.js
@@ -0,0 +1,210 @@
+import React from 'react'
+import { View, Text, TouchableOpacity, StyleSheet, Linking } from 'react-native'
+import { useTranslation } from 'react-i18next'
+import { useSelector } from 'react-redux'
+import { useNavigation } from '@react-navigation/native'
+
+import { agreementsURL, RESP_MANAGE_CONTENT } from 'store/constants'
+import getPlatformAgreements from 'store/selectors/getPlatformAgreements'
+import getMe from 'store/selectors/getMe'
+import hasResponsibilityForGroup from 'store/selectors/hasResponsibilityForGroup'
+import Avatar from 'components/Avatar'
+import MultiSelect from 'components/MultiSelect'
+import { groupUrl } from 'util/navigation'
+import Button from 'components/Button/Button'
+import { caribbeanGreen, mediumPurple, white } from 'style/colors'
+import PostListRow from 'components/PostListRow'
+
+const ModerationListItem = ({
+ moderationAction,
+ handleClearModerationAction,
+ handleConfirmModerationAction,
+ navigateToPost,
+ group
+}) => {
+ const { t } = useTranslation()
+ const currentUser = useSelector(getMe)
+ const navigation = useNavigation()
+ const canModerate = useSelector((state) => hasResponsibilityForGroup(state, { groupId: group.id, responsibility: [RESP_MANAGE_CONTENT] }))
+
+ const {
+ agreements,
+ anonymous,
+ post,
+ reporter,
+ status,
+ text
+ } = moderationAction
+
+ const platformAgreementsIds = moderationAction.platformAgreements.map(agreement => agreement.id)
+ const allPlatformAgreements = useSelector(getPlatformAgreements)
+ const platformAgreements = allPlatformAgreements.filter(agreement => platformAgreementsIds.includes(agreement.id))
+ const reporterUrl = `/user/${reporter.id}` // TODO COMOD, fix this
+ const groupAgreementsUrl = group ? groupUrl(group.slug) + `/group/${group.slug}` : ''
+ const currentUserIsReporter = reporter.id === currentUser.id
+ const navigateToReporter = () => {
+ navigation.navigate('UserProfile', { userId: reporter.id })
+ }
+
+ return (
+
+
+ {t('Reported by')}:
+ {anonymous && !canModerate
+ ? ({t('Anonymous')})
+ : (
+
+
+
+ {reporter.name}
+
+ )}
+
+
+
+ {t(`status-${status}`)}
+ {t('Complaint')}:
+ {text}
+ {t('Reported content')}:
+
+
+ {agreements.length > 0 && (
+ <>
+ {t('Group Agreements broken')}:
+
+ Linking.openURL(groupAgreementsUrl)}>
+ {t('Link to group agreements')}
+
+ >)}
+ {platformAgreements.length > 0 && (
+ <>
+ ----
+ {t('Platform Agreements broken')}:
+
+ Linking.openURL(agreementsURL)}>
+ {t('Link to platform agreements')}
+
+ >)}
+
+
+
+
+ {(canModerate || currentUserIsReporter) && status !== 'cleared' && (
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ moderationActionCard: {
+ borderWidth: 1,
+ borderColor: '#ccc',
+ borderRadius: 8,
+ marginHorizontal: 16,
+ marginBottom: 24,
+ shadowColor: 'rgba(35, 65, 91, 0.2)',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 1,
+ shadowRadius: 10,
+ backgroundColor: '#fefefe',
+ elevation: 5
+ },
+ cardHeader: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ padding: 5,
+ borderBottomWidth: 1,
+ borderBottomColor: 'rgba(235, 235, 235, 1.0)',
+ borderTopLeftRadius: 5,
+ borderTopRightRadius: 5
+ },
+ reporterInfo: {
+ marginLeft: 4,
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ complaint: {
+ marginBottom: 8
+ },
+ avatar: {
+ width: 30,
+ height: 30
+ },
+ userName: {
+ fontWeight: 'bold',
+ color: 'rgba(44, 64, 89, 0.8)',
+ fontSize: 14
+ },
+ cardBody: {
+ paddingHorizontal: 16,
+ paddingTop: 8,
+ marginBottom: 16
+ },
+ bodyText: {
+ fontWeight: 'normal',
+ color: 'rgba(44, 64, 89, 0.7)',
+ fontSize: 16,
+ lineHeight: 24,
+ marginBottom: 20
+ },
+ heading: {
+ fontWeight: 'bold',
+ color: 'rgba(35, 65, 91, 1.0)',
+ fontSize: 16,
+ marginBottom: 8
+ },
+ agreements: {
+ marginTop: 16
+ },
+ agreementsLink: {
+ color: '#2C405A',
+ fontSize: 14,
+ textDecorationLine: 'underline',
+ marginTop: 10
+ },
+ status: {
+ fontSize: 14
+ },
+ cleared: {
+ color: caribbeanGreen
+ },
+ active: {
+ color: '#f39c12'
+ },
+ cardFooter: {
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ height: 52, // $footer-height + 12px
+ borderTopWidth: 1,
+ borderTopColor: 'rgba(44, 64, 89, 0.1)',
+ paddingHorizontal: 8
+ }
+})
+
+export default ModerationListItem
diff --git a/src/components/ModerationListItem/index.js b/src/components/ModerationListItem/index.js
new file mode 100644
index 000000000..b0ecfb8db
--- /dev/null
+++ b/src/components/ModerationListItem/index.js
@@ -0,0 +1,3 @@
+import component from './ModerationListItem'
+
+export default component
diff --git a/src/components/MultiSelect/MultiSelect.js b/src/components/MultiSelect/MultiSelect.js
new file mode 100644
index 000000000..06b7f5bc5
--- /dev/null
+++ b/src/components/MultiSelect/MultiSelect.js
@@ -0,0 +1,103 @@
+import React, { useState } from 'react'
+import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
+import { useTranslation } from 'react-i18next'
+import CheckBox from '@react-native-community/checkbox'
+import PropTypes from 'prop-types'
+
+const MultiSelect = ({ items, selected = [], hideAfter, handleSelect }) => {
+ const { t } = useTranslation()
+ const [showAll, setShowAll] = useState(false)
+
+ const handleShowMore = () => {
+ setShowAll(true)
+ }
+
+ const renderItems = () => {
+ const itemsToRender = showAll || !hideAfter || items.length <= hideAfter
+ ? items
+ : items.slice(0, hideAfter)
+
+ return itemsToRender.map((item) => {
+ const itemText = item.text || item.title
+ const ellipsis = itemText.length > 35 ? '...' : ''
+
+ return (
+ handleSelect && handleSelect(item.id)}
+ key={item.id}
+ style={[styles.item, selected.includes(item.id) && styles.selectedItem]}
+ >
+ {itemText.slice(0, 35) + ellipsis}
+ {handleSelect && (
+
+ )}
+
+ )
+ })
+ }
+
+ return (
+
+ {renderItems()}
+ {hideAfter && items.length > hideAfter && !showAll && (
+
+ {t('Show more')}
+
+ )}
+
+ )
+}
+
+MultiSelect.propTypes = {
+ items: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ text: PropTypes.string,
+ title: PropTypes.string
+ })).isRequired,
+ selected: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
+ hideAfter: PropTypes.number,
+ handleSelect: PropTypes.func
+}
+
+const styles = StyleSheet.create({
+ multiSelect: {
+ // Styles for multiSelec
+ },
+ item: {
+ borderWidth: 1,
+ borderColor: '#ccc',
+ marginVertical: 5,
+ padding: 10,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ overflow: 'hidden',
+ flex: 1
+ },
+ itemText: {
+ fontSize: 14,
+ flex: 1,
+ marginRight: 10,
+ overflow: 'hidden'
+ },
+ itemText: {
+ fontSize: 14
+ },
+ selectedItem: {
+ backgroundColor: '#f0f0f0'
+ },
+ showMore: {
+ alignItems: 'center',
+ borderTopWidth: 2,
+ borderTopColor: '#ccc',
+ borderStyle: 'dashed'
+ },
+ showMoreText: {
+ color: '#1abc9c'
+ }
+})
+
+export default MultiSelect
diff --git a/src/components/MultiSelect/index.js b/src/components/MultiSelect/index.js
new file mode 100644
index 000000000..3666f65ff
--- /dev/null
+++ b/src/components/MultiSelect/index.js
@@ -0,0 +1,3 @@
+import component from './MultiSelect'
+
+export default component
diff --git a/src/components/PostCard/PostBody/PostBody.js b/src/components/PostCard/PostBody/PostBody.js
index 5b3c0af17..24f080faf 100644
--- a/src/components/PostCard/PostBody/PostBody.js
+++ b/src/components/PostCard/PostBody/PostBody.js
@@ -9,7 +9,7 @@ import EmojiRow from 'components/EmojiRow'
import LinkPreview from 'components/PostCard/LinkPreview'
import Icon from 'components/Icon'
import PopupMenuButton from 'components/PopupMenuButton'
-import PostBodyProposal from '../PostBodyProposal/PostBodyProposal'
+import PostBodyProposal from '../PostBodyProposal'
import { caribbeanGreen, rhino, white, white20onCaribbeanGreen } from 'style/colors'
import { useTranslation } from 'react-i18next'
@@ -22,6 +22,7 @@ export default function PostBody ({
details,
startTime,
endTime,
+ isFlagged,
post,
linkPreview,
// linkPreviewFeatured,
@@ -32,6 +33,7 @@ export default function PostBody ({
const presentedDetails = useMemo(() => {
return shouldTruncate ? TextHelpers.truncateHTML(details, MAX_DETAILS_LENGTH) : details
}, [details, shouldTruncate])
+ const { t } = useTranslation()
return (
@@ -39,7 +41,7 @@ export default function PostBody ({
{TextHelpers.formatDatePair(startTime, endTime)}
)}
-
+
{type === 'event' && !!respondToEvent && (
' + t('Contents obscured') + '
' : presentedDetails}
baseStyle={{ marginBottom: 8 }}
/>
{/* {!linkPreviewFeatured && ( */}
- {type === 'proposal' && }
+ {type === 'proposal' && }
{/* )} */}
- {proposalOptionsArray && proposalOptionsArray.map((option, i) => {
+ {!isFlagged && proposalOptionsArray && proposalOptionsArray.map((option, i) => {
const optionVotes = proposalVotesArray.filter(vote => vote.optionId === option.id)
const avatarUrls = optionVotes.map(vote => vote.user.avatarUrl)
return (
diff --git a/src/components/PostCard/PostCard.js b/src/components/PostCard/PostCard.js
index 4d5aacb6e..8b2b0558a 100644
--- a/src/components/PostCard/PostCard.js
+++ b/src/components/PostCard/PostCard.js
@@ -1,8 +1,10 @@
import React from 'react'
-import { View, Text } from 'react-native'
+import { View, Text, TouchableOpacity } from 'react-native'
import { useTranslation } from 'react-i18next'
+import { useDispatch } from 'react-redux'
import { LocationHelpers } from 'hylo-shared'
import { useCurrentUser } from 'hooks/useCurrentUser'
+import { recordClickthrough } from 'store/actions/moderationActions'
import PostHeader from './PostHeader'
import PostBody from './PostBody'
import PostGroups from './PostGroups'
@@ -13,9 +15,11 @@ import Icon from 'components/Icon'
import Topics from 'components/Topics'
import styles from 'components/PostCard/PostCard.styles'
+
export default function PostCard ({
goToGroup,
hideDetails,
+ groupId,
hideMenu,
onPress,
post = {},
@@ -26,9 +30,11 @@ export default function PostCard ({
showTopic: goToTopic
}) {
const { t } = useTranslation()
+ const dispatch = useDispatch()
const images = post.imageUrls && post.imageUrls.map(uri => ({ uri }))
const locationText = LocationHelpers.generalLocationString(post.locationObject, post.location)
const currentUser = useCurrentUser()
+ const isFlagged = post.flaggedGroups && post.flaggedGroups.includes(groupId)
return (
<>
@@ -45,12 +51,24 @@ export default function PostCard ({
currentUser={currentUser}
date={post.createdAt}
hideMenu={hideMenu}
+ isFlagged={isFlagged}
pinned={post.pinned}
postId={post.id}
showMember={showMember}
title={post.title}
type={post.type}
/>
+ {isFlagged && !post.clickthrough && (
+
+ {t('clickthroughExplainer')}
+ dispatch(recordClickthrough({ postId: post.id }))}
+ >
+ {t('View post')}
+
+
+ )}
{(!images || images.length === 0) && (
)}
- {(images && images.length > 0) && (
+ {(images && images.length > 0) && !(isFlagged && !post.clickthrough) && (
member.id === currentUser.id, post.members)
const locationText = LocationHelpers.generalLocationString(post.locationObject, post.location)
const images = post.imageUrls && post.imageUrls.map(uri => ({ uri }))
+ const isFlagged = post.flaggedGroups && post.flaggedGroups.includes(groupId)
return (
0) && (
)}
+ {isFlagged && !post.clickthrough && (
+
+ {t('clickthroughExplainer')}
+ dispatch(recordClickthrough({ postId: post.id }))}
+ >
+ {t('View post')}
+
+
+ )}
{!!locationText && (
@@ -108,6 +123,7 @@ export default function PostCardForDetails ({ post, showGroups = true }) {
linkPreviewFeatured={post.linkPreviewFeatured}
myEventResponse={post.myEventResponse}
respondToEvent={handleRespondToEvent}
+ isFlagged={isFlagged && !post.clickthrough}
startTime={post.startTime}
title={post.title}
type={post.type}
diff --git a/src/components/PostCard/PostHeader/PostHeader.js b/src/components/PostCard/PostHeader/PostHeader.js
index 56ec03c87..f8b0a1bda 100644
--- a/src/components/PostCard/PostHeader/PostHeader.js
+++ b/src/components/PostCard/PostHeader/PostHeader.js
@@ -8,18 +8,21 @@ import CondensingBadgeRow from 'components/CondensingBadgeRow'
import getCurrentGroup from 'store/selectors/getCurrentGroup'
import Avatar from 'components/Avatar'
import FlagContent from 'components/FlagContent'
+import FlagGroupContent from 'components/FlagGroupContent'
import Icon from 'components/Icon'
import styles, { labelStyles } from './PostHeader.styles'
import { useTranslation } from 'react-i18next'
import { RESP_ADMINISTRATION } from 'store/constants'
import hasResponsibilityForGroup from 'store/selectors/hasResponsibilityForGroup'
import getRolesForGroup from 'store/selectors/getRolesForGroup'
+import { useNavigation } from '@react-navigation/native'
export default function PostHeader ({
announcement,
closeOnDelete,
creator,
date,
+ isFlagged,
hideMenu,
pinned,
postId,
@@ -29,6 +32,7 @@ export default function PostHeader ({
title,
type
}) {
+ const navigation = useNavigation()
const [flaggingVisible, setFlaggingVisible] = useState(false)
const { showPostActionSheet } = usePostActionSheet({
postId,
@@ -62,6 +66,11 @@ export default function PostHeader ({
{TextHelpers.humanDate(date)}
+ {isFlagged && (
+ navigation.navigate('Decisions', { streamType: 'moderation', initial: false, options: { title: 'Moderation' } })}>
+
+
+ )}
{pinned && (
)}
@@ -69,7 +78,7 @@ export default function PostHeader ({
)}
{type && (
-
+
)}
{!hideMenu && (
)}
- {flaggingVisible && (
+ {flaggingVisible && !currentGroupId && (
setFlaggingVisible(false)}
/>
)}
+ {flaggingVisible && currentGroupId && (
+ setFlaggingVisible(false)}
+ />
+ )}
)
}
-export function PostLabel ({ type }) {
+export function PostLabel ({ type, condensed }) {
const { t } = useTranslation()
const labelTypeStyle = get(type, labelStyles) || labelStyles.discussion
const boxStyle = [labelStyles.box, labelTypeStyle.box]
@@ -113,7 +133,9 @@ export function PostLabel ({ type }) {
return (
- {t(type).toUpperCase()}
+ {condensed
+ ? `${t(type).toUpperCase().slice(0, 4)}.`
+ : t(type).toUpperCase()}
)
diff --git a/src/components/PostCard/PostHeader/PostHeader.styles.js b/src/components/PostCard/PostHeader/PostHeader.styles.js
index 233a2d73c..03030c41c 100644
--- a/src/components/PostCard/PostHeader/PostHeader.styles.js
+++ b/src/components/PostCard/PostHeader/PostHeader.styles.js
@@ -1,5 +1,5 @@
import { POST_TYPES } from 'store/models/Post'
-import { rhino30, rhino50, caribbeanGreen, rhino40 } from 'style/colors'
+import {amaranth, rhino30, rhino50, caribbeanGreen, rhino40 } from 'style/colors'
export const styles = {
container: {
@@ -63,6 +63,11 @@ export const styles = {
color: rhino50,
marginRight: 10
},
+ flagIcon: {
+ fontSize: 24,
+ color: amaranth,
+ marginRight: 6
+ },
moreIcon: {
fontSize: 20,
paddingLeft: 5,
diff --git a/src/components/PostListRow/PostListRow.js b/src/components/PostListRow/PostListRow.js
new file mode 100644
index 000000000..8975bae26
--- /dev/null
+++ b/src/components/PostListRow/PostListRow.js
@@ -0,0 +1,247 @@
+import React from 'react'
+import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
+import { useTranslation } from 'react-i18next'
+import { useNavigation } from '@react-navigation/native'
+import Moment from 'moment-timezone'
+
+import { modalScreenName } from 'hooks/useIsModalScreen'
+import Avatar from 'components/Avatar'
+import HyloHTML from 'components/HyloHTML'
+import Icon from 'components/Icon'
+import { personUrl } from 'util/navigation'
+
+const PostListRow = (props) => {
+ const {
+ childPost,
+ currentGroupId,
+ post,
+ expanded,
+ slug
+ } = props
+
+ const {
+ title,
+ details,
+ creator,
+ createdTimestamp,
+ commentersTotal
+ } = post
+
+ const navigation = useNavigation()
+
+ if (!creator) {
+ return null
+ }
+
+ const typeLowercase = post.type.toLowerCase()
+ const typeName = post.type.charAt(0).toUpperCase() + typeLowercase.slice(1)
+
+ const creatorUrl = personUrl(creator.id, slug)
+ const numOtherCommentors = commentersTotal - 1
+ const unread = false
+ const startTimeMoment = Moment(post.startTime)
+ const isFlagged = post.flaggedGroups && post.flaggedGroups.includes(currentGroupId)
+ const { t } = useTranslation()
+
+ return (
+ navigation.navigate(modalScreenName('Post Details'), { id: post.id })}
+ >
+
+
+ {isFlagged && }
+
+
+
+
+ {post.type === 'event'
+ ? (
+
+ {startTimeMoment.format('MMM')}
+ {startTimeMoment.format('D')}
+
+ )
+ : (
+
+
+
+ {creator.name} {
+ numOtherCommentors > 1 && (
+ <>
+ {' '}
+ {t('and')}{' '}
+
+ {numOtherCommentors} {t('others')}
+
+ >
+ )
+ }
+
+
+ )}
+
+ {childPost && (
+
+
+
+ )}
+
+ {createdTimestamp}
+
+
+
+ {title}
+
+ 200 ? details.slice(0, 200) + '...' : details} />
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ postRow: {
+ flexDirection: 'row',
+ position: 'relative',
+ marginLeft: 0,
+ padding: 12,
+ backgroundColor: 'white',
+ shadowColor: 'rgb(35, 65, 91)',
+ shadowOffset: { width: 0, height: 5 },
+ shadowOpacity: 0.1,
+ shadowRadius: 8,
+ elevation: 5,
+ borderBottomWidth: 1,
+ borderBottomColor: 'rgba(35, 65, 91, 0.1)'
+ },
+ iconContainer: {
+ backgroundColor: 'white',
+ shadowColor: 'rgba(47, 63, 87, 0.2)',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 1,
+ shadowRadius: 4,
+ elevation: 4,
+ borderRadius: 5,
+ padding: 2,
+ height: 24,
+ marginLeft: 'auto',
+ marginRight: 4
+ },
+ icon: {
+ fontSize: 12,
+ padding: 2
+ },
+ reactions: {
+ padding: 0
+ },
+ typeAuthor: {
+ flexDirection: 'row',
+ alignItems: 'flex-end'
+ },
+ postType: {
+ borderRadius: 3,
+ padding: 2,
+ paddingTop: 0,
+ marginRight: 5
+ },
+ chat: { borderColor: 'rgba(58, 160, 223, 0.25)', borderWidth: 1 },
+ discussion: { borderColor: 'rgba(58, 160, 223, 0.25)', borderWidth: 1 },
+ event: { borderColor: 'rgba(237, 86, 83, 0.25)', borderWidth: 1 },
+ offer: { borderColor: 'rgba(0, 196, 159, 0.25)', borderWidth: 1 },
+ resource: { borderColor: 'rgba(254, 214, 55, 0.25)', borderWidth: 1 },
+ project: { borderColor: 'rgba(238, 134, 14, 0.25)', borderWidth: 1 },
+ request: { borderColor: 'rgba(98, 75, 162, 0.25)', borderWidth: 1 },
+ proposal: { borderColor: 'rgba(58, 160, 223, 0.25)', borderWidth: 1 },
+ contentSummary: {
+ flex: 1,
+ position: 'relative',
+ overflow: 'hidden'
+ },
+ participants: {
+ fontSize: 12,
+ color: '#8C9DAE'
+ },
+ participantsContent: {
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ participantsText: {
+ fontSize: 12,
+ color: '#8C9DAE'
+ },
+ avatar: {
+ marginRight: 5
+ },
+ title: {
+ color: '#2F3D4C',
+ fontSize: 16,
+ marginTop: 0,
+ marginBottom: 0,
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ details: {
+ color: '#5F6C7D',
+ width: '100%',
+ maxHeight: 18,
+ overflow: 'hidden',
+ marginBottom: 4
+ },
+ topic: {
+ color: '#00BF8F',
+ marginRight: 10
+ },
+ timestamp: {
+ fontSize: 12,
+ color: '#8C9DAE'
+ },
+ pushToRight: {
+ marginLeft: 'auto'
+ },
+ expanded: {
+ margin: -6,
+ backgroundColor: 'white',
+ padding: 17,
+ zIndex: 10,
+ borderRadius: 5,
+ shadowColor: 'rgba(35, 65, 91, 0.3)',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 1,
+ shadowRadius: 15,
+ elevation: 8,
+ borderWidth: 1,
+ borderColor: 'rgba(0, 118, 223, 0.5)',
+ borderTopWidth: 3
+ },
+ isFlagged: {
+ opacity: 0.3 // React Native doesn't support blur, so we use opacity instead
+ },
+ flagIcon: {
+ marginTop: 1,
+ marginRight: 12,
+ fontSize: 24,
+ color: '#ED5653',
+ fontWeight: 'bold'
+ },
+ date: {
+ flexDirection: 'row'
+ },
+ dateText: {
+ fontSize: 12,
+ color: '#8C9DAE'
+ },
+ topics: {
+ flexDirection: 'row',
+ marginBottom: 0,
+ opacity: 0.5
+ },
+ bold: {
+ fontWeight: 'bold'
+ }
+})
+
+export default PostListRow
diff --git a/src/components/PostListRow/index.js b/src/components/PostListRow/index.js
new file mode 100644
index 000000000..7f4f27fa4
--- /dev/null
+++ b/src/components/PostListRow/index.js
@@ -0,0 +1,3 @@
+import component from './PostListRow'
+
+export default component
diff --git a/src/components/StreamList/PostRow/PostRow.js b/src/components/StreamList/PostRow/PostRow.js
index b8a990769..924dcae66 100644
--- a/src/components/StreamList/PostRow/PostRow.js
+++ b/src/components/StreamList/PostRow/PostRow.js
@@ -38,6 +38,7 @@ export default function PostRow ({
showGroups={showGroups}
showMember={showMember}
showTopic={showTopic}
+ groupId={forGroupId}
childPost={forGroupId !== 'all' && forGroupId !== 'public' && context !== 'my' && !groupIds.includes(forGroupId)}
/>
diff --git a/src/components/StreamList/StreamList.js b/src/components/StreamList/StreamList.js
index 56c87525d..3ed1eeebd 100644
--- a/src/components/StreamList/StreamList.js
+++ b/src/components/StreamList/StreamList.js
@@ -3,7 +3,7 @@ import { useSelector, useDispatch } from 'react-redux'
import { FlatList, View, TouchableOpacity } from 'react-native'
import { createSelector } from 'reselect'
import { isEmpty, get } from 'lodash/fp'
-import { useIsFocused } from '@react-navigation/native'
+import { useIsFocused, useNavigation } from '@react-navigation/native'
import { FETCH_POSTS } from 'store/constants'
import { ALL_GROUP_ID, isContextGroup, MY_CONTEXT_ID, PUBLIC_GROUP_ID } from 'store/models/Group'
import { makeGetQueryResults } from 'store/reducers/queryResults'
@@ -50,6 +50,12 @@ export const EVENT_STREAM_TIMEFRAME_OPTIONS = [
{ id: 'future', label: 'Upcoming Events' },
{ id: 'past', label: 'Past Events' }
]
+
+export const DECISIONS_OPTIONS = [
+ { id: 'proposal', label: 'Proposals' },
+ { id: 'moderation', label: 'Moderation' }
+]
+
export const DEFAULT_SORT_BY_ID = 'updated'
export const DEFAULT_TIMEFRAME_ID = 'future'
@@ -77,6 +83,7 @@ export default function StreamList (props) {
topicName
} = props
const dispatch = useDispatch()
+ const navigation = useNavigation()
const isFocused = useIsFocused()
const currentUser = useSelector(getMe)
const [filter, setFilter] = useState()
@@ -191,6 +198,11 @@ export default function StreamList (props) {
)}
+ {streamType === 'proposal' && (
+
+ navigation.navigate('Decisions', { streamType: 'moderation', initial: false, options: { title: 'Moderation' } })} options={DECISIONS_OPTIONS} />
+
+ )}
}
ListFooterComponent={pending ? : null}
diff --git a/src/graphql/fragments/postFieldsFragment.js b/src/graphql/fragments/postFieldsFragment.js
index 5a435a263..2dbcc56d0 100644
--- a/src/graphql/fragments/postFieldsFragment.js
+++ b/src/graphql/fragments/postFieldsFragment.js
@@ -93,7 +93,9 @@ const postFieldsFragment = withComments => `
}
}
createdAt
+ clickthrough
updatedAt
+ flaggedGroups
isAnonymousVote
isPublic
fulfilledAt
diff --git a/src/graphql/queries/MeQuery.js b/src/graphql/queries/MeQuery.js
index 39fcb9846..0491668ed 100644
--- a/src/graphql/queries/MeQuery.js
+++ b/src/graphql/queries/MeQuery.js
@@ -124,6 +124,14 @@ export default gql`
}
group {
id
+ agreements {
+ items {
+ id
+ description
+ order
+ title
+ }
+ }
avatarUrl
bannerUrl
name
diff --git a/src/navigation/AuthRootNavigator.js b/src/navigation/AuthRootNavigator.js
index 80c063e4d..28b9cbb65 100644
--- a/src/navigation/AuthRootNavigator.js
+++ b/src/navigation/AuthRootNavigator.js
@@ -22,6 +22,7 @@ import NotificationsList from 'screens/NotificationsList'
import Thread from 'screens/Thread'
import { white } from 'style/colors'
import fetchCommonRoles from 'store/actions/fetchCommonRoles'
+import fetchPlatformAgreements from 'store/actions/fetchPlatformAgreements'
const AuthRoot = createStackNavigator()
export default function AuthRootNavigator () {
@@ -32,6 +33,7 @@ export default function AuthRootNavigator () {
useHyloQuery({ action: fetchNotifications })
useHyloQuery({ action: updateNewNotificationCount })
useHyloQuery({ action: fetchCommonRoles })
+ useHyloQuery({ action: fetchPlatformAgreements })
useEffect(() => {
(async function () {
diff --git a/src/navigation/HomeNavigator.js b/src/navigation/HomeNavigator.js
index 0af96b200..89a5cf694 100644
--- a/src/navigation/HomeNavigator.js
+++ b/src/navigation/HomeNavigator.js
@@ -23,12 +23,14 @@ import ProjectMembers from 'screens/ProjectMembers/ProjectMembers'
import MapWebView from 'screens/MapWebView/MapWebView'
import GroupWelcomeLanding from 'screens/GroupWelcomeFlow/GroupWelcomeLanding'
import { GROUP_WELCOME_LANDING } from 'screens/GroupWelcomeFlow/GroupWelcomeFlow.store'
+import { useTranslation } from 'react-i18next'
const HomeTab = createStackNavigator()
export default function HomeNavigator ({ navigation }) {
const initialURL = useSelector(state => state.initialURL)
const returnToOnAuthPath = useSelector(getReturnToOnAuthPath)
const [, setCurrentGroup] = useCurrentGroup()
+ const { t } = useTranslation()
useEffect(() => {
if (!initialURL && !returnToOnAuthPath) {
@@ -86,7 +88,7 @@ export default function HomeNavigator ({ navigation }) {
-
+
diff --git a/src/navigation/linking/index.js b/src/navigation/linking/index.js
index 793c7e1a8..c57376c28 100644
--- a/src/navigation/linking/index.js
+++ b/src/navigation/linking/index.js
@@ -62,6 +62,7 @@ export const routingConfig = {
'/:context(groups)/:groupSlug': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Stream`,
'/:context(groups)/:groupSlug/custom/:customViewId': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Stream`,
'/:context(groups)/:groupSlug/explore': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Group Explore`,
+ '/:context(groups)/:groupSlug/proposals': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Stream`,
'/:context(groups)/:groupSlug/create': `${AUTH_ROOT_SCREEN_NAME}/Edit Post`,
'/:context(groups)/:groupSlug/post/:id': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Post Details`,
'/:context(groups)/:groupSlug/post/:id/comments/:commentId':`${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Post Details`,
diff --git a/src/screens/GroupNavigation/GroupNavigation.js b/src/screens/GroupNavigation/GroupNavigation.js
index 3c4bc1151..5aa947576 100644
--- a/src/screens/GroupNavigation/GroupNavigation.js
+++ b/src/screens/GroupNavigation/GroupNavigation.js
@@ -67,6 +67,12 @@ export default function GroupNavigation () {
onPress: () => navigate('Members'),
hidden: isContextGroup(currentGroup?.slug)
},
+ {
+ label: t('Decisions'),
+ iconName: 'Proposal',
+ onPress: () => navigate('Decisions'),
+ hidden: isContextGroup(currentGroup?.slug)
+ },
{
label: t('Groups'),
iconName: 'Groups',
diff --git a/src/screens/PostDetails/PostDetails.js b/src/screens/PostDetails/PostDetails.js
index dddb15fe9..6c357fa9b 100644
--- a/src/screens/PostDetails/PostDetails.js
+++ b/src/screens/PostDetails/PostDetails.js
@@ -84,6 +84,7 @@ export default function PostDetails () {
)}
onSelect={setSelectedComment}
diff --git a/src/screens/Stream/Stream.js b/src/screens/Stream/Stream.js
index 2a203246e..a755819c4 100644
--- a/src/screens/Stream/Stream.js
+++ b/src/screens/Stream/Stream.js
@@ -28,12 +28,14 @@ import SocketSubscriber from 'components/SocketSubscriber'
import GroupWelcomeCheck from 'components/GroupWelcomeCheck'
import { bannerlinearGradientColors } from 'style/colors'
import styles from './Stream.styles'
+import ModerationList from 'components/ModerationList'
export function headerTitle (currentGroup, streamType, myHome, t) {
if (myHome) return myHome
let title
title = currentGroup?.name
title = streamType ? capitalize(t(streamType) + 's') : title
+ if (streamType === 'Moderation') title = t('Moderation')
return title
}
@@ -154,26 +156,54 @@ export default function Stream ({ topicName: providedTopicName }) {
)
+ const moderationListHeader = (
+
+
+
+
+
+
+ {customViewName || myHome || name}
+
+
+
+
+ )
+
return (
<>
-
+ {streamType !== 'moderation' && (
+
+ )}
+ {streamType === 'moderation' && (
+
+ )}
{!topicName && currentGroup && (
)}
@@ -196,12 +226,13 @@ export function postPromptString (type = '', { firstName }, t) {
export function PostPrompt ({ currentUser, forGroup, currentType, currentTopicName }) {
const navigation = useNavigation()
const { t } = useTranslation()
+ const checkedCurrentType = currentType === 'moderation' ? 'discussion' : currentType
if (!currentUser) return null
const handleOpenPostEditor = () => (
navigation.navigate('Edit Post', {
- type: currentType,
+ type: checkedCurrentType,
groupId: forGroup.id,
topicName: currentTopicName
})
@@ -213,7 +244,7 @@ export function PostPrompt ({ currentUser, forGroup, currentType, currentTopicNa
- {postPromptString(currentType, { firstName: currentUser.firstName() }, t)}
+ {postPromptString(checkedCurrentType, { firstName: currentUser.firstName() }, t)}
)
diff --git a/src/store/actions/fetchPlatformAgreements.js b/src/store/actions/fetchPlatformAgreements.js
new file mode 100644
index 000000000..864876e47
--- /dev/null
+++ b/src/store/actions/fetchPlatformAgreements.js
@@ -0,0 +1,20 @@
+export const FETCH_PLATFORM_AGREEMENTS = 'FETCH_PLATFORM_AGREEMENTS'
+
+export default function fetchPlatformAgreements () {
+ return {
+ type: FETCH_PLATFORM_AGREEMENTS,
+ graphql: {
+ query: `query FetchPlatformAgreements {
+ platformAgreements {
+ id
+ text
+ type
+ }
+ }`,
+ variables: { }
+ },
+ meta: {
+ extractModel: 'PlatformAgreement'
+ }
+ }
+}
diff --git a/src/store/actions/moderationActions.js b/src/store/actions/moderationActions.js
new file mode 100644
index 000000000..778285cbb
--- /dev/null
+++ b/src/store/actions/moderationActions.js
@@ -0,0 +1,132 @@
+import { get } from 'lodash/fp'
+
+import {
+ CLEAR_MODERATION_ACTION,
+ CREATE_MODERATION_ACTION,
+ FETCH_MODERATION_ACTIONS,
+ RECORD_CLICKTHROUGH
+} from 'store/constants'
+
+export function clearModerationAction ({ postId, moderationActionId, groupId }) {
+ return {
+ type: CLEAR_MODERATION_ACTION,
+ graphql: {
+ query: `mutation ( $postId: ID, $moderationActionId: ID, $groupId: ID ) {
+ clearModerationAction ( postId: $postId, moderationActionId: $moderationActionId, groupId: $groupId ) {
+ success
+ }
+ }`,
+ variables: { postId, moderationActionId, groupId }
+ },
+ meta: {
+ moderationActionId,
+ groupId,
+ postId,
+ optimistic: true
+ }
+ }
+}
+
+export function createModerationAction (data) {
+ return {
+ type: CREATE_MODERATION_ACTION,
+ graphql: {
+ query: `mutation ($data: ModerationActionInput) {
+ createModerationAction (data: $data) {
+ id
+ postId
+ groupId
+ text
+ anonymous
+ agreements {
+ id
+ }
+ platformAgreements {
+ id
+ }
+ }
+ }`,
+ variables: { data }
+ },
+ meta: {
+ data,
+ optimistic: true
+ }
+ }
+}
+
+export function recordClickthrough ({ postId }) {
+ console.log(postId, 'whwhhwhw')
+ return {
+ type: RECORD_CLICKTHROUGH,
+ graphql: {
+ query: `mutation ($postId: ID) {
+ recordClickthrough (postId: $postId) {
+ success
+ }
+ }`,
+ variables: { postId }
+ },
+ meta: {
+ postId,
+ optimistic: true
+ }
+ }
+}
+
+export function fetchModerationActions ({ slug, offset, sortBy, first = 20 }) {
+ return {
+ type: FETCH_MODERATION_ACTIONS,
+ graphql: {
+ query: `query ($slug: String, $offset: Int, $sortBy: String, $first: Int) {
+ moderationActions (slug: $slug, offset: $offset, sortBy: $sortBy, first: $first) {
+ hasMore
+ items {
+ id
+ postId
+ groupId
+ status
+ post {
+ id
+ title
+ details
+ type
+ creator {
+ id
+ name
+ avatarUrl
+ }
+ groups {
+ id
+ }
+ }
+ text
+ reporter {
+ id
+ name
+ avatarUrl
+ }
+ anonymous
+ agreements {
+ id
+ description
+ order
+ title
+ }
+ platformAgreements {
+ id
+ }
+ }
+ }
+ }`,
+ variables: { slug, offset, sortBy, first }
+ },
+ meta: {
+ slug,
+ extractModel: 'ModerationAction',
+ extractQueryResults: {
+ getItems: get('payload.data.items.moderationActions')
+ }
+ }
+ }
+}
diff --git a/src/store/constants.js b/src/store/constants.js
index 0ec438fc0..630eb4f1b 100644
--- a/src/store/constants.js
+++ b/src/store/constants.js
@@ -18,6 +18,8 @@ export const CHECK_INVITATION = 'CHECK_INVITATION'
export const CHECK_LOGIN = 'CHECK_LOGIN'
export const CLEAR_USER_TYPING = 'CLEAR_USER_TYPING'
export const CLEAR_MODERATOR_SUGGESTIONS = 'CLEAR_MODERATOR_SUGGESTIONS'
+export const CLEAR_MODERATION_ACTION = 'CLEAR_MODERATION_ACTION'
+export const CLEAR_MODERATION_ACTION_PENDING = 'CLEAR_MODERATION_ACTION' + _PENDING
export const CREATE_AFFILIATION = 'CREATE_AFFILIATION'
export const CREATE_COMMENT = 'CREATE_COMMENT'
export const CREATE_COMMENT_PENDING = CREATE_COMMENT + _PENDING
@@ -27,6 +29,8 @@ export const CREATE_JOIN_REQUEST = 'CREATE_JOIN_REQUEST'
export const CREATE_JOIN_REQUEST_PENDING = 'CREATE_JOIN_REQUEST' + _PENDING
export const CREATE_MESSAGE = 'CREATE_MESSAGE'
export const CREATE_MESSAGE_PENDING = CREATE_MESSAGE + _PENDING
+export const CREATE_MODERATION_ACTION = 'CREATE_MODERATION_ACTION'
+export const CREATE_MODERATION_ACTION_PENDING = 'CREATE_MODERATION_ACTION' + _PENDING
export const CREATE_POST = 'CREATE_POST'
export const CREATE_PROJECT = 'CREATE_PROJECT'
export const DEACTIVATE_ME = 'DEACTIVATE_ME'
@@ -61,6 +65,8 @@ export const FETCH_JOIN_REQUESTS = 'FETCH_JOIN_REQUESTS'
export const FETCH_JOIN_REQUESTS_PENDING = FETCH_JOIN_REQUESTS + '_PENDING'
export const FETCH_MESSAGES = 'FETCH_MESSAGES'
export const FETCH_MESSAGES_PENDING = FETCH_MESSAGES + _PENDING
+export const FETCH_MODERATION_ACTIONS = 'FETCH_MODERATION_ACTIONS'
+export const FETCH_MODERATION_ACTIONS_PENDING = FETCH_MODERATION_ACTIONS + _PENDING
export const FETCH_MODERATOR_SUGGESTIONS = 'FETCH_MODERATOR_SUGGESTIONS'
export const FETCH_MY_JOIN_REQUESTS = 'FETCH_MY_JOIN_REQUESTS'
export const FETCH_MY_REQUESTS_AND_INVITES = 'FETCH_MY_REQUESTS_AND_INVITES'
@@ -115,6 +121,8 @@ export const REACT_ON_COMMENT = 'REACT_ON_COMMENT'
export const REACT_ON_COMMENT_PENDING = REACT_ON_COMMENT + _PENDING
export const REACT_ON_POST = 'REACT_ON_POST'
export const REACT_ON_POST_PENDING = REACT_ON_POST + _PENDING
+export const RECORD_CLICKTHROUGH = 'RECORD_CLICKTHROUGH'
+export const RECORD_CLICKTHROUGH_PENDING = 'RECORD_CLICKTHROUGH' + _PENDING
export const REGISTER = 'REGISTER'
export const REJECT_GROUP_RELATIONSHIP_INVITE = 'REJECT_GROUP_RELATIONSHIP_INVITE'
export const REMOVE_MODERATOR = 'REMOVE_MODERATOR'
diff --git a/src/store/models/ModerationAction.js b/src/store/models/ModerationAction.js
new file mode 100644
index 000000000..07f7abdb2
--- /dev/null
+++ b/src/store/models/ModerationAction.js
@@ -0,0 +1,19 @@
+import { attr, Model } from 'redux-orm'
+
+class ModerationAction extends Model {
+ toString () {
+ return `ModerationAction (${this.id}): ${this.title}`
+ }
+}
+
+export default ModerationAction
+
+ModerationAction.modelName = 'ModerationAction'
+
+ModerationAction.fields = {
+ id: attr(),
+ groupId: attr(),
+ postId: attr(),
+ status: attr(),
+ text: attr()
+}
diff --git a/src/store/models/PlatformAgreement.js b/src/store/models/PlatformAgreement.js
new file mode 100644
index 000000000..33c91660a
--- /dev/null
+++ b/src/store/models/PlatformAgreement.js
@@ -0,0 +1,17 @@
+import { attr, Model } from 'redux-orm'
+
+class PlatformAgreement extends Model {
+ toString () {
+ return `PlatformAgreement (${this.id}): ${this.text}`
+ }
+}
+
+export default PlatformAgreement
+
+PlatformAgreement.modelName = 'PlatformAgreement'
+
+PlatformAgreement.fields = {
+ id: attr(),
+ type: attr(),
+ text: attr()
+}
diff --git a/src/store/models/index.js b/src/store/models/index.js
index a276230f6..fd423fdaf 100644
--- a/src/store/models/index.js
+++ b/src/store/models/index.js
@@ -15,6 +15,7 @@ import Invitation from './Invitation'
import JoinRequest, { JoinRequestQuestionAnswer, Question } from './JoinRequest'
import LinkPreview from './LinkPreview'
import Location from './Location'
+import ModerationAction from './ModerationAction'
import Me, { MySkillsToLearn } from './Me'
import Membership from './Membership'
import Message from './Message'
@@ -22,6 +23,7 @@ import MessageThread from './MessageThread'
import Notification from './Notification'
import Person, { PersonSkillsToLearn } from './Person'
import PersonConnection from './PersonConnection'
+import PlatformAgreement from './PlatformAgreement'
import Post, { PostFollower, PostCommenter, ProjectMember } from './Post'
import PostMembership from './PostMembership'
import SearchResult from './SearchResult'
@@ -60,11 +62,13 @@ orm.register(
Membership,
Message,
MessageThread,
+ ModerationAction,
MySkillsToLearn,
Notification,
Person,
PersonConnection,
PersonSkillsToLearn,
+ PlatformAgreement,
Post,
PostCommenter,
PostFollower,
diff --git a/src/store/presenters/presentPost.js b/src/store/presenters/presentPost.js
index c51f855a8..56ffb3d5e 100644
--- a/src/store/presenters/presentPost.js
+++ b/src/store/presenters/presentPost.js
@@ -12,6 +12,7 @@ export default function presentPost (post, forGroupId) {
...post.ref,
attachments: post.attachments
.orderBy('position').toModelArray(),
+ clickthrough: post.clickthrough,
creator: post.creator,
commenters: post.commenters.toModelArray(),
eventInvitations: post.eventInvitations.toModelArray().map(eventInvitation => {
diff --git a/src/store/reducers/ormReducer/index.js b/src/store/reducers/ormReducer/index.js
index 996da3251..44486aeb8 100644
--- a/src/store/reducers/ormReducer/index.js
+++ b/src/store/reducers/ormReducer/index.js
@@ -43,7 +43,10 @@ import {
UPDATE_THREAD_READ_TIME,
UPDATE_USER_SETTINGS_PENDING as UPDATE_USER_SETTINGS_GLOBAL_PENDING,
UPDATE_WIDGET,
- USE_INVITATION
+ USE_INVITATION,
+ CREATE_MODERATION_ACTION_PENDING,
+ CLEAR_MODERATION_ACTION_PENDING,
+ RECORD_CLICKTHROUGH_PENDING
} from 'store/constants'
import {
CREATE_MESSAGE, CREATE_MESSAGE_PENDING, UPDATE_THREAD_READ_TIME_PENDING
@@ -135,6 +138,14 @@ export default function ormReducer (state = orm.getEmptyState(), action) {
break
}
+ case CLEAR_MODERATION_ACTION_PENDING: {
+ if (meta && meta?.moderationActionId) {
+ const moderationAction = session.ModerationAction.withId(meta.moderationActionId)
+ moderationAction.update({ status: 'cleared' })
+ }
+ break
+ }
+
case CANCEL_GROUP_RELATIONSHIP_INVITE:
case REJECT_GROUP_RELATIONSHIP_INVITE: {
const invite = GroupRelationshipInvite.withId(meta.id)
@@ -206,6 +217,21 @@ export default function ormReducer (state = orm.getEmptyState(), action) {
break
}
+ case CREATE_MODERATION_ACTION_PENDING: {
+ if (meta.data) {
+ post = Post.withId(meta?.data?.postId)
+ if (post) {
+ const flaggedGroups = post.flaggedGroups
+ if (flaggedGroups) post.flaggedGroups.push(meta?.data?.groupId)
+ const moderationActions = post.moderationActions
+ if (moderationActions) post.moderationActions.unshift(meta?.data)
+ post.update({ flaggedGroups: flaggedGroups || [meta?.data?.groupId] })
+ post.update({ moderationActions: moderationActions || [meta?.data] })
+ }
+ }
+ break
+ }
+
case DELETE_COMMENT_PENDING: {
const comment = Comment.withId(meta.id)
const post = comment.post
@@ -551,6 +577,11 @@ export default function ormReducer (state = orm.getEmptyState(), action) {
Invitation.filter({ email: me.email, group: payload.data.useInvitation.membership.group.id }).delete()
break
}
+ case RECORD_CLICKTHROUGH_PENDING: {
+ post = Post.withId(meta.postId)
+ post.update({ clickthrough: true })
+ break
+ }
case REACT_ON_COMMENT_PENDING: {
comment = session.Comment.withId(meta.commentId)
diff --git a/src/store/selectors/getModerationActions.js b/src/store/selectors/getModerationActions.js
new file mode 100644
index 000000000..ddd7d4b4a
--- /dev/null
+++ b/src/store/selectors/getModerationActions.js
@@ -0,0 +1,24 @@
+import { get } from 'lodash/fp'
+import { createSelector } from 'reselect'
+import { FETCH_MODERATION_ACTIONS } from 'store/constants'
+import orm from 'store/models'
+import { createSelector as ormCreateSelector } from 'redux-orm'
+import { makeGetQueryResults } from 'store/reducers/queryResults'
+
+export const getModerationActionResults = makeGetQueryResults(FETCH_MODERATION_ACTIONS)
+
+export const getModerationActions = ormCreateSelector(
+ orm,
+ (state, props) => props.groupId,
+ (session, groupId) => {
+ const moderationActions = session.ModerationAction.all().toModelArray()
+ return groupId
+ ? moderationActions.filter(ma => {
+ return ma.groupId === groupId
+ })
+ : moderationActions || []
+ }
+)
+
+export const getHasMoreModerationActions = createSelector(getModerationActionResults, get('hasMore'))
+export const getTotalModerationActions = createSelector(getModerationActionResults, get('total'))
diff --git a/src/store/selectors/getPlatformAgreements.js b/src/store/selectors/getPlatformAgreements.js
new file mode 100644
index 000000000..18daec432
--- /dev/null
+++ b/src/store/selectors/getPlatformAgreements.js
@@ -0,0 +1,11 @@
+import orm from '../models'
+import { createSelector as ormCreateSelector } from 'redux-orm'
+
+const getPlatformAgreements = ormCreateSelector(
+ orm,
+ session => {
+ return session.PlatformAgreement.all().toRefArray()
+ }
+)
+
+export default getPlatformAgreements
diff --git a/src/style/colors.js b/src/style/colors.js
index 3dc4aa938..bdbc60fee 100644
--- a/src/style/colors.js
+++ b/src/style/colors.js
@@ -33,6 +33,9 @@ export const slateGrey = '#67768A'
export const sunsetOrange = '#FE4850'
export const suvaGrey = '#929292'
export const treePoppy = '#FF9D21'
+export const mediumPurple = '#9883E5'
+export const regent = '#808C9B'
+export const mangoYellow = '#FDD549'
// these colors are the equivalent of reducing the opacity of the colors named
// above, on a white background. use these where possible, to avoid any impact
diff --git a/yarn.lock b/yarn.lock
index fe0e620c2..2436ab75e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2433,6 +2433,20 @@ __metadata:
languageName: node
linkType: hard
+"@react-native-community/checkbox@npm:^0.5.17":
+ version: 0.5.17
+ resolution: "@react-native-community/checkbox@npm:0.5.17"
+ peerDependencies:
+ react: "*"
+ react-native: ">= 0.62"
+ react-native-windows: ">=0.62"
+ peerDependenciesMeta:
+ react-native-windows:
+ optional: true
+ checksum: 98cddff11b976a1f712bfc4d7de946d5e102108bedc016bbcb668b3917757dd5092b5b93f651f740508dd8437ba3a0ce5ca15cef3ebf7bcfc15a490b0b7d0d6b
+ languageName: node
+ linkType: hard
+
"@react-native-community/cli-clean@npm:13.6.9":
version: 13.6.9
resolution: "@react-native-community/cli-clean@npm:13.6.9"
@@ -3958,6 +3972,7 @@ __metadata:
"@native-html/iframe-plugin": ^2.6.1
"@react-native-async-storage/async-storage": ^1.24.0
"@react-native-clipboard/clipboard": ^1.14.1
+ "@react-native-community/checkbox": ^0.5.17
"@react-native-community/eslint-config": ^3.2.0
"@react-native-community/hooks": ^2.8.0
"@react-native-google-signin/google-signin": ^8.0.1
From 741dc84fc6efe7bafcb5e3e20feb3d5ba68ca990 Mon Sep 17 00:00:00 2001
From: Tom
Date: Thu, 5 Sep 2024 17:31:37 -0700
Subject: [PATCH 2/7] 5.8.0 changelog updated
---
CHANGELOG | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG b/CHANGELOG
index f40a42e2c..8220ad47b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,12 @@ All notable changes to HyloReactNative (the Hylo mobile app) will be documented
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+### [5.8.0] - 2024-09-05
+
+- Add code of conduct, contribution guide and full license details
+- i18n bug-fix for location search
+- Update numerous dependencies
+
### [5.7.0] - 2024-07-15
- Add 'responsibilties' in place of 'moderators'
From 0541b42784e742d2374801dd1a8516f6c6733a66 Mon Sep 17 00:00:00 2001
From: Tom
Date: Thu, 5 Sep 2024 17:31:44 -0700
Subject: [PATCH 3/7] 5.8.0
---
android/app/build.gradle | 4 ++--
ios/HyloReactNative.xcodeproj/project.pbxproj | 4 ++--
ios/HyloReactNative/Info.plist | 20 +++++++++----------
ios/HyloReactNativeTests/Info.plist | 4 ++--
.../Info.plist | 4 ++--
package.json | 2 +-
6 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 9a13bd93c..a2bb54c14 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -87,8 +87,8 @@ android {
applicationId "com.hylo.hyloandroid"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 287
- versionName "5.7.1"
+ versionCode 288
+ versionName "5.8.0"
}
signingConfigs {
debug {
diff --git a/ios/HyloReactNative.xcodeproj/project.pbxproj b/ios/HyloReactNative.xcodeproj/project.pbxproj
index a7dbf732a..39fe48820 100644
--- a/ios/HyloReactNative.xcodeproj/project.pbxproj
+++ b/ios/HyloReactNative.xcodeproj/project.pbxproj
@@ -821,7 +821,7 @@
CODE_SIGN_ENTITLEMENTS = HyloReactNative/HyloReactNative.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 209;
+ CURRENT_PROJECT_VERSION = 210;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = L4KZBPS2F3;
INFOPLIST_FILE = HyloReactNative/Info.plist;
@@ -858,7 +858,7 @@
CODE_SIGN_ENTITLEMENTS = HyloReactNative/HyloReactNativeRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 209;
+ CURRENT_PROJECT_VERSION = 210;
DEVELOPMENT_TEAM = L4KZBPS2F3;
INFOPLIST_FILE = HyloReactNative/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
diff --git a/ios/HyloReactNative/Info.plist b/ios/HyloReactNative/Info.plist
index 87ee1a0e3..763bf5650 100644
--- a/ios/HyloReactNative/Info.plist
+++ b/ios/HyloReactNative/Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 5.7.1
+ 5.8.0
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleTypeRole
Editor
CFBundleURLIconFile
-
+
CFBundleURLName
hyloapp
CFBundleURLSchemes
@@ -50,26 +50,26 @@
CFBundleVersion
- 209
+ 210
NSAppTransportSecurity
NSAllowsArbitraryLoads
-
+
NSAllowsLocalNetworking
-
+
FacebookAdvertiserIDCollectionEnabled
-
+
FacebookAppID
$(FACEBOOK_APP_ID)
FacebookAutoLogAppEventsEnabled
-
+
FacebookClientToken
$(FACEBOOK_CLIENT_TOKEN)
FacebookDisplayName
Hylo
ITSAppUsesNonExemptEncryption
-
+
LSApplicationQueriesSchemes
fbapi
@@ -78,7 +78,7 @@
fbshareextension
LSRequiresIPhoneOS
-
+
NSLocationAlwaysAndWhenInUseUsageDescription
Allow access to your current location to center the map feature, searches, and location selection when creating a post. Background access is optional and used for notifications of nearby resources when the app is in the background
NSLocationWhenInUseUsageDescription
@@ -134,6 +134,6 @@
UIUserInterfaceStyle
Light
UIViewControllerBasedStatusBarAppearance
-
+
diff --git a/ios/HyloReactNativeTests/Info.plist b/ios/HyloReactNativeTests/Info.plist
index 7d7a14c63..dd6825d32 100644
--- a/ios/HyloReactNativeTests/Info.plist
+++ b/ios/HyloReactNativeTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 5.7.1
+ 5.8.0
CFBundleSignature
????
CFBundleVersion
- 209
+ 210
diff --git a/ios/OneSignalNotificationServiceExtension/Info.plist b/ios/OneSignalNotificationServiceExtension/Info.plist
index 08e2ecdce..0900d0760 100644
--- a/ios/OneSignalNotificationServiceExtension/Info.plist
+++ b/ios/OneSignalNotificationServiceExtension/Info.plist
@@ -17,9 +17,9 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 5.7.1
+ 5.8.0
CFBundleVersion
- 209
+ 210
NSExtension
NSExtensionPointIdentifier
diff --git a/package.json b/package.json
index 1673d2980..33bb29ba7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "HyloReactNative",
- "version": "5.7.1",
+ "version": "5.8.0",
"private": true,
"scripts": {
"android": "adb reverse tcp:3001 tcp:3001 && adb reverse tcp:3000 tcp:3000 && react-native run-android",
From 77e3849bf9ab61760cf2eadb3bac56ada7900125 Mon Sep 17 00:00:00 2001
From: Tom
Date: Thu, 3 Oct 2024 10:08:24 -0700
Subject: [PATCH 4/7] Updated snapshots
---
.../PostEditor/__snapshots__/PostEditor.connector.test.js.snap | 3 +++
.../SearchPage/__snapshots__/SearchPage.store.test.js.snap | 2 ++
src/store/actions/__snapshots__/fetchPosts.test.js.snap | 2 ++
src/store/presenters/__snapshots__/presentPost.test.js.snap | 1 +
4 files changed, 8 insertions(+)
diff --git a/src/screens/PostEditor/__snapshots__/PostEditor.connector.test.js.snap b/src/screens/PostEditor/__snapshots__/PostEditor.connector.test.js.snap
index 9c076b9d4..bfd423784 100644
--- a/src/screens/PostEditor/__snapshots__/PostEditor.connector.test.js.snap
+++ b/src/screens/PostEditor/__snapshots__/PostEditor.connector.test.js.snap
@@ -54,7 +54,9 @@ exports[`PostEditor mapDispatchToProps maps the action generators 2`] = `
}
}
createdAt
+ clickthrough
updatedAt
+ flaggedGroups
isAnonymousVote
isPublic
fulfilledAt
@@ -364,6 +366,7 @@ exports[`PostEditor mapStateToProps maps 1`] = `
},
},
],
+ "clickthrough": undefined,
"commenters": [],
"creator": SessionBoundModel {
"_fields": {
diff --git a/src/screens/SearchPage/__snapshots__/SearchPage.store.test.js.snap b/src/screens/SearchPage/__snapshots__/SearchPage.store.test.js.snap
index d54248d2b..fb209ada3 100644
--- a/src/screens/SearchPage/__snapshots__/SearchPage.store.test.js.snap
+++ b/src/screens/SearchPage/__snapshots__/SearchPage.store.test.js.snap
@@ -60,7 +60,9 @@ exports[`fetchSearchResults matches snapshot 1`] = `
}
}
createdAt
+ clickthrough
updatedAt
+ flaggedGroups
isAnonymousVote
isPublic
fulfilledAt
diff --git a/src/store/actions/__snapshots__/fetchPosts.test.js.snap b/src/store/actions/__snapshots__/fetchPosts.test.js.snap
index 40d85a0a3..90eb676a2 100644
--- a/src/store/actions/__snapshots__/fetchPosts.test.js.snap
+++ b/src/store/actions/__snapshots__/fetchPosts.test.js.snap
@@ -93,7 +93,9 @@ posts: viewPosts(
}
}
createdAt
+ clickthrough
updatedAt
+ flaggedGroups
isAnonymousVote
isPublic
fulfilledAt
diff --git a/src/store/presenters/__snapshots__/presentPost.test.js.snap b/src/store/presenters/__snapshots__/presentPost.test.js.snap
index af6eb2d02..302191164 100644
--- a/src/store/presenters/__snapshots__/presentPost.test.js.snap
+++ b/src/store/presenters/__snapshots__/presentPost.test.js.snap
@@ -3,6 +3,7 @@
exports[`presentPost matches the snapshot 1`] = `
{
"attachments": [],
+ "clickthrough": undefined,
"commenters": [],
"creator": null,
"eventInvitations": [
From 24bad34a616c2cb5a765cfeb0b4979efd9ac8d16 Mon Sep 17 00:00:00 2001
From: Tom
Date: Thu, 3 Oct 2024 11:38:02 -0700
Subject: [PATCH 5/7] Adjust images for post detail
---
src/components/PostCard/PostCardForDetails.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/PostCard/PostCardForDetails.js b/src/components/PostCard/PostCardForDetails.js
index 9d0da8be5..0e9e8b8d6 100644
--- a/src/components/PostCard/PostCardForDetails.js
+++ b/src/components/PostCard/PostCardForDetails.js
@@ -84,7 +84,7 @@ export default function PostCardForDetails ({ post, showGroups = true, groupId }
style={styles.topics}
/>
)}
- {(images && images.length > 0) && (
+ {(images && images.length > 0) && !(isFlagged && !post.clickthrough) && (
Date: Thu, 3 Oct 2024 13:18:35 -0700
Subject: [PATCH 6/7] Fix group-related crash
---
src/screens/Groups/Groups.graphql.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/screens/Groups/Groups.graphql.js b/src/screens/Groups/Groups.graphql.js
index bc3648cde..6e3359970 100644
--- a/src/screens/Groups/Groups.graphql.js
+++ b/src/screens/Groups/Groups.graphql.js
@@ -14,12 +14,14 @@ export const MeMembershipsMemberCountQuery = gql`
childGroups {
items {
id
+ name
memberCount
}
}
parentGroups {
items {
id
+ name
memberCount
}
}
From 389fda07dd80f4cb1eb4f16cdc1d5f6aaafdb181 Mon Sep 17 00:00:00 2001
From: Loren Johnson
Date: Fri, 4 Oct 2024 00:10:09 +0200
Subject: [PATCH 7/7] Fix file moves
---
.../mobile/src}/components/FlagGroupContent/FlagGroupContent.js | 0
{src => apps/mobile/src}/components/FlagGroupContent/index.js | 0
.../mobile/src}/components/ModerationList/ModerationList.js | 0
{src => apps/mobile/src}/components/ModerationList/index.js | 0
.../src}/components/ModerationListItem/ModerationListItem.js | 0
{src => apps/mobile/src}/components/ModerationListItem/index.js | 0
{src => apps/mobile/src}/components/MultiSelect/MultiSelect.js | 0
{src => apps/mobile/src}/components/MultiSelect/index.js | 0
{src => apps/mobile/src}/components/PostListRow/PostListRow.js | 0
{src => apps/mobile/src}/components/PostListRow/index.js | 0
10 files changed, 0 insertions(+), 0 deletions(-)
rename {src => apps/mobile/src}/components/FlagGroupContent/FlagGroupContent.js (100%)
rename {src => apps/mobile/src}/components/FlagGroupContent/index.js (100%)
rename {src => apps/mobile/src}/components/ModerationList/ModerationList.js (100%)
rename {src => apps/mobile/src}/components/ModerationList/index.js (100%)
rename {src => apps/mobile/src}/components/ModerationListItem/ModerationListItem.js (100%)
rename {src => apps/mobile/src}/components/ModerationListItem/index.js (100%)
rename {src => apps/mobile/src}/components/MultiSelect/MultiSelect.js (100%)
rename {src => apps/mobile/src}/components/MultiSelect/index.js (100%)
rename {src => apps/mobile/src}/components/PostListRow/PostListRow.js (100%)
rename {src => apps/mobile/src}/components/PostListRow/index.js (100%)
diff --git a/src/components/FlagGroupContent/FlagGroupContent.js b/apps/mobile/src/components/FlagGroupContent/FlagGroupContent.js
similarity index 100%
rename from src/components/FlagGroupContent/FlagGroupContent.js
rename to apps/mobile/src/components/FlagGroupContent/FlagGroupContent.js
diff --git a/src/components/FlagGroupContent/index.js b/apps/mobile/src/components/FlagGroupContent/index.js
similarity index 100%
rename from src/components/FlagGroupContent/index.js
rename to apps/mobile/src/components/FlagGroupContent/index.js
diff --git a/src/components/ModerationList/ModerationList.js b/apps/mobile/src/components/ModerationList/ModerationList.js
similarity index 100%
rename from src/components/ModerationList/ModerationList.js
rename to apps/mobile/src/components/ModerationList/ModerationList.js
diff --git a/src/components/ModerationList/index.js b/apps/mobile/src/components/ModerationList/index.js
similarity index 100%
rename from src/components/ModerationList/index.js
rename to apps/mobile/src/components/ModerationList/index.js
diff --git a/src/components/ModerationListItem/ModerationListItem.js b/apps/mobile/src/components/ModerationListItem/ModerationListItem.js
similarity index 100%
rename from src/components/ModerationListItem/ModerationListItem.js
rename to apps/mobile/src/components/ModerationListItem/ModerationListItem.js
diff --git a/src/components/ModerationListItem/index.js b/apps/mobile/src/components/ModerationListItem/index.js
similarity index 100%
rename from src/components/ModerationListItem/index.js
rename to apps/mobile/src/components/ModerationListItem/index.js
diff --git a/src/components/MultiSelect/MultiSelect.js b/apps/mobile/src/components/MultiSelect/MultiSelect.js
similarity index 100%
rename from src/components/MultiSelect/MultiSelect.js
rename to apps/mobile/src/components/MultiSelect/MultiSelect.js
diff --git a/src/components/MultiSelect/index.js b/apps/mobile/src/components/MultiSelect/index.js
similarity index 100%
rename from src/components/MultiSelect/index.js
rename to apps/mobile/src/components/MultiSelect/index.js
diff --git a/src/components/PostListRow/PostListRow.js b/apps/mobile/src/components/PostListRow/PostListRow.js
similarity index 100%
rename from src/components/PostListRow/PostListRow.js
rename to apps/mobile/src/components/PostListRow/PostListRow.js
diff --git a/src/components/PostListRow/index.js b/apps/mobile/src/components/PostListRow/index.js
similarity index 100%
rename from src/components/PostListRow/index.js
rename to apps/mobile/src/components/PostListRow/index.js