-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement messages threads in react (#1173)
* Add Thread component for /messages/<username> * Marginally nicer tribes-in-common code * Minor formatting update * Style refinements, more flex, less row * Indentation fix * Implement infinite scroller for chat thread * Update package lock * Some code tidying * Mark messages as read * Add message sending and scrolling to bottom * Small code tidy * Don't send empty messages * Save message draft in local storage * Don't add fake messages on initial fetch * Still show message input when no messages * Add "You haven't been talking yet" bit in messages * Add message for non-public user * Add "Your profile seems quite empty" bit * Use lodash like lodash/func not lodash.func I was wrong! * Add quick replies * Remove premature optimization Should probably use useMemo there anyway * Preserve position on resize and styling fixes * More styled-components, less inline style * Extra a few components into their own files * Add Prettier - an opinionated code formatter (#932) Lint and prettify staged files in pre-commit hook Reformat js, md, html and json files Remove eslint rules that conflict with prettier * Reformat messages thread files with prettier * Add message when user does not exist * Minor tidy * Put all messages text into i18n * A little refactor * Use loading indictor ... and fixup some imports * Add initial message thread tests * Update translations Not sure I should add this as some of the changes look odd... * Add note about fake mongo id generator * Move more general components into better places * Implement paginated messages loading * Fix tests to use new paginated API * Remove comment It's actually too hard to write a pagination test for now because there is no scrollHeight in jsdom as it's not actually doing the layout stuff, so can't really test that bit. Would be nice to have a way to test the _logic_ though... * Add some documentation about InfiniteMessages * Remove undeeded @todos * Redirect back to inbox if visiting self thread * Remove old angular messages stuff * Remove redundant error handling * Use mongolib to generate test id * Revert translations to master * Don't use translation function with dynamic text * Remove unused thread dimensions directive * Fix messages-read API response * Remove trailing <br> inside TrEditor Moving it a bit closer to the source * Show monkeybox for the correct chat user * Refocus input after sending a message * Fix link to profile in Monkeybox * Always show reply box for existing conversations * Add Monkeybox "Languages" heading to i18n Co-authored-by: Mikael Korpela <[email protected]>
- Loading branch information
1 parent
734c546
commit 3acc87e
Showing
35 changed files
with
16,397 additions
and
1,022 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
import React, { useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import MediumEditor from 'react-medium-editor'; | ||
import 'medium-editor/dist/css/medium-editor.css'; | ||
|
||
const options = { | ||
disableReturn: false, | ||
disableDoubleReturn: false, | ||
disableExtraSpaces: false, | ||
// Automatically turns URLs entered into | ||
// the text field into HTML anchor tags | ||
autoLink: false, | ||
paste: { | ||
// Forces pasting as plain text | ||
forcePlainText: false, | ||
// Cleans pasted content from different sources, like google docs etc | ||
cleanPastedHTML: true, | ||
// List of element attributes to remove during | ||
// paste when `cleanPastedHTML` is `true` | ||
cleanAttrs: [ | ||
'class', | ||
'style', | ||
'dir', | ||
'id', | ||
'title', | ||
'target', | ||
'tabindex', | ||
'onclick', | ||
'oncontextmenu', | ||
'ondblclick', | ||
'onmousedown', | ||
'onmouseenter', | ||
'onmouseleave', | ||
'onmousemove', | ||
'onmouseover', | ||
'onmouseout', | ||
'onmouseup', | ||
'onwheel', | ||
'onmousewheel', | ||
'onmessage', | ||
'ontouchstart', | ||
'ontouchmove', | ||
'ontouchend', | ||
'ontouchcancel', | ||
'onload', | ||
'onscroll', | ||
], | ||
// list of element tag names to remove during | ||
// paste when `cleanPastedHTML` is `true` | ||
cleanTags: [ | ||
'link', | ||
'iframe', | ||
'frameset', | ||
'noframes', | ||
'object', | ||
'video', | ||
'audio', | ||
'track', | ||
'source', | ||
'base', | ||
'basefont', | ||
'applet', | ||
'param', | ||
'embed', | ||
'script', | ||
'meta', | ||
'head', | ||
'title', | ||
'svg', | ||
'script', | ||
'style', | ||
'input', | ||
'textarea', | ||
'form', | ||
'hr', | ||
'select', | ||
'optgroup', | ||
'label', | ||
'img', | ||
'canvas', | ||
'area', | ||
'map', | ||
'figure', | ||
'picture', | ||
'figcaption', | ||
'noscript', | ||
], | ||
// list of element tag names to unwrap (remove the element tag but retain | ||
// its child elements) during paste when `cleanPastedHTML` is `true` | ||
unwrapTags: [ | ||
'!DOCTYPE', | ||
'html', | ||
'body', | ||
'h1', | ||
'h2', | ||
'h3', | ||
'h4', | ||
'h5', | ||
'h6', | ||
'table', | ||
'th', | ||
'tr', | ||
'td', | ||
'tbody', | ||
'thead', | ||
'tfoot', | ||
'article', | ||
'header', | ||
'footer', | ||
'section', | ||
'aside', | ||
'font', | ||
'center', | ||
'big', | ||
'code', | ||
'pre', | ||
'small', | ||
'button', | ||
'label', | ||
'fieldset', | ||
'legend', | ||
'datalist', | ||
'keygen', | ||
'output', | ||
'nav', | ||
'main', | ||
'div', | ||
'span', | ||
], | ||
}, | ||
// Toolbar buttons which appear when highlighting text | ||
toolbar: { | ||
buttons: [ | ||
{ | ||
name: 'bold', | ||
contentDefault: '<span class="icon-bold"></span>', | ||
}, | ||
{ | ||
name: 'italic', | ||
contentDefault: '<span class="icon-italic"></span>', | ||
}, | ||
{ | ||
name: 'underline', | ||
contentDefault: '<span class="icon-underline"></span>', | ||
}, | ||
{ | ||
name: 'anchor', | ||
contentDefault: '<span class="icon-link"></span>', | ||
}, | ||
{ | ||
name: 'quote', | ||
contentDefault: '<span class="icon-quote"></span>', | ||
}, | ||
{ | ||
name: 'unorderedlist', | ||
contentDefault: '<span class="icon-list"></span>', | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
// medium-editor can give us a <br> at the end that we don't want | ||
function removeTrailingBr(value) { | ||
return value.replace(/<br><\/p>$/, '</p>'); | ||
} | ||
|
||
export default function TrEditor({ id, text, onChange, onCtrlEnter }) { | ||
const ref = React.createRef(); | ||
|
||
useEffect(() => { | ||
const { medium } = ref.current; | ||
const onEnter = event => event.ctrlKey && onCtrlEnter(event); | ||
medium.subscribe('editableKeydownEnter', onEnter); | ||
return () => { | ||
// the onCtrlEnter that gets passed through will change quite a lot as it | ||
// probably gets redefined over and over with different bound state | ||
// this means it'll actually subscribe/unsubscribe per keypress... | ||
// seems a bit much, but that's how these react hooks work! | ||
medium.unsubscribe('editableKeydownEnter', onEnter); | ||
}; | ||
}, [onCtrlEnter]); | ||
|
||
const props = { id, text, options, className: 'tr-editor' }; | ||
return ( | ||
<MediumEditor | ||
ref={ref} | ||
onChange={value => onChange(removeTrailingBr(value))} | ||
{...props} | ||
/> | ||
); | ||
} | ||
|
||
TrEditor.defaultProps = { | ||
onCtrlEnter: () => {}, | ||
}; | ||
|
||
TrEditor.propTypes = { | ||
id: PropTypes.string, | ||
text: PropTypes.string.isRequired, | ||
onChange: PropTypes.func.isRequired, | ||
onCtrlEnter: PropTypes.func.isRequired, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import React from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
export default function Flashcard() { | ||
const { t } = useTranslation('messages'); | ||
|
||
const flashcards = [ | ||
{ | ||
title: t('Make sure your profile is complete'), | ||
content: t( | ||
"You're much more likely to get a positive response if you have written a bit about yourself.", | ||
), | ||
}, | ||
{ | ||
title: t('Tell a little bit about yourself'), | ||
content: t( | ||
"You're much more likely to get a positive response if you have written a bit about yourself.", | ||
), | ||
}, | ||
{ | ||
title: t('Explain to them why you are choosing them'), | ||
content: t( | ||
'...explaining that you are interested in meeting them, not just looking for free accommodation.', | ||
), | ||
}, | ||
{ | ||
title: t("Tell your host why you're on a trip"), | ||
content: t( | ||
'What are your expectations in regards with going through their town?', | ||
), | ||
}, | ||
{ | ||
title: t('Trustroots is very much about spontaneous travel'), | ||
content: t("Don't write to people 2 months ahead."), | ||
}, | ||
]; | ||
|
||
function getRandomCard() { | ||
return flashcards[Math.floor(Math.random() * flashcards.length)]; | ||
} | ||
|
||
const { title, content } = getRandomCard(); | ||
return ( | ||
<a href="/guide" className="tr-flashcards text-center font-brand-regular"> | ||
<small className="tr-flashcards-tip text-uppercase">{t('Tip')}</small> | ||
<p className="tr-flashcards-title">{title}</p> | ||
<p className="tr-flashcards-content">{content}</p> | ||
</a> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.