diff options
| -rw-r--r-- | .gitlab-ci.yml | 7 | ||||
| -rw-r--r-- | src/components/attachment/attachment.js | 24 | ||||
| -rw-r--r-- | src/components/attachment/attachment.vue | 18 | ||||
| -rw-r--r-- | src/components/favorite_button/favorite_button.js | 14 | ||||
| -rw-r--r-- | src/components/favorite_button/favorite_button.vue | 4 | ||||
| -rw-r--r-- | src/components/login_form/login_form.js | 12 | ||||
| -rw-r--r-- | src/components/login_form/login_form.vue | 11 | ||||
| -rw-r--r-- | src/components/nav_panel/nav_panel.vue | 33 | ||||
| -rw-r--r-- | src/components/retweet_button/retweet_button.js | 14 | ||||
| -rw-r--r-- | src/components/retweet_button/retweet_button.vue | 2 | ||||
| -rw-r--r-- | src/modules/users.js | 72 | ||||
| -rw-r--r-- | src/services/timeline_fetcher/timeline_fetcher.service.js | 7 |
12 files changed, 154 insertions, 64 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c31d2d31..296d6839 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,10 +28,17 @@ before_script: # - node_modules/ stages: + - lint - build - test - deploy +lint: + stage: lint + script: + - yarn + - npm run lint + test: stage: test script: diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index c3f52f57..7715add5 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -11,7 +11,9 @@ const Attachment = { return { nsfwImage, hideNsfwLocal: this.$store.state.config.hideNsfw, - showHidden: false + showHidden: false, + loading: false, + img: document.createElement('img') } }, computed: { @@ -20,6 +22,13 @@ const Attachment = { }, hidden () { return this.nsfw && this.hideNsfwLocal && !this.showHidden + }, + autoHeight () { + if (this.type === 'image' && this.nsfw) { + return { + 'min-height': '311px' + } + } } }, methods: { @@ -29,10 +38,15 @@ const Attachment = { } }, toggleHidden () { - let img = document.createElement('img') - img.src = this.attachment.url - img.onload = () => { - this.showHidden = !this.showHidden + if (this.img.onload) { + this.img.onload() + } else { + this.loading = true + this.img.src = this.attachment.url + this.img.onload = () => { + this.loading = false + this.showHidden = !this.showHidden + } } } } diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index ad60acf9..8f51b891 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -1,15 +1,14 @@ <template> - <div class="attachment" :class="type"> - <a class="image-attachment" v-if="hidden" v-on:click.prevent="toggleHidden()"> - <img :key="nsfwImage" :src="nsfwImage"></img> + <div class="attachment" :class="{[type]: true, loading}" :style="autoHeight"> + <a class="image-attachment" v-if="hidden" @click.prevent="toggleHidden()"> + <img :key="nsfwImage" :src="nsfwImage"/> </a> <div class="hider" v-if="nsfw && hideNsfwLocal && !hidden"> <a href="#" @click.prevent="toggleHidden()">Hide</a> </div> - <a class="image-attachment" v-if="type === 'image' && !hidden" - :href="attachment.url" target="_blank"> - <img class="base05-border" referrerpolicy="no-referrer" :src="attachment.large_thumb_url || attachment.url"></img> + <a v-if="type === 'image' && !hidden" class="image-attachment" :href="attachment.url" target="_blank"> + <img class="base05-border" referrerpolicy="no-referrer" :src="attachment.large_thumb_url || attachment.url"/> </a> <video v-if="type === 'video' && !hidden" :src="attachment.url" controls></video> @@ -18,7 +17,7 @@ <div @click.prevent="linkClicked" v-if="type === 'html' && attachment.oembed" class="oembed"> <div v-if="attachment.thumb_url" class="image"> - <img :src="attachment.thumb_url"></img> + <img :src="attachment.thumb_url"/> </div> <div class="text"> <h1><a :href="attachment.url">{{attachment.oembed.title}}</a></h1> @@ -45,6 +44,10 @@ display: flex; } + &.loading { + cursor: progress; + } + .hider { position: absolute; margin: 10px; @@ -111,7 +114,6 @@ flex: 1; img { - width: 100%; border-style: solid; border-width: 1px; border-radius: 5px; diff --git a/src/components/favorite_button/favorite_button.js b/src/components/favorite_button/favorite_button.js index 4ee3890f..466e9b84 100644 --- a/src/components/favorite_button/favorite_button.js +++ b/src/components/favorite_button/favorite_button.js @@ -1,5 +1,10 @@ const FavoriteButton = { - props: [ 'status' ], + props: ['status'], + data () { + return { + animated: false + } + }, methods: { favorite () { if (!this.status.favorited) { @@ -7,13 +12,18 @@ const FavoriteButton = { } else { this.$store.dispatch('unfavorite', {id: this.status.id}) } + this.animated = true + setTimeout(() => { + this.animated = false + }, 500) } }, computed: { classes () { return { 'icon-star-empty': !this.status.favorited, - 'icon-star': this.status.favorited + 'icon-star': this.status.favorited, + 'animate-spin': this.animated } } } diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue index fd53b505..0abece31 100644 --- a/src/components/favorite_button/favorite_button.vue +++ b/src/components/favorite_button/favorite_button.vue @@ -1,6 +1,6 @@ <template> <div> - <i :class='classes' class='favorite-button fa' v-on:click.prevent='favorite()'></i> + <i :class='classes' class='favorite-button fa' @click.prevent='favorite()'/> <span v-if='status.fave_num > 0'>{{status.fave_num}}</span> </div> </template> @@ -10,6 +10,7 @@ <style lang='scss'> .favorite-button { cursor: pointer; + animation-duration: 0.6s; &:hover { color: orange; } @@ -17,4 +18,5 @@ .icon-star { color: orange; } + </style> diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index 827c704c..1a6f6015 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -1,13 +1,21 @@ const LoginForm = { data: () => ({ - user: {} + user: {}, + authError: false }), computed: { loggingIn () { return this.$store.state.users.loggingIn } }, methods: { submit () { - this.$store.dispatch('loginUser', this.user) + this.$store.dispatch('loginUser', this.user).then( + () => {}, + (error) => { + this.authError = error + this.user.username = '' + this.user.password = '' + } + ) } } } diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue index c0273bae..b2fa5341 100644 --- a/src/components/login_form/login_form.vue +++ b/src/components/login_form/login_form.vue @@ -17,6 +17,9 @@ <div class='form-group'> <button :disabled="loggingIn" type='submit' class='btn btn-default base05 base01-background'>Submit</button> </div> + <div v-if="authError" class='form-group'> + <div class='error base05'>{{authError}}</div> + </div> </form> </div> </div> @@ -39,6 +42,14 @@ margin-top: 1.0em; min-height: 28px; } + + .error { + border-radius: 5px; + text-align: center; + background-color: rgba(255, 48, 16, 0.65); + min-height: 28px; + line-height: 28px; + } } </style> diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 240d7ae3..b62b4148 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -1,24 +1,24 @@ <template> <div class="nav-panel"> - <div class="panel panel-default base01-background"> + <div class="panel panel-default base02-background"> <ul class="base03-border"> <li v-if='currentUser'> - <router-link to='/main/friends'> + <router-link class="base01-background" to='/main/friends'> Timeline </router-link> </li> <li v-if='currentUser'> - <router-link :to="{ name: 'mentions', params: { username: currentUser.screen_name } }"> + <router-link class="base01-background" :to="{ name: 'mentions', params: { username: currentUser.screen_name } }"> Mentions </router-link> </li> <li> - <router-link to='/main/public'> + <router-link class="base01-background" to='/main/public'> Public Timeline </router-link> </li> <li> - <router-link to='/main/all'> + <router-link class="base01-background" to='/main/all'> The Whole Known Network </router-link> </li> @@ -30,7 +30,6 @@ <script src="./nav_panel.js" ></script> <style lang="scss"> - .nav-panel ul { list-style: none; margin: 0; @@ -40,7 +39,15 @@ .nav-panel li { border-bottom: 1px solid; border-color: inherit; - padding: 0.8em 0.85em; + padding: 0; + &:first-child a { + border-top-right-radius: 10px; + border-top-left-radius: 10px; + } + &:last-child a { + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; + } } .nav-panel li:last-child { @@ -49,10 +56,16 @@ .nav-panel a { display: block; - width: 100%; - + padding: 0.8em 0.85em; + &:hover { + background-color: transparent; + } &.router-link-active { - font-weight: bold + font-weight: bolder; + background-color: transparent; + &:hover { + text-decoration: underline; + } } } diff --git a/src/components/retweet_button/retweet_button.js b/src/components/retweet_button/retweet_button.js index e7318dc5..2280f315 100644 --- a/src/components/retweet_button/retweet_button.js +++ b/src/components/retweet_button/retweet_button.js @@ -1,16 +1,26 @@ const RetweetButton = { - props: [ 'status' ], + props: ['status'], + data () { + return { + animated: false + } + }, methods: { retweet () { if (!this.status.repeated) { this.$store.dispatch('retweet', {id: this.status.id}) } + this.animated = true + setTimeout(() => { + this.animated = false + }, 500) } }, computed: { classes () { return { - 'retweeted': this.status.repeated + 'retweeted': this.status.repeated, + 'animate-spin': this.animated } } } diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue index 9b2f5c7b..d923c5c4 100644 --- a/src/components/retweet_button/retweet_button.vue +++ b/src/components/retweet_button/retweet_button.vue @@ -11,12 +11,12 @@ @import '../../_variables.scss'; .icon-retweet { cursor: pointer; + animation-duration: 0.6s; &:hover { color: $green; } } .retweeted { - cursor: auto; color: $green; } </style> diff --git a/src/modules/users.js b/src/modules/users.js index 9367ec68..22e0133c 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -67,40 +67,52 @@ const users = { }) }, loginUser (store, userCredentials) { - const commit = store.commit - commit('beginLogin') - store.rootState.api.backendInteractor.verifyCredentials(userCredentials) - .then((response) => { - if (response.ok) { - response.json() - .then((user) => { - user.credentials = userCredentials - commit('setCurrentUser', user) - commit('addNewUsers', [user]) + return new Promise((resolve, reject) => { + const commit = store.commit + commit('beginLogin') + store.rootState.api.backendInteractor.verifyCredentials(userCredentials) + .then((response) => { + if (response.ok) { + response.json() + .then((user) => { + user.credentials = userCredentials + commit('setCurrentUser', user) + commit('addNewUsers', [user]) - // Set our new backend interactor - commit('setBackendInteractor', backendInteractorService(userCredentials)) + // Set our new backend interactor + commit('setBackendInteractor', backendInteractorService(userCredentials)) - // Start getting fresh tweets. - store.dispatch('startFetching', 'friends') + // Start getting fresh tweets. + store.dispatch('startFetching', 'friends') - // Get user mutes and follower info - store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => { - each(mutedUsers, (user) => { user.muted = true }) - store.commit('addNewUsers', mutedUsers) - }) + // Get user mutes and follower info + store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => { + each(mutedUsers, (user) => { user.muted = true }) + store.commit('addNewUsers', mutedUsers) + }) - // Fetch our friends - store.rootState.api.backendInteractor.fetchFriends() - .then((friends) => commit('addNewUsers', friends)) - }) - } - commit('endLogin') - }) - .catch((error) => { - console.log(error) - commit('endLogin') - }) + // Fetch our friends + store.rootState.api.backendInteractor.fetchFriends() + .then((friends) => commit('addNewUsers', friends)) + }) + } else { + // Authentication failed + commit('endLogin') + if (response.status === 401) { + reject('Wrong username or password') + } else { + reject('An error occurred, please try again') + } + } + commit('endLogin') + resolve() + }) + .catch((error) => { + console.log(error) + commit('endLogin') + reject('Failed to connect to server, try again') + }) + }) } } } diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 40f568c3..e684a170 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -5,6 +5,8 @@ import apiService from '../api/api.service.js' const update = ({store, statuses, timeline, showImmediately}) => { const ccTimeline = camelCase(timeline) + setError({store, timeline, value: false}) + store.dispatch('addNewStatuses', { timeline: ccTimeline, statuses, @@ -33,9 +35,8 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false } return apiService.fetchTimeline(args) - .then((statuses) => update({store, statuses, timeline, showImmediately})) - .then(() => setError({store, timeline, value: false})) - .catch(() => setError({store, timeline, value: true})) + .then((statuses) => update({store, statuses, timeline, showImmediately}), + () => setError({store, timeline, value: true})) } const startFetching = ({ timeline = 'friends', credentials, store }) => { |
