You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Upload.vue 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. <template>
  2. <div id="uploader" class="c-uploader">
  3. <div id="uploader-settings" class="uploader__settings box box--items">
  4. <div class="box__content">
  5. <box-item
  6. label="Upload mode"
  7. description="What will happen when uploading a photo.">
  8. <select v-model="$settings.app.uploadMode" class="input">
  9. <option value="none">
  10. Put in Pending
  11. </option>
  12. <option value="add-queue">
  13. Put in Queue
  14. </option>
  15. <option value="go-preferences">
  16. Put in Pending and open preferences
  17. </option>
  18. </select>
  19. </box-item>
  20. </div>
  21. </div>
  22. <!-- Dropzone -->
  23. <div
  24. id="uploader-dropzone"
  25. class="uploader__dropzone"
  26. :class="{'is-dragging': isDragging}"
  27. @dragenter="onDragEnter"
  28. @dragover="onDragOver"
  29. @dragleave="onDragLeave"
  30. @drop="openDrop">
  31. <p class="dropzone-hint">
  32. <font-awesome-icon icon="camera" />
  33. Drop the dream here!
  34. </p>
  35. </div>
  36. <div id="uploader-alternatives" class="uploader__alt">
  37. <!-- File -->
  38. <div class="box">
  39. <div class="box__header">
  40. <h2 class="title">
  41. <span class="icon"><font-awesome-icon icon="image" /></span>
  42. <span>Photo.</span>
  43. </h2>
  44. <h3 class="subtitle">
  45. Select one or more photos from your computer.
  46. </h3>
  47. </div>
  48. <div class="box__content">
  49. <input
  50. v-show="false"
  51. ref="photo"
  52. type="file"
  53. accept="image/jpeg, image/png, image/gif"
  54. multiple
  55. @change="openFile">
  56. <button class="button" @click.prevent="$refs.photo.click()">
  57. <span>open file</span>
  58. </button>
  59. </div>
  60. </div>
  61. <!-- Folder -->
  62. <div class="box">
  63. <div class="box__header">
  64. <h2 class="title">
  65. <span class="icon"><font-awesome-icon icon="folder-open" /></span>
  66. <span>Folder.</span>
  67. </h2>
  68. <h3 class="subtitle">
  69. Select one or more folders on your computer. All valid photos inside will be uploaded.
  70. </h3>
  71. </div>
  72. <div class="box__content">
  73. <button class="button" @click.prevent="openFolder">
  74. <span>Open folder</span>
  75. </button>
  76. </div>
  77. </div>
  78. <!-- Web Address -->
  79. <div class="box">
  80. <div class="box__header">
  81. <h2 class="title">
  82. <span class="icon"><font-awesome-icon icon="globe" /></span>
  83. <span>Web Address.</span>
  84. </h2>
  85. <h3 class="subtitle">
  86. Enter the web address of a photo. It must end in a valid extension (jpg, png, gif)
  87. </h3>
  88. </div>
  89. <div class="box__content">
  90. <input v-model="webAddress" type="url" class="input mb-2" placeholder="https://" data-private="lipsum">
  91. <button class="button" @click="openUrl">
  92. Submit
  93. </button>
  94. </div>
  95. </div>
  96. <!-- Web Address -->
  97. <div class="box">
  98. <div class="box__header">
  99. <h2 class="title">
  100. <span class="icon"><font-awesome-icon :icon="['fab', 'instagram']" /></span>
  101. <span>Instagram photo.</span>
  102. </h2>
  103. <h3 class="subtitle">
  104. Enter the web address or Media ID of an Instagram photo.
  105. </h3>
  106. </div>
  107. <div class="box__content">
  108. <input v-model="instagramPhoto" type="url" class="input mb-2" placeholder="https://www.instagram.com/p/dU4fHDw-Ho/" data-private="lipsum">
  109. <button class="button" @click="openInstagramPhoto">
  110. Submit
  111. </button>
  112. </div>
  113. </div>
  114. </div>
  115. </div>
  116. </template>
  117. <script>
  118. import {
  119. isNil, isEmpty, startsWith,
  120. map, isArray,
  121. } from 'lodash'
  122. import { Nudify } from '~/modules/nudify'
  123. import { Consola } from '~/modules/consola'
  124. import { tutorial } from '~/modules'
  125. const consola = Consola.create('upload')
  126. const { instagram } = $provider
  127. const { dialog } = $provider.api
  128. export default {
  129. props: {
  130. model: {
  131. type: String,
  132. default: undefined,
  133. },
  134. },
  135. data: () => ({
  136. webAddress: '',
  137. instagramPhoto: '',
  138. isDragging: false,
  139. }),
  140. mounted() {
  141. tutorial.upload()
  142. },
  143. methods: {
  144. /**
  145. * File selected, start a new transformation process
  146. */
  147. addFile(file) {
  148. if (isNil(file)) {
  149. return
  150. }
  151. Nudify.addFile(file.path)
  152. },
  153. /**
  154. *
  155. */
  156. async addFiles(files) {
  157. if (!isArray(files)) {
  158. return
  159. }
  160. await Nudify.addFiles(files)
  161. },
  162. /**
  163. *
  164. */
  165. openFile(event) {
  166. const { files } = event.target
  167. if (files.length === 0) {
  168. return
  169. }
  170. const paths = map(files, 'path')
  171. consola.track('FILE')
  172. this.addFiles(paths)
  173. event.target.value = ''
  174. },
  175. /**
  176. *
  177. */
  178. openFolder() {
  179. const paths = dialog.showOpenDialogSync({
  180. properties: ['openDirectory'],
  181. })
  182. this.addFiles(paths)
  183. },
  184. /**
  185. *
  186. */
  187. openUrl() {
  188. if (isEmpty(this.webAddress) || (!startsWith(this.webAddress, 'http://') && !startsWith(this.webAddress, 'https://'))) {
  189. throw new Warning('Upload failed.', 'Please enter a valid web address.')
  190. }
  191. Nudify.addUrl(this.webAddress)
  192. consola.track('URL')
  193. this.webAddress = ''
  194. },
  195. /**
  196. *
  197. */
  198. async openInstagramPhoto() {
  199. if (isEmpty(this.instagramPhoto)) {
  200. throw new Warning('Upload failed.', 'Please enter a valid Instagram photo.')
  201. }
  202. let post
  203. try {
  204. post = await instagram.getPost(this.instagramPhoto)
  205. } catch (error) {
  206. throw new Warning('Upload failed.', 'Unable to download the photo, please verify that the address is correct and that you are connected to the Internet.', error)
  207. }
  208. if (post.isVideo) {
  209. throw new Warning('Upload failed.', 'Videos are not supported yet.')
  210. }
  211. Nudify.addUrl(post.downloadUrl)
  212. consola.track('INSTAGRAM')
  213. this.instagramPhoto = ''
  214. },
  215. /**
  216. *
  217. */
  218. onDragEnter(event) {
  219. event.dataTransfer.dropEffect = 'copy'
  220. this.isDragging = true
  221. },
  222. /**
  223. *
  224. */
  225. onDragLeave() {
  226. this.isDragging = false
  227. },
  228. /**
  229. *
  230. */
  231. onDragOver(event) {
  232. event.preventDefault()
  233. event.stopPropagation()
  234. event.dataTransfer.dropEffect = 'copy'
  235. this.isDragging = true
  236. },
  237. /**
  238. *
  239. */
  240. openDrop(event) {
  241. event.preventDefault()
  242. event.stopPropagation()
  243. this.isDragging = false
  244. const { files } = event.dataTransfer
  245. const url = event.dataTransfer.getData('url')
  246. if (url.length > 0) {
  247. Nudify.addUrl(url)
  248. consola.track('DROP_URL')
  249. } else if (files.length > 0) {
  250. const paths = map(files, 'path')
  251. this.addFiles(paths)
  252. consola.track('DROP_FILE')
  253. }
  254. },
  255. },
  256. }
  257. </script>
  258. <style lang="scss" scoped>
  259. .c-uploader {
  260. @apply w-full relative;
  261. .uploader__alt {
  262. @apply flex flex-wrap justify-between;
  263. .box {
  264. @apply flex flex-col;
  265. width: calc(1/2*100% - (1 - 1/2)*1rem);
  266. min-height: 200px;
  267. .box__header {
  268. h2 {
  269. @apply text-lg font-bold;
  270. }
  271. h3 {
  272. @apply text-sm mb-4 font-light;
  273. }
  274. .help {
  275. @apply text-xs align-text-top font-bold underline;
  276. cursor: help;
  277. }
  278. }
  279. .box__content {
  280. @apply flex-1 flex flex-col justify-center items-center;
  281. }
  282. }
  283. }
  284. .uploader__dropzone {
  285. @apply flex items-center justify-center;
  286. @apply bg-dark-500 mb-6;
  287. @apply border-2 border-dashed border-dark-100;
  288. height: 200px;
  289. transition: all 0.1s linear;
  290. &.is-dragging {
  291. @apply bg-dark-700 border-dark-200;
  292. .dropzone-hint {
  293. @apply text-white text-xl;
  294. }
  295. }
  296. .dropzone-hint {
  297. @apply text-generic-300 uppercase;
  298. transition: all 0.1s linear;
  299. }
  300. }
  301. .upload-url {
  302. @apply mb-6 flex;
  303. .input {
  304. @apply flex-1 mr-4;
  305. }
  306. }
  307. &.is-dragging {
  308. @apply border-white border-dotted;
  309. .dragging-overlay {
  310. //display: block;
  311. }
  312. }
  313. .dragging-overlay {
  314. @apply bg-white absolute top-0 left-0 right-0 bottom-0 hidden;
  315. opacity: 0.3;
  316. }
  317. .fu-hint {
  318. @apply text-sm text-gray-600;
  319. }
  320. }
  321. </style>