-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #253 from podStation/enhancement#233__reply-to-com…
- Loading branch information
Showing
5 changed files
with
287 additions
and
4 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,129 @@ | ||
import * as React from 'react' | ||
|
||
import './styles.scss' | ||
|
||
interface ICommentMenuProps { | ||
/** | ||
* URL of the comment | ||
*/ | ||
url: string, | ||
commenterUrl: string, | ||
commenterAccount: string, | ||
} | ||
|
||
interface ICommentMenuState { | ||
interactorAccount?: string, | ||
} | ||
|
||
class CommentMenu extends React.PureComponent<ICommentMenuProps, ICommentMenuState> { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
interactorAccount: localStorage.getItem('commentsInteractorAccount') | ||
} | ||
} | ||
|
||
onClickCopyLink(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) { | ||
e.preventDefault(); | ||
navigator.clipboard.writeText(this.props.url); | ||
|
||
// Some form of "toast message" would be better, but I don't | ||
// think something like that is already implemented, meanwhile, | ||
// we use an alert, it is ugly, but it show the user there was | ||
// a reaction. | ||
alert('Link to post copied'); | ||
} | ||
|
||
async onClickReply(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>): Promise<void> { | ||
e.preventDefault(); | ||
|
||
const remoteInteractUrlPattern = await this.fetchRemoteInteractUrlPattern(); | ||
|
||
if(!remoteInteractUrlPattern) { | ||
// TODO: error handling | ||
} | ||
|
||
// alert(remoteInteractUrlPattern); | ||
const remoteInteractUrl = remoteInteractUrlPattern.replace('{uri}', encodeURI(this.props.url)); | ||
window.open(remoteInteractUrl, '_blank'); | ||
} | ||
|
||
async onClickFollow(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>): Promise<void> { | ||
e.preventDefault(); | ||
|
||
const remoteInteractUrlPattern = await this.fetchRemoteInteractUrlPattern(); | ||
|
||
if(!remoteInteractUrlPattern) { | ||
// TODO: error handling | ||
} | ||
|
||
// alert(remoteInteractUrlPattern); | ||
const remoteInteractUrl = remoteInteractUrlPattern.replace('{uri}', encodeURI(CommentMenu.stripLeadingAt(this.props.commenterAccount))); | ||
window.open(remoteInteractUrl, '_blank'); | ||
} | ||
|
||
async fetchRemoteInteractUrlPattern(): Promise<string> { | ||
let interactorAccount = localStorage.getItem('commentsInteractorAccount'); | ||
|
||
if(!interactorAccount) { | ||
interactorAccount = prompt('What is your Fediverse account?'); | ||
|
||
if(interactorAccount) { | ||
localStorage.setItem('commentsInteractorAccount', interactorAccount); | ||
} | ||
} | ||
|
||
if(!interactorAccount) { | ||
return; | ||
} | ||
|
||
let remoteInteractUrlPattern = localStorage.getItem('commentsRemoteInteractUrlPattern'); | ||
|
||
if(!remoteInteractUrlPattern) { | ||
const response = await fetch('/api/comments/remoteInteractUrlPattern?' + new URLSearchParams({ | ||
interactorAccount: interactorAccount | ||
})); | ||
|
||
const parsedResponse = await response.json(); | ||
|
||
remoteInteractUrlPattern = parsedResponse.remoteInteractUrlPattern; | ||
|
||
if(remoteInteractUrlPattern) { | ||
localStorage.setItem('commentsRemoteInteractUrlPattern', remoteInteractUrlPattern); | ||
} | ||
} | ||
|
||
return remoteInteractUrlPattern; | ||
} | ||
|
||
static stripLeadingAt(account: string) { | ||
return account.substring(1); | ||
} | ||
|
||
onClickForgetHomeInstanceHost(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>): void { | ||
e.preventDefault(); | ||
|
||
localStorage.removeItem('commentsInteractorAccount'); | ||
localStorage.removeItem('commentsRemoteInteractUrlPattern'); | ||
|
||
this.setState({ | ||
interactorAccount: undefined | ||
}); | ||
} | ||
|
||
render(): React.ReactNode { | ||
return ( | ||
<menu> | ||
<a href={this.props.url} target='_blank' onClick={(e) => this.onClickReply(e)}>Reply to this post</a> | ||
<a href={this.props.url} onClick={(e) => this.onClickCopyLink(e)}>Copy link to this post</a> | ||
<a href={this.props.url} target='_blank'>Open in original site</a> | ||
<a href={this.props.commenterUrl} target='_blank' onClick={(e) => this.onClickFollow(e)}>Follow {this.props.commenterAccount}</a> | ||
{this.state.interactorAccount && | ||
<a onClick={(e) => this.onClickForgetHomeInstanceHost(e)}>Forget {this.state.interactorAccount}</a> | ||
} | ||
</menu> | ||
) | ||
} | ||
} | ||
|
||
export default CommentMenu; |
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,84 @@ | ||
.context-menu { | ||
all: unset; | ||
position: relative; | ||
} | ||
|
||
.context-menu svg { | ||
display: block; | ||
} | ||
|
||
.context-menu menu { | ||
position: absolute; | ||
top: 100%; | ||
right: 0; | ||
display: flex; | ||
flex-direction: column; | ||
background: var(--button-background); | ||
margin: 0; | ||
padding: .75rem; | ||
z-index: 1; | ||
border-radius: .25rem; | ||
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.25); | ||
} | ||
|
||
.context-menu menu a { | ||
white-space: nowrap; | ||
text-decoration: none; | ||
line-height: 2; | ||
} | ||
|
||
.dialog-homeinstance { | ||
border: 1px solid rgba(0, 0, 0, 0.25); | ||
border-radius: 0.5rem; | ||
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.25); | ||
width: calc((100% - 6px) - 2em); | ||
max-width: 28rem; | ||
} | ||
|
||
.dialog-homeinstance form { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 0.5rem; | ||
margin: 0; | ||
} | ||
|
||
.dialog-homeinstance input { | ||
padding: 0.5rem; | ||
} | ||
|
||
.dialog-homeinstance menu { | ||
display: flex; | ||
flex-wrap: wrap; | ||
flex-direction: row; | ||
justify-content: flex-end; | ||
gap: .5rem; | ||
margin: 1.5rem 0 0 0; | ||
} | ||
|
||
.dialog-homeinstance button { | ||
border: none; | ||
padding: .5rem; | ||
border-radius: 4px; | ||
min-width: 4.5rem; | ||
background-color: #2962ff; | ||
font-size: 0.875rem; | ||
font-weight: 500; | ||
letter-spacing: .25px; | ||
line-height: 1rem; | ||
outline: none; | ||
color: #FFF; | ||
} | ||
|
||
.dialog-homeinstance button:hover { | ||
background-color: #2F7DE2; | ||
} | ||
|
||
.dialog-homeinstance button[type="reset"] { | ||
background-color: transparent; | ||
box-shadow: inset 0 0 0 1px #DADCE0; | ||
color: #2962ff; | ||
} | ||
|
||
.dialog-homeinstance button[type="reset"]:hover { | ||
background-color: #EAF2FD; | ||
} |
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