[Feature] Custom Reactions (#8886)
* add [ui] Reactions * move contend check from form to go functions * use else if * check if reaction is allowed only on react (so previous custom reaction can be still removed) * use $.AllowedReactions in templates * use ctx.Flash.Error * use it there too * add redirection * back to server error because a wrong reaction is a template issue ... * add emoji list link * add docs entry * small wording nit suggestions from @jolheiser - thx * same reactions as github * fix PR reactions * handle error so template JS could check * Add Integrations Test * add REACTIONS setting to cheat-sheet doc page
This commit is contained in:
		
							parent
							
								
									674bc772fb
								
							
						
					
					
						commit
						668eaf95d5
					
				
					 13 changed files with 76 additions and 18 deletions
				
			
		|  | @ -149,6 +149,9 @@ SHOW_USER_EMAIL = true | |||
| DEFAULT_THEME = gitea | ||||
| ; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`. | ||||
| THEMES = gitea,arc-green | ||||
| ; All available reactions. Allow users react with different emoji's | ||||
| : For the whole list look at https://gitea.com/gitea/gitea.com/issues/8 | ||||
| REACTIONS = +1, -1, laugh, hooray, confused, heart, rocket, eyes | ||||
| ; Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used. | ||||
| DEFAULT_SHOW_FULL_NAME = false | ||||
| ; Whether to search within description at repository search on explore page. | ||||
|  |  | |||
|  | @ -118,6 +118,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | |||
| - `DEFAULT_THEME`: **gitea**: \[gitea, arc-green\]: Set the default theme for the Gitea install. | ||||
| - `THEMES`:  **gitea,arc-green**: All available themes. Allow users select personalized themes | ||||
|   regardless of the value of `DEFAULT_THEME`. | ||||
| - `REACTIONS`: All available reactions. Allow users react with different emoji's. | ||||
| - `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used. | ||||
| - `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page. | ||||
| - `USE_SERVICE_WORKER`: **true**: Whether to enable a Service Worker to cache frontend assets. | ||||
|  |  | |||
|  | @ -161,6 +161,15 @@ Locales may change between versions, so keeping track of your customized locales | |||
| 
 | ||||
| To add a custom Readme, add a markdown formatted file (without an `.md` extension) to `custom/options/readme` | ||||
| 
 | ||||
| ### Reactions | ||||
| 
 | ||||
| To change reaction emoji's you can set allowed reactions at app.ini | ||||
| ``` | ||||
| [ui] | ||||
| REACTIONS = +1, -1, laugh, confused, heart, hooray, eyes | ||||
| ``` | ||||
| A full list of supported emoji's is at [emoji list](https://gitea.com/gitea/gitea.com/issues/8) | ||||
| 
 | ||||
| ## Customizing the look of Gitea | ||||
| 
 | ||||
| As of version 1.6.0 Gitea has built-in themes. The two built-in themes are, the default theme `gitea`, and a dark theme `arc-green`. To change the look of your Gitea install change the value of `DEFAULT_THEME` in the [ui](https://docs.gitea.io/en-us/config-cheat-sheet/#ui-ui) section of `app.ini` to another one of the available options.   | ||||
|  |  | |||
|  | @ -194,6 +194,32 @@ func TestIssueCommentClose(t *testing.T) { | |||
| 	assert.Equal(t, "Description", val) | ||||
| } | ||||
| 
 | ||||
| func TestIssueReaction(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
| 	session := loginUser(t, "user2") | ||||
| 	issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description") | ||||
| 
 | ||||
| 	req := NewRequest(t, "GET", issueURL) | ||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||
| 	htmlDoc := NewHTMLParser(t, resp.Body) | ||||
| 
 | ||||
| 	req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/reactions/react"), map[string]string{ | ||||
| 		"_csrf":   htmlDoc.GetCSRF(), | ||||
| 		"content": "8ball", | ||||
| 	}) | ||||
| 	session.MakeRequest(t, req, http.StatusInternalServerError) | ||||
| 	req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/reactions/react"), map[string]string{ | ||||
| 		"_csrf":   htmlDoc.GetCSRF(), | ||||
| 		"content": "eyes", | ||||
| 	}) | ||||
| 	session.MakeRequest(t, req, http.StatusOK) | ||||
| 	req = NewRequestWithValues(t, "POST", path.Join(issueURL, "/reactions/unreact"), map[string]string{ | ||||
| 		"_csrf":   htmlDoc.GetCSRF(), | ||||
| 		"content": "eyes", | ||||
| 	}) | ||||
| 	session.MakeRequest(t, req, http.StatusOK) | ||||
| } | ||||
| 
 | ||||
| func TestIssueCrossReference(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
| 
 | ||||
|  |  | |||
|  | @ -347,7 +347,7 @@ func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) | |||
| 
 | ||||
| // ReactionForm form for adding and removing reaction
 | ||||
| type ReactionForm struct { | ||||
| 	Content string `binding:"Required;In(+1,-1,laugh,confused,heart,hooray)"` | ||||
| 	Content string `binding:"Required"` | ||||
| } | ||||
| 
 | ||||
| // Validate validates the fields
 | ||||
|  |  | |||
|  | @ -169,6 +169,7 @@ var ( | |||
| 		DefaultShowFullName   bool | ||||
| 		DefaultTheme          string | ||||
| 		Themes                []string | ||||
| 		Reactions             []string | ||||
| 		SearchRepoDescription bool | ||||
| 		UseServiceWorker      bool | ||||
| 
 | ||||
|  | @ -198,6 +199,7 @@ var ( | |||
| 		MaxDisplayFileSize:  8388608, | ||||
| 		DefaultTheme:        `gitea`, | ||||
| 		Themes:              []string{`gitea`, `arc-green`}, | ||||
| 		Reactions:           []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, | ||||
| 		Admin: struct { | ||||
| 			UserPagingNum   int | ||||
| 			RepoPagingNum   int | ||||
|  |  | |||
|  | @ -673,6 +673,7 @@ func ViewIssue(ctx *context.Context) { | |||
| 		} | ||||
| 	} | ||||
| 	ctx.Data["IssueWatch"] = iw | ||||
| 	ctx.Data["AllowedReactions"] = setting.UI.Reactions | ||||
| 
 | ||||
| 	issue.RenderedContent = string(markdown.Render([]byte(issue.Content), ctx.Repo.RepoLink, | ||||
| 		ctx.Repo.Repository.ComposeMetas())) | ||||
|  | @ -1447,6 +1448,12 @@ func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) { | |||
| 
 | ||||
| 	switch ctx.Params(":action") { | ||||
| 	case "react": | ||||
| 		if !util.IsStringInSlice(form.Content, setting.UI.Reactions) { | ||||
| 			err := fmt.Errorf("ChangeIssueReaction: '%s' is not an allowed reaction", form.Content) | ||||
| 			ctx.ServerError(err.Error(), err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		reaction, err := models.CreateIssueReaction(ctx.User, issue, form.Content) | ||||
| 		if err != nil { | ||||
| 			log.Info("CreateIssueReaction: %s", err) | ||||
|  | @ -1542,6 +1549,12 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { | |||
| 
 | ||||
| 	switch ctx.Params(":action") { | ||||
| 	case "react": | ||||
| 		if !util.IsStringInSlice(form.Content, setting.UI.Reactions) { | ||||
| 			err := fmt.Errorf("ChangeIssueReaction: '%s' is not an allowed reaction", form.Content) | ||||
| 			ctx.ServerError(err.Error(), err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		reaction, err := models.CreateCommentReaction(ctx.User, comment.Issue, comment, form.Content) | ||||
| 		if err != nil { | ||||
| 			log.Info("CreateCommentReaction: %s", err) | ||||
|  |  | |||
|  | @ -422,6 +422,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare | |||
| 
 | ||||
| 	ctx.Data["NumCommits"] = compareInfo.Commits.Len() | ||||
| 	ctx.Data["NumFiles"] = compareInfo.NumFiles | ||||
| 	ctx.Data["AllowedReactions"] = setting.UI.Reactions | ||||
| 	return compareInfo | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ | |||
| 		{{$reactions := .Reactions.GroupByType}} | ||||
| 		{{if $reactions}} | ||||
| 			<div class="ui attached segment reactions"> | ||||
| 			{{template "repo/issue/view_content/reactions" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions }} | ||||
| 			{{template "repo/issue/view_content/reactions" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions "AllowedReactions" $.AllowedReactions }} | ||||
| 			</div> | ||||
| 		{{end}} | ||||
| 	</div> | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ | |||
| 					{{end}} | ||||
| 						{{if not $.Repository.IsArchived}} | ||||
| 							<div class="ui right actions"> | ||||
| 								{{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) }} | ||||
| 								{{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "AllowedReactions" $.AllowedReactions}} | ||||
| 								{{template "repo/issue/view_content/context_menu" Dict "ctx" $ "item" .Issue "delete" false "diff" false }} | ||||
| 							</div> | ||||
| 						{{end}} | ||||
|  | @ -47,7 +47,7 @@ | |||
| 					{{$reactions := .Issue.Reactions.GroupByType}} | ||||
| 					{{if $reactions}} | ||||
| 						<div class="ui attached segment reactions"> | ||||
| 							{{template "repo/issue/view_content/reactions" Dict "ctx" $ "ActionURL" (Printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions }} | ||||
| 							{{template "repo/issue/view_content/reactions" Dict "ctx" $ "ActionURL" (Printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions "AllowedReactions" $.AllowedReactions}} | ||||
| 						</div> | ||||
| 					{{end}} | ||||
| 					{{if .Issue.Attachments}} | ||||
|  |  | |||
|  | @ -7,12 +7,15 @@ | |||
| 	<div class="menu has-emoji"> | ||||
| 		<div class="header">{{ .ctx.i18n.Tr "repo.pick_reaction"}}</div> | ||||
| 		<div class="divider"></div> | ||||
| 		<div class="item" data-content="+1">:+1:</div> | ||||
| 		<div class="item" data-content="-1">:-1:</div> | ||||
| 		<div class="item" data-content="laugh">:laughing:</div> | ||||
| 		<div class="item" data-content="confused">:confused:</div> | ||||
| 		<div class="item" data-content="heart">:heart:</div> | ||||
| 		<div class="item" data-content="hooray">:tada:</div> | ||||
| 		{{range $value := .AllowedReactions}} | ||||
| 			{{if eq $value "hooray"}} | ||||
| 				<div class="item" data-content="hooray">:tada:</div> | ||||
| 			{{else if eq $value "laugh"}} | ||||
| 				<div class="item" data-content="laugh">:laughing:</div> | ||||
| 			{{else}} | ||||
| 				<div class="item" data-content="{{$value}}">:{{$value}}:</div> | ||||
| 			{{end}} | ||||
| 		{{end}} | ||||
| 	</div> | ||||
| </div> | ||||
| {{end}} | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ | |||
| 									{{end}} | ||||
| 								</div> | ||||
| 							{{end}} | ||||
| 							{{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.RepoLink .ID) }} | ||||
| 							{{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.RepoLink .ID) "AllowedReactions" $.AllowedReactions}} | ||||
| 							{{template "repo/issue/view_content/context_menu" Dict "ctx" $ "item" . "delete" true "diff" false }} | ||||
| 						</div> | ||||
| 					{{end}} | ||||
|  | @ -55,7 +55,7 @@ | |||
| 				{{$reactions := .Reactions.GroupByType}} | ||||
| 				{{if $reactions}} | ||||
| 					<div class="ui attached segment reactions"> | ||||
| 						{{template "repo/issue/view_content/reactions" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions }} | ||||
| 						{{template "repo/issue/view_content/reactions" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions "AllowedReactions" $.AllowedReactions}} | ||||
| 					</div> | ||||
| 				{{end}} | ||||
| 				{{if .Attachments}} | ||||
|  |  | |||
|  | @ -2,14 +2,14 @@ | |||
| 	<a class="ui label basic{{if $value.HasUser $.ctx.SignedUserID}} blue{{end}}{{if not $.ctx.IsSigned}} disabled{{end}} has-emoji" data-title="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ $.ctx.i18n.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}" data-content="{{ $key }}" data-action-url="{{ $.ActionURL }}"> | ||||
| 		{{if eq $key "hooray"}} | ||||
| 			:tada: | ||||
| 		{{else if eq $key "laugh"}} | ||||
| 			:laughing: | ||||
| 		{{else}} | ||||
| 			{{if eq $key "laugh"}} | ||||
| 				:laughing: | ||||
| 			{{else}} | ||||
| 				:{{$key}}: | ||||
| 			{{end}} | ||||
| 			:{{$key}}: | ||||
| 		{{end}} | ||||
| 		{{len $value}} | ||||
| 	</a> | ||||
| {{end}} | ||||
| {{template "repo/issue/view_content/add_reaction" Dict "ctx" $.ctx "ActionURL" .ActionURL }} | ||||
| {{if $.AllowedReactions}} | ||||
| 	{{template "repo/issue/view_content/add_reaction" Dict "ctx" $.ctx "ActionURL" .ActionURL "AllowedReactions" $.AllowedReactions}} | ||||
| {{end}} | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue