fix: support profile editor swipe and show account deletion entry
This commit is contained in:
@@ -151,6 +151,27 @@ Page(withDataCache({
|
||||
}
|
||||
},
|
||||
|
||||
onEditTouchStart: function(e) {
|
||||
this._editSwipe = null;
|
||||
if (!e.touches || e.touches.length !== 1 || (e.target && e.target.dataset && e.target.dataset.swipeIgnore)) return;
|
||||
const point = e.touches[0];
|
||||
this._editSwipe = {x:point.clientX,y:point.clientY};
|
||||
},
|
||||
onEditTouchCancel: function() { this._editSwipe = null; },
|
||||
onEditTouchEnd: function(e) {
|
||||
const start = this._editSwipe;
|
||||
this._editSwipe = null;
|
||||
const point = e.changedTouches && e.changedTouches[0];
|
||||
if (!start || !point) return;
|
||||
const dx = start.x - point.clientX, dy = Math.abs(start.y - point.clientY);
|
||||
if (dx < 80 || dx <= dy * 1.5) return;
|
||||
if (this.data.showEditPartnerModal && (this.data.partnerSaving || this.data.partnerAvatarUploading)) return;
|
||||
if (this.data.showEditProfileModal) this.hideEditProfileModal();
|
||||
else if (this.data.showEditPartnerModal) this.hideEditPartnerModal();
|
||||
else return;
|
||||
wx.hideKeyboard();
|
||||
},
|
||||
|
||||
_needRefresh: true,
|
||||
|
||||
onScroll: function(e) {
|
||||
|
||||
@@ -46,30 +46,30 @@
|
||||
|
||||
|
||||
|
||||
<view wx:if="{{showEditProfileModal}}" class="edit-root" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">
|
||||
<view wx:if="{{showEditProfileModal}}" class="edit-root" capture-bind:touchstart="onEditTouchStart" capture-bind:touchend="onEditTouchEnd" capture-bind:touchcancel="onEditTouchCancel">
|
||||
<view class="edit-nav"><view class="back-btn" bindtap="hideEditProfileModal"><image class="back-icon" src="/images/back.svg" mode="aspectFit" /></view></view>
|
||||
<scroll-view class="edit-body" scroll-y show-scrollbar="{{false}}" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">
|
||||
<scroll-view class="edit-body" scroll-y show-scrollbar="{{false}}">
|
||||
<view class="edit-avatar-wrap" bindtap="chooseAvatar"><image wx:if="{{editAvatarUrl}}" src="{{editAvatarUrl}}" mode="aspectFill" class="edit-avatar-img"></image><text wx:else class="edit-avatar-placeholder">{{editNicknameInitial || '?'}}</text><text class="edit-avatar-hint">点击更换头像</text></view>
|
||||
<text class="edit-id">我的ID: {{myDisplayId}}</text>
|
||||
<view class="input-group"><label>昵称</label><input placeholder-class="handwritten-placeholder" class="input" placeholder="你的昵称" value="{{editNickname}}" bindinput="onEditNicknameInput" /></view>
|
||||
<view class="input-group"><label>昵称</label><input data-swipe-ignore="{{true}}" placeholder-class="handwritten-placeholder" class="input" placeholder="你的昵称" value="{{editNickname}}" bindinput="onEditNicknameInput" /></view>
|
||||
<view class="input-group"><label>性别</label><picker mode="selector" range="{{genderOptions}}" bindchange="onEditGenderChange"><view class="input">{{editGenderText || '请选择'}}</view></picker></view>
|
||||
<view class="input-group"><label>生日</label><picker mode="date" value="{{editBirthday}}" bindchange="onEditBirthdayInput"><view class="input">{{editBirthday || '请选择日期'}}</view></picker><text style="font-size:24rpx;color:#B74768;margin-top:8rpx;display:block;" wx:if="{{editAge}}">自动计算:{{editAge}}岁</text></view>
|
||||
<view class="input-group" wx:if="{{hasPartner}}"><label>恋爱纪念日</label><picker mode="date" value="{{tempLoveDate}}" bindchange="onLoveDateInput"><view class="input">{{tempLoveDate || '设置你们在一起的日期'}}</view></picker></view>
|
||||
<view class="input-group" wx:if="{{hasPartner}}"><label>恋爱故事</label><textarea placeholder-class="handwritten-placeholder" class="input" style="min-height:120rpx;" placeholder="记录你们的故事..." value="{{tempLoveStory}}" bindinput="onLoveStoryInput"></textarea></view>
|
||||
<view class="input-group" wx:if="{{hasPartner}}"><label>恋爱故事</label><textarea data-swipe-ignore="{{true}}" placeholder-class="handwritten-placeholder" class="input" style="min-height:120rpx;" placeholder="记录你们的故事..." value="{{tempLoveStory}}" bindinput="onLoveStoryInput"></textarea></view>
|
||||
</scroll-view>
|
||||
<view class="edit-footer"><button class="btn btn-primary" bindtap="saveProfile" hover-class="none">保存</button></view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{showEditPartnerModal}}" class="edit-root partner-edit" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">
|
||||
<view wx:if="{{showEditPartnerModal}}" class="edit-root partner-edit" capture-bind:touchstart="onEditTouchStart" capture-bind:touchend="onEditTouchEnd" capture-bind:touchcancel="onEditTouchCancel">
|
||||
<view class="edit-nav"><view class="back-btn" bindtap="hideEditPartnerModal"><image class="back-icon" src="/images/back.svg" mode="aspectFit" /></view><text class="partner-nav-title">编辑TA的资料</text></view>
|
||||
<scroll-view class="edit-body" scroll-y show-scrollbar="{{false}}">
|
||||
<view class="edit-avatar-wrap" bindtap="choosePartnerAvatar"><image wx:if="{{editPartnerAvatarUrl}}" src="{{editPartnerAvatarUrl}}" mode="aspectFill" class="edit-avatar-img" /><text wx:else class="edit-avatar-placeholder">{{editPartnerNameInitial || '?'}}</text><text class="edit-avatar-hint">{{partnerAvatarUploading ? '正在上传头像…' : '点击更换头像'}}</text></view>
|
||||
<text class="edit-id" wx:if="{{partnerDisplayId}}">TA的ID: {{partnerDisplayId}}</text>
|
||||
<view class="input-group"><label>昵称</label><input placeholder-class="handwritten-placeholder" class="input" value="{{editPartnerNickname}}" placeholder="TA的昵称" bindinput="onEditPartnerNicknameInput" disabled="{{partnerSaving}}" /></view>
|
||||
<view class="input-group"><label>昵称</label><input data-swipe-ignore="{{true}}" placeholder-class="handwritten-placeholder" class="input" value="{{editPartnerNickname}}" placeholder="TA的昵称" bindinput="onEditPartnerNicknameInput" disabled="{{partnerSaving}}" /></view>
|
||||
<view class="input-group"><label>性别</label><picker mode="selector" range="{{genderOptions}}" bindchange="onEditPartnerGenderChange" disabled="{{partnerSaving}}"><view class="input">{{editPartnerGenderText || '请选择'}}</view></picker></view>
|
||||
<view class="input-group"><label>生日</label><picker mode="date" value="{{editPartnerBirthday}}" bindchange="onEditPartnerBirthdayInput" disabled="{{partnerSaving}}"><view class="input">{{editPartnerBirthday || '请选择日期'}}</view></picker><text class="edit-avatar-hint" wx:if="{{editPartnerAge}}">自动计算:{{editPartnerAge}}岁</text></view>
|
||||
<view class="input-group"><label>恋爱纪念日</label><picker mode="date" value="{{tempLoveDate}}" bindchange="onLoveDateInput" disabled="{{partnerSaving}}"><view class="input">{{tempLoveDate || '设置你们在一起的日期'}}</view></picker></view>
|
||||
<view class="input-group"><label>恋爱故事</label><textarea placeholder-class="handwritten-placeholder" class="input" style="min-height:120rpx;" value="{{tempLoveStory}}" placeholder="记录你们的故事…" bindinput="onLoveStoryInput" disabled="{{partnerSaving}}" /></view>
|
||||
<view class="input-group"><label>恋爱故事</label><textarea data-swipe-ignore="{{true}}" placeholder-class="handwritten-placeholder" class="input" style="min-height:120rpx;" value="{{tempLoveStory}}" placeholder="记录你们的故事…" bindinput="onLoveStoryInput" disabled="{{partnerSaving}}" /></view>
|
||||
</scroll-view>
|
||||
<view class="edit-footer"><button class="btn btn-primary" bindtap="savePartnerProfile" loading="{{partnerSaving}}" disabled="{{partnerSaving || partnerAvatarUploading}}" hover-class="none">保存</button></view>
|
||||
</view>
|
||||
|
||||
@@ -66,6 +66,10 @@ Page({
|
||||
|
||||
deleteAccount: async function() {
|
||||
if(this._deletingAccount)return;
|
||||
if (this.data.hasPartner) {
|
||||
wx.showModal({title:'暂无法注销',content:'请先完成情侣解绑后再注销账号;处于解绑申请或72小时冷静期内,也需要等待解绑完成。',showCancel:false,confirmText:'知道了'});
|
||||
return;
|
||||
}
|
||||
this._deletingAccount=true;
|
||||
try {
|
||||
const preview=await api.accountDeletionPreview();
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</view>
|
||||
|
||||
<view class="section-heading">关系与账号</view>
|
||||
<view class="j-profile-setting" wx:if="{{bindingKnown && !hasPartner}}" bindtap="deleteAccount"><view class="setting-info"><view class="j-small-title" style="color:#B74768">注销账号</view><view class="j-caption">未绑定时可注销,确认后删除全部个人数据</view></view><text class="j-link">›</text></view>
|
||||
<view class="j-profile-setting" bindtap="deleteAccount"><view class="setting-info"><view class="j-small-title" style="color:#B74768">注销账号</view><view class="j-caption">{{hasPartner ? '请先完成情侣解绑,再注销账号' : '确认后删除账号及全部个人数据'}}</view></view><text class="j-link">›</text></view>
|
||||
<!-- 操作记录 -->
|
||||
<view class="j-profile-setting" bindtap="goToHistory">
|
||||
<view class="setting-info">
|
||||
|
||||
@@ -38,3 +38,10 @@ test('concurrent homepage notice reads share a request and do not dismiss a fres
|
||||
test('a hidden homepage cannot consume a once-per-login notification',async()=>{
|
||||
let done;const e=page('index/index.js',{announcement:()=>new Promise(resolve=>done=resolve)});const a=e.p.loadAnnouncement();e.p.onHide();done({mode:'login_once',enabled:true,content:'hello',revision:'v1'});await a;assert.equal(e.p.data.showAnnouncement,false);e.p._announcementActive=true;const b=e.p.loadAnnouncement();done({mode:'login_once',enabled:true,content:'hello',revision:'v1'});await b;assert.equal(e.p.data.showAnnouncement,true);
|
||||
});
|
||||
|
||||
test('bound accounts see an explanation instead of starting account deletion',async()=>{
|
||||
let calls=0;const e=page('settings/settings.js',{accountDeletionPreview:async()=>{calls++;},accountDelete:async()=>{calls++;}});
|
||||
e.p.data.hasPartner=true;e.p.data.bindingKnown=true;
|
||||
await e.p.deleteAccount();assert.equal(calls,0);assert.equal(e.dialogs.length,1);
|
||||
assert.match(e.dialogs[0].content,/请先完成情侣解绑/);assert.equal(e.dialogs[0].showCancel,false);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
const test=require('node:test'),assert=require('node:assert/strict'),path=require('node:path');
|
||||
function setup(partner=false){
|
||||
let def;const calls=[];
|
||||
const load=require('./load-module.cjs')({Page:p=>def=p,console,Date,wx:{hideKeyboard:()=>calls.push('keyboard'),navigateBack:()=>calls.push('back')}},{'../../utils/page-cache':{withDataCache:p=>p},'../../utils/request':{api:{}}});
|
||||
load(path.join(__dirname,'../pages/profile/profile.js'));
|
||||
const p={...def,data:{...def.data,showEditProfileModal:!partner,showEditPartnerModal:partner},setData(v){Object.assign(this.data,v)},getTabBar:()=>({setHidden:v=>calls.push(v)})};return {p,calls};
|
||||
}
|
||||
const start=(x,y,ignore=false)=>({touches:[{clientX:x,clientY:y}],target:{dataset:{swipeIgnore:ignore}}});
|
||||
const end=(x,y)=>({changedTouches:[{clientX:x,clientY:y}]});
|
||||
test('left swipe closes either editor once, restores tabs and never navigates away from profile',()=>{
|
||||
for(const partner of [false,true]){const {p,calls}=setup(partner);p.onEditTouchStart(start(280,300));p.onEditTouchEnd(end(100,310));p.onEditTouchEnd(end(100,310));assert.equal(p.data.showEditProfileModal,false);assert.equal(p.data.showEditPartnerModal,false);assert.deepEqual(calls,[false,'keyboard']);}
|
||||
});
|
||||
test('vertical scrolling, short or rightward gestures and text selection keep the editor open',()=>{
|
||||
for(const [s,e] of [[start(280,300),end(240,305)],[start(280,300),end(160,500)],[start(100,300),end(280,310)],[start(280,300,true),end(100,310)]]){const {p,calls}=setup();p.onEditTouchStart(s);p.onEditTouchEnd(e);assert.equal(p.data.showEditProfileModal,true);assert.equal(calls.length,0);}
|
||||
});
|
||||
test('cancelled touches and partner saving never dismiss the editor',()=>{
|
||||
const {p}=setup();p.onEditTouchStart(start(280,300));p.onEditTouchCancel();p.onEditTouchEnd(end(100,300));assert.equal(p.data.showEditProfileModal,true);
|
||||
const e=setup(true);e.p.data.partnerSaving=true;e.p.onEditTouchStart(start(280,300));e.p.onEditTouchEnd(end(100,300));assert.equal(e.p.data.showEditPartnerModal,true);
|
||||
});
|
||||
Reference in New Issue
Block a user