From 33eb000ee33e5aa513083450f0a00abd7240efb0 Mon Sep 17 00:00:00 2001 From: ppelegrin Date: Tue, 15 Oct 2019 23:33:50 +0200 Subject: [PATCH 001/189] Start adding missing portuguese translation --- webapp/locales/pt.json | 946 ++++++++++++++++++++++++++++++++++------- 1 file changed, 791 insertions(+), 155 deletions(-) diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 6eccc2fc0..903be2d5d 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -1,8 +1,85 @@ { + "components": { + "password-reset": { + "request": { + "title": "Redefinir sua senha", + "form": { + "description": "Um e-mail de redefinição de senha será enviado ao endereço de e-mail fornecido.", + "submit": "Solicitar e-mail", + "submitted": "Um e-mail com mais instruções foi enviado para {email}" + } + }, + "change-password": { + "success": "Changing your password was successful!", + "error": "Changing your password failed. Maybe the security code was not correct?", + "help": "In case of problems, feel free to ask for help by sending us a mail to:" + } + }, + "enter-nonce": { + "form": { + "nonce": "Enter your code", + "description": "Open your inbox and enter the code that we've sent to you.", + "next": "Continue", + "validations": { + "length": "must be 6 characters long" + } + } + }, + "registration": { + "signup": { + "unavailable": "Unfortunately, public registration of user accounts is not available right now on this server.", + "title": "Join Human Connection!", + "form": { + "description": "To get started, enter your email address:", + "invitation-code": "Your invitation code is: {code}", + "errors": { + "email-exists": "There is already a user account with this email address!", + "invalid-invitation-token": "It looks like as if the invitation has been used already. Invitation links can only be used once." + }, + "submit": "Create an account", + "success": "A mail with a link to complete your registration has been sent to {email}" + } + }, + "create-user-account": { + "title": "Create user account", + "success": "Your account has been created!", + "error": "No user account could be created!", + "help": " Maybe the confirmation was invalid? In case of problems, feel free to ask for help by sending us a mail to:" + } + } + }, + "maintenance": { + "title": "Human Connection is under maintenance", + "explanation": "At the moment we are doing some scheduled maintenance, please try again later.", + "questions": "Any Questions or concerns, send an email to" + }, + "index": { + "no-results": "No contributions found.", + "change-filter-settings": "Change your filter settings to get more results." + }, + "filter-menu": { + "title": "Your filter bubble", + "hashtag-search": "Searching for #{hashtag}", + "clearSearch": "Clear search" + }, + "filter-posts": { + "categories": { + "header": "Categories of Content", + "all": "All" + }, + "general": { + "header": "Filter by …" + }, + "followers": { + "label": "Users I follow" + } + }, "site": { + "thanks": "Obrigado(a)!", + "error-occurred": "Ocorreu um erro.", "made": "Feito com ❤", "imprint": "Impressão", - "termsAc": "Termos e Condições", + "termsAndConditions": "Termos e Condições", "data-privacy": "Proteção de Dados", "changelog": "Mudanças e Histórico", "contact": "Contato", @@ -14,7 +91,11 @@ "bank": "conta bancária", "germany": "Alemanha", "code-of-conduct": "Codigo de Conduto", - "login": "Voltar para o Login" + "back-to-login": "Voltar para o Login" + }, + "sorting": { + "newest": "Newest", + "oldest": "Oldest" }, "login": { "copy": "Se você já tem uma conta no Human Connection, por favor faça o login.", @@ -22,211 +103,766 @@ "logout": "Sair", "email": "Seu email", "password": "Sua senha", + "forgotPassword": "Esqueceu a sua senha?", + "no-account": "Ainda não tem uma conta?", + "register": "Cadastrar-se", "moreInfo": "O que é o Human Connection?", + "moreInfoURL": "https://human-connection.org/en/", "hello": "Olá", - "forgotPassword": "Esqueceu a sua senha?" + "success": "Você está conectado!" }, - "password-reset": { - "form": { - "description": "Um e-mail de redefinição de senha será enviado para o endereço de e-mail fornecido.", - "submit": "Solicitar email", - "submitted": "Um e-mail com mais instruções foi enviado para {email}" + "editor": { + "placeholder": "Leave your inspirational thoughts …", + "mention": { + "noUsersFound": "No users found" + }, + "hashtag": { + "noHashtagsFound": "No hashtags found", + "addHashtag": "New hashtag", + "addLetter": "Type a letter" + }, + "embed": { + "data_privacy_warning": "Data Privacy Warning!", + "data_privacy_info": "Your data has not yet been shared with any third party providers. If you proceed to watch this video the following provider will likely collect user data:", + "play_now": "Watch now", + "always_allow": "Always allow embedded content by third party providers (this setting can be changed any time)" } }, "profile": { - "name": "Meu perfil", - "memberSince": "Membro desde", - "follow": "Seguir", - "followers": "Seguidores", - "following": "Seguindo", - "shouted": "Aclamou", - "commented": "Comentou", - "userAnonym": "Anonymous" - }, - "settings": { - "name": "Configurações", - "data": { - "name": "Seus dados", - "labelName": "Seu nome", - "namePlaceholder": "Anonymous", - "labelCity": "Sua cidade ou estado", - "labelBio": "Sobre você" - }, - "security": { - "name": "Segurança" + "name": "My Profile", + "memberSince": "Member since", + "follow": "Follow", + "followers": "Followers", + "following": "Following", + "shouted": "Shouted", + "commented": "Commented", + "userAnonym": "Anonymous", + "socialMedia": "Where else can I find", + "network": { + "title": "Network", + "following": "is following:", + "followingNobody": "follows nobody.", + "followedBy": "is followed by:", + "followedByNobody": "is not followed by anyone.", + "andMore": "and {number} more …" }, "invites": { - "name": "Convites" + "title": "Invite somebody to Human Connection!", + "description": "Enter thier email address for invitation.", + "emailPlaceholder": "Email to invite" + } + }, + "notifications": { + "reason": { + "mentioned_in_post": "Mentioned you in a post …", + "mentioned_in_comment": "Mentioned you in a comment …", + "commented_on_post": "Commented on your post …" + }, + "comment": "Comment" + }, + "search": { + "placeholder": "Search", + "hint": "What are you searching for?", + "failed": "Nothing found" + }, + "settings": { + "name": "Settings", + "data": { + "name": "Your data", + "labelName": "Your Name", + "labelSlug": "Your unique user name", + "namePlaceholder": "Femanon Funny", + "labelCity": "Your City or Region", + "labelBio": "About You", + "success": "Your data was successfully updated!" + }, + "email": { + "validation": { + "same-email": "This is your current email address" + }, + "name": "Your email", + "labelEmail": "Change your email address", + "labelNewEmail": "New email Address", + "labelNonce": "Enter your code", + "success": "A new email address has been registered.", + "submitted": "An email to verify your address has been sent to {email}.", + "change-successful": "Your email address has been changed successfully.", + "verification-error": { + "message": "Your email could not be changed.", + "explanation": "This can have different causes:", + "reason": { + "invalid-nonce": "Is the confirmation code invalid?", + "no-email-request": "Are you certain that you requested a change of your email address?" + }, + "support": "If the problem persists, please contact us by email at" + } + }, + "validation": { + "slug": { + "regex": "Allowed characters are only lowercase letters, numbers, underscores and hyphens.", + "alreadyTaken": "This user name is already taken." + } + }, + "security": { + "name": "Security", + "change-password": { + "button": "Change password", + "success": "Password successfully changed!", + "label-old-password": "Your old password", + "label-new-password": "Your new password", + "label-new-password-confirm": "Confirm new password", + "message-old-password-required": "Enter your old password", + "message-new-password-required": "Enter a new password", + "message-new-password-confirm-required": "Confirm your new password", + "message-new-password-missmatch": "Type the same password again", + "passwordSecurity": "Password security", + "passwordStrength0": "Very insecure password", + "passwordStrength1": "Insecure password", + "passwordStrength2": "Mediocre password", + "passwordStrength3": "Strong password", + "passwordStrength4": "Very strong password" + } + }, + "invites": { + "name": "Invites" }, "download": { - "name": "Baixar dados" + "name": "Download Data" }, - "delete": { - "name": "Apagar conta" + "deleteUserAccount": { + "name": "Delete data", + "contributionsCount": "Delete my {count} posts", + "commentedCount": "Delete my {count} comments", + "accountDescription": "Be aware that your Post and Comments are important to our community. If you still choose to delete them, you have to mark them below.", + "accountWarning": "You CAN'T MANAGE and CAN'T RECOVER your Account, Posts, or Comments after deleting your account!", + "success": "Account successfully deleted!", + "pleaseConfirm": "Destructive action! Type {confirm} to confirm" + }, + "embeds": { + "name": "Third party providers", + "info-description": "Here is the list of third-party providers whose content can be displayed as third-party code, e.g. in the form of embedded videos.", + "status": { + "description": "As a default for you, embedded code of third-party providers is", + "disabled": { + "off": "initially not displayed", + "on": "displayed immediately" + }, + "change": { + "question": "Should embedded source code from third parties always be displayed to you?", + "allow": "Sure", + "deny": "No thanks" + } + } }, "organizations": { - "name": "Minhas Organizações" + "name": "My Organizations" }, "languages": { - "name": "Linguagens" + "name": "Languages" + }, + "social-media": { + "name": "Social media", + "placeholder": "Your social media url", + "requireUnique": "You added this url already", + "submit": "Add link", + "successAdd": "Added social media. Updated user profile!", + "successDelete": "Deleted social media. Updated user profile!" + }, + "blocked-users": { + "name": "Blocked users", + "explanation": { + "intro": "If another user has been blocked by you, this is what happens:", + "your-perspective": "The blocked person's posts will no longer appear in your news feed.", + "their-perspective": "Vice versa: The blocked person will also no longer see your posts in their news feed.", + "search": "Posts of blocked people disappear from your search results.", + "notifications": "Blocked users will no longer receive notifications if they are mentioned in your posts.", + "closing": "This should be sufficient for now so that blocked users can no longer bother you." + }, + "columns": { + "name": "Name", + "slug": "Slug" + }, + "empty": "So far, you have not blocked anybody.", + "how-to": "You can block other users on their profile page via the content menu.", + "block": "Block user", + "unblock": "Unblock user" } }, "admin": { - "name": "Administração", + "name": "Admin", "dashboard": { - "name": "Painel de controle", - "users": "Usuários", - "posts": "Postagens", - "comments": "Comentários", - "notifications": "Notificações", - "organizations": "Organizações", - "projects": "Projetos", - "invites": "Convites", - "follows": "Segue", - "shouts": "Aclamações" + "name": "Dashboard", + "users": "Users", + "posts": "Posts", + "comments": "Comments", + "notifications": "Notifications", + "organizations": "Organizations", + "projects": "Projects", + "invites": "Invites", + "follows": "Follows", + "shouts": "Shouts" }, "organizations": { - "name": "Organizações" + "name": "Organizations" }, "users": { - "name": "Usuários" + "name": "Users", + "form": { + "placeholder": "email, name or description" + }, + "table": { + "columns": { + "number": "No.", + "name": "Name", + "email": "E-mail", + "slug": "Slug", + "role": "Role", + "createdAt": "Created at" + } + }, + "empty": "No users found" }, "pages": { - "name": "Páginas" + "name": "Pages" }, "notifications": { - "name": "Notificações" + "name": "Notifications" }, "categories": { - "name": "Categorias", - "categoryName": "Nome", - "postCount": "Postagens" + "name": "Categories", + "categoryName": "Name", + "postCount": "Posts" }, - "tags": { - "name": "Etiquetas", - "tagCountUnique": "Usuários", - "tagCount": "Postagens" + "hashtags": { + "name": "Hashtags", + "number": "No.", + "nameOfHashtag": "Name", + "tagCountUnique": "Users", + "tagCount": "Posts" }, "settings": { - "name": "Configurações" + "name": "Settings" + }, + "invites": { + "name": "Invite users", + "title": "Invite people", + "description": "Invitations are a wonderful way to have your friends in your network …" } }, "post": { - "name": "Postar", + "name": "Post", "moreInfo": { - "name": "Mais informações" + "name": "More info", + "title": "More information", + "description": "Here you can find more information about this topic.", + "titleOfCategoriesSection": "Categories", + "titleOfHashtagsSection": "Hashtags", + "titleOfRelatedContributionsSection": "Related posts" }, "takeAction": { - "name": "Tomar uma ação" + "name": "Take action" + }, + "menu": { + "edit": "Edit Post", + "delete": "Delete Post" }, "comment": { - "submit": "Commentar" + "submit": "Comment", + "submitted": "Comment Submitted", + "updated": "Changes Saved" }, - "edited": "editado" - }, - "quotes": { - "african": { - "quote": "Muitas pessoas pequenas, em muitos lugares pequenos, fazem muitas coisas pequenas, que podem mudar a face do mundo.", - "author": "Provérbio africano" - } - }, - "common": { - "post": "Postagem ::: Postagens", - "comment": "Comentário ::: Comentários", - "letsTalk": "Vamos Conversar", - "versus": "Contra", - "moreInfo": "Mais informações", - "takeAction": "Tomar uma ação", - "shout": "Aclamação ::: Aclamações", - "user": "Usuário ::: Usuários", - "category": "Categoria ::: Categorias", - "organization": "Organização ::: Organizações", - "project": "Projeto ::: Projetos", - "tag": "Etiqueta ::: Etiquetas", - "name": "Nome", - "loadMore": "Carregar mais", - "loading": "Carregando", - "reportContent": "Denunciar" - }, - "actions": { - "loading": "Carregando", - "loadMore": "Carregar mais", - "create": "Criar", - "save": "Salvar", - "edit": "Editar", - "delete": "Apagar", - "cancel": "Cancelar" - }, - "moderation": { - "name": "Moderação", - "reports": { - "empty": "Parabéns, nada a moderar.", - "name": "Denúncias", - "reporter": "Denunciado por" - } - }, - "disable": { - "user": { - "title": "Desativar usuário", - "type": "Usuário", - "message": "Você realmente deseja desativar o usuário \" {name} \"?" - }, - "contribution": { - "title": "Desativar Contribuição", - "type": "Contribuição", - "message": "Você realmente deseja desativar a contribuição \" {name} \"?" - }, - "comment": { - "title": "Desativar comentário", - "type": "Comentário", - "message": "Você realmente deseja desativar o comentário de \" {name} \"?" - } - }, - "report": { - "submit": "Enviar denúncia", - "cancel": "Cancelar", - "user": { - "title": "Denunciar usuário", - "type": "Usuário", - "message": "Você realmente deseja denunciar o usuário \" {name} \"?" - }, - "contribution": { - "title": "Denunciar Contribuição", - "type": "Contribuição", - "message": "Você realmente deseja denunciar a contribuição \" {name} \"?" - }, - "comment": { - "title": "Denunciar Comentário", - "type": "Comentário", - "message": "Você realmente deseja denunciar o comentário de \"{name}\"?" - } - }, - "contribution": { - "edit": "Editar Contribuição", - "delete": "Apagar Contribuição", - "teaserImage": { - "cropperConfirm": "Confirmar" - } + "edited": "edited" }, "comment": { "content": { - "unavailable-placeholder": "… este commenttário não está disponível" + "unavailable-placeholder": "… this comment is not available anymore" }, "menu": { - "edit": "Editar Comentário", - "delete": "Apagar Comentário" + "edit": "Edit Comment", + "delete": "Delete Comment" }, "show": { - "more": "mostrar mais", - "less": "mostrar menos" + "more": "show more", + "less": "show less" }, - "edited": "editado" + "edited": "edited" + }, + "quotes": { + "african": { + "quote": "Many small people in many small places do many small things, that can alter the face of the world.", + "author": "African proverb" + } + }, + "common": { + "post": "Post ::: Posts", + "comment": "Comment ::: Comments", + "letsTalk": "Let`s Talk", + "versus": "Versus", + "moreInfo": "More Info", + "takeAction": "Take Action", + "shout": "Shout ::: Shouts", + "user": "User ::: Users", + "category": "Category ::: Categories", + "organization": "Organization ::: Organizations", + "project": "Project ::: Projects", + "tag": "Tag ::: Tags", + "name": "Name", + "loadMore": "load more", + "loading": "loading", + "reportContent": "Report", + "validations": { + "email": "must be a valid email address", + "url": "must be a valid URL" + } + }, + "actions": { + "loading": "loading", + "loadMore": "load more", + "create": "Create", + "save": "Save", + "edit": "Edit", + "delete": "Delete", + "cancel": "Cancel" + }, + "moderation": { + "name": "Moderation", + "reports": { + "empty": "Congratulations, nothing to moderate.", + "name": "Reports", + "submitter": "reported by", + "disabledBy": "disabled by" + } + }, + "disable": { + "submit": "Disable", + "cancel": "Cancel", + "success": "Disabled successfully!", + "user": { + "title": "Disable User", + "type": "User", + "message": "Do you really want to disable the user \"{name}\"?" + }, + "contribution": { + "title": "Disable Contribution", + "type": "Contribution", + "message": "Do you really want to disable the contribution \"{name}\"?" + }, + "comment": { + "title": "Disable Comment", + "type": "Comment", + "message": "Do you really want to disable the comment from \"{name}\"?" + } + }, + "delete": { + "submit": "Delete", + "cancel": "Cancel", + "contribution": { + "title": "Delete Post", + "type": "Contribution", + "message": "Do you really want to delete the post \"{name}\"?", + "success": "Post successfully deleted!" + }, + "comment": { + "title": "Delete Comment", + "type": "Comment", + "message": "Do you really want to delete the comment \"{name}\"?", + "success": "Comment successfully deleted!" + } + }, + "report": { + "submit": "Report", + "cancel": "Cancel", + "success": "Thanks for reporting!", + "user": { + "title": "Report User", + "type": "User", + "message": "Do you really want to report the user \"{name}\"?", + "error": "You already reported the user!" + }, + "contribution": { + "title": "Report Post", + "type": "Contribution", + "message": "Do you really want to report the contribution \"{name}\"?", + "error": "You have already reported the contribution!" + }, + "comment": { + "title": "Report Comment", + "type": "Comment", + "message": "Do you really want to report the comment from \"{name}\"?", + "error": "You have already reported the comment!" + } }, "followButton": { - "follow": "Seguir", - "following": "Seguindo" + "follow": "Follow", + "following": "Following" }, "shoutButton": { - "shouted": "Aclamou" + "shouted": "shouted" + }, + "release": { + "submit": "Release", + "cancel": "Cancel", + "success": "Released successfully!", + "user": { + "title": "Release User", + "type": "User", + "message": "Do you really want to release the user \"{name}\"?", + "error": "You already reported the user!" + }, + "contribution": { + "title": "Release Contribution", + "type": "Contribution", + "message": "Do you really want to release the contribution \"{name}\"?", + "error": "You have already reported the contribution!!" + }, + "comment": { + "title": "Release Comment", + "type": "Comment", + "message": "Do you really want to release the comment from \"{name}\"?", + "error": "You have already reported the comment!" + } + }, + "user": { + "avatar": { + "submitted": "Upload successful!" + } + }, + "contribution": { + "newPost": "Create a new Post", + "filterFollow": "Filter contributions from users I follow", + "filterALL": "View all contributions", + "success": "Saved!", + "languageSelectLabel": "Language", + "categories": { + "infoSelectedNoOfMaxCategories": "{chosen} of {max} categories selected" + }, + "emotions-label": { + "funny": "Funny", + "happy": "Happy", + "surprised": "Surprised", + "cry": "Cry", + "angry": "Angry" + }, + "category": { + "name": { + "freedom-of-speech": "Freedom of Speech", + "consumption-sustainability": "Consumption & Sustainability", + "global-peace-nonviolence": "Global Peace & Nonviolence", + "just-for-fun": "Just for Fun", + "happiness-values": "Happiness & Values", + "health-wellbeing": "Health & Wellbeing", + "environment-nature": "Environment & Nature", + "animal-protection": "Animal Protection", + "human-rights-justice": "Human Rights & Justice", + "education-sciences": "Education & Sciences", + "cooperation-development": "Cooperation & Development", + "democracy-politics": "Democracy & Politics", + "economy-finances": "Economy & Finances", + "energy-technology": "Energy & Technology", + "it-internet-data-privacy": "IT, Internet & Data Privacy", + "art-culture-sport": "Art, Culture, & Sport" + } + }, + "teaserImage": { + "cropperConfirm": "Confirm" + } + }, + "code-of-conduct": { + "subheader": "for the social network of the Human Connection gGmbH", + "preamble": { + "title": "Preamble", + "description": "Human Connection is a non-profit social knowledge and action network of the next generation. By people - for people. Open Source, fair and transparent. For positive local and global change in all areas of life. We completely redesign the public exchange of knowledge, ideas and projects. The functions of Human Connection bring people together - offline and online - so that we can make the world a better place." + }, + "purpose": { + "title": "Purpose", + "description": "With these code of conduct we regulate the essential principles for behavior in our social network. The United Nations Charter of Human Rights is our orientation and forms the heart of our understanding of values. The code of conduct serves as guiding principles for our personal appearance and interaction with one another. Anyone who is active as a user in the Human Connection Network, writes articles, comments or contacts other users, including those outside the network,acknowledges these rules of conduct as binding." + }, + "expected-behaviour": { + "title": "Expected Behaviour", + "description": "The following behaviors are expected and requested of all community members:", + "list": { + "0": "Exercise consideration and respect in your speech and actions.", + "1": "Attempt collaboration before conflict.", + "2": "Refrain from demeaning, discriminatory, or harassing behavior and speech.", + "3": "Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential." + } + }, + "unacceptable-behaviour": { + "title": "Unacceptable Behavior", + "description": "The following behaviors are unacceptable within our community:", + "list": { + "0": "Discriminatory posts, comments, utterances or insults, particularly those relating to gender, sexual orientation, race, religion, political or philosophical orientation or disability.", + "1": "Posting or linking of clearly pornographic material.", + "2": "Glorification or trivialization of cruel or inhuman acts of violence.", + "3": "The disclosure of others' personal information without their consent or threat there of (\"doxing\").", + "4": "Intentional intimidation, stalking or persecution.", + "5": "Advertising products and services with commercial intent.", + "6": "Criminal behavior or violation of German law.", + "7": "Endorse or encourage such conduct." + } + }, + "consequences": { + "title": "Consequences of Unacceptable Behavior", + "description": "If a community member exhibits unacceptable behaviour, the responsible operators, moderators and administrators of the network may take appropriate measures, including but not limited to:", + "list": { + "0": "Request for immediate cessation of unacceptable conduct", + "1": "Locking or deleting comments", + "2": "Temporary exclusion from the respective post or contribution", + "3": "Blocking or deleting of content", + "4": "Temporary withdrawal of write permissions", + "5": "Temporary exclusion from the network", + "6": "Final exclusion from the network", + "7": "Violations of German law can be reported.", + "8": "Advocacy or encouragement to these behaviors." + } + }, + "get-help": "If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible and link or refer to the corresponding content:" + }, + "termsAndConditions": { + "newTermsAndConditions": "New Terms and Conditions", + "termsAndConditionsConfirmed": "I have read and confirmed the Terms and Conditions.", + "termsAndConditionsNewConfirmText": "Please read the new terms of use now!", + "termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.", + "agree": "I agree!", + "risk": { + "title": "Risk of accident", + "description": "This is a test version! All data, your profile and the server can be completely destroyed, wiped out, lost, burnt and eventually synchronised near Alpha Centauri at any time. Use on your own risk. Commercial effects are not likely though." + }, + "data-privacy": { + "title": "You and your data", + "description": "Please note that the content of the alpha version will be used for public web presentations etc. but we are sure, this is in your interest. Avoid real names and use anonymous profile pictures without your face. You can find more information in our data privacy policy" + }, + "work-in-progress": { + "title": "Work in progress", + "description": "This is still a test version. Please excuse if some applications are not working, blocking, irritating, displayed falsely or not able to be clicked on. Please report faults and bugs! mailto:support@human-connection.org" + }, + "code-of-conduct": { + "title": "Code of conduct", + "description": "The code of conduct serves as guiding principles for our personal appearance and interaction with one another. Anyone who is active as a user in the Human Connection Network, writes articles, comments or contacts other users, including those outside the network, acknowledges these rules of conduct as binding." + }, + "moderation": { + "title": "Moderation", + "description": "As long as there is no community moderation-system in operation, a rainbow colored unicorn decides, if you are physically and mentally stable enough to operate our test version. The unicorn can delete you from the alpha version at any time. So be so kind and leave rainbow food!" + }, + "fairness": { + "title": "Fairness", + "description": "If, against all expectations, our alpha version is not to your liking, we return your monthly payment within the first two months. Please send a mail to: info@human-connection.org Note that more features are added on a regular basis." + }, + "questions": { + "title": "Questions", + "description": "You can find the dates and links to our zoom-rooms here: https://human-connection.org/en/events/ " + }, + "human-connection": { + "title": "By humans for humans", + "description": "Please help us to get new donators for Human Connection, so the network can take off as soon as possible. https://human-connection.org " + }, + "have-fun": "Now have fun with the alpha version of Human Connection! For the first universal peace. ♥︎", + "closing": "Thank you very much

your Human Connection Team" } } + +// { +// "password-reset": { +// "form": { +// "description": "Um e-mail de redefinição de senha será enviado para o endereço de e-mail fornecido.", +// "submit": "Solicitar email", +// "submitted": "Um e-mail com mais instruções foi enviado para {email}" +// } +// }, +// "profile": { +// "name": "Meu perfil", +// "memberSince": "Membro desde", +// "follow": "Seguir", +// "followers": "Seguidores", +// "following": "Seguindo", +// "shouted": "Aclamou", +// "commented": "Comentou", +// "userAnonym": "Anonymous" +// }, +// "settings": { +// "name": "Configurações", +// "data": { +// "name": "Seus dados", +// "labelName": "Seu nome", +// "namePlaceholder": "Anonymous", +// "labelCity": "Sua cidade ou estado", +// "labelBio": "Sobre você" +// }, +// "security": { +// "name": "Segurança" +// }, +// "invites": { +// "name": "Convites" +// }, +// "download": { +// "name": "Baixar dados" +// }, +// "delete": { +// "name": "Apagar conta" +// }, +// "organizations": { +// "name": "Minhas Organizações" +// }, +// "languages": { +// "name": "Linguagens" +// } +// }, +// "admin": { +// "name": "Administração", +// "dashboard": { +// "name": "Painel de controle", +// "users": "Usuários", +// "posts": "Postagens", +// "comments": "Comentários", +// "notifications": "Notificações", +// "organizations": "Organizações", +// "projects": "Projetos", +// "invites": "Convites", +// "follows": "Segue", +// "shouts": "Aclamações" +// }, +// "organizations": { +// "name": "Organizações" +// }, +// "users": { +// "name": "Usuários" +// }, +// "pages": { +// "name": "Páginas" +// }, +// "notifications": { +// "name": "Notificações" +// }, +// "categories": { +// "name": "Categorias", +// "categoryName": "Nome", +// "postCount": "Postagens" +// }, +// "tags": { +// "name": "Etiquetas", +// "tagCountUnique": "Usuários", +// "tagCount": "Postagens" +// }, +// "settings": { +// "name": "Configurações" +// } +// }, +// "post": { +// "name": "Postar", +// "moreInfo": { +// "name": "Mais informações" +// }, +// "takeAction": { +// "name": "Tomar uma ação" +// }, +// "comment": { +// "submit": "Commentar" +// }, +// "edited": "editado" +// }, +// "quotes": { +// "african": { +// "quote": "Muitas pessoas pequenas, em muitos lugares pequenos, fazem muitas coisas pequenas, que podem mudar a face do mundo.", +// "author": "Provérbio africano" +// } +// }, +// "common": { +// "post": "Postagem ::: Postagens", +// "comment": "Comentário ::: Comentários", +// "letsTalk": "Vamos Conversar", +// "versus": "Contra", +// "moreInfo": "Mais informações", +// "takeAction": "Tomar uma ação", +// "shout": "Aclamação ::: Aclamações", +// "user": "Usuário ::: Usuários", +// "category": "Categoria ::: Categorias", +// "organization": "Organização ::: Organizações", +// "project": "Projeto ::: Projetos", +// "tag": "Etiqueta ::: Etiquetas", +// "name": "Nome", +// "loadMore": "Carregar mais", +// "loading": "Carregando", +// "reportContent": "Denunciar" +// }, +// "actions": { +// "loading": "Carregando", +// "loadMore": "Carregar mais", +// "create": "Criar", +// "save": "Salvar", +// "edit": "Editar", +// "delete": "Apagar", +// "cancel": "Cancelar" +// }, +// "moderation": { +// "name": "Moderação", +// "reports": { +// "empty": "Parabéns, nada a moderar.", +// "name": "Denúncias", +// "reporter": "Denunciado por" +// } +// }, +// "disable": { +// "user": { +// "title": "Desativar usuário", +// "type": "Usuário", +// "message": "Você realmente deseja desativar o usuário \" {name} \"?" +// }, +// "contribution": { +// "title": "Desativar Contribuição", +// "type": "Contribuição", +// "message": "Você realmente deseja desativar a contribuição \" {name} \"?" +// }, +// "comment": { +// "title": "Desativar comentário", +// "type": "Comentário", +// "message": "Você realmente deseja desativar o comentário de \" {name} \"?" +// } +// }, +// "report": { +// "submit": "Enviar denúncia", +// "cancel": "Cancelar", +// "user": { +// "title": "Denunciar usuário", +// "type": "Usuário", +// "message": "Você realmente deseja denunciar o usuário \" {name} \"?" +// }, +// "contribution": { +// "title": "Denunciar Contribuição", +// "type": "Contribuição", +// "message": "Você realmente deseja denunciar a contribuição \" {name} \"?" +// }, +// "comment": { +// "title": "Denunciar Comentário", +// "type": "Comentário", +// "message": "Você realmente deseja denunciar o comentário de \"{name}\"?" +// } +// }, +// "contribution": { +// "edit": "Editar Contribuição", +// "delete": "Apagar Contribuição", +// "teaserImage": { +// "cropperConfirm": "Confirmar" +// } +// }, +// "comment": { +// "content": { +// "unavailable-placeholder": "… este commenttário não está disponível" +// }, +// "menu": { +// "edit": "Editar Comentário", +// "delete": "Apagar Comentário" +// }, +// "show": { +// "more": "mostrar mais", +// "less": "mostrar menos" +// }, +// "edited": "editado" +// }, +// "followButton": { +// "follow": "Seguir", +// "following": "Seguindo" +// }, +// "shoutButton": { +// "shouted": "Aclamou" +// } +// } From 886f34ed30d4a23ebfa143abc2d8154331525b7e Mon Sep 17 00:00:00 2001 From: Brent Vardy Date: Thu, 17 Oct 2019 14:52:47 +0100 Subject: [PATCH 002/189] remove need to create new crop overlay --- webapp/components/TeaserImage/TeaserImage.vue | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/webapp/components/TeaserImage/TeaserImage.vue b/webapp/components/TeaserImage/TeaserImage.vue index dc096710d..a90bf91d6 100644 --- a/webapp/components/TeaserImage/TeaserImage.vue +++ b/webapp/components/TeaserImage/TeaserImage.vue @@ -48,6 +48,7 @@ export default { previewTemplate: this.template(), }, error: false, + showCropper: false, } }, watch: { @@ -63,6 +64,7 @@ export default { return `
+
` @@ -74,13 +76,12 @@ export default { transformImage(file) { let thumbnailElement, editor, confirm, thumbnailPreview, contributionImage // Create the image editor overlay - editor = document.createElement('div') + this.showCropper = false thumbnailElement = document.querySelectorAll('#postdropzone')[0] thumbnailPreview = document.querySelectorAll('.thumbnail-preview')[0] if (thumbnailPreview) thumbnailPreview.remove() contributionImage = document.querySelectorAll('.contribution-image')[0] if (contributionImage) contributionImage.remove() - editor.classList.add('crop-overlay') thumbnailElement.appendChild(editor) // Create the confirm button confirm = document.createElement('button') @@ -114,8 +115,9 @@ export default { let cropper = new Cropper(image, { zoomable: false }) }, dropzoneDrop() { - let cropOverlay = document.querySelectorAll('.crop-overlay')[0] - if (cropOverlay) cropOverlay.remove() + let cropperOverlay = this.$refs.cropperOverlay + cropperOverlay.innerHTML = '' + this.showCropper = true }, }, } From d3480534b1f97134685db4e0dbf75f139af7ce43 Mon Sep 17 00:00:00 2001 From: Brent Vardy Date: Thu, 17 Oct 2019 14:54:27 +0100 Subject: [PATCH 003/189] fix assigning editor --- webapp/components/TeaserImage/TeaserImage.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/components/TeaserImage/TeaserImage.vue b/webapp/components/TeaserImage/TeaserImage.vue index a90bf91d6..7ae64674d 100644 --- a/webapp/components/TeaserImage/TeaserImage.vue +++ b/webapp/components/TeaserImage/TeaserImage.vue @@ -76,6 +76,7 @@ export default { transformImage(file) { let thumbnailElement, editor, confirm, thumbnailPreview, contributionImage // Create the image editor overlay + editor = document.querySelectorAll('.crop-overlay')[0] this.showCropper = false thumbnailElement = document.querySelectorAll('#postdropzone')[0] thumbnailPreview = document.querySelectorAll('.thumbnail-preview')[0] From ddadcd919e457c226f30bf15859fb716c3835f7e Mon Sep 17 00:00:00 2001 From: Brent Vardy Date: Thu, 17 Oct 2019 15:18:21 +0100 Subject: [PATCH 004/189] update to use ref --- webapp/components/TeaserImage/TeaserImage.vue | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/webapp/components/TeaserImage/TeaserImage.vue b/webapp/components/TeaserImage/TeaserImage.vue index 7ae64674d..e095c3d23 100644 --- a/webapp/components/TeaserImage/TeaserImage.vue +++ b/webapp/components/TeaserImage/TeaserImage.vue @@ -9,6 +9,7 @@ @vdropzone-thumbnail="transformImage" @vdropzone-drop="dropzoneDrop" > +
-
` @@ -76,8 +76,7 @@ export default { transformImage(file) { let thumbnailElement, editor, confirm, thumbnailPreview, contributionImage // Create the image editor overlay - editor = document.querySelectorAll('.crop-overlay')[0] - this.showCropper = false + editor = this.$refs.cropperOverlay thumbnailElement = document.querySelectorAll('#postdropzone')[0] thumbnailPreview = document.querySelectorAll('.thumbnail-preview')[0] if (thumbnailPreview) thumbnailPreview.remove() @@ -90,6 +89,7 @@ export default { confirm.textContent = this.$t('contribution.teaserImage.cropperConfirm') confirm.addEventListener('click', () => { // Get the canvas with image data from Cropper.js + this.showCropper = false let canvas = cropper.getCroppedCanvas() canvas.toBlob(blob => { this.$refs.el.manuallyAddFile(blob, canvas.toDataURL(), null, null, { @@ -116,8 +116,6 @@ export default { let cropper = new Cropper(image, { zoomable: false }) }, dropzoneDrop() { - let cropperOverlay = this.$refs.cropperOverlay - cropperOverlay.innerHTML = '' this.showCropper = true }, }, From aeae72f6918861aa2a4c64d0b32c847d9e857e93 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 28 Oct 2019 13:49:32 +0100 Subject: [PATCH 005/189] first implementation --- webapp/components/FilterPosts/FilterPosts.vue | 3 + .../FilterPosts/LanguageFilterMenuItems.vue | 102 ++++++++++++++++++ webapp/locales/de.json | 6 +- webapp/locales/en.json | 6 +- webapp/store/posts.js | 14 +++ 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 webapp/components/FilterPosts/LanguageFilterMenuItems.vue diff --git a/webapp/components/FilterPosts/FilterPosts.vue b/webapp/components/FilterPosts/FilterPosts.vue index 58f0794d2..1dd0fa737 100644 --- a/webapp/components/FilterPosts/FilterPosts.vue +++ b/webapp/components/FilterPosts/FilterPosts.vue @@ -14,6 +14,7 @@ + @@ -24,12 +25,14 @@ import Dropdown from '~/components/Dropdown' import { mapGetters } from 'vuex' import CategoriesFilterMenuItems from './CategoriesFilterMenuItems' import GeneralFilterMenuItems from './GeneralFilterMenuItems' +import LanguageFilterMenuItems from './LanguageFilterMenuItems' export default { components: { Dropdown, CategoriesFilterMenuItems, GeneralFilterMenuItems, + LanguageFilterMenuItems, }, props: { placement: { type: String }, diff --git a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue new file mode 100644 index 000000000..5bfb931cb --- /dev/null +++ b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue @@ -0,0 +1,102 @@ + + + diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 67d3b5bbd..07b0fbc0e 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -86,7 +86,11 @@ }, "followers": { "label": "Benutzern, denen ich folge" - } + }, + "language": { + "header": "Sprachen", + "all": "Alle" + } }, "site": { "thanks": "Danke!", diff --git a/webapp/locales/en.json b/webapp/locales/en.json index d9321b13b..094c88e60 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -87,7 +87,11 @@ }, "followers": { "label": "Users I follow" - } + }, + "language": { + "header": "Languages", + "all": "All" + } }, "site": { "thanks": "Thanks!", diff --git a/webapp/store/posts.js b/webapp/store/posts.js index 97c0e1245..9437db956 100644 --- a/webapp/store/posts.js +++ b/webapp/store/posts.js @@ -48,12 +48,23 @@ export const mutations = { delete filter.categories_some state.filter = filter }, + RESET_LANGUAGES(state) { + const filter = clone(state.filter) + delete filter.language_in + state.filter = filter + }, TOGGLE_CATEGORY(state, categoryId) { const filter = clone(state.filter) update(filter, 'categories_some.id_in', categoryIds => xor(categoryIds, [categoryId])) if (isEmpty(get(filter, 'categories_some.id_in'))) delete filter.categories_some state.filter = filter }, + TOGGLE_LANGUAGE(state, languageCode) { + const filter = clone(state.filter) + update(filter, 'language_in', languageCodes => xor(languageCodes, [languageCode])) + if (isEmpty(get(filter, 'language_in'))) delete filter.language_in + state.filter = filter + }, TOGGLE_EMOTION(state, emotion) { const filter = clone(state.filter) update(filter, 'emotions_some.emotion_in', emotions => xor(emotions, [emotion])) @@ -75,6 +86,9 @@ export const getters = { filteredCategoryIds(state) { return get(state.filter, 'categories_some.id_in') || [] }, + filteredLanguagesCodes(state) { + return get(state.filter, 'language_in') || [] + }, filteredByUsersFollowed(state) { return !!get(state.filter, 'author.followedBy_some.id') }, From 529a8a636567324fd3197b111ef87b89e3954b8d Mon Sep 17 00:00:00 2001 From: Kapil Jain Date: Mon, 28 Oct 2019 15:40:29 -0400 Subject: [PATCH 006/189] send only one notification for mention and comment --- .../notifications/notificationsMiddleware.js | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.js b/backend/src/middleware/notifications/notificationsMiddleware.js index a494783cf..ff1f50c59 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.js +++ b/backend/src/middleware/notifications/notificationsMiddleware.js @@ -91,11 +91,30 @@ const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo const handleContentDataOfComment = async (resolve, root, args, context, resolveInfo) => { const idsOfUsers = extractMentionedUsers(args.content) - const comment = await resolve(root, args, context, resolveInfo) if (comment) { - await notifyUsers('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context) + const session = context.driver.session() + const cypherFindUser = ` + MATCH (user: User)-[:WROTE]->(:Post)<-[:COMMENTS]-(:Comment { id: $commentId }) + RETURN user { .id } + ` + const result = await session.run(cypherFindUser, { + commentId: comment.id, + }) + session.close() + const [postAuthor] = await result.records.map(record => { + return record.get('user') + }) + const idsOfUsersExcludingPostAuthor = idsOfUsers.filter(res => !res.equals([postAuthor])) + cosole.log('idsOfUsers1') + await notifyUsers( + 'Comment', + comment.id, + idsOfUsersExcludingPostAuthor, + 'mentioned_in_comment', + context, + ) } return comment From 5d1f7bd86eb1cf62f20e28f691e295eaf461c5fa Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Tue, 29 Oct 2019 11:16:35 +0300 Subject: [PATCH 007/189] remove unused readme files --- webapp/assets.md | 5 ----- webapp/layouts.md | 5 ----- webapp/middleware.md | 5 ----- webapp/pages.md | 6 ------ webapp/plugins.md | 5 ----- webapp/static.md | 9 --------- webapp/store.md | 7 ------- webapp/styleguide.md | 12 ------------ 8 files changed, 54 deletions(-) delete mode 100644 webapp/assets.md delete mode 100644 webapp/layouts.md delete mode 100644 webapp/middleware.md delete mode 100644 webapp/pages.md delete mode 100644 webapp/plugins.md delete mode 100644 webapp/static.md delete mode 100644 webapp/store.md delete mode 100644 webapp/styleguide.md diff --git a/webapp/assets.md b/webapp/assets.md deleted file mode 100644 index 6ac7dc388..000000000 --- a/webapp/assets.md +++ /dev/null @@ -1,5 +0,0 @@ -# ASSETS - -This directory contains your un-compiled assets such as LESS, SASS, or JavaScript – in our case SCSS styles. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). diff --git a/webapp/layouts.md b/webapp/layouts.md deleted file mode 100644 index a709d79fe..000000000 --- a/webapp/layouts.md +++ /dev/null @@ -1,5 +0,0 @@ -# LAYOUTS - -This directory contains your Application Layouts. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). diff --git a/webapp/middleware.md b/webapp/middleware.md deleted file mode 100644 index a899527e2..000000000 --- a/webapp/middleware.md +++ /dev/null @@ -1,5 +0,0 @@ -# MIDDLEWARE - -This directory contains our application middleware. The middleware lets you define custom functions to be ran before rendering a page or a group of pages \(layouts\). - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). diff --git a/webapp/pages.md b/webapp/pages.md deleted file mode 100644 index 352c9fa04..000000000 --- a/webapp/pages.md +++ /dev/null @@ -1,6 +0,0 @@ -# PAGES - -This directory contains your Application Views and Routes. The framework reads all the `*.vue` files inside this directory and create the router of your application. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). - diff --git a/webapp/plugins.md b/webapp/plugins.md deleted file mode 100644 index dc7b545f2..000000000 --- a/webapp/plugins.md +++ /dev/null @@ -1,5 +0,0 @@ -# PLUGINS - -This directory contains your Javascript plugins that you want to run before mounting the root Vue.js application. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). diff --git a/webapp/static.md b/webapp/static.md deleted file mode 100644 index db271e3a5..000000000 --- a/webapp/static.md +++ /dev/null @@ -1,9 +0,0 @@ -# STATIC - -This directory contains your static files. Each file inside this directory is mapped to `/`. - -Example: `/static/robots.txt` is mapped as `/robots.txt`. - -We use it for images. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). diff --git a/webapp/store.md b/webapp/store.md deleted file mode 100644 index 6c8e3735c..000000000 --- a/webapp/store.md +++ /dev/null @@ -1,7 +0,0 @@ -# STORE - -This directory contains your Vuex Store files. Vuex Store option is implemented in the Nuxt.js framework. - -Creating a file in this directory activates the option in the framework automatically. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). diff --git a/webapp/styleguide.md b/webapp/styleguide.md deleted file mode 100644 index 1c3951494..000000000 --- a/webapp/styleguide.md +++ /dev/null @@ -1,12 +0,0 @@ -# Styleguide - -For this Projoject we decided to use [Jörg Bayreuther's](https://github.com/visualjerk) _\(visualjerk\)_ fantastic Design System called [CION](https://cion.visualjerk.de/). _\(see a_ [_demo_](https://styleguide.cion.visualjerk.de/)_\)_ - -![Styleguide in action under https://localhost:8080](../.gitbook/assets/screenshot-styleguide.png) - -## Checkout the Styleguide - -It's now an npm package. Want to help with it's development or maintenance? - -[Head over to the repo](https://github.com/Human-Connection/Nitro-Styleguide) - From 8816f7be2a9662bc1333e37b306dee6b964fc2e0 Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Tue, 29 Oct 2019 11:43:16 +0300 Subject: [PATCH 008/189] add migration plan to webapp readme --- webapp/README.md | 65 +++++++++++++++++++++++++++++++-------------- webapp/storybook.md | 33 +++++++++++++++++++++++ 2 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 webapp/storybook.md diff --git a/webapp/README.md b/webapp/README.md index b9c235196..a7be4de97 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -33,48 +33,73 @@ $ yarn build $ yarn start ``` -### Storybook +### Run tests -We encourage contributors to use Storybook to test out new components in an isolated way, and benefit from its many features. -See the docs for live examples and answers to FAQ, among other helpful information. ![Storybook docs](https://storybook.js.org/docs/basics/introduction/) +We ensure the quality of our frontend code by using +- [ESLint](https://eslint.org/) for checking our JavaScript code +- [Jest](https://jestjs.io/) and [Vue Test Utils](https://vue-test-utils.vuejs.org/) to unit test our components +- [Storybook](https://storybook.js.org/) to manually test our components in an isolated playground + +Use these commands to run the tests: {% tabs %} -{% tab title="Docker" %} +{% tab title="With Docker" %} -After you have started the application following the instructions above, in another terminal run: +After starting the application following the above guidelines open new terminal windows for each of these commands: ```bash +# run eslint +$ docker-compose exec webapp yarn lint +``` + +```bash +# run unit tests +$ docker-compose exec webapp yarn test +``` + +```bash +# start storybook $ docker-compose exec webapp yarn storybook ``` -The output should look similar to this: -![Storybook output](../.gitbook/assets/storybook-output.png) - -Click on the link http://localhost:3002/ to open the browser to your interactive storybook. +You can then visit the Storybook playground on `http://localhost:3002` {% endtab %} {% tab title="Without Docker" %} -Run the following command: + +After starting the application following the above guidelines open new terminal windows for each of these commands: ```bash -# in webapp/ -yarn storybook +# run eslint +$ yarn lint ``` -Open http://localhost:3002/ in your browser +```bash +# run unit tests +$ yarn test +``` + +```bash +# start storybook +$ yarn storybook +``` + +You can then visit the Storybook playground on `http://localhost:3002` {% endtab %} {% endtabs %} +## Styleguide Migration +We are currently in the process of migrating our styleguide components and design tokens from the [Nitro Styleguide](https://github.com/Human-Connection/Nitro-Styleguide) into the main [Human Connection repository](https://github.com/Human-Connection/Human-Connection) and refactoring our components in the process. During this migration our new components will live in a `view` folder to separate them from the old, yet untouched components. -## Styleguide +### Folder Structure -All reusable Components \(for example avatar\) should be done inside the [Nitro-Styleguide](https://github.com/Human-Connection/Nitro-Styleguide) repository. +The folder structure we are aiming for is based on the [directory setup proposed by Nuxt.js](https://nuxtjs.org/guide/directory-structure): -![Styleguide Screenshot](../.gitbook/assets/screenshot-styleguide%20%281%29.png) - -More information can be found here: [https://github.com/Human-Connection/Nitro-Styleguide](https://github.com/Human-Connection/Nitro-Styleguide) - -If you need to change something in the styleguide and want to see the effects on the frontend immediately, then we have you covered. You need to clone the styleguide to the parent directory `../Nitro-Styleguide` and run `yarn && yarn run dev`. After that you run `yarn run dev:styleguide` instead of `yarn run dev` and you will see your changes reflected inside the frontend! +- **assets** contains icons, images and logos in `svg` format +- **components** are the generic building blocks of the app – small, reusable and usually not coupled to state +- **features** are composed of components but tied to a particular function of the app (e.g. `comment` or `post`) +- **pages** are the entry points for all `routes` in the app and are composed of features and components +- **styles** holds all shared SCSS files such as `variables` and `mixins` diff --git a/webapp/storybook.md b/webapp/storybook.md new file mode 100644 index 000000000..b987f2064 --- /dev/null +++ b/webapp/storybook.md @@ -0,0 +1,33 @@ +### Storybook + +We encourage contributors to use Storybook to test out new components in an isolated way, and benefit from its many features. +See the docs for live examples and answers to FAQ, among other helpful information. ![Storybook docs](https://storybook.js.org/docs/basics/introduction/) + +{% tabs %} +{% tab title="Docker" %} + +After you have started the application following the instructions above, in another terminal run: + +```bash +$ docker-compose exec webapp yarn storybook +``` +The output should look similar to this: + +![Storybook output](../.gitbook/assets/storybook-output.png) + +Click on the link http://localhost:3002/ to open the browser to your interactive storybook. + +{% endtab %} + +{% tab title="Without Docker" %} +Run the following command: + +```bash +# in webapp/ +yarn storybook +``` + +Open http://localhost:3002/ in your browser + +{% endtab %} +{% endtabs %} From 13f14aec25ae9fa3122f66a50def46b773ddb4c0 Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Tue, 29 Oct 2019 12:22:30 +0300 Subject: [PATCH 009/189] add component guidelines --- webapp/README.md | 5 +++-- webapp/components.md | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/webapp/README.md b/webapp/README.md index a7be4de97..5a92a644e 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -40,7 +40,7 @@ We ensure the quality of our frontend code by using - [Jest](https://jestjs.io/) and [Vue Test Utils](https://vue-test-utils.vuejs.org/) to unit test our components - [Storybook](https://storybook.js.org/) to manually test our components in an isolated playground -Use these commands to run the tests: +For more information see our [frontend testing guide](testing.md). Use these commands to run the tests: {% tabs %} {% tab title="With Docker" %} @@ -101,5 +101,6 @@ The folder structure we are aiming for is based on the [directory setup proposed - **assets** contains icons, images and logos in `svg` format - **components** are the generic building blocks of the app – small, reusable and usually not coupled to state - **features** are composed of components but tied to a particular function of the app (e.g. `comment` or `post`) -- **pages** are the entry points for all `routes` in the app and are composed of features and components +- **layouts** can use components to create templates for pages +- **pages** are the entry points for all `routes` in the app and are composed of layouts, features and components - **styles** holds all shared SCSS files such as `variables` and `mixins` diff --git a/webapp/components.md b/webapp/components.md index 92b3dd1fb..822ebe142 100644 --- a/webapp/components.md +++ b/webapp/components.md @@ -1,5 +1,24 @@ -# COMPONENTS +# Components – Code Guidelines -The components directory contains your Vue.js Components. +## We adhere to the [single responsibility principle](https://en.wikipedia.org/wiki/Single_responsibility_principle) -_Nuxt.js doesn't supercharge these components._ +Each component does _exactly one job_. The goal is to end up with many small components that are: +- easy to understand +- easy to maintain +- easy to reuse + +**How do you decide what is a separate component?** Try to describe what it does in _one sentence_! When you find yourself using `and` and `or` the code you are talking about should probably be split up into two or more components. + +On the other hand, when something is easily expressed in a few lines of HTML and SCSS and not likely to be reused this is a good indicator that it should _not_ go into a separate component. + +## We compose with components + +Usually `pages` use `layouts` as templates and will be composed of `features`. `features` are composed of `components`, the smallest building blocks of the app. The further down we go in this hierarchy the simpler and more generic the components become. Here is an example: + +- The `index` page is responsible for displaying a list of posts. It uses the `default` layout and the `PostList` feature. +- The `PostList` feature uses a `List` component to render `PostTeaser` features. +- The `PostTeaser` feature consists of a `Card` wrapped around a `CardImage`, `CardTitle` and `CardContent` component. + +The `index` page is unique in the app and will never be reused. The `PostList` knows it is handling post data and can therefore not be used for anything else – but it can display posts on the `index` as well as the `user` page. + +The `Card` on the other hand does not care about the type of data it needs to handle. It just takes whatever it receives and renders it in a certain way, so it can be reused throughout the app for many different features. From 05606bfdaf2fa3ecf1157e8be3153a4c9e8341c2 Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Tue, 29 Oct 2019 12:24:35 +0300 Subject: [PATCH 010/189] add html guidelines --- webapp/README.md | 2 +- webapp/html.md | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 webapp/html.md diff --git a/webapp/README.md b/webapp/README.md index 5a92a644e..b47f89c8d 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -101,6 +101,6 @@ The folder structure we are aiming for is based on the [directory setup proposed - **assets** contains icons, images and logos in `svg` format - **components** are the generic building blocks of the app – small, reusable and usually not coupled to state - **features** are composed of components but tied to a particular function of the app (e.g. `comment` or `post`) -- **layouts** can use components to create templates for pages +- **layouts** can use components to create layout templates for pages - **pages** are the entry points for all `routes` in the app and are composed of layouts, features and components - **styles** holds all shared SCSS files such as `variables` and `mixins` diff --git a/webapp/html.md b/webapp/html.md new file mode 100644 index 000000000..e2779c424 --- /dev/null +++ b/webapp/html.md @@ -0,0 +1,22 @@ +# HTML – Code Guidelines + +## We write semantic markup + +We avoid using `divs` and `spans` and try to choose more meaningful HTML elements instead. If unsure which element to use [this list by MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) can be of help. + +Why? +- semantic markup is crucial for accessibility +- it makes the code more readable for other developers +- it benefits our SEO + +For more background [see this article](https://css-tricks.com/why-how-and-when-to-use-semantic-html-and-aria/). + +This doesn’t mean you can’t ever use a `div` – just think twice before you do! + +## We write as little HTML as possible – and as much as necessary + +HTML is used to _structure content on the page_ and should therefore reflect its complexity. Not more and not less. Most content does not require deep nesting of HTML elements – if you find yourself wrapping `container` around `container` or adding an element just to correctly position another element on the page this calls for the use of CSS instead! + +Why? +- deep nesting makes it hard to understand, style and maintain components +- it can lead to performance issues From 7d2801a636dcbf4713a2e46349c64577517218aa Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 29 Oct 2019 11:05:49 +0100 Subject: [PATCH 011/189] after successful login the saved language of the user is set --- webapp/pages/login.vue | 11 +++++++++++ webapp/store/auth.js | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/webapp/pages/login.vue b/webapp/pages/login.vue index e969fe46f..4db3a7c3d 100644 --- a/webapp/pages/login.vue +++ b/webapp/pages/login.vue @@ -7,12 +7,18 @@ @@ -207,4 +217,10 @@ export default { top: 10px; z-index: 1; } +.crop-cancel { + position: absolute; + right: 10px; + top: 10px; + z-index: 1; +} From 99816f1c2130a427bfa199c3b01120b1bc8c5cbf Mon Sep 17 00:00:00 2001 From: Brent Vardy Date: Sat, 2 Nov 2019 12:09:28 +0000 Subject: [PATCH 045/189] update cancel crop to keep previous image - Also refactor to split into more methods Co-Authored-By: mattwr18 --- webapp/components/TeaserImage/TeaserImage.vue | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/webapp/components/TeaserImage/TeaserImage.vue b/webapp/components/TeaserImage/TeaserImage.vue index e3afafa3c..d7ba0e7d8 100644 --- a/webapp/components/TeaserImage/TeaserImage.vue +++ b/webapp/components/TeaserImage/TeaserImage.vue @@ -57,6 +57,7 @@ export default { editor: null, cropper: null, thumbnailElement: null, + oldImage: null, error: false, showCropper: false, } @@ -85,41 +86,47 @@ export default { transformImage(file) { this.file = file this.showCropper = true - // Create the image editor overlay this.initEditor() - - // Load the image + this.initCropper() + }, + initEditor() { + this.editor = this.$refs.cropperOverlay + this.clearImages() + this.thumbnailElement.appendChild(this.editor) + }, + clearImages() { + this.thumbnailElement = document.querySelectorAll('#postdropzone')[0] + const thumbnailPreview = document.querySelectorAll('.thumbnail-preview')[0] + if (thumbnailPreview) thumbnailPreview.remove() + const contributionImage = document.querySelectorAll('.contribution-image')[0] + this.oldImage = contributionImage + if (contributionImage) contributionImage.remove() + }, + initCropper() { this.image = new Image() this.image.src = URL.createObjectURL(this.file) this.editor.appendChild(this.image) this.cropper = new Cropper(this.image, { zoomable: false, autoCropArea: 0.9 }) }, - initEditor() { - let thumbnailPreview, contributionImage - this.editor = this.$refs.cropperOverlay - this.thumbnailElement = document.querySelectorAll('#postdropzone')[0] - thumbnailPreview = document.querySelectorAll('.thumbnail-preview')[0] - if (thumbnailPreview) thumbnailPreview.remove() - contributionImage = document.querySelectorAll('.contribution-image')[0] - if (contributionImage) contributionImage.remove() - this.thumbnailElement.appendChild(this.editor) - }, cropImage() { this.showCropper = false - let canvas = this.cropper.getCroppedCanvas() + const canvas = this.cropper.getCroppedCanvas() canvas.toBlob(blob => { - this.image = new Image() - this.image.src = canvas.toDataURL() - this.image.classList.add('thumbnail-preview') - this.thumbnailElement.appendChild(this.image) - // Remove the editor from view + this.setupPreview(canvas) this.removeCropper() const croppedImageFile = new File([blob], this.file.name, { type: 'image/jpeg' }) this.$emit('addTeaserImage', croppedImageFile) }, 'image/jpeg') }, + setupPreview(canvas) { + this.image = new Image() + this.image.src = canvas.toDataURL() + this.image.classList.add('thumbnail-preview') + this.thumbnailElement.appendChild(this.image) + }, cancelCrop() { this.showCropper = false + if (this.oldImage) this.thumbnailElement.appendChild(this.oldImage) this.removeCropper() }, removeCropper() { From 4e0b1d06e3255dcd72900be7d2e6b5f3c6c406e5 Mon Sep 17 00:00:00 2001 From: Ewald Arnold Date: Sat, 2 Nov 2019 16:18:18 +0100 Subject: [PATCH 046/189] added Russian to locales --- webapp/locales/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/webapp/locales/index.js b/webapp/locales/index.js index c4a21c724..28cbd27d0 100644 --- a/webapp/locales/index.js +++ b/webapp/locales/index.js @@ -1,4 +1,4 @@ -import { enUS, de, nl, fr, es, it, pt, pl } from 'date-fns/locale' +import { enUS, de, nl, fr, es, it, pt, pl, ru } from 'date-fns/locale' import find from 'lodash/find' const locales = [ @@ -58,6 +58,13 @@ const locales = [ enabled: true, dateFnsLocale: pl, }, + { + name: 'Русский', + code: 'ru', + iso: 'ru-RU', + enabled: true, + dateFnsLocale: ru, + }, ] export default locales From a78a58ad8dcda2f070ea87ae3a8cffff5fc80632 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Sat, 2 Nov 2019 17:02:19 +0100 Subject: [PATCH 047/189] Mock $t in TeaserImage test --- webapp/components/TeaserImage/TeaserImage.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/components/TeaserImage/TeaserImage.spec.js b/webapp/components/TeaserImage/TeaserImage.spec.js index 4043cb978..048a7914c 100644 --- a/webapp/components/TeaserImage/TeaserImage.spec.js +++ b/webapp/components/TeaserImage/TeaserImage.spec.js @@ -15,6 +15,7 @@ describe('TeaserImage.vue', () => { $toast: { error: jest.fn(), }, + $t: jest.fn(string => string), } }) describe('mount', () => { From c3d28af321c9e14d794dac6d92202b20f9c7b2d8 Mon Sep 17 00:00:00 2001 From: Tomas Bednarik Date: Sun, 3 Nov 2019 12:34:56 +0100 Subject: [PATCH 048/189] Clickable tags by nuxt-link --- webapp/components/Hashtag/Hashtag.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/webapp/components/Hashtag/Hashtag.vue b/webapp/components/Hashtag/Hashtag.vue index 91066eaf9..35762c81c 100644 --- a/webapp/components/Hashtag/Hashtag.vue +++ b/webapp/components/Hashtag/Hashtag.vue @@ -1,6 +1,6 @@ @@ -10,5 +10,10 @@ export default { props: { id: { type: String, required: true }, }, + computed: { + hashtagUrl() { + return `/?hashtag=${this.id}` + }, + }, } From 5fbe0da2ae9c43c82861036f6526507206a88c32 Mon Sep 17 00:00:00 2001 From: Ewald Arnold Date: Sun, 3 Nov 2019 17:31:10 +0100 Subject: [PATCH 049/189] add empty russian template --- webapp/locales/ru.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 webapp/locales/ru.json diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json new file mode 100644 index 000000000..e69de29bb From 97bd1b4816cb0eb61c733aa451af9140a73efe55 Mon Sep 17 00:00:00 2001 From: Alexander Friedland Date: Sun, 3 Nov 2019 19:12:20 +0100 Subject: [PATCH 050/189] in ru.json i started this once in ru.json i started this once. as a template you can use german or english. both languages are complete. you can copy and paste the ru.json. what is not translated. default will be shown in english. --- webapp/locales/ru.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index e69de29bb..6f97cdf10 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -0,0 +1,28 @@ +{ + "components": { + "password-reset": { + "request": { + "title": "Сбросить пароль", + "form": { + "description": "На указанный адрес электронной почты будет отправлено письмо для сброса пароля.", + "submit": "Запросить электронную почту", + "submitted": "На {email} отправлено письмо с дополнительными инструкциями." + } + }, + "change-password": { + "success": "Смена пароля прошла успешно!", + "error": "Смена пароля не удалась. Может быть, код безопасности был неверным?", + "help": "В случае возникновения проблем, не стесняйтесь обратиться за помощью, отправив нам письмо по адресу:" + } + }, + "enter-nonce": { + "form": { + "nonce": "Введите ваш код", + "description": "Откройте свой почтовый ящик и введите код, который мы вам отправили.", + "next": "Продолжить", + "validations": { + "length": "должно быть длиной 6 символов" + } + } + } + } From 60c2bba2718cda5507024ea1224f5f025657ef0e Mon Sep 17 00:00:00 2001 From: Alexander Friedland Date: Sun, 3 Nov 2019 19:14:44 +0100 Subject: [PATCH 051/189] fix missing bracket fix missing bracket --- webapp/locales/ru.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index 6f97cdf10..a103ef7a9 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -25,4 +25,5 @@ } } } - } + } +} From c7ef9ba04a3fc8a6afd9b0066a813e02430f4bf4 Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Mon, 4 Nov 2019 11:45:17 +0300 Subject: [PATCH 052/189] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: mattwr18 Co-Authored-By: Robert Schäfer --- webapp/README.md | 6 +++--- webapp/testing.md | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/webapp/README.md b/webapp/README.md index 9f44267c2..b692b3d9c 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -45,7 +45,7 @@ For more information see our [frontend testing guide](testing.md). Use these com {% tabs %} {% tab title="With Docker" %} -After starting the application following the above guidelines open new terminal windows for each of these commands: +After starting the application following the above guidelines, open new terminal windows for each of these commands: ```bash # run eslint @@ -68,7 +68,7 @@ You can then visit the Storybook playground on `http://localhost:3002` {% tab title="Without Docker" %} -After starting the application following the above guidelines open new terminal windows for each of these commands: +After starting the application following the above guidelines, open new terminal windows for each of these commands: ```bash # run eslint @@ -92,7 +92,7 @@ You can then visit the Storybook playground on `http://localhost:3002` ## Styleguide Migration -We are currently in the process of migrating our styleguide components and design tokens from the [Nitro Styleguide](https://github.com/Human-Connection/Nitro-Styleguide) into the main [Human Connection repository](https://github.com/Human-Connection/Human-Connection) and refactoring our components in the process. During this migration our new components will live in a `view` folder to separate them from the old, yet untouched components. +We are currently in the process of migrating our styleguide components and design tokens from the [Nitro Styleguide](https://github.com/Human-Connection/Nitro-Styleguide) into the main [Human Connection repository](https://github.com/Human-Connection/Human-Connection) and refactoring our components in the process. During this migration, our new components will live in a `view` folder to separate them from the old, yet untouched components. ### Folder Structure diff --git a/webapp/testing.md b/webapp/testing.md index 9e530c4bf..fb300e14f 100644 --- a/webapp/testing.md +++ b/webapp/testing.md @@ -13,6 +13,7 @@ We write unit tests with the help of [Jest](https://jestjs.io/) and [Vue Test Ut To run all tests use the command `yarn test` in the `/webapp` directory. Other useful commands are: - `yarn test -t test-name` to run tests including `test-name` in their file or test names - `yarn test -o` to run tests related to files that have been changed since the latest commit +- `yarn run path/to/component.spec.js` to run a single test file ## Documentation and manual testing @@ -20,17 +21,17 @@ To run all tests use the command `yarn test` in the `/webapp` directory. Other u ### Component documentation -With Storybook our components can be documented in detail and offer a visual reference to other developers. When all components are properly documented Storybook can be used as a big component library – where developers can browse through design tokens and components and immediately verify that the component offers the desired functionality. +With Storybook our components can be documented in detail and offer a visual reference to other developers. When all components are properly documented, Storybook can be used as a big component library – where developers can browse through design tokens and components and immediately verify that the component offers the desired functionality. ### Manual testing in an isolated environment -When adding new components or changing existing ones Storybook can be helpful not only to document the feature for future use but also to test different use cases (e.g. by passing different types of `props`) in an isolated playground. +When adding new components or changing existing ones, Storybook can be helpful not only to document the feature for future use, but also to test different use cases (e.g. by passing different types of `props`) in an isolated playground. -With the right addons Storybook also gives immediate feedback on how well the component complies with accessibility guidelines. +With the right addons, Storybook also gives immediate feedback on how well the component complies with accessibility guidelines. ------ -To run Storybook first start the app, then enter the following command in a new terminal window: `yarn storybook`. The output should look similar to this: +To run Storybook, first start the app, then enter the following command in a new terminal window: `yarn storybook`. The output should look similar to this: ![Storybook output](../.gitbook/assets/storybook-output.png) From 9caf3e7d8a270c04358d947fe45aaa2e7f6f6e6a Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Mon, 4 Nov 2019 12:19:53 +0300 Subject: [PATCH 053/189] follow even more suggestions by @mattwr18 --- webapp/README.md | 8 ++++---- webapp/vue.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webapp/README.md b/webapp/README.md index b692b3d9c..def0b739e 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -68,20 +68,20 @@ You can then visit the Storybook playground on `http://localhost:3002` {% tab title="Without Docker" %} -After starting the application following the above guidelines, open new terminal windows for each of these commands: +After starting the application following the above guidelines, open new terminal windows and navigate to the `/webapp` directory for each of these commands: ```bash -# run eslint +# run eslint in /webapp $ yarn lint ``` ```bash -# run unit tests +# run unit tests in /webapp $ yarn test ``` ```bash -# start storybook +# start storybook in /webapp $ yarn storybook ``` diff --git a/webapp/vue.md b/webapp/vue.md index 47d0620f0..5116bd713 100644 --- a/webapp/vue.md +++ b/webapp/vue.md @@ -15,9 +15,9 @@ Placed in the same folder are also: ## We use typed props -Vue.js allows us to define component props either as strings or as objects with `type`, `default` and `required` values. Always go for the second option! +Vue.js allows us to define component props either as strings or as objects (with `type` and `default` or `required` values). Always go for the second option! -Also: define defaults _only and always_ for non-required props. +Also: only (and always!) define a `default` for props that are _not required_. Why? - it makes our code more robust – a warning will be shown when passing a wrong prop type From a05d63e34e75bcb7fadfd5fe723603eebba3dd6c Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Mon, 4 Nov 2019 12:38:45 +0100 Subject: [PATCH 054/189] v0.1.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ceb433da..d6b6e77a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nitro-cypress", - "version": "0.1.8", + "version": "0.1.9", "description": "Fullstack tests with cypress for Human Connection", "author": "Human Connection gGmbh", "license": "MIT", From 0644380afc26d41ca1698257b83560a2de5d2443 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Mon, 4 Nov 2019 12:39:18 +0100 Subject: [PATCH 055/189] Update to version 0.1.9 --- CHANGELOG.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++-- VERSION | 2 +- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f41d1062..580e9e333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,72 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [0.1.8](https://github.com/Human-Connection/Human-Connection/compare/0.1.7...0.1.8) +#### [v0.1.9](https://github.com/Human-Connection/Human-Connection/compare/v0.1.8...v0.1.9) + +> 4 November 2019 + +- Refactor and tidy up crop image implementation [`#1956`](https://github.com/Human-Connection/Human-Connection/pull/1956) +- 🍰 First Implementation Of Filtering Posts By Language [`#2059`](https://github.com/Human-Connection/Human-Connection/pull/2059) +- build(deps-dev): bump cypress from 3.5.0 to 3.6.0 [`#2105`](https://github.com/Human-Connection/Human-Connection/pull/2105) +- Fix: Poll Interval [`#2108`](https://github.com/Human-Connection/Human-Connection/pull/2108) +- build(deps-dev): bump cypress-plugin-retries from 1.3.0 to 1.4.0 [`#2104`](https://github.com/Human-Connection/Human-Connection/pull/2104) +- build(deps): bump metascraper-title from 5.7.14 to 5.7.17 in /backend [`#2082`](https://github.com/Human-Connection/Human-Connection/pull/2082) +- build(deps): bump metascraper-publisher from 5.7.14 to 5.7.17 in /backend [`#2098`](https://github.com/Human-Connection/Human-Connection/pull/2098) +- build(deps): bump metascraper-description from 5.7.14 to 5.7.17 in /backend [`#2096`](https://github.com/Human-Connection/Human-Connection/pull/2096) +- build(deps): bump metascraper-audio from 5.7.14 to 5.7.17 in /backend [`#2097`](https://github.com/Human-Connection/Human-Connection/pull/2097) +- build(deps): bump metascraper-url from 5.7.14 to 5.7.17 in /backend [`#2086`](https://github.com/Human-Connection/Human-Connection/pull/2086) +- build(deps): bump metascraper-image from 5.7.6 to 5.7.17 in /backend [`#2090`](https://github.com/Human-Connection/Human-Connection/pull/2090) +- deleted posts no longer displayed in user profile [`#2093`](https://github.com/Human-Connection/Human-Connection/pull/2093) +- Send only one notification for mention and comment [`#2062`](https://github.com/Human-Connection/Human-Connection/pull/2062) +- build(deps): bump metascraper-date from 5.7.14 to 5.7.17 in /backend [`#2089`](https://github.com/Human-Connection/Human-Connection/pull/2089) +- build(deps): bump @nuxtjs/apollo from 4.0.0-rc16 to 4.0.0-rc17 in /webapp [`#2088`](https://github.com/Human-Connection/Human-Connection/pull/2088) +- build(deps): bump metascraper-lang from 5.7.14 to 5.7.17 in /backend [`#2087`](https://github.com/Human-Connection/Human-Connection/pull/2087) +- build(deps): bump metascraper-video from 5.7.14 to 5.7.17 in /backend [`#2084`](https://github.com/Human-Connection/Human-Connection/pull/2084) +- build(deps): bump metascraper-soundcloud from 5.7.14 to 5.7.17 in /backend [`#2081`](https://github.com/Human-Connection/Human-Connection/pull/2081) +- build(deps-dev): bump auto-changelog from 1.16.1 to 1.16.2 [`#2085`](https://github.com/Human-Connection/Human-Connection/pull/2085) +- build(deps): bump metascraper-logo from 5.7.14 to 5.7.17 in /backend [`#2083`](https://github.com/Human-Connection/Human-Connection/pull/2083) +- build(deps): bump metascraper-youtube from 5.7.14 to 5.7.17 in /backend [`#2080`](https://github.com/Human-Connection/Human-Connection/pull/2080) +- build(deps): bump metascraper-author from 5.7.14 to 5.7.17 in /backend [`#2079`](https://github.com/Human-Connection/Human-Connection/pull/2079) +- build(deps): bump date-fns from 2.5.0 to 2.6.0 in /webapp [`#2007`](https://github.com/Human-Connection/Human-Connection/pull/2007) +- build(deps-dev): bump eslint from 6.5.1 to 6.6.0 in /backend [`#2071`](https://github.com/Human-Connection/Human-Connection/pull/2071) +- build(deps): bump node from 12.13.0-alpine to 13.0.1-alpine in /webapp [`#2019`](https://github.com/Human-Connection/Human-Connection/pull/2019) +- [FIX # 2058] Typo Fixed. Password -> Passwort [`#2060`](https://github.com/Human-Connection/Human-Connection/pull/2060) +- build(deps-dev): bump eslint-config-prettier from 6.4.0 to 6.5.0 in /backend [`#2064`](https://github.com/Human-Connection/Human-Connection/pull/2064) +- build(deps): bump date-fns from 2.5.1 to 2.6.0 in /backend [`#2010`](https://github.com/Human-Connection/Human-Connection/pull/2010) +- build(deps-dev): bump apollo-server-testing from 2.9.6 to 2.9.7 in /backend [`#1984`](https://github.com/Human-Connection/Human-Connection/pull/1984) +- build(deps): bump metascraper-date from 5.7.6 to 5.7.14 in /backend [`#2070`](https://github.com/Human-Connection/Human-Connection/pull/2070) +- build(deps): bump metascraper-video from 5.7.6 to 5.7.14 in /backend [`#2072`](https://github.com/Human-Connection/Human-Connection/pull/2072) +- build(deps-dev): bump eslint-plugin-jest from 22.20.0 to 23.0.2 in /backend [`#2069`](https://github.com/Human-Connection/Human-Connection/pull/2069) +- build(deps): bump metascraper-audio from 5.7.6 to 5.7.14 in /backend [`#2068`](https://github.com/Human-Connection/Human-Connection/pull/2068) +- build(deps-dev): bump eslint-plugin-jest from 23.0.0 to 23.0.2 in /webapp [`#2066`](https://github.com/Human-Connection/Human-Connection/pull/2066) +- build(deps-dev): bump cucumber from 6.0.2 to 6.0.3 in /backend [`#2065`](https://github.com/Human-Connection/Human-Connection/pull/2065) +- build(deps): bump metascraper-logo from 5.7.6 to 5.7.14 in /backend [`#2039`](https://github.com/Human-Connection/Human-Connection/pull/2039) +- build(deps): bump metascraper-url from 5.7.6 to 5.7.14 in /backend [`#2053`](https://github.com/Human-Connection/Human-Connection/pull/2053) +- build(deps): bump metascraper-youtube from 5.7.6 to 5.7.14 in /backend [`#2054`](https://github.com/Human-Connection/Human-Connection/pull/2054) +- build(deps): bump neo4j-graphql-js from 2.7.2 to 2.8.0 in /backend [`#2036`](https://github.com/Human-Connection/Human-Connection/pull/2036) +- build(deps): bump metascraper-soundcloud from 5.7.7 to 5.7.14 in /backend [`#2052`](https://github.com/Human-Connection/Human-Connection/pull/2052) +- build(deps): bump metascraper-author from 5.7.6 to 5.7.14 in /backend [`#2055`](https://github.com/Human-Connection/Human-Connection/pull/2055) +- build(deps-dev): bump eslint-plugin-jest from 22.20.0 to 23.0.0 in /webapp [`#2051`](https://github.com/Human-Connection/Human-Connection/pull/2051) +- build(deps): bump metascraper-title from 5.7.6 to 5.7.14 in /backend [`#2050`](https://github.com/Human-Connection/Human-Connection/pull/2050) +- build(deps-dev): bump eslint-config-prettier from 6.4.0 to 6.5.0 in /webapp [`#2049`](https://github.com/Human-Connection/Human-Connection/pull/2049) +- build(deps): bump metascraper-description from 5.7.6 to 5.7.14 in /backend [`#2038`](https://github.com/Human-Connection/Human-Connection/pull/2038) +- build(deps-dev): bump node-sass from 4.12.0 to 4.13.0 in /webapp [`#2037`](https://github.com/Human-Connection/Human-Connection/pull/2037) +- build(deps): bump metascraper-publisher from 5.7.6 to 5.7.14 in /backend [`#2033`](https://github.com/Human-Connection/Human-Connection/pull/2033) +- build(deps): bump apollo-server from 2.9.6 to 2.9.7 in /backend [`#2004`](https://github.com/Human-Connection/Human-Connection/pull/2004) +- fix #1993 [`#2043`](https://github.com/Human-Connection/Human-Connection/pull/2043) +- Update to version 0.1.8 [`#2032`](https://github.com/Human-Connection/Human-Connection/pull/2032) +- build(deps): bump graphql-shield from 6.1.0 to 7.0.0 in /backend [`#2035`](https://github.com/Human-Connection/Human-Connection/pull/2035) +- build(deps): bump metascraper-lang from 5.7.6 to 5.7.14 in /backend [`#2034`](https://github.com/Human-Connection/Human-Connection/pull/2034) +- change Changes & History to Changes [`#2030`](https://github.com/Human-Connection/Human-Connection/pull/2030) +- chnage Änderungen & Verlauf zu Änderungen [`#2029`](https://github.com/Human-Connection/Human-Connection/pull/2029) +- build(deps): bump @nuxtjs/apollo from 4.0.0-rc15 to 4.0.0-rc16 in /webapp [`#1990`](https://github.com/Human-Connection/Human-Connection/pull/1990) +- Merge pull request #2043 from Human-Connection/fix-1993 [`#1993`](https://github.com/Human-Connection/Human-Connection/issues/1993) +- fix #1993 [`#1993`](https://github.com/Human-Connection/Human-Connection/issues/1993) +- first implementation [`aeae72f`](https://github.com/Human-Connection/Human-Connection/commit/aeae72f6918861aa2a4c64d0b32c847d9e857e93) +- build(deps-dev): bump eslint-plugin-jest in /backend [`6c1bd53`](https://github.com/Human-Connection/Human-Connection/commit/6c1bd535ac482eb0a05d21e227a476800717a19e) +- Add auto changelog [`6f4517b`](https://github.com/Human-Connection/Human-Connection/commit/6f4517b0e9d832abab271471cedeea0aa00f4d43) + +#### [v0.1.8](https://github.com/Human-Connection/Human-Connection/compare/0.1.7...v0.1.8) > 25 October 2019 @@ -23,8 +88,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - build(deps-dev): bump @vue/cli-shared-utils from 4.0.4 to 4.0.5 in /webapp [`#2002`](https://github.com/Human-Connection/Human-Connection/pull/2002) - Update to version 0.1.7 [`#2015`](https://github.com/Human-Connection/Human-Connection/pull/2015) - Update to version 0.1.8 [`d45264b`](https://github.com/Human-Connection/Human-Connection/commit/d45264b3afa1557c2205e7ca1b77c778ee37ab5a) +- build(deps): bump @nuxtjs/apollo in /webapp [`26c21b5`](https://github.com/Human-Connection/Human-Connection/commit/26c21b5b76c96206d98ff6bbfdbd1ca973ffcd4f) - build(deps-dev): bump @storybook/addon-actions in /webapp [`7e95d37`](https://github.com/Human-Connection/Human-Connection/commit/7e95d376a311a5ede6351d577d30e25aea9cb65d) -- new terms and conditions fixed [`ff8680f`](https://github.com/Human-Connection/Human-Connection/commit/ff8680ff862846cf619423007809ea3139cada96) #### [0.1.7](https://github.com/Human-Connection/Human-Connection/compare/0.1.6...0.1.7) diff --git a/VERSION b/VERSION index 699c6c6d4..1a030947e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.8 +0.1.9 From 24143f835033da306523bf9610b54e66094be6e8 Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Mon, 4 Nov 2019 16:27:36 +0300 Subject: [PATCH 056/189] ask for a more detailed description in the feature template --- .github/ISSUE_TEMPLATE/feature_request.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 1fba3fa58..ef3b30be2 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -6,11 +6,19 @@ title: 🚀 [Feature] --- ## :rocket: Feature - + + +### User Problem + + +### Implementation + ### Design & Layout - + +### Validation + ### Additional context - + From a704709ba3ec9e8013f9af4f8141a712a52e5aa6 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Mon, 4 Nov 2019 18:52:34 +0100 Subject: [PATCH 057/189] fix: resolver returned undefined for `nameRU` This registers the language Russian in the backend. Unfortunately, the locations seem to be implemented with a hard coded attribute for each language. :disappointed: We should refactor this. --- backend/src/middleware/nodes/locations.js | 4 +++- backend/src/models/Location.js | 1 + backend/src/schema/types/Location.gql | 1 + backend/src/seed/seed-db.js | 5 +++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/src/middleware/nodes/locations.js b/backend/src/middleware/nodes/locations.js index a90d8c0d7..e72869cb6 100644 --- a/backend/src/middleware/nodes/locations.js +++ b/backend/src/middleware/nodes/locations.js @@ -19,7 +19,7 @@ const fetch = url => { }) } -const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl'] +const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl', 'ru'] const createLocation = async (session, mapboxData) => { const data = { @@ -32,6 +32,7 @@ const createLocation = async (session, mapboxData) => { nameES: mapboxData.text_es, namePT: mapboxData.text_pt, namePL: mapboxData.text_pl, + nameRU: mapboxData.text_ru, type: mapboxData.id.split('.')[0].toLowerCase(), lat: mapboxData.center && mapboxData.center.length ? mapboxData.center[0] : null, lng: mapboxData.center && mapboxData.center.length ? mapboxData.center[1] : null, @@ -48,6 +49,7 @@ const createLocation = async (session, mapboxData) => { 'l.nameES = $nameES, ' + 'l.namePT = $namePT, ' + 'l.namePL = $namePL, ' + + 'l.nameRU = $nameRU, ' + 'l.type = $type' if (data.lat && data.lng) { diff --git a/backend/src/models/Location.js b/backend/src/models/Location.js index bd6e0b5d9..2c62877f7 100644 --- a/backend/src/models/Location.js +++ b/backend/src/models/Location.js @@ -12,6 +12,7 @@ module.exports = { nameDE: { type: 'string' }, nameNL: { type: 'string' }, namePL: { type: 'string' }, + nameRU: { type: 'string' }, isIn: { type: 'relationship', relationship: 'IS_IN', diff --git a/backend/src/schema/types/Location.gql b/backend/src/schema/types/Location.gql index e7053e345..78bc07656 100644 --- a/backend/src/schema/types/Location.gql +++ b/backend/src/schema/types/Location.gql @@ -9,6 +9,7 @@ type Location { nameES: String namePT: String namePL: String + nameRU: String type: String! lat: Float lng: Float diff --git a/backend/src/seed/seed-db.js b/backend/src/seed/seed-db.js index 76fbb4875..1fc5af85a 100644 --- a/backend/src/seed/seed-db.js +++ b/backend/src/seed/seed-db.js @@ -39,6 +39,7 @@ import { gql } from '../jest/helpers' nameDE: 'Hamburg', nameNL: 'Hamburg', namePL: 'Hamburg', + nameRU: 'Гамбург', }), factory.create('Location', { id: 'region.14880313158564380', @@ -54,6 +55,7 @@ import { gql } from '../jest/helpers' nameDE: 'Berlin', nameNL: 'Berlijn', namePL: 'Berlin', + nameRU: 'Берлин', }), factory.create('Location', { id: 'country.10743216036480410', @@ -67,6 +69,7 @@ import { gql } from '../jest/helpers' nameFR: 'Allemagne', nameIT: 'Germania', nameEN: 'Germany', + nameRU: 'Германия', }), factory.create('Location', { id: 'region.9397217726497330', @@ -82,6 +85,7 @@ import { gql } from '../jest/helpers' nameDE: 'Paris', nameNL: 'Parijs', namePL: 'Paryż', + nameRU: 'Париж', }), factory.create('Location', { id: 'country.9759535382641660', @@ -95,6 +99,7 @@ import { gql } from '../jest/helpers' nameFR: 'France', nameIT: 'Francia', nameEN: 'France', + nameRU: 'Франция', }), ]) await Promise.all([ From 7e070208e0d3abd8510881066db2ed28de4d6bb0 Mon Sep 17 00:00:00 2001 From: ppelegrin Date: Mon, 4 Nov 2019 20:49:50 +0100 Subject: [PATCH 058/189] Continue portuguese translations --- webapp/locales/pt.json | 290 ++++++++++++++++++++--------------------- 1 file changed, 145 insertions(+), 145 deletions(-) diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 8877fe6a7..0b0b235f8 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -134,263 +134,263 @@ "follow": "Seguir", "followers": "Seguidores", "following": "Seguindo", - "shouted": "Aclamou", + "shouted": "Anunciou", "commented": "Comentou", - "userAnonym": "Anonymous", - "socialMedia": "Where else can I find", + "userAnonym": "Anônimo", + "socialMedia": "Onde mais posso encontrar", "network": { - "title": "Network", - "following": "is following:", - "followingNobody": "follows nobody.", - "followedBy": "is followed by:", - "followedByNobody": "is not followed by anyone.", - "andMore": "and {number} more …" + "title": "Rede", + "following": "está seguindo:", + "followingNobody": "não segue ninguém.", + "followedBy": "é seguido por:", + "followedByNobody": "não é seguido por ninguém.", + "andMore": "e {number} mais …" }, "invites": { - "title": "Invite somebody to Human Connection!", - "description": "Enter thier email address for invitation.", - "emailPlaceholder": "Email to invite" + "title": "Convidar alguém para Human Connection!", + "description": "Digite o endereço de e-mail para o convite.", + "emailPlaceholder": "E-mail para convidar" } }, "notifications": { "reason": { - "mentioned_in_post": "Mentioned you in a post …", - "mentioned_in_comment": "Mentioned you in a comment …", - "commented_on_post": "Commented on your post …" + "mentioned_in_post": "Mencionou você em um post …", + "mentioned_in_comment": "Mencionou você em um comentário …", + "commented_on_post": "Comentou no seu post …" }, - "comment": "Comment" + "comment": "Comentário" }, "search": { - "placeholder": "Search", - "hint": "What are you searching for?", - "failed": "Nothing found" + "placeholder": "Pesquisar", + "hint": "O que você está pesquisando??", + "failed": "Nada foi encontrado" }, "settings": { - "name": "Settings", + "name": "Configurações", "data": { - "name": "Your data", - "labelName": "Your Name", - "labelSlug": "Your unique user name", + "name": "Seus dados", + "labelName": "Seu Nome", + "labelSlug": "Seu nome de usuário exclusivo", "namePlaceholder": "Femanon Funny", - "labelCity": "Your City or Region", - "labelBio": "About You", - "success": "Your data was successfully updated!" + "labelCity": "Sua cidade ou região", + "labelBio": "Sobre você", + "success": "Seus dados foram atualizados com sucesso!" }, "email": { "validation": { - "same-email": "This is your current email address" + "same-email": "Este é o seu endereço de e-mail atual" }, - "name": "Your email", - "labelEmail": "Change your email address", - "labelNewEmail": "New email Address", - "labelNonce": "Enter your code", - "success": "A new email address has been registered.", - "submitted": "An email to verify your address has been sent to {email}.", - "change-successful": "Your email address has been changed successfully.", + "name": "Seu email", + "labelEmail": "Alterar o seu endereço de e-mail", + "labelNewEmail": "Novo endereço de e-mail", + "labelNonce": "Digite o seu código", + "success": "Um novo endereço de e-mail foi registrado.", + "submitted": "Um e-mail para verificar o seu endereço foi enviado para {email}.", + "change-successful": "O seu endereço de e-mail foi alterado com sucesso.", "verification-error": { - "message": "Your email could not be changed.", - "explanation": "This can have different causes:", + "message": "O seu e-mail não pode ser alterado.", + "explanation": "Isto pode ter diferentes causas:", "reason": { - "invalid-nonce": "Is the confirmation code invalid?", - "no-email-request": "Are you certain that you requested a change of your email address?" + "invalid-nonce": "O código de confirmação esta inválido?", + "no-email-request": "Você tem certeza de que solicitou uma alteração no seu endereço de e-mail?" }, - "support": "If the problem persists, please contact us by email at" + "support": "Se o problema persistir, por favor contacte-nos por e-mail" } }, "validation": { "slug": { - "regex": "Allowed characters are only lowercase letters, numbers, underscores and hyphens.", - "alreadyTaken": "This user name is already taken." + "regex": "Os caracteres permitidos são apenas letras minúsculas, números, sublinhados e hífens.", + "alreadyTaken": "Este nome de usuário já está registrado." } }, "security": { - "name": "Security", + "name": "Segurança", "change-password": { - "button": "Change password", - "success": "Password successfully changed!", - "label-old-password": "Your old password", - "label-new-password": "Your new password", - "label-new-password-confirm": "Confirm new password", - "message-old-password-required": "Enter your old password", - "message-new-password-required": "Enter a new password", - "message-new-password-confirm-required": "Confirm your new password", - "message-new-password-missmatch": "Type the same password again", - "passwordSecurity": "Password security", - "passwordStrength0": "Very insecure password", - "passwordStrength1": "Insecure password", - "passwordStrength2": "Mediocre password", - "passwordStrength3": "Strong password", - "passwordStrength4": "Very strong password" + "button": "Alterar senha", + "success": "Senha alterada com sucesso!", + "label-old-password": "Sua senha antiga", + "label-new-password": "Sua nova senha", + "label-new-password-confirm": "Confirme sua nova senha", + "message-old-password-required": "Digite sua senha antiga", + "message-new-password-required": "Digite uma nova senha", + "message-new-password-confirm-required": "Confirme sua nova senha", + "message-new-password-missmatch": "Digite a mesma senha novamente", + "passwordSecurity": "Segurança da senha", + "passwordStrength0": "Senha muito insegura", + "passwordStrength1": "Senha insegura", + "passwordStrength2": "Senha medíocre", + "passwordStrength3": "Senha forte", + "passwordStrength4": "Senha muito forte" } }, "invites": { - "name": "Invites" + "name": "Convites" }, "download": { - "name": "Download Data" + "name": "Baixar Dados" }, "deleteUserAccount": { - "name": "Delete data", - "contributionsCount": "Delete my {count} posts", - "commentedCount": "Delete my {count} comments", - "accountDescription": "Be aware that your Post and Comments are important to our community. If you still choose to delete them, you have to mark them below.", - "accountWarning": "You CAN'T MANAGE and CAN'T RECOVER your Account, Posts, or Comments after deleting your account!", - "success": "Account successfully deleted!", - "pleaseConfirm": "Destructive action! Type {confirm} to confirm" + "name": "Deletar dados", + "contributionsCount": "Deletar meus {count} posts", + "commentedCount": "Deletar meus {count} comentários", + "accountDescription": "Esteja ciente de que o seus Posts e Comentários são importantes para a nossa comunidade. Se você ainda optar por excluí-los, você tem que marcá-los abaixo.", + "accountWarning": "Você NÃO PODE GERENCIAR e NÃO PODE RECUPERAR sua conta, Posts, ou Comentários após excluir sua conta!", + "success": "Conta eliminada com sucesso!", + "pleaseConfirm": "Ação destrutiva! Digitar {confirm} para confirmar" }, "embeds": { - "name": "Third party providers", - "info-description": "Here is the list of third-party providers whose content can be displayed as third-party code, e.g. in the form of embedded videos.", + "name": "Fornecedores de terceiros", + "info-description": "Aqui está a lista de fornecedores terceiros, cujo conteúdo pode ser exibido como código de terceiros, por exemplo, sob a forma de vídeos incorporados.", "status": { - "description": "As a default for you, embedded code of third-party providers is", + "description": "Como padrão para você, o código incorporado de provedores de terceiros é", "disabled": { - "off": "initially not displayed", - "on": "displayed immediately" + "off": "não exibido inicialmente", + "on": "exibido inicialmente" }, "change": { - "question": "Should embedded source code from third parties always be displayed to you?", - "allow": "Sure", - "deny": "No thanks" + "question": "O código-fonte incorporado de terceiros deve sempre ser exibido para você?", + "allow": "Sim", + "deny": "Não, obrigado" } } }, "organizations": { - "name": "My Organizations" + "name": "Minhas Organizações" }, "languages": { - "name": "Languages" + "name": "Linguagens" }, "social-media": { - "name": "Social media", - "placeholder": "Your social media url", - "requireUnique": "You added this url already", - "submit": "Add link", - "successAdd": "Added social media. Updated user profile!", - "successDelete": "Deleted social media. Updated user profile!" + "name": "Mídias sociais", + "placeholder": "Sua url de mídia social", + "requireUnique": "Você já adicionou esta url", + "submit": "Adicionar link", + "successAdd": "Mídias sociais adicionadas. Perfil de usuário atualizado!", + "successDelete": "Mídias sociais removidas. Perfil de usuário atualizado!" }, "blocked-users": { - "name": "Blocked users", + "name": "Usuários bloqueados", "explanation": { - "intro": "If another user has been blocked by you, this is what happens:", - "your-perspective": "The blocked person's posts will no longer appear in your news feed.", - "their-perspective": "Vice versa: The blocked person will also no longer see your posts in their news feed.", - "search": "Posts of blocked people disappear from your search results.", - "notifications": "Blocked users will no longer receive notifications if they are mentioned in your posts.", - "closing": "This should be sufficient for now so that blocked users can no longer bother you." + "intro": "Se outro usuário foi bloqueado por você, isto é o que acontece:", + "your-perspective": "As mensagens da pessoa bloqueada não aparecerão mais no seu feed de notícias.", + "their-perspective": "Vice versa: A pessoa bloqueada também não verá mais suas mensagens em seu feed de notícias.", + "search": "Posts de pessoas bloqueadas desaparecem dos resultados da sua pesquisa.", + "notifications": "Usuários bloqueados não receberão mais notificações se forem mencionados em suas mensagens.", + "closing": "Isso deve ser suficiente por enquanto para que os usuários bloqueados não possam mais incomodá-lo." }, "columns": { - "name": "Name", + "name": "Nome", "slug": "Slug" }, - "empty": "So far, you have not blocked anybody.", - "how-to": "You can block other users on their profile page via the content menu.", - "block": "Block user", - "unblock": "Unblock user" + "empty": "Até agora, você não bloqueou ninguém.", + "how-to": "Você pode bloquear outros usuários em suas páginas de perfil através do menu de conteúdo.", + "block": "Bloquear usuário", + "unblock": "Desbloquear usuário" } }, "admin": { - "name": "Admin", + "name": "Administração", "dashboard": { - "name": "Dashboard", - "users": "Users", - "posts": "Posts", - "comments": "Comments", - "notifications": "Notifications", - "organizations": "Organizations", - "projects": "Projects", - "invites": "Invites", - "follows": "Follows", - "shouts": "Shouts" + "name": "Painel de controle", + "users": "Usuários", + "posts": "Postagens", + "comments": "Comentários", + "notifications": "Notificações", + "organizations": "Organizações", + "projects": "Projetos", + "invites": "Convites", + "follows": "Segue", + "shouts": "Recomendar" }, "organizations": { - "name": "Organizations" + "name": "Organizações" }, "users": { - "name": "Users", + "name": "Usuários", "form": { - "placeholder": "email, name or description" + "placeholder": "e-mail, nome ou descrição" }, "table": { "columns": { - "number": "No.", - "name": "Name", + "number": "N.º", + "name": "Nome", "email": "E-mail", "slug": "Slug", - "role": "Role", - "createdAt": "Created at" + "role": "Função", + "createdAt": "Criado em" } }, - "empty": "No users found" + "empty": "Nenhum usuário encontrado" }, "pages": { - "name": "Pages" + "name": "Páginas" }, "notifications": { - "name": "Notifications" + "name": "Notificações" }, "categories": { - "name": "Categories", - "categoryName": "Name", + "name": "Categorias", + "categoryName": "Nome", "postCount": "Posts" }, "hashtags": { "name": "Hashtags", - "number": "No.", - "nameOfHashtag": "Name", - "tagCountUnique": "Users", + "number": "Não.", + "nameOfHashtag": "Nome", + "tagCountUnique": "Usuários", "tagCount": "Posts" }, "settings": { - "name": "Settings" + "name": "Configurações" }, "invites": { - "name": "Invite users", - "title": "Invite people", - "description": "Invitations are a wonderful way to have your friends in your network …" + "name": "Convidar usuários", + "title": "Convidar pessoas", + "description": "Convites são uma maneira maravilhosa de ter seus amigos em sua rede …" } }, "post": { - "name": "Post", + "name": "Publicação", "moreInfo": { - "name": "More info", - "title": "More information", - "description": "Here you can find more information about this topic.", - "titleOfCategoriesSection": "Categories", + "name": "Mais informações", + "title": "Mais informações", + "description": "Aqui você pode encontrar mais informações sobre este tópico.", + "titleOfCategoriesSection": "Categorias", "titleOfHashtagsSection": "Hashtags", - "titleOfRelatedContributionsSection": "Related posts" + "titleOfRelatedContributionsSection": "Publicações relacionadas" }, "takeAction": { - "name": "Take action" + "name": "Tomar providências" }, "menu": { - "edit": "Edit Post", - "delete": "Delete Post" + "edit": "Editar publicação", + "delete": "Excluir publicação" }, "comment": { - "submit": "Comment", - "submitted": "Comment Submitted", - "updated": "Changes Saved" + "submit": "Comentário", + "submitted": "Comentário Enviado", + "updated": "Alterações salvas" }, - "edited": "edited" + "edited": "editado" }, "comment": { "content": { - "unavailable-placeholder": "… this comment is not available anymore" + "unavailable-placeholder": "… este comentário não está mais disponível" }, "menu": { - "edit": "Edit Comment", - "delete": "Delete Comment" + "edit": "Editar Comentário", + "delete": "Apagar Comentário" }, "show": { - "more": "show more", - "less": "show less" + "more": "mostrar mais", + "less": "mostrar menos" }, - "edited": "edited" + "edited": "editado" }, "quotes": { "african": { - "quote": "Many small people in many small places do many small things, that can alter the face of the world.", - "author": "African proverb" + "quote": "Muitas pessoas pequenas em muitos lugares pequenos fazem muitas coisas pequenas, que podem alterar a face do mundo.", + "author": "Provérbio africano" } }, "common": { From fea0b60c2d2fc75bed952b68919094d7dae14742 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Mon, 4 Nov 2019 20:55:08 +0100 Subject: [PATCH 059/189] Fix lint errors --- webapp/pages/login.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/pages/login.vue b/webapp/pages/login.vue index b9228cde5..25480a0df 100644 --- a/webapp/pages/login.vue +++ b/webapp/pages/login.vue @@ -25,8 +25,8 @@ export default { } }, methods: { - handleSuccess() { - this.$i18n.set(this.user.locale) + handleSuccess() { + this.$i18n.set(this.user.locale) this.$router.replace(this.$route.query.path || '/') }, }, From 3f7a7913c0fd170bb084128379f10ef0934a04cc Mon Sep 17 00:00:00 2001 From: Stephen Hogsten Date: Wed, 30 Oct 2019 10:53:39 -0700 Subject: [PATCH 060/189] convert email to lowercase before submitting --- webapp/components/PasswordReset/Request.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/webapp/components/PasswordReset/Request.vue b/webapp/components/PasswordReset/Request.vue index bcececcca..6f0176f12 100644 --- a/webapp/components/PasswordReset/Request.vue +++ b/webapp/components/PasswordReset/Request.vue @@ -68,9 +68,12 @@ export default { } }, computed: { - submitMessage() { + lowercaseEmail() { const { email } = this.formData - return this.$t('components.password-reset.request.form.submitted', { email }) + return email.toLowerCase() + }, + submitMessage() { + return this.$t('components.password-reset.request.form.submitted', { email: this.lowercaseEmail }) }, }, methods: { @@ -86,7 +89,7 @@ export default { requestPasswordReset(email: $email) } ` - const { email } = this.formData + const email = this.lowercaseEmail try { await this.$apollo.mutate({ mutation, variables: { email } }) From 1d1517650066cc91ea227085c0827dc74ce04973 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Mon, 4 Nov 2019 22:07:05 +0100 Subject: [PATCH 061/189] Write a test for #2057 --- webapp/components/PasswordReset/Request.spec.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/webapp/components/PasswordReset/Request.spec.js b/webapp/components/PasswordReset/Request.spec.js index 594d6628d..4a6dbde9f 100644 --- a/webapp/components/PasswordReset/Request.spec.js +++ b/webapp/components/PasswordReset/Request.spec.js @@ -84,5 +84,18 @@ describe('Request', () => { }) }) }) + + describe('capital letters in a gmail address', () => { + beforeEach(async () => { + wrapper = Wrapper() + wrapper.find('input#email').setValue('mAiL@gmail.com') + await wrapper.find('form').trigger('submit') + }) + + it('normalizes email to lower case letters', () => { + const expected = expect.objectContaining({ variables: { email: 'mail@gmail.com' } }) + expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) + }) + }) }) }) From bcf06dff25eec69786a98f5a30aacfc1835c2e21 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Mon, 4 Nov 2019 23:09:49 +0100 Subject: [PATCH 062/189] Implement backend lookup with `normalizeEmail` --- backend/package.json | 1 + backend/src/schema/resolvers/passwordReset.js | 29 +---------------- .../schema/resolvers/passwordReset.spec.js | 5 +-- .../passwordReset/createPasswordReset.js | 31 +++++++++++++++++++ .../passwordReset/createPasswordReset.spec.js | 31 +++++++++++++++++++ backend/yarn.lock | 5 +++ 6 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 backend/src/schema/resolvers/passwordReset/createPasswordReset.js create mode 100644 backend/src/schema/resolvers/passwordReset/createPasswordReset.spec.js diff --git a/backend/package.json b/backend/package.json index 622a8313d..78dd26bc0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -101,6 +101,7 @@ "slug": "~1.1.0", "trunc-html": "~1.1.2", "uuid": "~3.3.3", + "validator": "^12.0.0", "wait-on": "~3.3.0", "xregexp": "^4.2.4" }, diff --git a/backend/src/schema/resolvers/passwordReset.js b/backend/src/schema/resolvers/passwordReset.js index 3c5f4636c..e03378ec1 100644 --- a/backend/src/schema/resolvers/passwordReset.js +++ b/backend/src/schema/resolvers/passwordReset.js @@ -1,34 +1,7 @@ import uuid from 'uuid/v4' import bcrypt from 'bcryptjs' +import createPasswordReset from './passwordReset/createPasswordReset' -export async function createPasswordReset(options) { - const { driver, nonce, email, issuedAt = new Date() } = options - const session = driver.session() - let response = {} - try { - const cypher = ` - MATCH (u:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email:$email}) - CREATE(pr:PasswordReset {nonce: $nonce, issuedAt: datetime($issuedAt), usedAt: NULL}) - MERGE (u)-[:REQUESTED]->(pr) - RETURN e, pr, u - ` - const transactionRes = await session.run(cypher, { - issuedAt: issuedAt.toISOString(), - nonce, - email, - }) - const records = transactionRes.records.map(record => { - const { email } = record.get('e').properties - const { nonce } = record.get('pr').properties - const { name } = record.get('u').properties - return { email, nonce, name } - }) - response = records[0] || {} - } finally { - session.close() - } - return response -} export default { Mutation: { diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js index fabee1c7e..03de77493 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.js +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -1,7 +1,7 @@ import Factory from '../../seed/factories' import { gql } from '../../jest/helpers' import { neode as getNeode, getDriver } from '../../bootstrap/neo4j' -import { createPasswordReset } from './passwordReset' +import { createPasswordReset } from './passwordReset/createPasswordReset' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' @@ -109,10 +109,7 @@ describe('passwordReset', () => { describe('resetPassword', () => { const setup = async (options = {}) => { const { email = 'user@example.org', issuedAt = new Date(), nonce = 'abcdef' } = options - - const session = driver.session() await createPasswordReset({ driver, email, issuedAt, nonce }) - session.close() } const mutation = gql` diff --git a/backend/src/schema/resolvers/passwordReset/createPasswordReset.js b/backend/src/schema/resolvers/passwordReset/createPasswordReset.js new file mode 100644 index 000000000..8d575abfc --- /dev/null +++ b/backend/src/schema/resolvers/passwordReset/createPasswordReset.js @@ -0,0 +1,31 @@ +import { normalizeEmail } from 'validator' + +export default async function createPasswordReset(options) { + const { driver, nonce, email, issuedAt = new Date() } = options + const normalizedEmail = normalizeEmail(email) + const session = driver.session() + let response = {} + try { + const cypher = ` + MATCH (u:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email:$email}) + CREATE(pr:PasswordReset {nonce: $nonce, issuedAt: datetime($issuedAt), usedAt: NULL}) + MERGE (u)-[:REQUESTED]->(pr) + RETURN e, pr, u + ` + const transactionRes = await session.run(cypher, { + issuedAt: issuedAt.toISOString(), + nonce, + email: normalizedEmail, + }) + const records = transactionRes.records.map(record => { + const { email } = record.get('e').properties + const { nonce } = record.get('pr').properties + const { name } = record.get('u').properties + return { email, nonce, name } + }) + response = records[0] || {} + } finally { + session.close() + } + return response +} diff --git a/backend/src/schema/resolvers/passwordReset/createPasswordReset.spec.js b/backend/src/schema/resolvers/passwordReset/createPasswordReset.spec.js new file mode 100644 index 000000000..a5c4d75a5 --- /dev/null +++ b/backend/src/schema/resolvers/passwordReset/createPasswordReset.spec.js @@ -0,0 +1,31 @@ +import createPasswordReset from './createPasswordReset' + +describe('createPasswordReset', () => { + const issuedAt = new Date() + const nonce = 'abcdef' + + describe('email lookup', () => { + let driver + let mockSession + beforeEach(() => { + mockSession = { + close() {}, + run: jest.fn().mockReturnValue({ + records: { + map: jest.fn(() => []) + } + }) + } + driver = { session: () => mockSession } + }) + + it('lowercases email address', async () => { + const email = 'stRaNGeCaSiNG@ExAmplE.ORG' + await createPasswordReset({ driver, email, issuedAt, nonce }) + expect(mockSession.run.mock.calls) + .toEqual([[expect.any(String), expect.objectContaining({ + email: 'strangecasing@example.org' + })]]) + }) + }) +}) diff --git a/backend/yarn.lock b/backend/yarn.lock index e6c662229..59254dfc7 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -8418,6 +8418,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validator@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-12.0.0.tgz#fb33221f5320abe2422cda2f517dc3838064e813" + integrity sha512-r5zA1cQBEOgYlesRmSEwc9LkbfNLTtji+vWyaHzRZUxCTHdsX3bd+sdHfs5tGZ2W6ILGGsxWxCNwT/h3IY/3ng== + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" From 61a89149892c2c225824b789325363e681dd8a18 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Mon, 4 Nov 2019 23:41:46 +0100 Subject: [PATCH 063/189] Always normalize email in backend --- backend/src/schema/resolvers/emails.js | 9 ++++++--- .../createPasswordReset.js | 0 .../createPasswordReset.spec.js | 18 +++++++++++------- .../resolvers/helpers/existingEmailAddress.js | 7 +++---- backend/src/schema/resolvers/passwordReset.js | 3 +-- .../src/schema/resolvers/passwordReset.spec.js | 2 +- backend/src/schema/resolvers/registration.js | 13 +++++++------ 7 files changed, 29 insertions(+), 23 deletions(-) rename backend/src/schema/resolvers/{passwordReset => helpers}/createPasswordReset.js (100%) rename backend/src/schema/resolvers/{passwordReset => helpers}/createPasswordReset.spec.js (68%) diff --git a/backend/src/schema/resolvers/emails.js b/backend/src/schema/resolvers/emails.js index ce93a28e9..06c0dbd1a 100644 --- a/backend/src/schema/resolvers/emails.js +++ b/backend/src/schema/resolvers/emails.js @@ -3,11 +3,14 @@ import Resolver from './helpers/Resolver' import existingEmailAddress from './helpers/existingEmailAddress' import { UserInputError } from 'apollo-server' import Validator from 'neode/build/Services/Validator.js' +import { normalizeEmail } from 'validator' export default { Mutation: { AddEmailAddress: async (_parent, args, context, _resolveInfo) => { let response + args.email = normalizeEmail(args.email) + try { const { neode } = context await new Validator(neode, neode.model('UnverifiedEmailAddress'), args) @@ -16,13 +19,13 @@ export default { } // check email does not belong to anybody - await existingEmailAddress(_parent, args, context) + await existingEmailAddress({ args, context }) const nonce = generateNonce() const { user: { id: userId }, } = context - const { email } = args + const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async txc => { const result = await txc.run( @@ -32,7 +35,7 @@ export default { SET email.createdAt = toString(datetime()) RETURN email, user `, - { userId, email, nonce }, + { userId, email: args.email, nonce }, ) return result.records.map(record => ({ name: record.get('user').properties.name, diff --git a/backend/src/schema/resolvers/passwordReset/createPasswordReset.js b/backend/src/schema/resolvers/helpers/createPasswordReset.js similarity index 100% rename from backend/src/schema/resolvers/passwordReset/createPasswordReset.js rename to backend/src/schema/resolvers/helpers/createPasswordReset.js diff --git a/backend/src/schema/resolvers/passwordReset/createPasswordReset.spec.js b/backend/src/schema/resolvers/helpers/createPasswordReset.spec.js similarity index 68% rename from backend/src/schema/resolvers/passwordReset/createPasswordReset.spec.js rename to backend/src/schema/resolvers/helpers/createPasswordReset.spec.js index a5c4d75a5..a566e225a 100644 --- a/backend/src/schema/resolvers/passwordReset/createPasswordReset.spec.js +++ b/backend/src/schema/resolvers/helpers/createPasswordReset.spec.js @@ -12,9 +12,9 @@ describe('createPasswordReset', () => { close() {}, run: jest.fn().mockReturnValue({ records: { - map: jest.fn(() => []) - } - }) + map: jest.fn(() => []), + }, + }), } driver = { session: () => mockSession } }) @@ -22,10 +22,14 @@ describe('createPasswordReset', () => { it('lowercases email address', async () => { const email = 'stRaNGeCaSiNG@ExAmplE.ORG' await createPasswordReset({ driver, email, issuedAt, nonce }) - expect(mockSession.run.mock.calls) - .toEqual([[expect.any(String), expect.objectContaining({ - email: 'strangecasing@example.org' - })]]) + expect(mockSession.run.mock.calls).toEqual([ + [ + expect.any(String), + expect.objectContaining({ + email: 'strangecasing@example.org', + }), + ], + ]) }) }) }) diff --git a/backend/src/schema/resolvers/helpers/existingEmailAddress.js b/backend/src/schema/resolvers/helpers/existingEmailAddress.js index 007d2de6b..ee1a6af82 100644 --- a/backend/src/schema/resolvers/helpers/existingEmailAddress.js +++ b/backend/src/schema/resolvers/helpers/existingEmailAddress.js @@ -1,7 +1,6 @@ import { UserInputError } from 'apollo-server' -export default async function alreadyExistingMail(_parent, args, context) { - let { email } = args - email = email.toLowerCase() + +export default async function alreadyExistingMail({ args, context }) { const cypher = ` MATCH (email:EmailAddress {email: $email}) OPTIONAL MATCH (email)-[:BELONGS_TO]-(user) @@ -10,7 +9,7 @@ export default async function alreadyExistingMail(_parent, args, context) { let transactionRes const session = context.driver.session() try { - transactionRes = await session.run(cypher, { email }) + transactionRes = await session.run(cypher, { email: args.email }) } finally { session.close() } diff --git a/backend/src/schema/resolvers/passwordReset.js b/backend/src/schema/resolvers/passwordReset.js index e03378ec1..7c0d9e747 100644 --- a/backend/src/schema/resolvers/passwordReset.js +++ b/backend/src/schema/resolvers/passwordReset.js @@ -1,7 +1,6 @@ import uuid from 'uuid/v4' import bcrypt from 'bcryptjs' -import createPasswordReset from './passwordReset/createPasswordReset' - +import createPasswordReset from './helpers/createPasswordReset' export default { Mutation: { diff --git a/backend/src/schema/resolvers/passwordReset.spec.js b/backend/src/schema/resolvers/passwordReset.spec.js index 03de77493..e9f986acd 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.js +++ b/backend/src/schema/resolvers/passwordReset.spec.js @@ -1,7 +1,7 @@ import Factory from '../../seed/factories' import { gql } from '../../jest/helpers' import { neode as getNeode, getDriver } from '../../bootstrap/neo4j' -import { createPasswordReset } from './passwordReset/createPasswordReset' +import { createPasswordReset } from './helpers/createPasswordReset' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' diff --git a/backend/src/schema/resolvers/registration.js b/backend/src/schema/resolvers/registration.js index bd62b32c3..bdcb6f04f 100644 --- a/backend/src/schema/resolvers/registration.js +++ b/backend/src/schema/resolvers/registration.js @@ -4,6 +4,7 @@ import fileUpload from './fileUpload' import encryptPassword from '../../helpers/encryptPassword' import generateNonce from './helpers/generateNonce' import existingEmailAddress from './helpers/existingEmailAddress' +import { normalizeEmail } from 'validator' const instance = neode() @@ -29,9 +30,9 @@ export default { return response }, Signup: async (_parent, args, context) => { - const nonce = generateNonce() - args.nonce = nonce - let emailAddress = await existingEmailAddress(_parent, args, context) + args.nonce = generateNonce() + args.email = normalizeEmail(args.email) + let emailAddress = await existingEmailAddress({ args, context }) if (emailAddress) return emailAddress try { emailAddress = await instance.create('EmailAddress', args) @@ -42,8 +43,8 @@ export default { }, SignupByInvitation: async (_parent, args, context) => { const { token } = args - const nonce = generateNonce() - args.nonce = nonce + args.nonce = generateNonce() + args.email = normalizeEmail(args.email) let emailAddress = await existingEmailAddress(_parent, args, context) if (emailAddress) return emailAddress try { @@ -78,7 +79,7 @@ export default { args.termsAndConditionsAgreedAt = new Date().toISOString() let { nonce, email } = args - email = email.toLowerCase() + email = normalizeEmail(email) const result = await instance.cypher( ` MATCH(email:EmailAddress {nonce: {nonce}, email: {email}}) From 0cf59743ab57532a9d2e770d5fd93c38d98898a7 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Mon, 4 Nov 2019 23:45:01 +0100 Subject: [PATCH 064/189] refactor: replace 'isemail' with 'validator' ..and use `normalizeEmail` everywhere in the webapp. --- webapp/components/PasswordReset/Request.vue | 12 +- webapp/components/Registration/Signup.vue | 9 +- webapp/package.json | 2 +- webapp/pages/admin/users.vue | 6 +- .../pages/settings/my-email-address/index.vue | 14 +- webapp/yarn.lock | 235 ++---------------- 6 files changed, 47 insertions(+), 231 deletions(-) diff --git a/webapp/components/PasswordReset/Request.vue b/webapp/components/PasswordReset/Request.vue index 6f0176f12..1cf575574 100644 --- a/webapp/components/PasswordReset/Request.vue +++ b/webapp/components/PasswordReset/Request.vue @@ -46,6 +46,7 @@ - From b79770469fd7773d5c9816ab5a4325cd6010ebfa Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 22 Oct 2019 14:40:10 +0200 Subject: [PATCH 104/189] Update notifications page - display more notifications by simplifying the table - get sorting working --- .../Notification/Notification.spec.js | 0 .../Notification/Notification.vue | 0 .../NotificationList/NotificationList.spec.js | 0 .../NotificationList/NotificationList.vue | 0 .../NotificationMenu/NotificationMenu.spec.js | 0 .../NotificationMenu/NotificationMenu.vue | 20 ++- webapp/graphql/User.js | 4 +- webapp/locales/en.json | 2 +- webapp/pages/notifications/index.vue | 135 +++++++----------- 9 files changed, 71 insertions(+), 90 deletions(-) rename webapp/components/{notifications => }/Notification/Notification.spec.js (100%) rename webapp/components/{notifications => }/Notification/Notification.vue (100%) rename webapp/components/{notifications => }/NotificationList/NotificationList.spec.js (100%) rename webapp/components/{notifications => }/NotificationList/NotificationList.vue (100%) rename webapp/components/{notifications => }/NotificationMenu/NotificationMenu.spec.js (100%) rename webapp/components/{notifications => }/NotificationMenu/NotificationMenu.vue (87%) diff --git a/webapp/components/notifications/Notification/Notification.spec.js b/webapp/components/Notification/Notification.spec.js similarity index 100% rename from webapp/components/notifications/Notification/Notification.spec.js rename to webapp/components/Notification/Notification.spec.js diff --git a/webapp/components/notifications/Notification/Notification.vue b/webapp/components/Notification/Notification.vue similarity index 100% rename from webapp/components/notifications/Notification/Notification.vue rename to webapp/components/Notification/Notification.vue diff --git a/webapp/components/notifications/NotificationList/NotificationList.spec.js b/webapp/components/NotificationList/NotificationList.spec.js similarity index 100% rename from webapp/components/notifications/NotificationList/NotificationList.spec.js rename to webapp/components/NotificationList/NotificationList.spec.js diff --git a/webapp/components/notifications/NotificationList/NotificationList.vue b/webapp/components/NotificationList/NotificationList.vue similarity index 100% rename from webapp/components/notifications/NotificationList/NotificationList.vue rename to webapp/components/NotificationList/NotificationList.vue diff --git a/webapp/components/notifications/NotificationMenu/NotificationMenu.spec.js b/webapp/components/NotificationMenu/NotificationMenu.spec.js similarity index 100% rename from webapp/components/notifications/NotificationMenu/NotificationMenu.spec.js rename to webapp/components/NotificationMenu/NotificationMenu.spec.js diff --git a/webapp/components/notifications/NotificationMenu/NotificationMenu.vue b/webapp/components/NotificationMenu/NotificationMenu.vue similarity index 87% rename from webapp/components/notifications/NotificationMenu/NotificationMenu.vue rename to webapp/components/NotificationMenu/NotificationMenu.vue index 255101139..4ab3dedca 100644 --- a/webapp/components/notifications/NotificationMenu/NotificationMenu.vue +++ b/webapp/components/NotificationMenu/NotificationMenu.vue @@ -13,7 +13,9 @@ - {{ $t('notifications.page') }} + + {{ $t('notifications.page') }} + @@ -35,6 +37,7 @@ export default { return { displayedNotifications: [], notifications: [], + nofiticationRead: null, } }, props: { @@ -75,15 +78,24 @@ export default { } return countUnread }, + updateNotifications() { + return this.notificationRead + }, }, apollo: { notifications: { query() { return notificationQuery(this.$i18n) }, + variables() { + return { + read: false, + orderBy: 'updatedAt_desc', + } + }, pollInterval: NOTIFICATIONS_POLL_INTERVAL, - update(data) { - const newNotifications = data.notifications.filter(newN => { + update({ notifications }) { + const newNotifications = notifications.filter(newN => { return !this.displayedNotifications.find(oldN => this.equalNotification(newN, oldN)) }) this.displayedNotifications = newNotifications @@ -91,7 +103,7 @@ export default { .sort((a, b) => { return new Date(b.createdAt) - new Date(a.createdAt) }) - return data.notifications + return notifications }, error(error) { this.$toast.error(error.message) diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 54d5889e2..5a4b18b6a 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -51,8 +51,8 @@ export const notificationQuery = i18n => { ${commentFragment(lang)} ${postFragment(lang)} - query { - notifications(orderBy: updatedAt_desc) { + query($read: Boolean, $orderBy: NotificationOrdering) { + notifications(read: $read, orderBy: $orderBy) { read reason createdAt diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 59b8a9390..c978a35a7 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -182,7 +182,7 @@ "createdAt": "Notification created at", "type": "Type", "user": "User", - "action": "Action" + "content": "Content" }, "search": { "placeholder": "Search", diff --git a/webapp/pages/notifications/index.vue b/webapp/pages/notifications/index.vue index fcbebd1c2..118ca0d45 100644 --- a/webapp/pages/notifications/index.vue +++ b/webapp/pages/notifications/index.vue @@ -14,7 +14,7 @@ @click.prevent="toggleMenu()" > - {{ 'All' }} + {{ selected }} - {{ item.route.option }} + {{ item.route.label }} @@ -39,51 +39,12 @@ - - + + @@ -136,23 +98,29 @@ export default { }, data() { return { - notifications: [], - sortingOptions: ['All', 'Read', 'Unread'], + selectedNotifications: [], + selected: 'All', + sortingOptions: [ + { label: 'All', value: null }, + { label: 'Read', value: true }, + { label: 'Unread', value: false }, + ], nofiticationRead: null, } }, computed: { fields() { return { - notifications: null, - // user: this.$t('notifications.user'), - // type: this.$t('notifications.type'), + user: this.$t('notifications.user'), + post: this.$t('notifications.type'), + content: this.$t('notifications.content'), } }, routes() { let routes = this.sortingOptions.map(option => { return { - option, + label: option.label, + value: option.value, } }) return routes @@ -160,24 +128,25 @@ export default { }, methods: { sortNotifications(option, toggleMenu) { - if (option === 'Read') { - this.notificationRead = true - } else if (option === 'Unread') { - this.notificationRead = false - } else { - this.notificationRead = null - } - this.$apollo.queries.notificationsPage.refetch() + this.notificationRead = option.value + this.selected = option.label + this.$apollo.queries.notifications.refresh() toggleMenu() }, }, apollo: { - notificationsPage: { + notifications: { query() { return notificationQuery(this.$i18n) }, + variables() { + return { + read: this.notificationRead, + orderBy: 'updatedAt_desc', + } + }, update({ notifications }) { - this.notifications = notifications + this.selectedNotifications = notifications }, fetchPolicy: 'cache-and-network', error(error) { @@ -191,7 +160,7 @@ export default { .sorting-dropdown { float: right; } -#notifications-table thead { - display: none; +.notifications-table td { + width: 500px; } From cf8124fb2d96c04c44c35f3542d1a6eaac10fe47 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Thu, 24 Oct 2019 09:11:49 +0200 Subject: [PATCH 105/189] Clean up/add german translations --- .../components/Notification/Notification.vue | 68 +++++++++---------- webapp/locales/de.json | 11 ++- webapp/locales/en.json | 11 +-- webapp/pages/notifications/index.vue | 10 +-- 4 files changed, 55 insertions(+), 45 deletions(-) diff --git a/webapp/components/Notification/Notification.vue b/webapp/components/Notification/Notification.vue index 9649b0d7e..321d1df39 100644 --- a/webapp/components/Notification/Notification.vue +++ b/webapp/components/Notification/Notification.vue @@ -1,39 +1,37 @@ - diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 25ce3742f..534ef819f 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -185,7 +185,8 @@ "all": "Alle", "read": "Lesen ", "unread": "Ungelesen" - } + }, + "empty": "Sorry, du hast im Moment keine Benachrichtigungen." }, "search": { "placeholder": "Suchen", @@ -684,7 +685,7 @@ "terms-of-service": { "title": "Nutzungsbedingungen", "description": "Die folgenden Nutzungsbedingungen sind Basis für die Nutzung unseres Netzwerkes. Beim Registrieren musst Du sie anerkennen und wir werden Dich auch später über ggf. stattfindende Änderungen informieren. Das Human Connection Netzwerk wird in Deutschland betrieben und unterliegt daher deutschem Recht. Gerichtsstand ist Kirchheim / Teck. Zu Details schau in unser Impressum: https://human-connection.org/impressum " - }, + }, "use-and-license" : { "title": "Nutzung und Lizenz", "description": "Sind Inhalte, die Du bei uns einstellst, durch Rechte am geistigen Eigentum geschützt, erteilst Du uns eine nicht-exklusive, übertragbare, unterlizenzierbare und weltweite Lizenz für die Nutzung dieser Inhalte für die Bereitstellung in unserem Netzwerk. Diese Lizenz endet, sobald Du Deine Inhalte oder Deinen ganzen Account löscht. Bedenke, dass andere Deine Inhalte weiter teilen können und wir diese nicht löschen können." @@ -712,6 +713,6 @@ "addition" : { "title": "Zusätzliche machen wir regelmäßig Veranstaltungen, wo Du auch Eindrücke wiedergeben und Fragen stellen kannst. Du findest eine aktuelle Übersicht hier:", "description": " https://human-connection.org/veranstaltungen/ " - } + } } } diff --git a/webapp/locales/en.json b/webapp/locales/en.json index b77922b74..775132eab 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -186,7 +186,8 @@ "all": "All", "read": "Read", "unread": "Unread" - } + }, + "empty": "Sorry, you don't have any notifications at the moment." }, "search": { "placeholder": "Search", @@ -685,7 +686,7 @@ "terms-of-service": { "title": "Terms of Service", "description": "The following terms of use form the basis for the use of our network. When you register, you must accept them and we will inform you later about any changes that may take place. The Human Connection Network is operated in Germany and is therefore subject to German law. Place of jurisdiction is Kirchheim / Teck. For details see our imprint: https://human-connection.org/imprint " - }, + }, "use-and-license" : { "title": "Use and License", "description": "If any content you post to us is protected by intellectual property rights, you grant us a non-exclusive, transferable, sublicensable, worldwide license to use such content for posting to our network. This license expires when you delete your content or your entire account. Remember that others may share your content and we cannot delete it." @@ -713,10 +714,10 @@ "addition" : { "title": "In addition, we regularly hold events where you can also share your impressions and ask questions. You can find a current overview here:", "description": " https://human-connection.org/events/ " - } + } } } - - - + + + diff --git a/webapp/pages/notifications/index.vue b/webapp/pages/notifications/index.vue index 0ed768dfc..2ff6fe2d5 100644 --- a/webapp/pages/notifications/index.vue +++ b/webapp/pages/notifications/index.vue @@ -44,19 +44,35 @@ :fields="fields" class="notifications-table" > + - + @@ -88,7 +106,7 @@ import HcUser from '~/components/User/User' import HcEmpty from '~/components/Empty.vue' import Dropdown from '~/components/Dropdown' -import { notificationQuery } from '~/graphql/User' +import { notificationQuery, markAsReadMutation } from '~/graphql/User' export default { components: { @@ -111,7 +129,14 @@ export default { computed: { fields() { return { - user: this.$t('notifications.user'), + icon: { + label: ' ', + width: '60px', + }, + user: { + label: this.$t('notifications.user'), + width: '350px', + }, post: this.$t('notifications.post'), content: this.$t('notifications.content'), } @@ -133,6 +158,16 @@ export default { this.$apollo.queries.notifications.refresh() toggleMenu() }, + async markNotificationAsRead(notificationSourceId) { + try { + await this.$apollo.mutate({ + mutation: markAsReadMutation(this.$i18n), + variables: { id: notificationSourceId }, + }) + } catch (error) { + this.$toast.error(error.message) + } + }, }, apollo: { notifications: { @@ -160,7 +195,8 @@ export default { .sorting-dropdown { float: right; } -.notifications-table td { - width: 500px; +.notification-status { + opacity: 0.6; /* Real browsers */ + filter: alpha(opacity = 60); /* MSIE */ } From 994a0b049d1803784d9c06383872f1c9e33095a0 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Fri, 25 Oct 2019 13:12:55 +0200 Subject: [PATCH 108/189] Extract AvatarMenu into its own component --- webapp/components/AvatarMenu/AvatarMenu.vue | 162 ++++++++++++++++++++ webapp/layouts/default.vue | 138 +---------------- 2 files changed, 165 insertions(+), 135 deletions(-) create mode 100644 webapp/components/AvatarMenu/AvatarMenu.vue diff --git a/webapp/components/AvatarMenu/AvatarMenu.vue b/webapp/components/AvatarMenu/AvatarMenu.vue new file mode 100644 index 000000000..96d26f210 --- /dev/null +++ b/webapp/components/AvatarMenu/AvatarMenu.vue @@ -0,0 +1,162 @@ + + + diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue index 35af4d25f..091bfb7c8 100644 --- a/webapp/layouts/default.vue +++ b/webapp/layouts/default.vue @@ -71,52 +71,7 @@ - - - - + @@ -144,21 +99,19 @@ import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch' import SearchInput from '~/components/SearchInput.vue' import Modal from '~/components/Modal' import NotificationMenu from '~/components/notifications/NotificationMenu/NotificationMenu' -import Dropdown from '~/components/Dropdown' -import HcAvatar from '~/components/Avatar/Avatar.vue' import seo from '~/mixins/seo' import FilterPosts from '~/components/FilterPosts/FilterPosts.vue' import CategoryQuery from '~/graphql/CategoryQuery.js' import PageFooter from '~/components/PageFooter/PageFooter' +import AvatarMenu from '~/components/AvatarMenu/AvatarMenu' export default { components: { - Dropdown, LocaleSwitch, SearchInput, Modal, NotificationMenu, - HcAvatar, + AvatarMenu, FilterPosts, PageFooter, }, @@ -172,49 +125,10 @@ export default { }, computed: { ...mapGetters({ - user: 'auth/user', isLoggedIn: 'auth/isLoggedIn', - isModerator: 'auth/isModerator', - isAdmin: 'auth/isAdmin', quickSearchResults: 'search/quickResults', quickSearchPending: 'search/quickPending', }), - userName() { - const { name } = this.user || {} - return name || this.$t('profile.userAnonym') - }, - routes() { - if (!this.user.slug) { - return [] - } - let routes = [ - { - name: this.$t('profile.name'), - path: `/profile/${this.user.slug}`, - icon: 'user', - }, - { - name: this.$t('settings.name'), - path: `/settings`, - icon: 'cogs', - }, - ] - if (this.isModerator) { - routes.push({ - name: this.$t('moderation.name'), - path: `/moderation`, - icon: 'balance-scale', - }) - } - if (this.isAdmin) { - routes.push({ - name: this.$t('admin.name'), - path: `/admin`, - icon: 'shield', - }) - } - return routes - }, showFilterPostsDropdown() { const [firstRoute] = this.$route.matched return firstRoute && firstRoute.name === 'index' @@ -239,13 +153,6 @@ export default { }) }) }, - matcher(url, route) { - if (url.indexOf('/profile') === 0) { - // do only match own profile - return this.$route.path === url - } - return this.$route.path.indexOf(url) === 0 - }, toggleMobileMenuView() { this.toggleMobileMenu = !this.toggleMobileMenu }, @@ -289,45 +196,6 @@ export default { .main-navigation-right .desktop-view { float: right; } -.avatar-menu { - margin: 2px 0px 0px 5px; -} -.avatar-menu-trigger { - user-select: none; - display: flex; - align-items: center; - padding-left: $space-xx-small; -} -.avatar-menu-popover { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - hr { - color: $color-neutral-90; - background-color: $color-neutral-90; - } - .logout-link { - margin-left: -$space-small; - margin-right: -$space-small; - margin-top: -$space-xxx-small; - margin-bottom: -$space-x-small; - padding: $space-x-small $space-small; - // subtract menu border with from padding - padding-left: $space-small - 2; - color: $text-color-base; - &:hover { - color: $text-color-link-active; - } - } - nav { - margin-left: -$space-small; - margin-right: -$space-small; - margin-top: -$space-xx-small; - margin-bottom: -$space-xx-small; - a { - padding-left: 12px; - } - } -} @media only screen and (min-width: 960px) { .mobile-hamburger-menu { display: none; From d83a42451318cdc1fe85c58dfea0d6b21fa56b77 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Mon, 28 Oct 2019 17:29:01 +0100 Subject: [PATCH 109/189] Add styling - to link in NotificationMenu - make table more mobile responsive - remove unneccessary props Co-authored-by: Alina Beck --- webapp/components/AvatarMenu/AvatarMenu.vue | 5 ++-- .../NotificationMenu/NotificationMenu.vue | 28 ++++++++++--------- webapp/layouts/default.vue | 2 +- webapp/pages/notifications/index.vue | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/webapp/components/AvatarMenu/AvatarMenu.vue b/webapp/components/AvatarMenu/AvatarMenu.vue index 96d26f210..18865f457 100644 --- a/webapp/components/AvatarMenu/AvatarMenu.vue +++ b/webapp/components/AvatarMenu/AvatarMenu.vue @@ -1,5 +1,5 @@ diff --git a/webapp/pages/notifications/index.vue b/webapp/pages/notifications/index.vue index 2ff6fe2d5..95d30df1a 100644 --- a/webapp/pages/notifications/index.vue +++ b/webapp/pages/notifications/index.vue @@ -135,7 +135,7 @@ export default { }, user: { label: this.$t('notifications.user'), - width: '350px', + width: '33.333%', }, post: this.$t('notifications.post'), content: this.$t('notifications.content'), From 4448c12f2e153d2eec04e7734c39992e7b04f408 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Mon, 28 Oct 2019 17:30:28 +0100 Subject: [PATCH 110/189] Add componenet test for AvatarMenu Co-authored-by: mindcodemediator Co-authored-by: Mike Aono --- .../components/AvatarMenu/AvatarMenu.spec.js | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 webapp/components/AvatarMenu/AvatarMenu.spec.js diff --git a/webapp/components/AvatarMenu/AvatarMenu.spec.js b/webapp/components/AvatarMenu/AvatarMenu.spec.js new file mode 100644 index 000000000..17237d37e --- /dev/null +++ b/webapp/components/AvatarMenu/AvatarMenu.spec.js @@ -0,0 +1,148 @@ +import { config, mount, createLocalVue } from '@vue/test-utils' +import Vuex from 'vuex' +import VTooltip from 'v-tooltip' +import Styleguide from '@human-connection/styleguide' +import AvatarMenu from './AvatarMenu.vue' +import Filters from '~/plugins/vue-filters' + +const localVue = createLocalVue() +localVue.use(Styleguide) +localVue.use(Vuex) +localVue.use(Filters) +localVue.use(VTooltip) + +config.stubs['nuxt-link'] = '' +config.stubs['router-link'] = '' + +describe('AvatarMenu.vue', () => { + let propsData, getters, wrapper, mocks + + beforeEach(() => { + propsData = {} + mocks = { + $route: { + path: '', + }, + $router: { + resolve: jest.fn(() => { + return { href: '/profile/u343/matt' } + }), + }, + $t: jest.fn(a => a), + } + getters = { + 'auth/user': () => { + return { id: 'u343', name: 'Matt' } + }, + } + }) + + const Wrapper = () => { + const store = new Vuex.Store({ + getters, + }) + return mount(AvatarMenu, { propsData, localVue, store, mocks }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the HcAvatar component', () => { + wrapper.find('.avatar-menu-trigger').trigger('click') + expect(wrapper.find('.ds-avatar').exists()).toBe(true) + }) + + describe('given a userName', () => { + it('displays the userName', () => { + expect(wrapper.find('b').text()).toEqual('Matt') + }) + }) + + describe('no userName', () => { + beforeEach(() => { + getters = { + 'auth/user': () => { + return { id: 'u343' } + }, + } + wrapper = Wrapper() + wrapper.find('.avatar-menu-trigger').trigger('click') + }) + + it('displays anonymous user', () => { + expect(wrapper.find('b').text()).toEqual('profile.userAnonym') + }) + }) + + describe('routes', () => { + beforeEach(() => { + getters = { + 'auth/user': () => { + return { id: 'u343', slug: 'matt' } + }, + 'auth/isModerator': () => false, + 'auth/isAdmin': () => false, + } + wrapper = Wrapper() + wrapper.find('.avatar-menu-trigger').trigger('click') + }) + + describe('role user', () => { + it('displays a link to user profile', () => { + const profileLink = wrapper.findAll('.ds-menu-item span').at(0) + expect(profileLink.attributes().to).toEqual('/profile/u343/matt') + }) + + it('displays a link to the notifications page', () => { + const notificationsLink = wrapper.findAll('.ds-menu-item span').at(2) + expect(notificationsLink.attributes().to).toEqual('/notifications') + }) + + it('displays a link to the settings page', () => { + const settingsLink = wrapper.findAll('.ds-menu-item span').at(4) + expect(settingsLink.attributes().to).toEqual('/settings') + }) + }) + + describe('role moderator', () => { + beforeEach(() => { + getters = { + 'auth/user': () => { + return { id: 'u343', slug: 'matt' } + }, + 'auth/isModerator': () => true, + 'auth/isAdmin': () => false, + } + wrapper = Wrapper() + wrapper.find('.avatar-menu-trigger').trigger('click') + }) + + it('displays a link to moderation page', () => { + const moderationLink = wrapper.findAll('.ds-menu-item span').at(6) + expect(moderationLink.attributes().to).toEqual('/moderation') + }) + }) + + describe('role admin', () => { + beforeEach(() => { + getters = { + 'auth/user': () => { + return { id: 'u343', slug: 'matt' } + }, + 'auth/isModerator': () => true, + 'auth/isAdmin': () => true, + } + wrapper = Wrapper() + wrapper.find('.avatar-menu-trigger').trigger('click') + }) + + it('displays a link to admin page', () => { + const adminLink = wrapper.findAll('.ds-menu-item span').at(8) + expect(adminLink.attributes().to).toEqual('/admin') + }) + }) + }) + }) +}) From f34c8f44e0b5fa816fd79a11dfc310c443a7aefe Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 29 Oct 2019 18:02:49 +0100 Subject: [PATCH 111/189] Update resolver for paginated notifications --- backend/src/schema/resolvers/notifications.js | 9 +++++---- backend/src/schema/types/type/NOTIFIED.gql | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/src/schema/resolvers/notifications.js b/backend/src/schema/resolvers/notifications.js index 4cab1ffc4..8fe45bde3 100644 --- a/backend/src/schema/resolvers/notifications.js +++ b/backend/src/schema/resolvers/notifications.js @@ -18,9 +18,8 @@ export default { notifications: async (_parent, args, context, _resolveInfo) => { const { user: currentUser } = context const session = context.driver.session() - let notifications - let whereClause - let orderByClause + let notifications, whereClause, orderByClause + switch (args.read) { case true: whereClause = 'WHERE notification.read = TRUE' @@ -41,13 +40,15 @@ export default { default: orderByClause = '' } - + const offset = args.offset ? `SKIP ${args.offset}` : '' + const limit = args.first ? `LIMIT ${args.first}` : '' try { const cypher = ` MATCH (resource {deleted: false, disabled: false})-[notification:NOTIFIED]->(user:User {id:$id}) ${whereClause} RETURN resource, notification, user ${orderByClause} + ${offset} ${limit} ` const result = await session.run(cypher, { id: currentUser.id }) notifications = await result.records.map(transformReturnType) diff --git a/backend/src/schema/types/type/NOTIFIED.gql b/backend/src/schema/types/type/NOTIFIED.gql index 5082b5f7f..42da6a39b 100644 --- a/backend/src/schema/types/type/NOTIFIED.gql +++ b/backend/src/schema/types/type/NOTIFIED.gql @@ -23,7 +23,7 @@ enum NotificationReason { } type Query { - notifications(read: Boolean, orderBy: NotificationOrdering): [NOTIFIED] + notifications(read: Boolean, orderBy: NotificationOrdering, first: Int, offset: Int): [NOTIFIED] } type Mutation { From 084388a21ec669607bdb6245963dc011c523e142 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 29 Oct 2019 18:04:51 +0100 Subject: [PATCH 112/189] Follow new file structure - avoid nested directories --- webapp/components/{ => Empty}/Empty.vue | 0 webapp/layouts/default.vue | 2 +- webapp/pages/admin/notifications.vue | 2 +- webapp/pages/admin/organizations.vue | 2 +- webapp/pages/admin/pages.vue | 2 +- webapp/pages/admin/settings.vue | 2 +- webapp/pages/index.vue | 2 +- webapp/pages/moderation/index.vue | 2 +- webapp/pages/post/_id/_slug/more-info.vue | 2 +- webapp/pages/post/_id/_slug/take-action.vue | 2 +- webapp/pages/profile/_id/_slug.vue | 2 +- webapp/pages/registration/signup.vue | 2 +- webapp/pages/settings/data-download.vue | 2 +- webapp/pages/settings/invites.vue | 2 +- webapp/pages/settings/languages.vue | 2 +- webapp/pages/settings/my-organizations.vue | 2 +- 16 files changed, 15 insertions(+), 15 deletions(-) rename webapp/components/{ => Empty}/Empty.vue (100%) diff --git a/webapp/components/Empty.vue b/webapp/components/Empty/Empty.vue similarity index 100% rename from webapp/components/Empty.vue rename to webapp/components/Empty/Empty.vue diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue index 7a95ada76..a39e0a148 100644 --- a/webapp/layouts/default.vue +++ b/webapp/layouts/default.vue @@ -98,7 +98,7 @@ import { mapGetters, mapActions } from 'vuex' import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch' import SearchInput from '~/components/SearchInput.vue' import Modal from '~/components/Modal' -import NotificationMenu from '~/components/notifications/NotificationMenu/NotificationMenu' +import NotificationMenu from '~/components/NotificationMenu/NotificationMenu' import seo from '~/mixins/seo' import FilterPosts from '~/components/FilterPosts/FilterPosts.vue' import CategoryQuery from '~/graphql/CategoryQuery.js' diff --git a/webapp/pages/admin/notifications.vue b/webapp/pages/admin/notifications.vue index 0d348633f..faad87a46 100644 --- a/webapp/pages/admin/notifications.vue +++ b/webapp/pages/admin/notifications.vue @@ -5,7 +5,7 @@ diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 5a4b18b6a..7b2a012a4 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -51,8 +51,8 @@ export const notificationQuery = i18n => { ${commentFragment(lang)} ${postFragment(lang)} - query($read: Boolean, $orderBy: NotificationOrdering) { - notifications(read: $read, orderBy: $orderBy) { + query($read: Boolean, $orderBy: NotificationOrdering, $first: Int, $offset: Int) { + notifications(read: $read, orderBy: $orderBy, first: $first, offset: $offset) { read reason createdAt diff --git a/webapp/pages/notifications/index.vue b/webapp/pages/notifications/index.vue index 95d30df1a..5d22aa0b4 100644 --- a/webapp/pages/notifications/index.vue +++ b/webapp/pages/notifications/index.vue @@ -5,158 +5,52 @@ {{ $t('notifications.title') }} - - - - {{ selected }} - - - - - {{ item.route.label }} - - - + + + - - - - - - - + + diff --git a/webapp/components/NotificationsTable/NotificationsTable.vue b/webapp/components/NotificationsTable/NotificationsTable.vue new file mode 100644 index 000000000..8366ea5c5 --- /dev/null +++ b/webapp/components/NotificationsTable/NotificationsTable.vue @@ -0,0 +1,105 @@ + + + diff --git a/webapp/pages/notifications/index.vue b/webapp/pages/notifications/index.vue index 5d22aa0b4..9e2346061 100644 --- a/webapp/pages/notifications/index.vue +++ b/webapp/pages/notifications/index.vue @@ -103,8 +103,4 @@ export default { .sorting-dropdown { float: right; } -.notification-status { - opacity: 0.6; /* Real browsers */ - filter: alpha(opacity = 60); /* MSIE */ -} From b916c9fe8308d58b0998993e13bd47008a6c024d Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Wed, 30 Oct 2019 12:26:05 +0100 Subject: [PATCH 115/189] Add portuguese translations Co-authored-by: ppelegrin <56610168+ppelegrin@users.noreply.github.com> --- webapp/locales/pt.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 0765ff465..8ff168b72 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -182,6 +182,25 @@ "hint": "O que você está pesquisando??", "failed": "Nada foi encontrado" }, + "notifications": { + "reason": { + "mentioned_in_post": "Mencinou você em um post …", + "mentioned_in_comment": "Mentionou você em um comentário …", + "commented_on_post": "Comentou no seu post …" + }, + "comment": "Comentário", + "title": "Notificações", + "pageLink": "Todas as notificações", + "post": "Post", + "user": "Usuário", + "content": "Conteúdo", + "sortingLabel": { + "all": "Todos", + "read": "Lido", + "unread": "Não lido" + }, + "empty": "Desculpe, não tem nenhuma notificação neste momento." + }, "settings": { "name": "Configurações", "data": { From aabaa2e637db5eb815c1476f0ae2ddb94c13368b Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Wed, 30 Oct 2019 13:37:30 +0100 Subject: [PATCH 116/189] Fix typo Co-authored-by: Alina Beck --- webapp/components/Paginate/Paginate.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/components/Paginate/Paginate.spec.js b/webapp/components/Paginate/Paginate.spec.js index 36e22d1b4..034d33301 100644 --- a/webapp/components/Paginate/Paginate.spec.js +++ b/webapp/components/Paginate/Paginate.spec.js @@ -20,6 +20,7 @@ describe('Paginate.vue', () => { beforeEach(() => { wrapper = Wrapper() }) + describe('next button', () => { beforeEach(() => { propsData.hasNext = true @@ -38,7 +39,7 @@ describe('Paginate.vue', () => { expect(nextButton.attributes().disabled).toBeUndefined() }) - it('emits back when clicked', async () => { + it('emits next when clicked', async () => { await nextButton.trigger('click') expect(wrapper.emitted().next).toHaveLength(1) }) From a1af4f5037f0d5445328385946a4f59d1e73a39f Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Wed, 30 Oct 2019 13:38:17 +0100 Subject: [PATCH 117/189] Add storybook notes Co-authored-by: @alina-beck --- webapp/package.json | 1 + webapp/storybook/addons.js | 1 + webapp/yarn.lock | 140 +++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) diff --git a/webapp/package.json b/webapp/package.json index 4e63bb3d6..649788ac7 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -97,6 +97,7 @@ "@babel/preset-env": "~7.7.1", "@storybook/addon-a11y": "^5.2.5", "@storybook/addon-actions": "^5.2.5", + "@storybook/addon-notes": "^5.2.5", "@storybook/vue": "~5.2.5", "@vue/cli-shared-utils": "~4.0.5", "@vue/eslint-config-prettier": "~5.0.0", diff --git a/webapp/storybook/addons.js b/webapp/storybook/addons.js index 44a5acb23..47c4cbd22 100644 --- a/webapp/storybook/addons.js +++ b/webapp/storybook/addons.js @@ -1,4 +1,5 @@ import '@storybook/addon-actions/register' import '@storybook/addon-a11y/register' import 'storybook-design-token/register' +import '@storybook/addon-notes/register-panel' // import '@storybook/addon-links/register' diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 712696431..ef0decedc 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2113,6 +2113,25 @@ react-inspector "^3.0.2" uuid "^3.3.2" +"@storybook/addon-notes@^5.2.5": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/addon-notes/-/addon-notes-5.2.6.tgz#bf74ff4f8018e315a4c07c3d5e90cd9154ce6e8e" + integrity sha512-CfWOkoPFI1ZAWQYnwFVqGmeCeXnVQGoFyDSVc3NcIFF1lsk2aagGV+ifJMJuDTXIKu0FClKpvMcENWt+bBpA+w== + dependencies: + "@storybook/addons" "5.2.6" + "@storybook/api" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/components" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/router" "5.2.6" + "@storybook/theming" "5.2.6" + core-js "^3.0.1" + global "^4.3.2" + markdown-to-jsx "^6.10.3" + memoizerific "^1.11.3" + prop-types "^15.7.2" + util-deprecate "^1.0.2" + "@storybook/addons@5.1.9": version "5.1.9" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.1.9.tgz#ecf218d08508b97ca5e6e0f1ed361081385bd3ff" @@ -2138,6 +2157,19 @@ global "^4.3.2" util-deprecate "^1.0.2" +"@storybook/addons@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.2.6.tgz#c1278137acb3502e068b0b0d07a8371c607e9c02" + integrity sha512-5MF64lsAhIEMxTbVpYROz5Wez595iwSw45yXyP8gWt12d+EmFO5tdy7cYJCxcMuVhDfaCI78tFqS9orr1atVyA== + dependencies: + "@storybook/api" "5.2.6" + "@storybook/channels" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/core-events" "5.2.6" + core-js "^3.0.1" + global "^4.3.2" + util-deprecate "^1.0.2" + "@storybook/api@5.1.9": version "5.1.9" resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.1.9.tgz#eec5b2f775392ce0803930104c6ce14fa4931e8b" @@ -2184,6 +2216,29 @@ telejson "^3.0.2" util-deprecate "^1.0.2" +"@storybook/api@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.2.6.tgz#43d3c20b90e585e6c94b36e29845d39704ae2135" + integrity sha512-X/di44/SAL68mD6RHTX2qdWwhjRW6BgcfPtu0dMd38ErB3AfsfP4BITXs6kFOeSM8kWiaQoyuw0pOBzA8vlYug== + dependencies: + "@storybook/channels" "5.2.6" + "@storybook/client-logger" "5.2.6" + "@storybook/core-events" "5.2.6" + "@storybook/router" "5.2.6" + "@storybook/theming" "5.2.6" + core-js "^3.0.1" + fast-deep-equal "^2.0.1" + global "^4.3.2" + lodash "^4.17.15" + memoizerific "^1.11.3" + prop-types "^15.6.2" + react "^16.8.3" + semver "^6.0.0" + shallow-equal "^1.1.0" + store2 "^2.7.1" + telejson "^3.0.2" + util-deprecate "^1.0.2" + "@storybook/channel-postmessage@5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-5.2.5.tgz#47397e543a87ea525cbe93f7d85bd8533edc9127" @@ -2209,6 +2264,13 @@ dependencies: core-js "^3.0.1" +"@storybook/channels@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-5.2.6.tgz#e2837508864dc4d5b5e03f078886f0ce113762ea" + integrity sha512-/UsktYsXuvb1efjVPCEivhh5ywRhm7hl73pQnpJLJHRqyLMM2I5nGPFELTTNuU9yWy7sP9QL5gRqBBPe1sqjZQ== + dependencies: + core-js "^3.0.1" + "@storybook/client-api@5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-5.2.5.tgz#53151a236b6ffc2088acc4535a08e010013e3278" @@ -2244,6 +2306,13 @@ dependencies: core-js "^3.0.1" +"@storybook/client-logger@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.2.6.tgz#cfc4536e9b724b086f7509c2bb34c221016713c9" + integrity sha512-hJvPD267cCwLIRMOISjDH8h9wbwOcXIJip29UlJbU9iMtZtgE+YelmlpmZJvqcDfUiXWWrOh7tP76mj8EAfwIQ== + dependencies: + core-js "^3.0.1" + "@storybook/components@5.1.9": version "5.1.9" resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.1.9.tgz#2a5258780fff07172d103287759946dbb4b13e2d" @@ -2293,6 +2362,31 @@ react-textarea-autosize "^7.1.0" simplebar-react "^1.0.0-alpha.6" +"@storybook/components@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.2.6.tgz#cddb60227720aea7cae34fe782d0370bcdbd4005" + integrity sha512-C7OS90bZ1ZvxlWUZ3B2MPFFggqAtUo7X8DqqS3IwsuDUiK9dD/KS0MwPgOuFDnOTW1R5XqmQd/ylt53w3s/U5g== + dependencies: + "@storybook/client-logger" "5.2.6" + "@storybook/theming" "5.2.6" + "@types/react-syntax-highlighter" "10.1.0" + "@types/react-textarea-autosize" "^4.3.3" + core-js "^3.0.1" + global "^4.3.2" + markdown-to-jsx "^6.9.1" + memoizerific "^1.11.3" + polished "^3.3.1" + popper.js "^1.14.7" + prop-types "^15.7.2" + react "^16.8.3" + react-dom "^16.8.3" + react-focus-lock "^1.18.3" + react-helmet-async "^1.0.2" + react-popper-tooltip "^2.8.3" + react-syntax-highlighter "^8.0.1" + react-textarea-autosize "^7.1.0" + simplebar-react "^1.0.0-alpha.6" + "@storybook/core-events@5.1.9": version "5.1.9" resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.1.9.tgz#441a6297e2ccfa743e15d1db1f4ac445b91f40d8" @@ -2307,6 +2401,13 @@ dependencies: core-js "^3.0.1" +"@storybook/core-events@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.2.6.tgz#34c9aae256e7e5f4a565b81f1e77dda8bccc6752" + integrity sha512-W8kLJ7tc0aAxs11CPUxUOCReocKL4MYGyjTg8qwk0USLzPUb/FUQWmhcm2ilFz6Nz8dXLcKrXdRVYTmiMsgAeg== + dependencies: + core-js "^3.0.1" + "@storybook/core@5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@storybook/core/-/core-5.2.5.tgz#cc04313480a1847aa6881420c675517cc400dc2e" @@ -2416,6 +2517,19 @@ memoizerific "^1.11.3" qs "^6.6.0" +"@storybook/router@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.2.6.tgz#5180d3785501699283c6c3717986c877f84fead5" + integrity sha512-/FZd3fYg5s2QzOqSIP8UMOSnCIFFIlli/jKlOxvm3WpcpxgwQOY4lfHsLO+r9ThCLs2UvVg2R/HqGrOHqDFU7A== + dependencies: + "@reach/router" "^1.2.1" + "@types/reach__router" "^1.2.3" + core-js "^3.0.1" + global "^4.3.2" + lodash "^4.17.15" + memoizerific "^1.11.3" + qs "^6.6.0" + "@storybook/theming@5.1.9": version "5.1.9" resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.1.9.tgz#c425f5867fae0db79e01112853b1808332a5f1a2" @@ -2452,6 +2566,24 @@ prop-types "^15.7.2" resolve-from "^5.0.0" +"@storybook/theming@5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.2.6.tgz#e04170b3e53dcfc791b2381c8a39192ae88cd291" + integrity sha512-Xa9R/H8DDgmvxsCHloJUJ2d9ZQl80AeqHrL+c/AKNpx05s9lV74DcinusCf0kz72YGUO/Xt1bAjuOvLnAaS8Gw== + dependencies: + "@emotion/core" "^10.0.14" + "@emotion/styled" "^10.0.14" + "@storybook/client-logger" "5.2.6" + common-tags "^1.8.0" + core-js "^3.0.1" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.14" + global "^4.3.2" + memoizerific "^1.11.3" + polished "^3.3.1" + prop-types "^15.7.2" + resolve-from "^5.0.0" + "@storybook/ui@5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-5.2.5.tgz#0c2c67216e4c808e39cdb48301cafde81b77d074" @@ -10713,6 +10845,14 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-to-jsx@^6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.10.3.tgz#7f0946684acd321125ff2de7fd258a9b9c7c40b7" + integrity sha512-PSoUyLnW/xoW6RsxZrquSSz5eGEOTwa15H5eqp3enmrp8esmgDJmhzd6zmQ9tgAA9TxJzx1Hmf3incYU/IamoQ== + dependencies: + prop-types "^15.6.2" + unquote "^1.1.0" + markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3: version "6.10.2" resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.10.2.tgz#644f602b81d088f10aef1c3674874876146cf38b" From 3ae71544d6cd6912bf35ea692e0ff65d0ef251a8 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Wed, 30 Oct 2019 13:39:02 +0100 Subject: [PATCH 118/189] Add story for Empty component Co-authored-by: @alina-beck --- webapp/components/Empty/Empty.story.js | 24 ++++++++++++++++++++++++ webapp/components/Empty/Empty.vue | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 webapp/components/Empty/Empty.story.js diff --git a/webapp/components/Empty/Empty.story.js b/webapp/components/Empty/Empty.story.js new file mode 100644 index 000000000..44d241df7 --- /dev/null +++ b/webapp/components/Empty/Empty.story.js @@ -0,0 +1,24 @@ +import { storiesOf } from '@storybook/vue' +import { withA11y } from '@storybook/addon-a11y' +import HcEmpty from '~/components/Empty/Empty' +import helpers from '~/storybook/helpers' + +helpers.init() + +storiesOf('Empty', module) + .addDecorator(withA11y) + .addDecorator(helpers.layout) + .add( + 'tasks icon with message', + () => ({ + components: { HcEmpty }, + template: '', + }), + { + notes: "Possible icons include 'messages', 'events', 'alert', 'tasks', 'docs', and 'file'", + }, + ) + .add('default icon, no message', () => ({ + components: { HcEmpty }, + template: '', + })) diff --git a/webapp/components/Empty/Empty.vue b/webapp/components/Empty/Empty.vue index 8760a6e6f..ea99702b5 100644 --- a/webapp/components/Empty/Empty.vue +++ b/webapp/components/Empty/Empty.vue @@ -26,7 +26,7 @@ export default { */ icon: { type: String, - required: true, + default: 'alert', validator: value => { return value.match(/(messages|events|alert|tasks|docs|file)/) }, From 542647f4186780595a0d831158f20144526a8727 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Wed, 30 Oct 2019 14:40:02 +0100 Subject: [PATCH 119/189] Add NotificationsDropdownFilter story, refactor - rename incorrectly named varaibles - use filterOptions to remove unneccessary computed routes Co-authored-by: @alina-beck --- .../NotificationsDropdownFilter.story.js | 18 ++++++++++ .../NotificationsDropdownFilter.vue | 33 +++++++------------ webapp/locales/de.json | 2 +- webapp/locales/en.json | 2 +- webapp/locales/pt.json | 2 +- webapp/pages/notifications/index.vue | 6 ++-- 6 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.story.js diff --git a/webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.story.js b/webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.story.js new file mode 100644 index 000000000..35724fe7e --- /dev/null +++ b/webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.story.js @@ -0,0 +1,18 @@ +import { storiesOf } from '@storybook/vue' +import { withA11y } from '@storybook/addon-a11y' +import { action } from '@storybook/addon-actions' +import NotificationsDropdownFilter from '~/components/NotificationsDropdownFilter/NotificationsDropdownFilter' +import helpers from '~/storybook/helpers' + +helpers.init() + +storiesOf('NotificationsDropdownFilter', module) + .addDecorator(withA11y) + .addDecorator(helpers.layout) + .add('filter dropdown', () => ({ + components: { NotificationsDropdownFilter }, + methods: { + filterNotifications: action('filterNotifications'), + }, + template: '', + })) diff --git a/webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.vue b/webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.vue index b0d1deb7d..7c5e0a42a 100644 --- a/webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.vue +++ b/webapp/components/NotificationsDropdownFilter/NotificationsDropdownFilter.vue @@ -1,5 +1,5 @@ From a215093bc3d55f3d0b8b762e70fd64664c89b0f6 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 12 Nov 2019 13:26:30 +0100 Subject: [PATCH 177/189] Make Donations reactive Co-authored-by: @alina-beck --- backend/src/schema/resolvers/donations.js | 4 ---- backend/src/schema/resolvers/donations.spec.js | 2 ++ webapp/graphql/Donations.js | 1 + webapp/pages/admin/donations.vue | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/src/schema/resolvers/donations.js b/backend/src/schema/resolvers/donations.js index 4057748e0..88149077d 100644 --- a/backend/src/schema/resolvers/donations.js +++ b/backend/src/schema/resolvers/donations.js @@ -1,13 +1,9 @@ -import uuid from 'uuid/v4' - export default { Mutation: { UpdateDonations: async (_parent, params, context, _resolveInfo) => { const { driver } = context const session = driver.session() let donations - params.id = params.id || uuid() - const writeTxResultPromise = session.writeTransaction(async txc => { const updateDonationsTransactionResponse = await txc.run( ` diff --git a/backend/src/schema/resolvers/donations.spec.js b/backend/src/schema/resolvers/donations.spec.js index 9c62a7dda..327688d3a 100644 --- a/backend/src/schema/resolvers/donations.spec.js +++ b/backend/src/schema/resolvers/donations.spec.js @@ -12,6 +12,7 @@ const driver = getDriver() const updateDonationsMutation = gql` mutation($goal: Int, $progress: Int) { UpdateDonations(goal: $goal, progress: $progress) { + id goal progress createdAt @@ -22,6 +23,7 @@ const updateDonationsMutation = gql` const donationsQuery = gql` query { Donations { + id goal progress } diff --git a/webapp/graphql/Donations.js b/webapp/graphql/Donations.js index 41d7cb87e..cc2a6a783 100644 --- a/webapp/graphql/Donations.js +++ b/webapp/graphql/Donations.js @@ -3,6 +3,7 @@ import gql from 'graphql-tag' export const DonationsQuery = () => gql` query { Donations { + id goal progress } diff --git a/webapp/pages/admin/donations.vue b/webapp/pages/admin/donations.vue index 22bd43e81..9ce2eb76f 100644 --- a/webapp/pages/admin/donations.vue +++ b/webapp/pages/admin/donations.vue @@ -49,7 +49,7 @@ export default { query() { return DonationsQuery() }, - result({ data: { Donations } }) { + update({ Donations }) { const { goal, progress } = Donations[0] this.formData = { goal, From e19990a58b3ca87b984db1ef2982834db5072231 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 12 Nov 2019 13:43:51 +0100 Subject: [PATCH 178/189] Fix lint --- webapp/pages/index.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/pages/index.spec.js b/webapp/pages/index.spec.js index aeb033cb9..d8587aaf4 100644 --- a/webapp/pages/index.spec.js +++ b/webapp/pages/index.spec.js @@ -54,7 +54,7 @@ describe('PostIndex', () => { 'auth/user': () => { return { id: 'u23' } }, - 'posts/currentPosts': () => [], + 'posts/currentPosts': () => [], }, mutations, }) From 8b5cd48d3c3e9093f089ae85000e32fbbe6f620a Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 12 Nov 2019 15:33:21 +0100 Subject: [PATCH 179/189] Translate success message, guard clause for no Donations --- webapp/components/DonationInfo/DonationInfo.vue | 5 +++-- webapp/locales/de.json | 3 ++- webapp/locales/en.json | 3 ++- webapp/locales/pt.json | 6 ++++++ webapp/pages/admin/donations.vue | 3 ++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/webapp/components/DonationInfo/DonationInfo.vue b/webapp/components/DonationInfo/DonationInfo.vue index 947f4bdbc..c7d42252b 100644 --- a/webapp/components/DonationInfo/DonationInfo.vue +++ b/webapp/components/DonationInfo/DonationInfo.vue @@ -17,7 +17,7 @@ export default { }, data() { return { - goal: 0, + goal: 15000, progress: 0, } }, @@ -39,7 +39,8 @@ export default { query() { return DonationsQuery() }, - result({ data: { Donations } }) { + update({ Donations }) { + if (!Donations[0]) return const { goal, progress } = Donations[0] this.goal = goal this.progress = progress diff --git a/webapp/locales/de.json b/webapp/locales/de.json index e70fd025a..a9cf7ce2c 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -384,7 +384,8 @@ "donations": { "name": "Spendeninfo", "goal": "Monatlich benötigte Spenden", - "progress": "Bereits gesammelte Spenden" + "progress": "Bereits gesammelte Spenden", + "successfulUpdate": "Spenden-Info erfolgreich aktualisiert!" } }, "post": { diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 958025370..15ef63a95 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -385,7 +385,8 @@ "donations": { "name": "Donations info", "goal": "Monthly donations needed", - "progress": "Donations collected so far" + "progress": "Donations collected so far", + "successfulUpdate": "Donations info updated successfully!" } }, "post": { diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 900b65f67..76560aba9 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -380,6 +380,12 @@ "name": "Convidar usuários", "title": "Convidar pessoas", "description": "Convites são uma maneira maravilhosa de ter seus amigos em sua rede …" + }, + "donations": { + "name": "Informações sobre Doações", + "goal": "Doações mensais necessárias", + "progress": "Doações arrecadadas até o momento", + "successfulUpdate": "Informações sobre doações atualizadas com sucesso!" } }, "post": { diff --git a/webapp/pages/admin/donations.vue b/webapp/pages/admin/donations.vue index 9ce2eb76f..7f0205be5 100644 --- a/webapp/pages/admin/donations.vue +++ b/webapp/pages/admin/donations.vue @@ -39,7 +39,7 @@ export default { }, }) .then(() => { - this.$toast.success('yay!!') + this.$toast.success(this.$t('admin.donations.successfulUpdate')) }) .catch(error => this.$toast.error(error.message)) }, @@ -50,6 +50,7 @@ export default { return DonationsQuery() }, update({ Donations }) { + if (!Donations[0]) return const { goal, progress } = Donations[0] this.formData = { goal, From f8b16b35aa3cf26d6144e0ed087dd83948efd78c Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Tue, 12 Nov 2019 18:12:25 +0300 Subject: [PATCH 180/189] change times to german time --- CONTRIBUTING.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 148b74d8c..0b2e66b9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,38 +49,40 @@ You can see the core team behind Human Connection [on our website](https://human ## Meetings and Pair Programming Sessions +Times below refer to **German Time** – that's CET (GMT+1) in winter and CEST (GMT+2) in summer – because most Human Connection core team members are living in Germany. + Daily standup -* every Monday–Friday 10:45 UTC +* every Monday–Friday 11:45 * in the discord `Conference Room` * all contributors welcome! * everybody shares what they are working on and asks for help if they are blocked Regular pair programming sessions -* every Monday, Wednesday and Thursday 14:00 UTC +* every Monday, Wednesday and Thursday 15:00 * the link will be posted in the [discord chat](https://discord.gg/6ub73U3) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project) * all contributors welcome! * we team up and work on an issue together (often using Visual Studio live sharing sessions) Open-Source Community Meeting -* every Thursday 12:00 UTC +* every Thursday 13:00 * the link will be posted in the [discord chat](https://discord.gg/6ub73U3) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project) * all contributors welcome! Meet the team -* every Monday 20:00 UTC (at the moment only in German) +* every Monday 21:00 (at the moment only in German) * details here https://human-connection.org/veranstaltungen/ * via this [zoom link](https://zoom.us/j/936943532) * all contributors and users of the network welcome! * users of the network chat with the Human Connection team and discuss current questions and issues Sprint planning -* bi-weekly on Tuesday 12:00 UTC +* bi-weekly on Tuesday 13:00 * via this [zoom link](https://zoom.us/j/7743582385) * all contributors welcome (recommended for those who want to work on an issue in this sprint) * we select and prioritise the issues we will work on in the following two weeks Sprint retrospective -* bi-weekly on Monday 12:00 UTC +* bi-weekly on Monday 13:00 * via this [zoom link](https://zoom.us/j/7743582385) * all contributors welcome (most interesting for those who participated in the sprint) * we review the past sprint and talk about what went well and what we could improve From 63d223e368273156ae9e173b29a54eb4f75605b0 Mon Sep 17 00:00:00 2001 From: Alina Beck Date: Tue, 12 Nov 2019 18:52:38 +0300 Subject: [PATCH 181/189] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Robert Schäfer --- webapp/components/DonationInfo/DonationInfo.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/DonationInfo/DonationInfo.vue b/webapp/components/DonationInfo/DonationInfo.vue index c7d42252b..10f42e880 100644 --- a/webapp/components/DonationInfo/DonationInfo.vue +++ b/webapp/components/DonationInfo/DonationInfo.vue @@ -1,7 +1,7 @@