mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-06-05 13:26:08 +00:00
Compare commits
1 Commits
codex/add-
...
codex/enab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea4f59af7 |
@@ -0,0 +1,39 @@
|
||||
package com.openisle.config;
|
||||
|
||||
import com.openisle.model.NotificationType;
|
||||
import com.openisle.model.User;
|
||||
import com.openisle.repository.UserRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Ensure default notification preferences are applied to existing users.
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class NotificationPreferenceInitializer implements CommandLineRunner {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
List<User> users = userRepository.findAll();
|
||||
for (User user : users) {
|
||||
Set<NotificationType> disabled = user.getDisabledNotificationTypes();
|
||||
boolean changed = false;
|
||||
if (disabled.add(NotificationType.POST_VIEWED)) {
|
||||
changed = true;
|
||||
}
|
||||
if (disabled.add(NotificationType.USER_ACTIVITY)) {
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
userRepository.save(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import lombok.Setter;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -68,7 +68,10 @@ public class User {
|
||||
@CollectionTable(name = "user_disabled_notification_types", joinColumns = @JoinColumn(name = "user_id"))
|
||||
@Column(name = "notification_type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Set<NotificationType> disabledNotificationTypes = new HashSet<>();
|
||||
private Set<NotificationType> disabledNotificationTypes = EnumSet.of(
|
||||
NotificationType.POST_VIEWED,
|
||||
NotificationType.USER_ACTIVITY
|
||||
);
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.openisle.config;
|
||||
|
||||
import com.openisle.model.NotificationType;
|
||||
import com.openisle.model.User;
|
||||
import com.openisle.repository.UserRepository;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class NotificationPreferenceInitializerTest {
|
||||
|
||||
@Test
|
||||
void addsDefaultsToUsers() throws Exception {
|
||||
User u1 = new User();
|
||||
u1.setId(1L);
|
||||
u1.getDisabledNotificationTypes().clear();
|
||||
|
||||
User u2 = new User();
|
||||
u2.setId(2L);
|
||||
u2.getDisabledNotificationTypes().clear();
|
||||
u2.getDisabledNotificationTypes().add(NotificationType.POST_VIEWED);
|
||||
|
||||
UserRepository repo = mock(UserRepository.class);
|
||||
when(repo.findAll()).thenReturn(List.of(u1, u2));
|
||||
|
||||
NotificationPreferenceInitializer init = new NotificationPreferenceInitializer(repo);
|
||||
init.run();
|
||||
|
||||
assertTrue(u1.getDisabledNotificationTypes().containsAll(
|
||||
EnumSet.of(NotificationType.POST_VIEWED, NotificationType.USER_ACTIVITY)));
|
||||
assertTrue(u2.getDisabledNotificationTypes().containsAll(
|
||||
EnumSet.of(NotificationType.POST_VIEWED, NotificationType.USER_ACTIVITY)));
|
||||
|
||||
verify(repo).save(u1);
|
||||
verify(repo).save(u2);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
text="建站送奶茶活动火热进行中,快来参与吧!"
|
||||
@close="closeMilkTeaPopup"
|
||||
/>
|
||||
<NotificationSettingPopup :visible="showNotificationPopup" @close="closeNotificationPopup" />
|
||||
<MedalPopup :visible="showMedalPopup" :medals="newMedals" @close="closeMedalPopup" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -14,18 +13,16 @@
|
||||
<script>
|
||||
import ActivityPopup from '~/components/ActivityPopup.vue'
|
||||
import MedalPopup from '~/components/MedalPopup.vue'
|
||||
import NotificationSettingPopup from '~/components/NotificationSettingPopup.vue'
|
||||
import { API_BASE_URL } from '~/main'
|
||||
import { authState } from '~/utils/auth'
|
||||
|
||||
export default {
|
||||
name: 'GlobalPopups',
|
||||
components: { ActivityPopup, MedalPopup, NotificationSettingPopup },
|
||||
components: { ActivityPopup, MedalPopup },
|
||||
data() {
|
||||
return {
|
||||
showMilkTeaPopup: false,
|
||||
milkTeaIcon: '',
|
||||
showNotificationPopup: false,
|
||||
showMedalPopup: false,
|
||||
newMedals: [],
|
||||
}
|
||||
@@ -33,10 +30,7 @@ export default {
|
||||
async mounted() {
|
||||
await this.checkMilkTeaActivity()
|
||||
if (!this.showMilkTeaPopup) {
|
||||
await this.checkNotificationSetting()
|
||||
if (!this.showNotificationPopup) {
|
||||
await this.checkNewMedals()
|
||||
}
|
||||
await this.checkNewMedals()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -61,18 +55,6 @@ export default {
|
||||
if (!process.client) return
|
||||
localStorage.setItem('milkTeaActivityPopupShown', 'true')
|
||||
this.showMilkTeaPopup = false
|
||||
this.checkNotificationSetting()
|
||||
},
|
||||
async checkNotificationSetting() {
|
||||
if (!process.client) return
|
||||
if (!authState.loggedIn) return
|
||||
if (localStorage.getItem('notificationSettingPopupShown')) return
|
||||
this.showNotificationPopup = true
|
||||
},
|
||||
closeNotificationPopup() {
|
||||
if (!process.client) return
|
||||
localStorage.setItem('notificationSettingPopupShown', 'true')
|
||||
this.showNotificationPopup = false
|
||||
this.checkNewMedals()
|
||||
},
|
||||
async checkNewMedals() {
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<BasePopup :visible="visible" @close="close">
|
||||
<div class="notification-popup">
|
||||
<div class="notification-popup-title">通知设置上线啦</div>
|
||||
<div class="notification-popup-text">现在可以调整通知类型</div>
|
||||
<div class="notification-popup-actions">
|
||||
<div class="notification-popup-close" @click="close">知道了</div>
|
||||
<div class="notification-popup-button" @click="gotoSetting">去看看</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasePopup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasePopup from '~/components/BasePopup.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
export default {
|
||||
name: 'NotificationSettingPopup',
|
||||
components: { BasePopup },
|
||||
props: {
|
||||
visible: { type: Boolean, default: false },
|
||||
},
|
||||
emits: ['close'],
|
||||
setup(props, { emit }) {
|
||||
const router = useRouter()
|
||||
const gotoSetting = () => {
|
||||
emit('close')
|
||||
router.push('/message?tab=control')
|
||||
}
|
||||
const close = () => emit('close')
|
||||
return { gotoSetting, close }
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.notification-popup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: 10px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.notification-popup-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.notification-popup-actions {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.notification-popup-button {
|
||||
background-color: var(--primary-color);
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.notification-popup-button:hover {
|
||||
background-color: var(--primary-color-hover);
|
||||
}
|
||||
|
||||
.notification-popup-close {
|
||||
cursor: pointer;
|
||||
color: var(--primary-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.notification-popup-close:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -480,7 +480,7 @@
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { API_BASE_URL } from '../main'
|
||||
import BaseTimeline from '../components/BaseTimeline.vue'
|
||||
import BasePlaceholder from '../components/BasePlaceholder.vue'
|
||||
@@ -503,12 +503,9 @@ export default {
|
||||
components: { BaseTimeline, BasePlaceholder, NotificationContainer },
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const notifications = ref([])
|
||||
const isLoadingMessage = ref(false)
|
||||
const selectedTab = ref(
|
||||
['all', 'unread', 'control'].includes(route.query.tab) ? route.query.tab : 'unread',
|
||||
)
|
||||
const selectedTab = ref('unread')
|
||||
const notificationPrefs = ref([])
|
||||
const filteredNotifications = computed(() =>
|
||||
selectedTab.value === 'all'
|
||||
|
||||
Reference in New Issue
Block a user