SettingsGeneral.vue 6.0 KB

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