Skip to content

Commit a56279d

Browse files
committed
Add a feature to delete draft article.
1 parent 594afd7 commit a56279d

File tree

11 files changed

+415
-13
lines changed

11 files changed

+415
-13
lines changed

app/components/atoms/AppModal.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export default {
7979
'requestWalletPasswordModal'
8080
]),
8181
...mapGetters('report', ['userReportModal', 'articleReportModal']),
82-
...mapGetters('badge', ['badgeDescriptionModal'])
82+
...mapGetters('badge', ['badgeDescriptionModal']),
83+
...mapGetters('articleModals', ['articleDeleteModal'])
8384
},
8485
mounted() {
8586
this.$root.$on('closeModal', async () => {
@@ -216,6 +217,9 @@ export default {
216217
if (this.badgeDescriptionModal.isShow) {
217218
this.setBadgeDescriptionModal({ isShow: false, tokenId: 0, metadata: {} })
218219
}
220+
if (this.articleDeleteModal.isShow) {
221+
this.setArticleDeleteModal({ isShow: false })
222+
}
219223
this.$emit('close')
220224
this.resetPassword()
221225
},
@@ -268,6 +272,7 @@ export default {
268272
'setArticleReportConfirmationModal',
269273
'resetArticleReportData'
270274
]),
275+
...mapActions('articleModals', ['setArticleDeleteModal']),
271276
...mapActions('badge', ['setBadgeDescriptionModal'])
272277
},
273278
watch: {
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<template>
2+
<div class="modal-body">
3+
<div class="article-delete-modal-content">
4+
<p class="confirm-text">
5+
本当に削除してもよろしいですか?
6+
</p>
7+
<p class="caution-text">
8+
※ 削除した記事は元に戻せません。
9+
</p>
10+
<app-button class="report-button" :disabled="isProcessing" @click="deleteArticle">
11+
削除する
12+
</app-button>
13+
<app-button class="close-button" type="secondary" @click="closeModal">
14+
閉じる
15+
</app-button>
16+
</div>
17+
</div>
18+
</template>
19+
20+
<script>
21+
import { mapActions, mapGetters } from 'vuex'
22+
import { ADD_TOAST_MESSAGE } from 'vuex-toast'
23+
import AppButton from '../atoms/AppButton'
24+
25+
export default {
26+
components: {
27+
AppButton
28+
},
29+
data() {
30+
return {
31+
isProcessing: false
32+
}
33+
},
34+
computed: {
35+
...mapGetters('article', ['articleId']),
36+
...mapGetters('user', ['currentUserInfo'])
37+
},
38+
methods: {
39+
async deleteArticle() {
40+
try {
41+
if (this.isProcessing) return
42+
this.isProcessing = true
43+
await this.deleteArticleDraft({ articleId: this.articleId })
44+
this.setArticleDeleteModal({ isShow: false })
45+
this.$router.push(`/users/${this.currentUserInfo.user_id}/drafts`)
46+
this.sendNotification({ text: '該当記事を削除しました' })
47+
} catch (error) {
48+
this.setArticleDeleteModal({ isShow: false })
49+
const text = 'エラーが発生しました。しばらく時間を置いて再度お試しください'
50+
this.sendNotification({ text, type: 'warning' })
51+
} finally {
52+
this.isProcessing = false
53+
}
54+
},
55+
closeModal() {
56+
this.setArticleDeleteModal({ isShow: false })
57+
},
58+
...mapActions({
59+
sendNotification: ADD_TOAST_MESSAGE
60+
}),
61+
...mapActions('articleModals', ['setArticleDeleteModal']),
62+
...mapActions('article', ['deleteArticleDraft'])
63+
}
64+
}
65+
</script>
66+
67+
<style lang="scss" scoped>
68+
.confirm-text {
69+
color: #030303;
70+
font-size: 14px;
71+
font-weight: 500;
72+
line-height: 21px;
73+
text-align: center;
74+
padding: 50px 0 0;
75+
display: block;
76+
}
77+
.caution-text {
78+
color: red;
79+
font-size: 14px;
80+
font-weight: 500;
81+
line-height: 21px;
82+
text-align: center;
83+
display: block;
84+
}
85+
86+
.report-button {
87+
margin: 60px auto 0;
88+
}
89+
90+
.close-button {
91+
margin: 20px auto 100px;
92+
}
93+
94+
@media screen and (max-width: 550px) {
95+
.confirm-text {
96+
padding: 20vh 0 0;
97+
}
98+
99+
.close-button {
100+
margin: 20px auto 30vh;
101+
}
102+
}
103+
</style>

app/components/molecules/EditHeaderNavV2.vue

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,22 @@
1616
下書き
1717
</nuxt-link>
1818
<span class="area-save-status">{{ saveStatus }}</span>
19+
<div v-if="type === 'draft-article'" class="area-article-menu" @click="toggleArticlePopup">
20+
<div v-show="isArticlePopupShown" class="article-popup">
21+
<nuxt-link
22+
class="article-popup-content"
23+
:to="`${historiesPath}`"
24+
@click.native="resetHistories()"
25+
>
26+
編集履歴一覧
27+
</nuxt-link>
28+
<span class="article-popup-content" @click="showArticleDeleteModal">
29+
削除
30+
</span>
31+
</div>
32+
</div>
1933
<nuxt-link
34+
v-if="type !== 'draft-article'"
2035
:to="`${historiesPath}`"
2136
class="area-article-histories fa fa-history"
2237
@click.native="resetHistories()"
@@ -42,15 +57,39 @@ export default {
4257
},
4358
data() {
4459
return {
60+
isArticlePopupShown: false,
4561
isFixed: false
4662
}
4763
},
4864
mounted() {
65+
this.listen(window, 'click', (event) => {
66+
if (
67+
this.$el.querySelector('.area-article-menu') &&
68+
!this.$el.querySelector('.area-article-menu').contains(event.target)
69+
) {
70+
this.closeEtcPopup()
71+
}
72+
})
73+
this.listen(window, 'touchstart', (event) => {
74+
if (
75+
this.$el.querySelector('.area-article-menu') &&
76+
!this.$el.querySelector('.area-article-menu').contains(event.target)
77+
) {
78+
this.closeEtcPopup()
79+
}
80+
})
4981
window.addEventListener('scroll', this.handleScroll)
5082
},
5183
beforeDestroy() {
5284
window.removeEventListener('scroll', this.handleScroll)
5385
},
86+
destroyed() {
87+
if (this._eventRemovers) {
88+
this._eventRemovers.forEach((eventRemover) => {
89+
eventRemover.remove()
90+
})
91+
}
92+
},
5493
computed: {
5594
historiesPath() {
5695
const articleStatus = this.$route.path.startsWith('/me/articles/public/') ? 'public' : 'draft'
@@ -60,13 +99,34 @@ export default {
6099
...mapGetters('user', ['currentUserInfo'])
61100
},
62101
methods: {
102+
showArticleDeleteModal() {
103+
this.setArticleDeleteModal({ isShow: true })
104+
},
105+
toggleArticlePopup() {
106+
this.isArticlePopupShown = !this.isArticlePopupShown
107+
},
108+
closeEtcPopup() {
109+
this.isArticlePopupShown = false
110+
},
63111
handleScroll() {
64112
this.isFixed = window.scrollY >= 100
65113
},
66114
resetHistories() {
67115
this.resetArticleContentEditHistories()
68116
},
69-
...mapActions('article', ['resetArticleContentEditHistories'])
117+
listen(target, eventType, callback) {
118+
if (!this._eventRemovers) {
119+
this._eventRemovers = []
120+
}
121+
target.addEventListener(eventType, callback)
122+
this._eventRemovers.push({
123+
remove: function() {
124+
target.removeEventListener(eventType, callback)
125+
}
126+
})
127+
},
128+
...mapActions('article', ['resetArticleContentEditHistories']),
129+
...mapActions('articleModals', ['setArticleDeleteModal'])
70130
}
71131
}
72132
</script>
@@ -93,7 +153,7 @@ export default {
93153
width: 640px;
94154
/* prettier-ignore */
95155
grid-template-areas:
96-
"articles-link ... save-status article-histories post-article";
156+
"articles-link ... save-status article-menu post-article";
97157
}
98158
99159
&.is-fixed {
@@ -118,7 +178,7 @@ export default {
118178
}
119179
120180
.area-article-histories {
121-
grid-area: article-histories;
181+
grid-area: article-menu;
122182
align-items: center;
123183
color: gray;
124184
display: flex;
@@ -135,6 +195,44 @@ export default {
135195
}
136196
}
137197
198+
.area-article-menu {
199+
grid-area: article-menu;
200+
background: #fff url('~assets/images/pc/article/icon_etc.png') no-repeat;
201+
background-size: 24px;
202+
cursor: pointer;
203+
position: relative;
204+
width: 30px;
205+
margin: 5px 8px 8px 8px;
206+
207+
.article-popup {
208+
background-color: #ffffff;
209+
border-radius: 4px;
210+
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.25));
211+
cursor: default;
212+
box-sizing: border-box;
213+
font-size: 14px;
214+
padding: 8px 16px;
215+
position: absolute;
216+
right: 0px;
217+
top: 24px;
218+
z-index: 1;
219+
220+
.article-popup-content {
221+
color: #6e6e6e;
222+
cursor: pointer;
223+
display: inline-block;
224+
font-size: 14px;
225+
font-weight: 500;
226+
line-height: 2;
227+
text-align: left;
228+
text-decoration: none;
229+
user-select: none;
230+
white-space: nowrap;
231+
width: 100%;
232+
}
233+
}
234+
}
235+
138236
.area-save-status {
139237
grid-area: save-status;
140238
display: flex;

app/components/organisms/AppHeader.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<withdrawal-detail-modal v-if="withdrawalDetailModal.isShow" />
2626
<input-withdraw-auth-code-modal v-if="inputWithdrawAuthCodeModal.isShow" />
2727
<badge-description-modal v-if="badgeDescriptionModal.isShow" />
28+
<article-delete-modal v-if="articleDeleteModal.isShow" />
2829
<toast position="n" />
2930
</header>
3031
</template>
@@ -51,6 +52,7 @@ const ConfirmPurchaseArticleModal = () => import('../organisms/ConfirmPurchaseAr
5152
const WithdrawalDetailModal = () => import('../organisms/WithdrawalDetailModal')
5253
const InputWithdrawAuthCodeModal = () => import('../organisms/InputWithdrawAuthCodeModal')
5354
const BadgeDescriptionModal = () => import('../organisms/BadgeDescriptionModal')
55+
const ArticleDeleteModal = () => import('../organisms/ArticleDeleteModal')
5456
5557
export default {
5658
components: {
@@ -72,7 +74,8 @@ export default {
7274
WithdrawalDetailModal,
7375
InputWithdrawAuthCodeModal,
7476
BadgeDescriptionModal,
75-
RequestWalletPasswordModal
77+
RequestWalletPasswordModal,
78+
ArticleDeleteModal
7679
},
7780
computed: {
7881
...mapGetters('user', [
@@ -92,7 +95,8 @@ export default {
9295
'inputWithdrawAuthCodeModal'
9396
]),
9497
...mapGetters('report', ['userReportModal', 'articleReportModal']),
95-
...mapGetters('badge', ['badgeDescriptionModal'])
98+
...mapGetters('badge', ['badgeDescriptionModal']),
99+
...mapGetters('articleModals', ['articleDeleteModal'])
96100
},
97101
methods: {
98102
resetData() {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<template>
2+
<app-modal :title="'下書き記事の削除'" :show-modal-content="articleDeleteModal.isShow">
3+
<div slot="modal-content">
4+
<article-delete-confirmation-modal />
5+
</div>
6+
</app-modal>
7+
</template>
8+
9+
<script>
10+
import { mapGetters } from 'vuex'
11+
import AppModal from '../atoms/AppModal'
12+
import ArticleDeleteConfirmationModal from '../molecules/ArticleDeleteConfirmationModal'
13+
14+
export default {
15+
components: {
16+
AppModal,
17+
ArticleDeleteConfirmationModal
18+
},
19+
computed: {
20+
isConfirmationModal() {
21+
return this.articleDeleteConfirmModal.isConfirmationModal
22+
},
23+
...mapGetters('articleModals', ['articleDeleteModal'])
24+
}
25+
}
26+
</script>

0 commit comments

Comments
 (0)