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:
Devin Howard 2017-01-19 14:50:08 -05:00 committed by GitHub
parent a9f19815e4
commit af2c6ebef1
9 changed files with 153 additions and 45 deletions

View file

@ -1558,6 +1558,7 @@ h3.filterBox {
box-sizing: border-box;
margin: 0.75em;
padding: 0.75em;
padding-top: 0.85em;
height: 3em;
background-color: #AAB0FB;
border-radius: 0.3em;
@ -2051,6 +2052,43 @@ and it won't be important on password protected instances */
.yourMap .mapInfoDelete {
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 {
position: absolute;
width: 100%;

View file

@ -34,6 +34,11 @@
<p class="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
<p class="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
<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="deleteMap"></div>
<span>Delete</span>

View file

@ -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="mapEditedAt"><span>Last edited:</span> <%= @map.updated_at.strftime("%m/%d/%Y") %></p>
<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="deleteMap"></div>
<span>Delete</span>

View file

@ -7,6 +7,7 @@ import outdent from 'outdent'
import ImportDialogBox from '../../components/ImportDialogBox'
import PasteInput from '../PasteInput'
import Map from '../Map'
const ImportDialog = {
openLightbox: null,
@ -24,14 +25,19 @@ const ImportDialog = {
`))
ReactDOM.render(React.createElement(ImportDialogBox, {
onFileAdded: PasteInput.handleFile,
exampleImageUrl: serverData['import-example.png']
exampleImageUrl: serverData['import-example.png'],
downloadScreenshot: ImportDialog.downloadScreenshot
}), $('.importDialogWrapper').get(0))
},
show: function() {
ImportDialog.openLightbox('import-dialog')
},
hide: function() {
ImportDialog.closeLightbox('import-dialog')
ImportDialog.closeLightbox()
},
downloadScreenshot: function() {
ImportDialog.hide()
Map.offerScreenshotDownload()
}
}

View file

@ -12,9 +12,11 @@ import NotificationIcon from './NotificationIcon'
const GlobalUI = {
notifyTimeout: null,
notifyQueue: [],
notifying: false,
lightbox: null,
init: function(serverData) {
var self = GlobalUI
const self = GlobalUI
self.Search.init(serverData)
self.CreateMap.init(serverData)
@ -45,7 +47,7 @@ const GlobalUI = {
}, 200, 'easeInCubic', function() { $(this).hide() })
},
openLightbox: function(which) {
var self = GlobalUI
const self = GlobalUI
$('.lightboxContent').hide()
$('#' + which).show()
@ -72,7 +74,7 @@ const GlobalUI = {
},
closeLightbox: function(event) {
var self = GlobalUI
const self = GlobalUI
if (event) event.preventDefault()
@ -96,23 +98,45 @@ const GlobalUI = {
}
self.lightbox = null
},
notifyUser: function(message, leaveOpen) {
var self = GlobalUI
notifyUser: function(message, opts = {}) {
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)
self.showDiv('#toast')
clearTimeout(self.notifyTimeOut)
if (!leaveOpen) {
self.notifyTimeOut = setTimeout(function() {
self.hideDiv('#toast')
}, 8000)
GlobalUI.clearNotify()
}, timeOut)
}
self.notifying = true
},
clearNotify: function() {
var self = GlobalUI
const self = GlobalUI
clearTimeout(self.notifyTimeOut)
self.hideDiv('#toast')
// if there are messages remaining, display them
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) {
clipboard.copy({

View file

@ -35,9 +35,11 @@ const InfoBox = {
data-bip-value="{{desc}}"
>{{desc}}</span>`,
userImageUrl: '',
init: function(serverData) {
init: function(serverData, updateThumbnail) {
var self = InfoBox
self.updateThumbnail = updateThumbnail
$('.mapInfoIcon').click(self.toggleBox)
$('.mapInfoBox').click(function(event) {
event.stopPropagation()
@ -181,6 +183,7 @@ const InfoBox = {
$('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
$('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
$('.mapInfoThumbnail').unbind().click(self.updateThumbnail)
$('.mapContributors span, #mapContribs').unbind().click(function(event) {
$('.mapContributors .tip').toggle()

View file

@ -45,7 +45,10 @@ const Map = {
GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
self.updateStar()
InfoBox.init(serverData)
InfoBox.init(serverData, function updateThumbnail() {
self.uploadMapScreenshot()
})
CheatSheet.init(serverData)
$('.viewOnly .requestAccess').click(self.requestAccess)
@ -251,6 +254,48 @@ const Map = {
}
},
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 = {}
canvas.canvas = document.createElement('canvas')
@ -336,8 +381,9 @@ const Map = {
node.visited = !T
})
var map = Active.Map
return canvas
},
getMapScreenshotFilename: map => {
var today = new Date()
var dd = today.getDate()
var mm = today.getMonth() + 1 // January is 0!
@ -352,30 +398,7 @@ const Map = {
var mapName = map.get('name').split(' ').join(['-'])
const filename = `metamap-${map.id}-${mapName}-${today}.png`
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')
}
})
})
return filename
}
}

View file

@ -149,7 +149,7 @@ export const invitedToCall = self => inviter => {
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 btn-no no">No</button>'
GlobalUI.notifyUser(notifyText, true)
GlobalUI.notifyUser(notifyText, { leaveOpen: true })
$('#toast button.yes').click(e => self.acceptCall(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?'
notifyText += ' <button type="button" class="toast-button button yes">Yes</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.no').click(e => self.denyInvite(inviter))
}
@ -201,7 +201,7 @@ export const callInProgress = self => () => {
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 btn-no no">No</button>'
GlobalUI.notifyUser(notifyText, true)
GlobalUI.notifyUser(notifyText, { leaveOpen: true })
$('#toast button.yes').click(e => self.joinCall())
$('#toast button.no').click(e => GlobalUI.clearNotify())
ChatView.conversationInProgress()
@ -212,7 +212,7 @@ export const callStarted = self => () => {
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 btn-no">No</button>'
GlobalUI.notifyUser(notifyText, true)
GlobalUI.notifyUser(notifyText, { leaveOpen: true })
$('#toast button.yes').click(e => self.joinCall())
$('#toast button.no').click(e => GlobalUI.clearNotify())
ChatView.conversationInProgress()

View file

@ -27,6 +27,9 @@ class ImportDialogBox extends Component {
<div className="import-blue-button" onClick={this.handleExport('json')}>
Export as JSON
</div>
<div className="import-blue-button" onClick={this.props.downloadScreenshot}>
Download screenshot
</div>
<h3>IMPORT</h3>
<p>To upload a file, drop it here:</p>
<Dropzone onDropAccepted={this.handleFile}
@ -42,7 +45,8 @@ class ImportDialogBox extends Component {
ImportDialogBox.propTypes = {
onFileAdded: PropTypes.func,
exampleImageUrl: PropTypes.string
exampleImageUrl: PropTypes.string,
downloadScreenshot: PropTypes.func
}
export default ImportDialogBox