@@ -13,6 +13,26 @@ import (
1313 "code.gitea.io/gitea/modules/timeutil"
1414)
1515
16+ // ErrIssueStopwatchNotExist represents an error that stopwatch is not exist
17+ type ErrIssueStopwatchNotExist struct {
18+ UserID int64
19+ IssueID int64
20+ }
21+
22+ func (err ErrIssueStopwatchNotExist ) Error () string {
23+ return fmt .Sprintf ("issue stopwatch is not exist[uid: %d, issue_id: %d" , err .UserID , err .IssueID )
24+ }
25+
26+ // ErrIssueStopwatchAlreadyExist represents an error that stopwatch is already exist
27+ type ErrIssueStopwatchAlreadyExist struct {
28+ UserID int64
29+ IssueID int64
30+ }
31+
32+ func (err ErrIssueStopwatchAlreadyExist ) Error () string {
33+ return fmt .Sprintf ("issue stopwatch is already exist[uid: %d, issue_id: %d" , err .UserID , err .IssueID )
34+ }
35+
1636// Stopwatch represents a stopwatch for time tracking.
1737type Stopwatch struct {
1838 ID int64 `xorm:"pk autoincr"`
@@ -35,9 +55,9 @@ func (s Stopwatch) Duration() string {
3555 return SecToTime (s .Seconds ())
3656}
3757
38- func getStopwatch (e db. Engine , userID , issueID int64 ) (sw * Stopwatch , exists bool , err error ) {
58+ func getStopwatch (ctx context. Context , userID , issueID int64 ) (sw * Stopwatch , exists bool , err error ) {
3959 sw = new (Stopwatch )
40- exists , err = e .
60+ exists , err = db . GetEngine ( ctx ) .
4161 Where ("user_id = ?" , userID ).
4262 And ("issue_id = ?" , issueID ).
4363 Get (sw )
@@ -66,7 +86,7 @@ func CountUserStopwatches(userID int64) (int64, error) {
6686
6787// StopwatchExists returns true if the stopwatch exists
6888func StopwatchExists (userID , issueID int64 ) bool {
69- _ , exists , _ := getStopwatch (db .GetEngine ( db . DefaultContext ) , userID , issueID )
89+ _ , exists , _ := getStopwatch (db .DefaultContext , userID , issueID )
7090 return exists
7191}
7292
@@ -83,93 +103,110 @@ func hasUserStopwatch(e db.Engine, userID int64) (exists bool, sw *Stopwatch, er
83103 return
84104}
85105
86- // CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline.
87- func CreateOrStopIssueStopwatch ( user * User , issue * Issue ) error {
88- ctx , committer , err := db . TxContext ( )
106+ // FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore
107+ func FinishIssueStopwatchIfPossible ( ctx context. Context , user * User , issue * Issue ) error {
108+ _ , exists , err := getStopwatch ( ctx , user . ID , issue . ID )
89109 if err != nil {
90110 return err
91111 }
92- defer committer .Close ()
93- if err := createOrStopIssueStopwatch (ctx , user , issue ); err != nil {
94- return err
112+ if ! exists {
113+ return nil
95114 }
96- return committer . Commit ( )
115+ return FinishIssueStopwatch ( ctx , user , issue )
97116}
98117
99- func createOrStopIssueStopwatch ( ctx context. Context , user * User , issue * Issue ) error {
100- e := db . GetEngine ( ctx )
101- sw , exists , err := getStopwatch (e , user .ID , issue .ID )
118+ // CreateOrStopIssueStopwatch create an issue stopwatch if it's not exist, otherwise finish it
119+ func CreateOrStopIssueStopwatch ( user * User , issue * Issue ) error {
120+ _ , exists , err := getStopwatch (db . DefaultContext , user .ID , issue .ID )
102121 if err != nil {
103122 return err
104123 }
105- if err := issue .loadRepo (e ); err != nil {
106- return err
107- }
108-
109124 if exists {
110- // Create tracked time out of the time difference between start date and actual date
111- timediff := time .Now ().Unix () - int64 (sw .CreatedUnix )
125+ return FinishIssueStopwatch (db .DefaultContext , user , issue )
126+ }
127+ return CreateIssueStopwatch (db .DefaultContext , user , issue )
128+ }
112129
113- // Create TrackedTime
114- tt := & TrackedTime {
115- Created : time .Now (),
116- IssueID : issue .ID ,
130+ // FinishIssueStopwatch if stopwatch exist then finish it otherwise return an error
131+ func FinishIssueStopwatch (ctx context.Context , user * User , issue * Issue ) error {
132+ sw , exists , err := getStopwatch (ctx , user .ID , issue .ID )
133+ if err != nil {
134+ return err
135+ }
136+ if ! exists {
137+ return ErrIssueStopwatchNotExist {
117138 UserID : user .ID ,
118- Time : timediff ,
139+ IssueID : issue . ID ,
119140 }
141+ }
120142
121- if _ , err := e .Insert (tt ); err != nil {
122- return err
123- }
143+ // Create tracked time out of the time difference between start date and actual date
144+ timediff := time .Now ().Unix () - int64 (sw .CreatedUnix )
124145
125- if _ , err := createComment (ctx , & CreateCommentOptions {
126- Doer : user ,
127- Issue : issue ,
128- Repo : issue .Repo ,
129- Content : SecToTime (timediff ),
130- Type : CommentTypeStopTracking ,
131- TimeID : tt .ID ,
132- }); err != nil {
133- return err
134- }
135- if _ , err := e .Delete (sw ); err != nil {
136- return err
137- }
138- } else {
139- // if another stopwatch is running: stop it
140- exists , sw , err := hasUserStopwatch (e , user .ID )
141- if err != nil {
142- return err
143- }
144- if exists {
145- issue , err := getIssueByID (e , sw .IssueID )
146- if err != nil {
147- return err
148- }
149- if err := createOrStopIssueStopwatch (ctx , user , issue ); err != nil {
150- return err
151- }
152- }
146+ // Create TrackedTime
147+ tt := & TrackedTime {
148+ Created : time .Now (),
149+ IssueID : issue .ID ,
150+ UserID : user .ID ,
151+ Time : timediff ,
152+ }
153+
154+ if err := db .Insert (ctx , tt ); err != nil {
155+ return err
156+ }
157+
158+ if _ , err := createComment (ctx , & CreateCommentOptions {
159+ Doer : user ,
160+ Issue : issue ,
161+ Repo : issue .Repo ,
162+ Content : SecToTime (timediff ),
163+ Type : CommentTypeStopTracking ,
164+ TimeID : tt .ID ,
165+ }); err != nil {
166+ return err
167+ }
168+ _ , err = db .GetEngine (ctx ).Delete (sw )
169+ return err
170+ }
153171
154- // Create stopwatch
155- sw = & Stopwatch {
172+ // CreateIssueStopwatch creates a stopwatch if not exist, otherwise return an error
173+ func CreateIssueStopwatch (ctx context.Context , user * User , issue * Issue ) error {
174+ e := db .GetEngine (ctx )
175+ if err := issue .loadRepo (e ); err != nil {
176+ return err
177+ }
178+
179+ // if another stopwatch is running: stop it
180+ exists , _ , err := hasUserStopwatch (e , user .ID )
181+ if err != nil {
182+ return err
183+ }
184+ if exists {
185+ return ErrIssueStopwatchAlreadyExist {
156186 UserID : user .ID ,
157187 IssueID : issue .ID ,
158188 }
189+ }
159190
160- if err := db .Insert (ctx , sw ); err != nil {
161- return err
162- }
191+ // Create stopwatch
192+ var sw = & Stopwatch {
193+ UserID : user .ID ,
194+ IssueID : issue .ID ,
195+ }
163196
164- if _ , err := createComment (ctx , & CreateCommentOptions {
165- Doer : user ,
166- Issue : issue ,
167- Repo : issue .Repo ,
168- Type : CommentTypeStartTracking ,
169- }); err != nil {
170- return err
171- }
197+ if err := db .Insert (ctx , sw ); err != nil {
198+ return err
172199 }
200+
201+ if _ , err := createComment (ctx , & CreateCommentOptions {
202+ Doer : user ,
203+ Issue : issue ,
204+ Repo : issue .Repo ,
205+ Type : CommentTypeStartTracking ,
206+ }); err != nil {
207+ return err
208+ }
209+
173210 return nil
174211}
175212
@@ -188,7 +225,7 @@ func CancelStopwatch(user *User, issue *Issue) error {
188225
189226func cancelStopwatch (ctx context.Context , user * User , issue * Issue ) error {
190227 e := db .GetEngine (ctx )
191- sw , exists , err := getStopwatch (e , user .ID , issue .ID )
228+ sw , exists , err := getStopwatch (ctx , user .ID , issue .ID )
192229 if err != nil {
193230 return err
194231 }
0 commit comments