split screenshot function/button into two parts (#1027)
* split screenshot function into 4 separate helpers * screenshot download button in import dialog box * thumbnail button inside map info box * import blue button styling * fight with styling to make the button at least appear * add more text * fix tooltip display * automatically start downloading the screenshot * eslint * revamp GlobalUI.notifyUser * fix object destructuring syntax * fix
This commit is contained in:
parent
a9f19815e4
commit
af2c6ebef1
9 changed files with 153 additions and 45 deletions
|
@ -1558,6 +1558,7 @@ h3.filterBox {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0.75em;
|
margin: 0.75em;
|
||||||
padding: 0.75em;
|
padding: 0.75em;
|
||||||
|
padding-top: 0.85em;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
background-color: #AAB0FB;
|
background-color: #AAB0FB;
|
||||||
border-radius: 0.3em;
|
border-radius: 0.3em;
|
||||||
|
@ -2051,6 +2052,43 @@ and it won't be important on password protected instances */
|
||||||
.yourMap .mapInfoDelete {
|
.yourMap .mapInfoDelete {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mapInfoButtonsWrapper .mapInfoThumbnail {
|
||||||
|
display: block;
|
||||||
|
background-image: url(<%= asset_path('screenshot_sprite.png') %>);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
bottom: -9px;
|
||||||
|
right: 3px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #eee;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-position: -32px 0;
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 3px 5px 2px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mapInfoButtonsWrapper span {
|
.mapInfoButtonsWrapper span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -34,6 +34,11 @@
|
||||||
<p class="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
|
<p class="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
|
||||||
<p class="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
|
<p class="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
|
||||||
<div class="mapInfoButtonsWrapper">
|
<div class="mapInfoButtonsWrapper">
|
||||||
|
<div class="mapInfoThumbnail">
|
||||||
|
<div class="thumbnail"></div>
|
||||||
|
<div class="tooltip">Update Thumbnail</div>
|
||||||
|
<span>Thumb</span>
|
||||||
|
</div>
|
||||||
<div class="mapInfoDelete">
|
<div class="mapInfoDelete">
|
||||||
<div class="deleteMap"></div>
|
<div class="deleteMap"></div>
|
||||||
<span>Delete</span>
|
<span>Delete</span>
|
||||||
|
|
|
@ -77,6 +77,11 @@
|
||||||
<p class="mapCreatedAt"><span>Created by:</span> <%= @map.user == user ? "You" : @map.user.name %> on <%= @map.created_at.strftime("%m/%d/%Y") %></p>
|
<p class="mapCreatedAt"><span>Created by:</span> <%= @map.user == user ? "You" : @map.user.name %> on <%= @map.created_at.strftime("%m/%d/%Y") %></p>
|
||||||
<p class="mapEditedAt"><span>Last edited:</span> <%= @map.updated_at.strftime("%m/%d/%Y") %></p>
|
<p class="mapEditedAt"><span>Last edited:</span> <%= @map.updated_at.strftime("%m/%d/%Y") %></p>
|
||||||
<div class="mapInfoButtonsWrapper">
|
<div class="mapInfoButtonsWrapper">
|
||||||
|
<div class="mapInfoThumbnail">
|
||||||
|
<div class="thumbnail"></div>
|
||||||
|
<div class="tooltip">Update Thumbnail</div>
|
||||||
|
<span>Thumb</span>
|
||||||
|
</div>
|
||||||
<div class="mapInfoDelete">
|
<div class="mapInfoDelete">
|
||||||
<div class="deleteMap"></div>
|
<div class="deleteMap"></div>
|
||||||
<span>Delete</span>
|
<span>Delete</span>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import outdent from 'outdent'
|
||||||
import ImportDialogBox from '../../components/ImportDialogBox'
|
import ImportDialogBox from '../../components/ImportDialogBox'
|
||||||
|
|
||||||
import PasteInput from '../PasteInput'
|
import PasteInput from '../PasteInput'
|
||||||
|
import Map from '../Map'
|
||||||
|
|
||||||
const ImportDialog = {
|
const ImportDialog = {
|
||||||
openLightbox: null,
|
openLightbox: null,
|
||||||
|
@ -24,14 +25,19 @@ const ImportDialog = {
|
||||||
`))
|
`))
|
||||||
ReactDOM.render(React.createElement(ImportDialogBox, {
|
ReactDOM.render(React.createElement(ImportDialogBox, {
|
||||||
onFileAdded: PasteInput.handleFile,
|
onFileAdded: PasteInput.handleFile,
|
||||||
exampleImageUrl: serverData['import-example.png']
|
exampleImageUrl: serverData['import-example.png'],
|
||||||
|
downloadScreenshot: ImportDialog.downloadScreenshot
|
||||||
}), $('.importDialogWrapper').get(0))
|
}), $('.importDialogWrapper').get(0))
|
||||||
},
|
},
|
||||||
show: function() {
|
show: function() {
|
||||||
ImportDialog.openLightbox('import-dialog')
|
ImportDialog.openLightbox('import-dialog')
|
||||||
},
|
},
|
||||||
hide: function() {
|
hide: function() {
|
||||||
ImportDialog.closeLightbox('import-dialog')
|
ImportDialog.closeLightbox()
|
||||||
|
},
|
||||||
|
downloadScreenshot: function() {
|
||||||
|
ImportDialog.hide()
|
||||||
|
Map.offerScreenshotDownload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,11 @@ import NotificationIcon from './NotificationIcon'
|
||||||
|
|
||||||
const GlobalUI = {
|
const GlobalUI = {
|
||||||
notifyTimeout: null,
|
notifyTimeout: null,
|
||||||
|
notifyQueue: [],
|
||||||
|
notifying: false,
|
||||||
lightbox: null,
|
lightbox: null,
|
||||||
init: function(serverData) {
|
init: function(serverData) {
|
||||||
var self = GlobalUI
|
const self = GlobalUI
|
||||||
|
|
||||||
self.Search.init(serverData)
|
self.Search.init(serverData)
|
||||||
self.CreateMap.init(serverData)
|
self.CreateMap.init(serverData)
|
||||||
|
@ -45,7 +47,7 @@ const GlobalUI = {
|
||||||
}, 200, 'easeInCubic', function() { $(this).hide() })
|
}, 200, 'easeInCubic', function() { $(this).hide() })
|
||||||
},
|
},
|
||||||
openLightbox: function(which) {
|
openLightbox: function(which) {
|
||||||
var self = GlobalUI
|
const self = GlobalUI
|
||||||
|
|
||||||
$('.lightboxContent').hide()
|
$('.lightboxContent').hide()
|
||||||
$('#' + which).show()
|
$('#' + which).show()
|
||||||
|
@ -72,7 +74,7 @@ const GlobalUI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
closeLightbox: function(event) {
|
closeLightbox: function(event) {
|
||||||
var self = GlobalUI
|
const self = GlobalUI
|
||||||
|
|
||||||
if (event) event.preventDefault()
|
if (event) event.preventDefault()
|
||||||
|
|
||||||
|
@ -96,23 +98,45 @@ const GlobalUI = {
|
||||||
}
|
}
|
||||||
self.lightbox = null
|
self.lightbox = null
|
||||||
},
|
},
|
||||||
notifyUser: function(message, leaveOpen) {
|
notifyUser: function(message, opts = {}) {
|
||||||
var self = GlobalUI
|
const self = GlobalUI
|
||||||
|
|
||||||
|
if (self.notifying) {
|
||||||
|
self.notifyQueue.push({ message, opts })
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
self._notifyUser(message, opts)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// note: use the wrapper function notifyUser instead of this one
|
||||||
|
_notifyUser: function(message, opts = {}) {
|
||||||
|
const self = GlobalUI
|
||||||
|
|
||||||
|
const { leaveOpen = false, timeOut = 8000 } = opts
|
||||||
|
|
||||||
$('#toast').html(message)
|
$('#toast').html(message)
|
||||||
self.showDiv('#toast')
|
self.showDiv('#toast')
|
||||||
clearTimeout(self.notifyTimeOut)
|
clearTimeout(self.notifyTimeOut)
|
||||||
|
|
||||||
if (!leaveOpen) {
|
if (!leaveOpen) {
|
||||||
self.notifyTimeOut = setTimeout(function() {
|
self.notifyTimeOut = setTimeout(function() {
|
||||||
self.hideDiv('#toast')
|
GlobalUI.clearNotify()
|
||||||
}, 8000)
|
}, timeOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.notifying = true
|
||||||
},
|
},
|
||||||
clearNotify: function() {
|
clearNotify: function() {
|
||||||
var self = GlobalUI
|
const self = GlobalUI
|
||||||
|
|
||||||
clearTimeout(self.notifyTimeOut)
|
// if there are messages remaining, display them
|
||||||
self.hideDiv('#toast')
|
if (self.notifyQueue.length > 0) {
|
||||||
|
const { message, opts } = self.notifyQueue.shift()
|
||||||
|
self._notifyUser(message, opts)
|
||||||
|
} else {
|
||||||
|
self.hideDiv('#toast')
|
||||||
|
self.notifying = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
shareInvite: function(inviteLink) {
|
shareInvite: function(inviteLink) {
|
||||||
clipboard.copy({
|
clipboard.copy({
|
||||||
|
|
|
@ -35,9 +35,11 @@ const InfoBox = {
|
||||||
data-bip-value="{{desc}}"
|
data-bip-value="{{desc}}"
|
||||||
>{{desc}}</span>`,
|
>{{desc}}</span>`,
|
||||||
userImageUrl: '',
|
userImageUrl: '',
|
||||||
init: function(serverData) {
|
init: function(serverData, updateThumbnail) {
|
||||||
var self = InfoBox
|
var self = InfoBox
|
||||||
|
|
||||||
|
self.updateThumbnail = updateThumbnail
|
||||||
|
|
||||||
$('.mapInfoIcon').click(self.toggleBox)
|
$('.mapInfoIcon').click(self.toggleBox)
|
||||||
$('.mapInfoBox').click(function(event) {
|
$('.mapInfoBox').click(function(event) {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
@ -181,6 +183,7 @@ const InfoBox = {
|
||||||
$('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
|
$('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
|
||||||
|
|
||||||
$('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
|
$('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
|
||||||
|
$('.mapInfoThumbnail').unbind().click(self.updateThumbnail)
|
||||||
|
|
||||||
$('.mapContributors span, #mapContribs').unbind().click(function(event) {
|
$('.mapContributors span, #mapContribs').unbind().click(function(event) {
|
||||||
$('.mapContributors .tip').toggle()
|
$('.mapContributors .tip').toggle()
|
||||||
|
|
|
@ -45,7 +45,10 @@ const Map = {
|
||||||
GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
|
GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
|
||||||
|
|
||||||
self.updateStar()
|
self.updateStar()
|
||||||
InfoBox.init(serverData)
|
|
||||||
|
InfoBox.init(serverData, function updateThumbnail() {
|
||||||
|
self.uploadMapScreenshot()
|
||||||
|
})
|
||||||
CheatSheet.init(serverData)
|
CheatSheet.init(serverData)
|
||||||
|
|
||||||
$('.viewOnly .requestAccess').click(self.requestAccess)
|
$('.viewOnly .requestAccess').click(self.requestAccess)
|
||||||
|
@ -251,6 +254,48 @@ const Map = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exportImage: function() {
|
exportImage: function() {
|
||||||
|
Map.uploadMapScreenshot()
|
||||||
|
Map.offerScreenshotDownload()
|
||||||
|
GlobalUI.notifyUser('Note: this button is going away. Check the map card or the import box for setting the map thumbnail or downloading a screenshot.')
|
||||||
|
},
|
||||||
|
offerScreenshotDownload: () => {
|
||||||
|
const canvas = Map.getMapCanvasForScreenshots()
|
||||||
|
const filename = Map.getMapScreenshotFilename(Active.Map)
|
||||||
|
|
||||||
|
var downloadMessage = outdent`
|
||||||
|
Captured map screenshot!
|
||||||
|
<a id="map-screenshot-download-link"
|
||||||
|
href="${canvas.canvas.toDataURL()}"
|
||||||
|
download="${filename}"
|
||||||
|
>
|
||||||
|
DOWNLOAD
|
||||||
|
</a>`
|
||||||
|
GlobalUI.notifyUser(downloadMessage)
|
||||||
|
},
|
||||||
|
uploadMapScreenshot: () => {
|
||||||
|
const canvas = Map.getMapCanvasForScreenshots()
|
||||||
|
const filename = Map.getMapScreenshotFilename(Active.Map)
|
||||||
|
|
||||||
|
canvas.canvas.toBlob(imageBlob => {
|
||||||
|
const formData = new window.FormData()
|
||||||
|
formData.append('map[screenshot]', imageBlob, filename)
|
||||||
|
$.ajax({
|
||||||
|
type: 'PATCH',
|
||||||
|
dataType: 'json',
|
||||||
|
url: `/maps/${Active.Map.id}`,
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(data) {
|
||||||
|
GlobalUI.notifyUser('Successfully updated map screenshot.')
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
GlobalUI.notifyUser('Failed to update map screenshot.')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getMapCanvasForScreenshots: () => {
|
||||||
var canvas = {}
|
var canvas = {}
|
||||||
|
|
||||||
canvas.canvas = document.createElement('canvas')
|
canvas.canvas = document.createElement('canvas')
|
||||||
|
@ -336,8 +381,9 @@ const Map = {
|
||||||
node.visited = !T
|
node.visited = !T
|
||||||
})
|
})
|
||||||
|
|
||||||
var map = Active.Map
|
return canvas
|
||||||
|
},
|
||||||
|
getMapScreenshotFilename: map => {
|
||||||
var today = new Date()
|
var today = new Date()
|
||||||
var dd = today.getDate()
|
var dd = today.getDate()
|
||||||
var mm = today.getMonth() + 1 // January is 0!
|
var mm = today.getMonth() + 1 // January is 0!
|
||||||
|
@ -352,30 +398,7 @@ const Map = {
|
||||||
|
|
||||||
var mapName = map.get('name').split(' ').join(['-'])
|
var mapName = map.get('name').split(' ').join(['-'])
|
||||||
const filename = `metamap-${map.id}-${mapName}-${today}.png`
|
const filename = `metamap-${map.id}-${mapName}-${today}.png`
|
||||||
|
return filename
|
||||||
var downloadMessage = outdent`
|
|
||||||
Captured map screenshot!
|
|
||||||
<a href="${canvas.canvas.toDataURL()}" download="${filename}">DOWNLOAD</a>`
|
|
||||||
GlobalUI.notifyUser(downloadMessage)
|
|
||||||
|
|
||||||
canvas.canvas.toBlob(imageBlob => {
|
|
||||||
const formData = new window.FormData()
|
|
||||||
formData.append('map[screenshot]', imageBlob, filename)
|
|
||||||
$.ajax({
|
|
||||||
type: 'PATCH',
|
|
||||||
dataType: 'json',
|
|
||||||
url: `/maps/${map.id}`,
|
|
||||||
data: formData,
|
|
||||||
processData: false,
|
|
||||||
contentType: false,
|
|
||||||
success: function(data) {
|
|
||||||
console.log('successfully uploaded map screenshot')
|
|
||||||
},
|
|
||||||
error: function() {
|
|
||||||
console.log('failed to save map screenshot')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ export const invitedToCall = self => inviter => {
|
||||||
notifyText += username + ' is inviting you to a conversation. Join live?'
|
notifyText += username + ' is inviting you to a conversation. Join live?'
|
||||||
notifyText += ' <button type="button" class="toast-button button yes">Yes</button>'
|
notifyText += ' <button type="button" class="toast-button button yes">Yes</button>'
|
||||||
notifyText += ' <button type="button" class="toast-button button btn-no no">No</button>'
|
notifyText += ' <button type="button" class="toast-button button btn-no no">No</button>'
|
||||||
GlobalUI.notifyUser(notifyText, true)
|
GlobalUI.notifyUser(notifyText, { leaveOpen: true })
|
||||||
$('#toast button.yes').click(e => self.acceptCall(inviter))
|
$('#toast button.yes').click(e => self.acceptCall(inviter))
|
||||||
$('#toast button.no').click(e => self.denyCall(inviter))
|
$('#toast button.no').click(e => self.denyCall(inviter))
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ export const invitedToJoin = self => inviter => {
|
||||||
var notifyText = username + ' is inviting you to the conversation. Join?'
|
var notifyText = username + ' is inviting you to the conversation. Join?'
|
||||||
notifyText += ' <button type="button" class="toast-button button yes">Yes</button>'
|
notifyText += ' <button type="button" class="toast-button button yes">Yes</button>'
|
||||||
notifyText += ' <button type="button" class="toast-button button btn-no no">No</button>'
|
notifyText += ' <button type="button" class="toast-button button btn-no no">No</button>'
|
||||||
GlobalUI.notifyUser(notifyText, true)
|
GlobalUI.notifyUser(notifyText, { leaveOpen: true })
|
||||||
$('#toast button.yes').click(e => self.joinCall())
|
$('#toast button.yes').click(e => self.joinCall())
|
||||||
$('#toast button.no').click(e => self.denyInvite(inviter))
|
$('#toast button.no').click(e => self.denyInvite(inviter))
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ export const callInProgress = self => () => {
|
||||||
var notifyText = "There's a conversation happening, want to join?"
|
var notifyText = "There's a conversation happening, want to join?"
|
||||||
notifyText += ' <button type="button" class="toast-button button yes">Yes</button>'
|
notifyText += ' <button type="button" class="toast-button button yes">Yes</button>'
|
||||||
notifyText += ' <button type="button" class="toast-button button btn-no no">No</button>'
|
notifyText += ' <button type="button" class="toast-button button btn-no no">No</button>'
|
||||||
GlobalUI.notifyUser(notifyText, true)
|
GlobalUI.notifyUser(notifyText, { leaveOpen: true })
|
||||||
$('#toast button.yes').click(e => self.joinCall())
|
$('#toast button.yes').click(e => self.joinCall())
|
||||||
$('#toast button.no').click(e => GlobalUI.clearNotify())
|
$('#toast button.no').click(e => GlobalUI.clearNotify())
|
||||||
ChatView.conversationInProgress()
|
ChatView.conversationInProgress()
|
||||||
|
@ -212,7 +212,7 @@ export const callStarted = self => () => {
|
||||||
var notifyText = "There's a conversation starting, want to join?"
|
var notifyText = "There's a conversation starting, want to join?"
|
||||||
notifyText += ' <button type="button" class="toast-button button">Yes</button>'
|
notifyText += ' <button type="button" class="toast-button button">Yes</button>'
|
||||||
notifyText += ' <button type="button" class="toast-button button btn-no">No</button>'
|
notifyText += ' <button type="button" class="toast-button button btn-no">No</button>'
|
||||||
GlobalUI.notifyUser(notifyText, true)
|
GlobalUI.notifyUser(notifyText, { leaveOpen: true })
|
||||||
$('#toast button.yes').click(e => self.joinCall())
|
$('#toast button.yes').click(e => self.joinCall())
|
||||||
$('#toast button.no').click(e => GlobalUI.clearNotify())
|
$('#toast button.no').click(e => GlobalUI.clearNotify())
|
||||||
ChatView.conversationInProgress()
|
ChatView.conversationInProgress()
|
||||||
|
|
|
@ -27,6 +27,9 @@ class ImportDialogBox extends Component {
|
||||||
<div className="import-blue-button" onClick={this.handleExport('json')}>
|
<div className="import-blue-button" onClick={this.handleExport('json')}>
|
||||||
Export as JSON
|
Export as JSON
|
||||||
</div>
|
</div>
|
||||||
|
<div className="import-blue-button" onClick={this.props.downloadScreenshot}>
|
||||||
|
Download screenshot
|
||||||
|
</div>
|
||||||
<h3>IMPORT</h3>
|
<h3>IMPORT</h3>
|
||||||
<p>To upload a file, drop it here:</p>
|
<p>To upload a file, drop it here:</p>
|
||||||
<Dropzone onDropAccepted={this.handleFile}
|
<Dropzone onDropAccepted={this.handleFile}
|
||||||
|
@ -42,7 +45,8 @@ class ImportDialogBox extends Component {
|
||||||
|
|
||||||
ImportDialogBox.propTypes = {
|
ImportDialogBox.propTypes = {
|
||||||
onFileAdded: PropTypes.func,
|
onFileAdded: PropTypes.func,
|
||||||
exampleImageUrl: PropTypes.string
|
exampleImageUrl: PropTypes.string,
|
||||||
|
downloadScreenshot: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImportDialogBox
|
export default ImportDialogBox
|
||||||
|
|
Loading…
Add table
Reference in a new issue