SettingsGeneral.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <template>
  2. <div class='route_container'>
  3. <confirm-modal
  4. v-model='picture.showRemoveProfilePictureModal'
  5. @confirm='removeProfilePicture'
  6. color='red'
  7. text='Yes, remove it'
  8. >
  9. Are you sure you want to remove your profile picture?
  10. </confirm-modal>
  11. <modal-window v-model='picture.showProfilePictureModal' width='25rem' @input='hideProflePictureModal'>
  12. <div
  13. class='profile_picture_modal__overlay'
  14. :class='{
  15. "profile_picture_modal__overlay--show": picture.loading
  16. }'
  17. >
  18. <loading-icon></loading-icon>
  19. </div>
  20. <div
  21. class='profile_picture_modal'
  22. :class='{ "profile_picture_modal--picture.dragging": picture.dragging }'
  23. @dragover='handleDragOver'
  24. @drkagend='picture.dragging = false'
  25. @drkgleave='picture.dragging = false'
  26. @drop='handleFileDrop'
  27. >
  28. <div class='h3'>Add a profile picture</div>
  29. <p class='p--condensed'>
  30. Drag and drop an image or
  31. <label class='button profile_picture_modal__upload_button'>
  32. <input type='file' accept='image/*' @change='processImage($event.target.files[0])'>
  33. upload a file
  34. </label>
  35. </p>
  36. <div class='profile_picture_modal__drag_area'>
  37. <span
  38. v-if='!picture.dataURL'
  39. class='fa fa-cloud-upload profile_picture_modal__drag_area__icon'
  40. :class='{ "profile_picture_modal__drag_area__icon--picture.dragging": picture.dragging }'
  41. ></span>
  42. <div
  43. class='profile_picture_modal__drag_area__image picture_circle'
  44. :style='{ "background-image": "url(" + picture.dataURL + ")" }'
  45. v-else
  46. ></div>
  47. </div>
  48. <div class='profile_picture_modal__buttons'>
  49. <button class='button button--modal button--borderless' @click='hideProflePictureModal'>Cancel</button>
  50. <button
  51. class='button button--modal button--green'
  52. :class='{ "button--disabled": !picture.dataURL }'
  53. @click='uploadProfilePicture'
  54. >Upload picture</button>
  55. </div>
  56. </div>
  57. </modal-window>
  58. <div class='h1'>General settings</div>
  59. <p>
  60. <div class='h3'>About me</div>
  61. <p class='p--condensed'>
  62. Write something about yourself to be displayed on your user page
  63. </p>
  64. <fancy-textarea
  65. placeholder='About me description'
  66. v-model='description.value'
  67. :error='description.error'
  68. type='password'
  69. ></fancy-textarea>
  70. <loading-button
  71. class='button button--green'
  72. :loading='description.loading'
  73. @click='saveDescription'
  74. >
  75. Save description
  76. </loading-button>
  77. </p>
  78. <p>
  79. <div class='h3'>Profile picture</div>
  80. <p class='p--condensed'>
  81. This will be displayed by your posts on the site
  82. </p>
  83. <p
  84. class='p--condensed profile_picture_preview picture_circle'
  85. :style='{ "background-image": "url(" + picture.current + ")" }'
  86. v-if='picture.current'
  87. ></p>
  88. <button class='button' @click='picture.showProfilePictureModal = true'>
  89. {{picture.current ? "Change" : "Add" }} profile picture
  90. </button>
  91. <button
  92. v-if='picture.current'
  93. class='button'
  94. style='margin-left: 0.5rem;'
  95. @click='picture.showRemoveProfilePictureModal = true'
  96. >
  97. Remove
  98. </button>
  99. </p>
  100. </div>
  101. </template>
  102. <script>
  103. import FancyTextarea from '../FancyTextarea'
  104. import LoadingButton from '../LoadingButton'
  105. import LoadingIcon from '../LoadingIcon'
  106. import ModalWindow from '../ModalWindow'
  107. import ConfirmModal from '../ConfirmModal'
  108. import AjaxErrorHandler from '../../assets/js/errorHandler'
  109. import logger from '../../assets/js/logger'
  110. export default {
  111. name: 'settingsGeneral',
  112. components: {
  113. FancyTextarea,
  114. LoadingButton,
  115. LoadingIcon,
  116. ModalWindow,
  117. ConfirmModal
  118. },
  119. data () {
  120. return {
  121. description: {
  122. value: '',
  123. loading: false,
  124. error: ''
  125. },
  126. picture: {
  127. current: null,
  128. showProfilePictureModal: false,
  129. showRemoveProfilePictureModal: false,
  130. dragging: false,
  131. dataURL: null,
  132. file: null,
  133. loading: false
  134. }
  135. }
  136. },
  137. computed: {},
  138. methods: {
  139. saveDescription () {
  140. this.description.error = ''
  141. this.description.loading = true
  142. this.axios
  143. .put('/api/v1/user/' + this.$store.state.username, {
  144. description: this.description.value
  145. })
  146. .then(res => {
  147. this.description.loading = false
  148. })
  149. .catch(e => {
  150. this.description.loading = false
  151. AjaxErrorHandler(this.$store)(e, error => {
  152. this.description.error = error.message
  153. })
  154. })
  155. },
  156. uploadProfilePicture () {
  157. this.picture.loading = true
  158. let formData = new FormData()
  159. formData.append('picture', this.picture.file)
  160. this.axios
  161. .post('/api/v1/user/' + this.$store.state.username + '/picture', formData, {
  162. headers: {
  163. 'Content-Type': 'multipart/form-data'
  164. }
  165. })
  166. .then(res => {
  167. this.hideProflePictureModal()
  168. this.picture.current = res.data.picture
  169. })
  170. .catch(e => {
  171. this.picture.loading = false
  172. AjaxErrorHandler(this.$store)(e)
  173. })
  174. },
  175. removeProfilePicture () {
  176. this.axios
  177. .delete('/api/v1/user/' + this.$store.state.username + '/picture')
  178. .then(res => {
  179. this.picture.current = null
  180. })
  181. .catch(AjaxErrorHandler(this.$store))
  182. },
  183. hideProflePictureModal () {
  184. this.picture.showProfilePictureModal = false
  185. //Wait for transition to complete
  186. setTimeout(() => {
  187. this.picture.dataURL = null
  188. this.picture.loading = false
  189. }, 200)
  190. },
  191. handleDragOver (e) {
  192. e.preventDefault()
  193. this.picture.dragging = true
  194. },
  195. handleFileDrop (e) {
  196. e.preventDefault()
  197. this.picture.dragging = false
  198. if(e.dataTransfer && e.dataTransfer.items) {
  199. let file = e.dataTransfer.items[0]
  200. if(file.type.match('^image/')) {
  201. this.processImage(file.getAsFile())
  202. }
  203. }
  204. },
  205. processImage (file) {
  206. let reader = new FileReader()
  207. reader.readAsDataURL(file)
  208. this.picture.file = file
  209. reader.addEventListener('load', () => {
  210. this.picture.dataURL = reader.result
  211. })
  212. }
  213. },
  214. created () {
  215. this.$store.dispatch('setTitle', 'general settings')
  216. this.$nextTick(() => {
  217. this.axios
  218. .get('/api/v1/user/' + this.$store.state.username)
  219. .then(res => {
  220. this.description.value = res.data.description || ''
  221. this.picture.current = res.data.picture
  222. })
  223. .catch(e => {
  224. AjaxErrorHandler(this.$store)(e)
  225. })
  226. })
  227. logger('settingsGeneral')
  228. }
  229. }
  230. </script>
  231. <style lang='scss' scoped>
  232. @import '../../assets/scss/variables.scss';
  233. .profile_picture_preview {
  234. height: 5rem;
  235. width: 5rem;
  236. }
  237. .profile_picture_modal {
  238. padding: 1rem;
  239. transition: all 0.2s;
  240. @at-root #{&}--picture .dragging {
  241. background-color: $color__lightgray--primary;
  242. }
  243. @at-root #{&}__overlay {
  244. @include loading-overlay(rgba(0, 0, 0, 0.5), 0.125rem);
  245. }
  246. @at-root #{&}__upload_button input[type="file"] {
  247. display: none;
  248. }
  249. @at-root #{&}__drag_area {
  250. padding: 1rem;
  251. text-align: center;
  252. @at-root #{&}__image {
  253. width: 5rem;
  254. height: 5rem;
  255. display: inline-block;
  256. margin-top: -1rem;
  257. }
  258. @at-root #{&}__icon {
  259. font-size: 6rem;
  260. color: $color__gray--darker;
  261. transition: all 0.2s;
  262. @at-root #{&}--picture.dragging {
  263. transform: translateY(-0.5rem) scale(1.1);
  264. color: $color__gray--darkest;
  265. }
  266. }
  267. }
  268. }
  269. @media (max-width: 420px) {
  270. .h1 {
  271. display: none;
  272. }
  273. }
  274. </style>