Skip to content

Commit 015ac63

Browse files
committed
fix reminder setting
1 parent a24882f commit 015ac63

4 files changed

Lines changed: 552 additions & 27 deletions

File tree

MainWindow.xaml.cs

Lines changed: 97 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,16 @@ public partial class MainWindow : Window
333333
private int _remindersShownToday = 0;
334334
private DateTime _reminderCounterDate = DateTime.Today;
335335
private readonly Dictionary<string, TaskInteractionState> _taskInteractionStates = new Dictionary<string, TaskInteractionState>();
336-
private const int DailyStuckNudgeLimit = 2;
336+
private const int DefaultDailyStuckNudgeLimit = 2;
337337
private static readonly TimeSpan StuckNudgeCooldown = TimeSpan.FromHours(2);
338-
private static readonly TimeSpan StuckNoProgressThreshold = TimeSpan.FromMinutes(90);
338+
private static readonly TimeSpan DefaultStuckNoProgressThreshold = TimeSpan.FromMinutes(90);
339+
private static readonly TimeSpan MinStuckNoProgressThreshold = TimeSpan.FromMinutes(60);
340+
private static readonly TimeSpan MaxStuckNoProgressThreshold = TimeSpan.FromMinutes(180);
341+
private const int MinDailyStuckNudgeLimit = 1;
342+
private const int MaxDailyStuckNudgeLimit = 3;
343+
private TimeSpan _adaptiveStuckNoProgressThreshold = DefaultStuckNoProgressThreshold;
344+
private int _adaptiveDailyStuckNudgeLimit = DefaultDailyStuckNudgeLimit;
345+
private DateTime _lastAdaptiveTuneAt = DateTime.MinValue;
339346
private int _stuckNudgesShownToday = 0;
340347
private DateTime _stuckNudgeCounterDate = DateTime.Today;
341348
private DateTime _lastStuckNudgeAt = DateTime.MinValue;
@@ -585,6 +592,7 @@ public MainWindow()
585592

586593
_llmService = LlmService.Create();
587594
_userProfileManager = new UserProfileManager();
595+
UpdateAdaptiveNudgeParameters(force: true);
588596

589597
var normalizedPosition = NormalizeWindowPosition(
590598
(double)Properties.Settings.Default.Left,
@@ -979,6 +987,9 @@ private class TaskInteractionState
979987
public int SnoozeCount { get; set; } = 0;
980988
public int DismissCount { get; set; } = 0;
981989
public DateTime? LastNudgedAt { get; set; } = null;
990+
public string LastSuggestedActionId { get; set; } = null;
991+
public string PendingSuggestedActionId { get; set; } = null;
992+
public DateTime? PendingSuggestedAt { get; set; } = null;
982993
}
983994

984995
private void StartPeriodicTaskReminderChecks()
@@ -991,10 +1002,52 @@ private void StartPeriodicTaskReminderChecks()
9911002

9921003
private async void TaskReminderTimer_Tick(object sender, EventArgs e)
9931004
{
1005+
UpdateAdaptiveNudgeParameters();
9941006
await CheckForStaleTasksAndRemind();
9951007
CheckForPotentialStuckTasks();
9961008
}
9971009

1010+
private void UpdateAdaptiveNudgeParameters(bool force = false)
1011+
{
1012+
DateTime now = DateTime.Now;
1013+
if (!force && (now - _lastAdaptiveTuneAt) < TimeSpan.FromHours(6))
1014+
{
1015+
return;
1016+
}
1017+
1018+
try
1019+
{
1020+
var recommendation = _userProfileManager?.GetAdaptiveNudgeRecommendation(7);
1021+
if (recommendation == null)
1022+
{
1023+
return;
1024+
}
1025+
1026+
_adaptiveStuckNoProgressThreshold = ClampThreshold(TimeSpan.FromMinutes(recommendation.RecommendedStuckThresholdMinutes));
1027+
_adaptiveDailyStuckNudgeLimit = Math.Max(MinDailyStuckNudgeLimit, Math.Min(MaxDailyStuckNudgeLimit, recommendation.RecommendedDailyNudgeLimit));
1028+
_lastAdaptiveTuneAt = now;
1029+
}
1030+
catch (Exception ex)
1031+
{
1032+
Console.WriteLine($"Adaptive tuning failed: {ex.Message}");
1033+
}
1034+
}
1035+
1036+
private static TimeSpan ClampThreshold(TimeSpan threshold)
1037+
{
1038+
if (threshold < MinStuckNoProgressThreshold)
1039+
{
1040+
return MinStuckNoProgressThreshold;
1041+
}
1042+
1043+
if (threshold > MaxStuckNoProgressThreshold)
1044+
{
1045+
return MaxStuckNoProgressThreshold;
1046+
}
1047+
1048+
return threshold;
1049+
}
1050+
9981051
private void CheckForPotentialStuckTasks()
9991052
{
10001053
DateTime now = DateTime.Now;
@@ -1004,7 +1057,7 @@ private void CheckForPotentialStuckTasks()
10041057
_stuckNudgesShownToday = 0;
10051058
}
10061059

1007-
if (_stuckNudgesShownToday >= DailyStuckNudgeLimit || (now - _lastStuckNudgeAt) < StuckNudgeCooldown)
1060+
if (_stuckNudgesShownToday >= _adaptiveDailyStuckNudgeLimit || (now - _lastStuckNudgeAt) < StuckNudgeCooldown)
10081061
{
10091062
return;
10101063
}
@@ -1041,7 +1094,7 @@ private void CheckForPotentialStuckTasks()
10411094
continue;
10421095
}
10431096

1044-
if (noProgressDuration >= StuckNoProgressThreshold && (hasHighFriction || hasAvoidancePattern))
1097+
if (noProgressDuration >= _adaptiveStuckNoProgressThreshold && (hasHighFriction || hasAvoidancePattern))
10451098
{
10461099
if (candidate == null || noProgressDuration > candidateNoProgress)
10471100
{
@@ -1058,15 +1111,20 @@ private void CheckForPotentialStuckTasks()
10581111
return;
10591112
}
10601113

1061-
ShowPassiveStuckSuggestion(candidate, candidateNoProgress);
1114+
ShowPassiveStuckSuggestion(candidate, candidateNoProgress, candidateState);
10621115
candidateState.LastNudgedAt = now;
10631116
_stuckNudgesShownToday++;
10641117
_lastStuckNudgeAt = now;
10651118
}
10661119

1067-
private void ShowPassiveStuckSuggestion(ItemGrid task, TimeSpan noProgressDuration)
1120+
private void ShowPassiveStuckSuggestion(ItemGrid task, TimeSpan noProgressDuration, TaskInteractionState state)
10681121
{
1069-
string nextStep = BuildStuckNextStep(task, noProgressDuration);
1122+
var suggestion = BuildStuckNextStep(task, noProgressDuration, state);
1123+
string nextStep = suggestion.Text;
1124+
state.LastSuggestedActionId = suggestion.Id;
1125+
state.PendingSuggestedActionId = suggestion.Id;
1126+
state.PendingSuggestedAt = DateTime.Now;
1127+
_userProfileManager?.RecordSuggestionShown(suggestion.Id);
10701128

10711129
var notification = new System.Windows.Forms.NotifyIcon
10721130
{
@@ -1084,28 +1142,21 @@ private void ShowPassiveStuckSuggestion(ItemGrid task, TimeSpan noProgressDurati
10841142
});
10851143
}
10861144

1087-
private static string BuildStuckNextStep(ItemGrid task, TimeSpan noProgressDuration)
1145+
private UserProfileManager.StuckActionSuggestion BuildStuckNextStep(ItemGrid task, TimeSpan noProgressDuration, TaskInteractionState state)
10881146
{
1089-
int hours = Math.Max(1, (int)Math.Round(noProgressDuration.TotalHours));
1090-
bool highImportance = string.Equals(task.Importance, "High", StringComparison.OrdinalIgnoreCase);
1091-
bool highUrgency = string.Equals(task.Urgency, "High", StringComparison.OrdinalIgnoreCase);
1092-
1093-
if (highImportance && highUrgency)
1094-
{
1095-
return $"已卡住约 {hours} 小时,先做 10 分钟最小动作,完成后再扩展。";
1096-
}
1097-
1098-
if (highImportance && !highUrgency)
1147+
var suggestions = _userProfileManager?.GetRankedStuckSuggestions(task, noProgressDuration, state?.LastSuggestedActionId);
1148+
if (suggestions != null && suggestions.Count > 0)
10991149
{
1100-
return $"已卡住约 {hours} 小时,拆成一个 20 分钟子任务并安排到今天。";
1150+
return suggestions[0];
11011151
}
11021152

1103-
if (!highImportance && highUrgency)
1153+
int hours = Math.Max(1, (int)Math.Round(noProgressDuration.TotalHours));
1154+
return new UserProfileManager.StuckActionSuggestion
11041155
{
1105-
return $"已卡住约 {hours} 小时,建议先确认是否委托或降低优先级。";
1106-
}
1107-
1108-
return $"已卡住约 {hours} 小时,建议先暂停此任务,转到更关键事项。";
1156+
Id = "fallback_min_step",
1157+
Text = $"已卡住约 {hours} 小时,先做一个最小下一步动作。",
1158+
Score = 0
1159+
};
11091160
}
11101161

11111162
private void TrackTaskInteraction(ItemGrid task, string interactionType)
@@ -1143,11 +1194,14 @@ private void TrackTaskInteraction(ItemGrid task, string interactionType)
11431194
break;
11441195
case "snooze":
11451196
state.SnoozeCount++;
1197+
TryResolvePendingSuggestionFeedback(state, now, "deferred");
11461198
break;
11471199
case "dismiss":
11481200
state.DismissCount++;
1201+
TryResolvePendingSuggestionFeedback(state, now, "rejected");
11491202
break;
11501203
case "progress":
1204+
TryResolvePendingSuggestionFeedback(state, now, "accepted");
11511205
state.EditCount = 0;
11521206
state.ReorderCount = 0;
11531207
state.MoveCount = 0;
@@ -1158,6 +1212,25 @@ private void TrackTaskInteraction(ItemGrid task, string interactionType)
11581212
}
11591213
}
11601214

1215+
private void TryResolvePendingSuggestionFeedback(TaskInteractionState state, DateTime now, string feedbackType)
1216+
{
1217+
if (state == null || string.IsNullOrWhiteSpace(state.PendingSuggestedActionId) || !state.PendingSuggestedAt.HasValue)
1218+
{
1219+
return;
1220+
}
1221+
1222+
if ((now - state.PendingSuggestedAt.Value) > TimeSpan.FromMinutes(30))
1223+
{
1224+
state.PendingSuggestedActionId = null;
1225+
state.PendingSuggestedAt = null;
1226+
return;
1227+
}
1228+
1229+
_userProfileManager?.RecordSuggestionFeedback(state.PendingSuggestedActionId, feedbackType);
1230+
state.PendingSuggestedActionId = null;
1231+
state.PendingSuggestedAt = null;
1232+
}
1233+
11611234
private static void ResetInteractionWindowIfNeeded(TaskInteractionState state, DateTime now)
11621235
{
11631236
if ((now - state.WindowStart) <= TimeSpan.FromHours(2))

ReminderSettingsWindow.xaml

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Window x:Class="TimeTask.ReminderSettingsWindow"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4-
Title="任务提醒设置" Height="450" Width="500"
4+
Title="任务提醒设置" Height="620" Width="500"
55
WindowStartupLocation="CenterScreen"
66
ResizeMode="NoResize"
77
Background="#F5F5F5">
@@ -179,6 +179,54 @@
179179
</Grid>
180180
</StackPanel>
181181
</Border>
182+
183+
<!-- 主动协同指标 -->
184+
<Border Style="{StaticResource SettingGroupStyle}">
185+
<StackPanel>
186+
<TextBlock Text="📈 主动协同指标(只读)"
187+
FontWeight="Bold"
188+
FontSize="14"
189+
Foreground="#333"
190+
Margin="0,0,0,10"/>
191+
192+
<Grid>
193+
<Grid.ColumnDefinitions>
194+
<ColumnDefinition Width="180"/>
195+
<ColumnDefinition Width="*"/>
196+
</Grid.ColumnDefinitions>
197+
<Grid.RowDefinitions>
198+
<RowDefinition Height="Auto"/>
199+
<RowDefinition Height="Auto"/>
200+
<RowDefinition Height="Auto"/>
201+
<RowDefinition Height="Auto"/>
202+
<RowDefinition Height="Auto"/>
203+
<RowDefinition Height="Auto"/>
204+
<RowDefinition Height="Auto"/>
205+
</Grid.RowDefinitions>
206+
207+
<TextBlock Grid.Row="0" Grid.Column="0" Text="统计窗口:" Margin="0,4" Foreground="#555"/>
208+
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding MetricsWindowLabel}" Margin="0,4" FontWeight="SemiBold"/>
209+
210+
<TextBlock Grid.Row="1" Grid.Column="0" Text="建议命中率:" Margin="0,4" Foreground="#555"/>
211+
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding SuggestionHitRateText}" Margin="0,4" FontWeight="SemiBold"/>
212+
213+
<TextBlock Grid.Row="2" Grid.Column="0" Text="过度打扰指数:" Margin="0,4" Foreground="#555"/>
214+
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding InterruptionIndexText}" Margin="0,4" FontWeight="SemiBold"/>
215+
216+
<TextBlock Grid.Row="3" Grid.Column="0" Text="最有效动作类型:" Margin="0,4" Foreground="#555"/>
217+
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding TopEffectiveActionText}" Margin="0,4" FontWeight="SemiBold"/>
218+
219+
<TextBlock Grid.Row="4" Grid.Column="0" Text="建议反馈计数:" Margin="0,4" Foreground="#555"/>
220+
<TextBlock Grid.Row="4" Grid.Column="1" Text="{Binding SuggestionOutcomeText}" Margin="0,4" TextWrapping="Wrap"/>
221+
222+
<TextBlock Grid.Row="5" Grid.Column="0" Text="推荐卡点阈值:" Margin="0,4" Foreground="#555"/>
223+
<TextBlock Grid.Row="5" Grid.Column="1" Text="{Binding RecommendedStuckThresholdText}" Margin="0,4" FontWeight="SemiBold"/>
224+
225+
<TextBlock Grid.Row="6" Grid.Column="0" Text="推荐每日上限:" Margin="0,4" Foreground="#555"/>
226+
<TextBlock Grid.Row="6" Grid.Column="1" Text="{Binding RecommendedDailyNudgeLimitText}" Margin="0,4" FontWeight="SemiBold"/>
227+
</Grid>
228+
</StackPanel>
229+
</Border>
182230

183231
<!-- 说明信息 -->
184232
<Border Style="{StaticResource SettingGroupStyle}">
@@ -227,4 +275,4 @@
227275
Click="ResetButton_Click"/>
228276
</StackPanel>
229277
</Grid>
230-
</Window>
278+
</Window>

0 commit comments

Comments
 (0)