Compare commits

..

9 Commits

Author SHA1 Message Date
Yarden Shoham
28625fba5b
Redirect to the commit page after applying patch (#23056) (#23127)
Backport #23056

Fixes https://github.com/go-gitea/gitea/issues/22621

Co-authored-by: yp05327 <576951401@qq.com>
2023-02-24 22:43:26 -05:00
Yarden Shoham
7c3196ceac
Avoid warning for system setting when start up (#23054) (#23116)
Backport #23054

Partially fix #23050

After #22294 merged, it always has a warning log like `cannot get
context cache` when starting up. This should not affect any real life
but it's annoying. This PR will fix the problem. That means when
starting up, getting the system settings will not try from the cache but
will read from the database directly.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
2023-02-24 16:46:11 -05:00
Yarden Shoham
80c1264f4b
Show empty repos in Admin Repository Management page (#23114) (#23130)
Backport #23114

The **Admin Repository Management** page and the **Explore Repository**
page both use the `RenderRepoSearch` function. In this function, the
`OnlyShowRelevant` search option is `true` when querying repositories
for admin page.


edf98a2dc3/routers/web/explore/repo.go (L99-L115)

Refer to
[#19361](https://github.com/go-gitea/gitea/pull/19361/files#diff-8058dfb85557010e0592d586675ec62ce406af7068e6311f39c160deac37f149R497),
the repositories with `is_empty=true` will be hidden if
`OnlyShowRelevant` is `true`.

Administrators should be able to see all repositories. So
`OnlyShowRelevant` shouldn't be set to `true` .

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Andrew Thornton <art27@cantab.net>
2023-02-24 20:17:21 +00:00
silverwind
f0340c28f1
Change style to improve whitespaces trimming inside inline markdown code (#23093) (#23120)
Backport #23093

Given mardown source
```
x ` a` y
x `a ` y
x ` a ` y
```

Render

<img width="1421" alt="2023-02-23 15 33 14"

src="https://user-images.githubusercontent.com/17645053/220844280-a304c788-ac79-4a26-a55a-0db00f2fb3f3.png">

Fixes #23080.

Co-authored-by: HesterG <hestergong@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-24 17:05:36 +00:00
Yarden Shoham
5beb29ad35
Fix height for sticky head on large screen on PR page (#23111) (#23123)
Backport #23111

Right now on the PR 'File Change' Tab, the file title header sticky to
the top on large screens has wrong height, resulting in wrong ui
behavior when scrolling down. This PR is to fix this.

Before:

<img width="964" alt="截屏2023-02-24 17 12 29"
src="https://user-images.githubusercontent.com/17645053/221140409-025c4a84-6bbe-4b5b-a13f-bd2b79063522.png">

After:
<img width="1430" alt="截屏2023-02-24 21 10 12"
src="https://user-images.githubusercontent.com/17645053/221186750-0344d652-4610-4a90-a4c0-7f6269f950d6.png">

Co-authored-by: HesterG <hestergong@gmail.com>
2023-02-24 14:47:48 +00:00
Yarden Shoham
27e307142b
Fix db.Find bug (#23115) (#23119)
Backport #23115

Caused by #20821 

Fix #23110
2023-02-24 14:05:36 +00:00
Yarden Shoham
e02e752f68
Fix nil context in RenderMarkdownToHtml (#23092) (#23108)
Backport #23092

Fix #23082.

This bug is caused by a nil context in
https://github.com/go-gitea/gitea/issues/23082#issuecomment-1441276546 .

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-24 21:17:03 +08:00
Yarden Shoham
5ddf67a9c2
Make issue meta dropdown support Enter, confirm before reloading (#23014) (#23102)
Backport #23014

As the title. Label/assignee share the same code.

* Close #22607
* Close #20727

Also:

* partially fix for #21742, now the comment reaction and menu work with
keyboard.
* partially fix for #17705, in most cases the comment won't be lost.
* partially fix for #21539
* partially fix for #20347
* partially fix for #7329

### The `Enter` support

Before, if user presses Enter, the dropdown just disappears and nothing
happens or the window reloads.

After, Enter can be used to select/deselect labels, and press Esc to
hide the dropdown to update the labels (still no way to cancel ....
maybe you can do a Cmd+R or F5 to refresh the window to discard the
changes .....)


This is only a quick patch, the UX is still not perfect, but it's much
better than before.


### The `confirm` before reloading

And more fixes for the `reload` problem, the new behaviors:

* If nothing changes (just show/hide the dropdown), then the page won't
be reloaded.
* If there are draft comments, show a confirm dialog before reloading,
to avoid losing comments.

That's the best effect can be done at the moment, unless completely
refactor these dropdown related code.

Screenshot of the confirm dialog:

<details>


![image](https://user-images.githubusercontent.com/2114189/220538288-e2da8459-6a4e-43cb-8596-74057f8a03a2.png)

</details>

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Brecht Van Lommel <brecht@blender.org>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-24 17:40:36 +08:00
Yarden Shoham
4d3e2b23b8
Fix SyncOnCommit always return false in API of push_mirrors (#23088) (#23100)
Backport #23088

Fix: #22990

---
Before, the return value of the api is always false,regrardless of
whether the entry of `sync_on_commit` is true or false.
I have confirmed that the value of `sync_on_commit` dropped into the
database is correct.
So, I think it is enough to make some small changes.

Co-authored-by: sillyguodong <33891828+sillyguodong@users.noreply.github.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-24 14:36:25 +08:00
36 changed files with 194 additions and 116 deletions

View File

@ -153,7 +153,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
return DefaultAvatarLink() return DefaultAvatarLink()
} }
enableFederatedAvatar := system_model.GetSettingBool(ctx, system_model.KeyPictureEnableFederatedAvatar) enableFederatedAvatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
var err error var err error
if enableFederatedAvatar && system_model.LibravatarService != nil { if enableFederatedAvatar && system_model.LibravatarService != nil {
@ -174,7 +174,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
return urlStr return urlStr
} }
disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
if !disableGravatar { if !disableGravatar {
// copy GravatarSourceURL, because we will modify its Path. // copy GravatarSourceURL, because we will modify its Path.
avatarURLCopy := *system_model.GravatarSourceURL avatarURLCopy := *system_model.GravatarSourceURL

View File

@ -28,7 +28,7 @@ func enableGravatar(t *testing.T) {
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false") err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
assert.NoError(t, err) assert.NoError(t, err)
setting.GravatarSource = gravatarSource setting.GravatarSource = gravatarSource
err = system_model.Init() err = system_model.Init(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@ -134,7 +134,7 @@ func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error {
if !opts.IsListAll() { if !opts.IsListAll() {
sess.Limit(opts.GetSkipTake()) sess.Limit(opts.GetSkipTake())
} }
return sess.Find(&objects) return sess.Find(objects)
} }
// Count represents a common count function which accept an options interface // Count represents a common count function which accept an options interface
@ -148,5 +148,5 @@ func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (i
if !opts.IsListAll() { if !opts.IsListAll() {
sess.Limit(opts.GetSkipTake()) sess.Limit(opts.GetSkipTake())
} }
return sess.FindAndCount(&objects) return sess.FindAndCount(objects)
} }

48
models/db/list_test.go Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package db_test
import (
"testing"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
"xorm.io/builder"
)
type mockListOptions struct {
db.ListOptions
}
func (opts *mockListOptions) IsListAll() bool {
return true
}
func (opts *mockListOptions) ToConds() builder.Cond {
return builder.NewCond()
}
func TestFind(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
xe := unittest.GetXORMEngine()
assert.NoError(t, xe.Sync(&repo_model.RepoUnit{}))
opts := mockListOptions{}
var repoUnits []repo_model.RepoUnit
err := db.Find(db.DefaultContext, &opts, &repoUnits)
assert.NoError(t, err)
assert.EqualValues(t, 83, len(repoUnits))
cnt, err := db.Count(db.DefaultContext, &opts, new(repo_model.RepoUnit))
assert.NoError(t, err)
assert.EqualValues(t, 83, cnt)
repoUnits = make([]repo_model.RepoUnit, 0, 10)
newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits)
assert.NoError(t, err)
assert.EqualValues(t, cnt, newCnt)
}

View File

@ -39,9 +39,9 @@ import (
var ItemsPerPage = 40 var ItemsPerPage = 40
// Init initialize model // Init initialize model
func Init() error { func Init(ctx context.Context) error {
unit.LoadUnitConfig() unit.LoadUnitConfig()
return system_model.Init() return system_model.Init(ctx)
} }
// DeleteRepository deletes a repository for a user or organization. // DeleteRepository deletes a repository for a user or organization.

View File

@ -79,8 +79,8 @@ func IsErrDataExpired(err error) bool {
return ok return ok
} }
// GetSettingNoCache returns specific setting without using the cache // GetSetting returns specific setting without using the cache
func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) { func GetSetting(ctx context.Context, key string) (*Setting, error) {
v, err := GetSettings(ctx, []string{key}) v, err := GetSettings(ctx, []string{key})
if err != nil { if err != nil {
return nil, err return nil, err
@ -93,11 +93,11 @@ func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) {
const contextCacheKey = "system_setting" const contextCacheKey = "system_setting"
// GetSetting returns the setting value via the key // GetSettingWithCache returns the setting value via the key
func GetSetting(ctx context.Context, key string) (string, error) { func GetSettingWithCache(ctx context.Context, key string) (string, error) {
return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) {
return cache.GetString(genSettingCacheKey(key), func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) {
res, err := GetSettingNoCache(ctx, key) res, err := GetSetting(ctx, key)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -110,6 +110,15 @@ func GetSetting(ctx context.Context, key string) (string, error) {
// none existing keys and errors are ignored and result in false // none existing keys and errors are ignored and result in false
func GetSettingBool(ctx context.Context, key string) bool { func GetSettingBool(ctx context.Context, key string) bool {
s, _ := GetSetting(ctx, key) s, _ := GetSetting(ctx, key)
if s == nil {
return false
}
v, _ := strconv.ParseBool(s.SettingValue)
return v
}
func GetSettingWithCacheBool(ctx context.Context, key string) bool {
s, _ := GetSettingWithCache(ctx, key)
v, _ := strconv.ParseBool(s) v, _ := strconv.ParseBool(s)
return v return v
} }
@ -120,7 +129,7 @@ func GetSettings(ctx context.Context, keys []string) (map[string]*Setting, error
keys[i] = strings.ToLower(keys[i]) keys[i] = strings.ToLower(keys[i])
} }
settings := make([]*Setting, 0, len(keys)) settings := make([]*Setting, 0, len(keys))
if err := db.GetEngine(db.DefaultContext). if err := db.GetEngine(ctx).
Where(builder.In("setting_key", keys)). Where(builder.In("setting_key", keys)).
Find(&settings); err != nil { Find(&settings); err != nil {
return nil, err return nil, err
@ -151,9 +160,9 @@ func (settings AllSettings) GetVersion(key string) int {
} }
// GetAllSettings returns all settings from user // GetAllSettings returns all settings from user
func GetAllSettings() (AllSettings, error) { func GetAllSettings(ctx context.Context) (AllSettings, error) {
settings := make([]*Setting, 0, 5) settings := make([]*Setting, 0, 5)
if err := db.GetEngine(db.DefaultContext). if err := db.GetEngine(ctx).
Find(&settings); err != nil { Find(&settings); err != nil {
return nil, err return nil, err
} }
@ -168,12 +177,12 @@ func GetAllSettings() (AllSettings, error) {
func DeleteSetting(ctx context.Context, setting *Setting) error { func DeleteSetting(ctx context.Context, setting *Setting) error {
cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey) cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey)
cache.Remove(genSettingCacheKey(setting.SettingKey)) cache.Remove(genSettingCacheKey(setting.SettingKey))
_, err := db.GetEngine(db.DefaultContext).Delete(setting) _, err := db.GetEngine(ctx).Delete(setting)
return err return err
} }
func SetSettingNoVersion(ctx context.Context, key, value string) error { func SetSettingNoVersion(ctx context.Context, key, value string) error {
s, err := GetSettingNoCache(ctx, key) s, err := GetSetting(ctx, key)
if IsErrSettingIsNotExist(err) { if IsErrSettingIsNotExist(err) {
return SetSetting(ctx, &Setting{ return SetSetting(ctx, &Setting{
SettingKey: key, SettingKey: key,
@ -189,7 +198,7 @@ func SetSettingNoVersion(ctx context.Context, key, value string) error {
// SetSetting updates a users' setting for a specific key // SetSetting updates a users' setting for a specific key
func SetSetting(ctx context.Context, setting *Setting) error { func SetSetting(ctx context.Context, setting *Setting) error {
if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil { if err := upsertSettingValue(ctx, strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
return err return err
} }
@ -205,8 +214,8 @@ func SetSetting(ctx context.Context, setting *Setting) error {
return nil return nil
} }
func upsertSettingValue(key, value string, version int) error { func upsertSettingValue(parentCtx context.Context, key, value string, version int) error {
return db.WithTx(db.DefaultContext, func(ctx context.Context) error { return db.WithTx(parentCtx, func(ctx context.Context) error {
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
// here we use a general method to do a safe upsert for different databases (and most transaction levels) // here we use a general method to do a safe upsert for different databases (and most transaction levels)
@ -249,9 +258,9 @@ var (
LibravatarService *libravatar.Libravatar LibravatarService *libravatar.Libravatar
) )
func Init() error { func Init(ctx context.Context) error {
var disableGravatar bool var disableGravatar bool
disableGravatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureDisableGravatar) disableGravatarSetting, err := GetSetting(ctx, KeyPictureDisableGravatar)
if IsErrSettingIsNotExist(err) { if IsErrSettingIsNotExist(err) {
disableGravatar = setting_module.GetDefaultDisableGravatar() disableGravatar = setting_module.GetDefaultDisableGravatar()
disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)} disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
@ -262,7 +271,7 @@ func Init() error {
} }
var enableFederatedAvatar bool var enableFederatedAvatar bool
enableFederatedAvatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureEnableFederatedAvatar) enableFederatedAvatarSetting, err := GetSetting(ctx, KeyPictureEnableFederatedAvatar)
if IsErrSettingIsNotExist(err) { if IsErrSettingIsNotExist(err) {
enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar) enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar)
enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)} enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
@ -275,13 +284,13 @@ func Init() error {
if setting_module.OfflineMode { if setting_module.OfflineMode {
disableGravatar = true disableGravatar = true
enableFederatedAvatar = false enableFederatedAvatar = false
if !GetSettingBool(db.DefaultContext, KeyPictureDisableGravatar) { if !GetSettingBool(ctx, KeyPictureDisableGravatar) {
if err := SetSettingNoVersion(db.DefaultContext, KeyPictureDisableGravatar, "true"); err != nil { if err := SetSettingNoVersion(ctx, KeyPictureDisableGravatar, "true"); err != nil {
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err) return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err)
} }
} }
if GetSettingBool(db.DefaultContext, KeyPictureEnableFederatedAvatar) { if GetSettingBool(ctx, KeyPictureEnableFederatedAvatar) {
if err := SetSettingNoVersion(db.DefaultContext, KeyPictureEnableFederatedAvatar, "false"); err != nil { if err := SetSettingNoVersion(ctx, KeyPictureEnableFederatedAvatar, "false"); err != nil {
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err) return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err)
} }
} }

View File

@ -40,10 +40,10 @@ func TestSettings(t *testing.T) {
value, err := system.GetSetting(db.DefaultContext, keyName) value, err := system.GetSetting(db.DefaultContext, keyName)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, updatedSetting.SettingValue, value) assert.EqualValues(t, updatedSetting.SettingValue, value.SettingValue)
// get all settings // get all settings
settings, err = system.GetAllSettings() settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, settings, 3) assert.Len(t, settings, 3)
assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue) assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue)
@ -51,7 +51,7 @@ func TestSettings(t *testing.T) {
// delete setting // delete setting
err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)}) err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)})
assert.NoError(t, err) assert.NoError(t, err)
settings, err = system.GetAllSettings() settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, settings, 2) assert.Len(t, settings, 2)
} }

View File

@ -113,7 +113,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
if err = storage.Init(); err != nil { if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err) fatalTestError("storage.Init: %v\n", err)
} }
if err = system_model.Init(); err != nil { if err = system_model.Init(db.DefaultContext); err != nil {
fatalTestError("models.Init: %v\n", err) fatalTestError("models.Init: %v\n", err)
} }

View File

@ -67,7 +67,7 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
useLocalAvatar := false useLocalAvatar := false
autoGenerateAvatar := false autoGenerateAvatar := false
disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
switch { switch {
case u.UseCustomAvatar: case u.UseCustomAvatar:

View File

@ -106,7 +106,7 @@ func enableGravatar(t *testing.T) {
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false") err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
assert.NoError(t, err) assert.NoError(t, err)
setting.GravatarSource = "https://secure.gravatar.com/avatar" setting.GravatarSource = "https://secure.gravatar.com/avatar"
err = system_model.Init() err = system_model.Init(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@ -92,7 +92,7 @@ func NewFuncMap() []template.FuncMap {
return setting.AssetVersion return setting.AssetVersion
}, },
"DisableGravatar": func(ctx context.Context) bool { "DisableGravatar": func(ctx context.Context) bool {
return system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) return system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
}, },
"DefaultShowFullName": func() bool { "DefaultShowFullName": func() bool {
return setting.UI.DefaultShowFullName return setting.UI.DefaultShowFullName
@ -174,8 +174,9 @@ func NewFuncMap() []template.FuncMap {
"RenderEmojiPlain": emoji.ReplaceAliases, "RenderEmojiPlain": emoji.ReplaceAliases,
"ReactionToEmoji": ReactionToEmoji, "ReactionToEmoji": ReactionToEmoji,
"RenderNote": RenderNote, "RenderNote": RenderNote,
"RenderMarkdownToHtml": func(input string) template.HTML { "RenderMarkdownToHtml": func(ctx context.Context, input string) template.HTML {
output, err := markdown.RenderString(&markup.RenderContext{ output, err := markdown.RenderString(&markup.RenderContext{
Ctx: ctx,
URLPrefix: setting.AppSubURL, URLPrefix: setting.AppSubURL,
}, input) }, input)
if err != nil { if err != nil {

View File

@ -150,7 +150,7 @@ func GlobalInitInstalled(ctx context.Context) {
mustInit(system.Init) mustInit(system.Init)
mustInit(oauth2.Init) mustInit(oauth2.Init)
mustInit(models.Init) mustInitCtx(ctx, models.Init)
mustInit(repo_service.Init) mustInit(repo_service.Init)
// Booting long running goroutines. // Booting long running goroutines.

View File

@ -103,7 +103,7 @@ func Config(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminConfig"] = true ctx.Data["PageIsAdminConfig"] = true
systemSettings, err := system_model.GetAllSettings() systemSettings, err := system_model.GetAllSettings(ctx)
if err != nil { if err != nil {
ctx.ServerError("system_model.GetAllSettings", err) ctx.ServerError("system_model.GetAllSettings", err)
return return

View File

@ -36,6 +36,7 @@ func Repos(ctx *context.Context) {
Private: true, Private: true,
PageSize: setting.UI.Admin.RepoPagingNum, PageSize: setting.UI.Admin.RepoPagingNum,
TplName: tplRepos, TplName: tplRepos,
OnlyShowRelevant: false,
}) })
} }

View File

@ -27,10 +27,13 @@ type RepoSearchOptions struct {
Private bool Private bool
Restricted bool Restricted bool
PageSize int PageSize int
OnlyShowRelevant bool
TplName base.TplName TplName base.TplName
} }
// RenderRepoSearch render repositories search page // RenderRepoSearch render repositories search page
// This function is also used to render the Admin Repository Management page.
// The isAdmin param should be set to true when rendering the Admin page.
func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
// Sitemap index for sitemap paths // Sitemap index for sitemap paths
page := int(ctx.ParamsInt64("idx")) page := int(ctx.ParamsInt64("idx"))
@ -52,7 +55,6 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
count int64 count int64
err error err error
orderBy db.SearchOrderBy orderBy db.SearchOrderBy
onlyShowRelevant bool
) )
ctx.Data["SortType"] = ctx.FormString("sort") ctx.Data["SortType"] = ctx.FormString("sort")
@ -84,11 +86,9 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
orderBy = db.SearchOrderByRecentUpdated orderBy = db.SearchOrderByRecentUpdated
} }
onlyShowRelevant = !ctx.FormBool(relevantReposOnlyParam)
keyword := ctx.FormTrim("q") keyword := ctx.FormTrim("q")
ctx.Data["OnlyShowRelevant"] = onlyShowRelevant ctx.Data["OnlyShowRelevant"] = opts.OnlyShowRelevant
topicOnly := ctx.FormBool("topic") topicOnly := ctx.FormBool("topic")
ctx.Data["TopicOnly"] = topicOnly ctx.Data["TopicOnly"] = topicOnly
@ -111,7 +111,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
TopicOnly: topicOnly, TopicOnly: topicOnly,
Language: language, Language: language,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
OnlyShowRelevant: onlyShowRelevant, OnlyShowRelevant: opts.OnlyShowRelevant,
}) })
if err != nil { if err != nil {
ctx.ServerError("SearchRepository", err) ctx.ServerError("SearchRepository", err)
@ -162,5 +162,6 @@ func Repos(ctx *context.Context) {
OwnerID: ownerID, OwnerID: ownerID,
Private: ctx.Doer != nil, Private: ctx.Doer != nil,
TplName: tplExploreRepos, TplName: tplExploreRepos,
OnlyShowRelevant: !ctx.FormBool(relevantReposOnlyParam),
}) })
} }

View File

@ -27,8 +27,6 @@ func NewDiffPatch(ctx *context.Context) {
ctx.Data["PageIsPatch"] = true ctx.Data["PageIsPatch"] = true
ctx.Data["TreePath"] = ""
ctx.Data["commit_summary"] = "" ctx.Data["commit_summary"] = ""
ctx.Data["commit_message"] = "" ctx.Data["commit_message"] = ""
if canCommit { if canCommit {
@ -54,7 +52,6 @@ func NewDiffPatchPost(ctx *context.Context) {
branchName = form.NewBranchName branchName = form.NewBranchName
} }
ctx.Data["PageIsPatch"] = true ctx.Data["PageIsPatch"] = true
ctx.Data["TreePath"] = ""
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
ctx.Data["FileContent"] = form.Content ctx.Data["FileContent"] = form.Content
ctx.Data["commit_summary"] = form.CommitSummary ctx.Data["commit_summary"] = form.CommitSummary
@ -89,13 +86,14 @@ func NewDiffPatchPost(ctx *context.Context) {
message += "\n\n" + form.CommitMessage message += "\n\n" + form.CommitMessage
} }
if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{ fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{
LastCommitID: form.LastCommit, LastCommitID: form.LastCommit,
OldBranch: ctx.Repo.BranchName, OldBranch: ctx.Repo.BranchName,
NewBranch: branchName, NewBranch: branchName,
Message: message, Message: message,
Content: strings.ReplaceAll(form.Content, "\r", ""), Content: strings.ReplaceAll(form.Content, "\r", ""),
}); err != nil { })
if err != nil {
if models.IsErrBranchAlreadyExists(err) { if models.IsErrBranchAlreadyExists(err) {
// User has specified a branch that already exists // User has specified a branch that already exists
branchErr := err.(models.ErrBranchAlreadyExists) branchErr := err.(models.ErrBranchAlreadyExists)
@ -114,6 +112,6 @@ func NewDiffPatchPost(ctx *context.Context) {
if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) { if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) {
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName))
} else { } else {
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath)) ctx.Redirect(ctx.Repo.RepoLink + "/commit/" + fileResponse.Commit.SHA)
} }
} }

View File

@ -24,6 +24,7 @@ func ToPushMirror(pm *repo_model.PushMirror) (*api.PushMirror, error) {
LastUpdateUnix: pm.LastUpdateUnix.FormatLong(), LastUpdateUnix: pm.LastUpdateUnix.FormatLong(),
LastError: pm.LastError, LastError: pm.LastError,
Interval: pm.Interval.String(), Interval: pm.Interval.String(),
SyncOnCommit: pm.SyncOnCommit,
}, nil }, nil
} }

View File

@ -26,7 +26,7 @@ git-fetch-with-cli = true</code></pre></div>
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}} {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4> <h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}} {{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}}
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}</div>{{end}} {{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
{{end}} {{end}}
{{if .PackageDescriptor.Metadata.Dependencies}} {{if .PackageDescriptor.Metadata.Dependencies}}

View File

@ -20,7 +20,7 @@
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4> <h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
<div class="ui attached segment"> <div class="ui attached segment">
{{if .PackageDescriptor.Metadata.Description}}<p>{{.PackageDescriptor.Metadata.Description}}</p>{{end}} {{if .PackageDescriptor.Metadata.Description}}<p>{{.PackageDescriptor.Metadata.Description}}</p>{{end}}
{{if .PackageDescriptor.Metadata.LongDescription}}{{RenderMarkdownToHtml .PackageDescriptor.Metadata.LongDescription}}{{end}} {{if .PackageDescriptor.Metadata.LongDescription}}{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.LongDescription}}{{end}}
</div> </div>
{{end}} {{end}}

View File

@ -25,7 +25,7 @@
<div class="ui attached segment"> <div class="ui attached segment">
{{if .PackageDescriptor.Metadata.Readme}} {{if .PackageDescriptor.Metadata.Readme}}
<div class="markup markdown"> <div class="markup markdown">
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}} {{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}
</div> </div>
{{else if .PackageDescriptor.Metadata.Description}} {{else if .PackageDescriptor.Metadata.Description}}
{{.PackageDescriptor.Metadata.Description}} {{.PackageDescriptor.Metadata.Description}}

View File

@ -14,6 +14,6 @@
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}} {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4> <h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}} {{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}}
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}</div>{{end}} {{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
{{end}} {{end}}
{{end}} {{end}}

View File

@ -16,9 +16,9 @@
<div class="ui attached segment"> <div class="ui attached segment">
<p>{{if .PackageDescriptor.Metadata.Summary}}{{.PackageDescriptor.Metadata.Summary}}{{end}}</p> <p>{{if .PackageDescriptor.Metadata.Summary}}{{.PackageDescriptor.Metadata.Summary}}{{end}}</p>
{{if .PackageDescriptor.Metadata.LongDescription}} {{if .PackageDescriptor.Metadata.LongDescription}}
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.LongDescription}} {{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.LongDescription}}
{{else if .PackageDescriptor.Metadata.Description}} {{else if .PackageDescriptor.Metadata.Description}}
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Description}} {{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}

View File

@ -2,15 +2,15 @@
<input type="hidden" name="template-file" value="{{.TemplateFile}}"> <input type="hidden" name="template-file" value="{{.TemplateFile}}">
{{range .Fields}} {{range .Fields}}
{{if eq .Type "input"}} {{if eq .Type "input"}}
{{template "repo/issue/fields/input" .}} {{template "repo/issue/fields/input" Dict "Context" $.Context "item" .}}
{{else if eq .Type "markdown"}} {{else if eq .Type "markdown"}}
{{template "repo/issue/fields/markdown" .}} {{template "repo/issue/fields/markdown" Dict "Context" $.Context "item" .}}
{{else if eq .Type "textarea"}} {{else if eq .Type "textarea"}}
{{template "repo/issue/fields/textarea" .}} {{template "repo/issue/fields/textarea" Dict "Context" $.Context "item" .}}
{{else if eq .Type "dropdown"}} {{else if eq .Type "dropdown"}}
{{template "repo/issue/fields/dropdown" .}} {{template "repo/issue/fields/dropdown" Dict "Context" $.Context "item" .}}
{{else if eq .Type "checkboxes"}} {{else if eq .Type "checkboxes"}}
{{template "repo/issue/fields/checkboxes" .}} {{template "repo/issue/fields/checkboxes" Dict "Context" $.Context "item" .}}
{{end}} {{end}}
{{end}} {{end}}
{{else}} {{else}}

View File

@ -1,7 +1,7 @@
<div class="field"> <div class="field">
{{template "repo/issue/fields/header" .}} {{template "repo/issue/fields/header" .}}
{{$field := .}} {{$field := .}}
{{range $i, $opt := .Attributes.options}} {{range $i, $opt := .item.Attributes.options}}
<div class="field"> <div class="field">
<div class="ui checkbox"> <div class="ui checkbox">
<input type="checkbox" name="form-field-{{$field.ID}}-{{$i}}" {{if $opt.required}}readonly checked{{end}}> <input type="checkbox" name="form-field-{{$field.ID}}-{{$i}}" {{if $opt.required}}readonly checked{{end}}>

View File

@ -1,12 +1,12 @@
<div class="field"> <div class="field">
{{template "repo/issue/fields/header" .}} {{template "repo/issue/fields/header" .}}
{{/* FIXME: required validation */}} {{/* FIXME: required validation */}}
<div class="ui fluid selection dropdown {{if .Attributes.multiple}}multiple clearable{{end}}"> <div class="ui fluid selection dropdown {{if .item.Attributes.multiple}}multiple clearable{{end}}">
<input type="hidden" name="form-field-{{.ID}}" value="0"> <input type="hidden" name="form-field-{{.item.ID}}" value="0">
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
<div class="default text"></div> <div class="default text"></div>
<div class="menu"> <div class="menu">
{{range $i, $opt := .Attributes.options}} {{range $i, $opt := .item.Attributes.options}}
<div class="item" data-value="{{$i}}">{{$opt}}</div> <div class="item" data-value="{{$i}}">{{$opt}}</div>
{{end}} {{end}}
</div> </div>

View File

@ -1,6 +1,6 @@
{{if .Attributes.label}} {{if .item.Attributes.label}}
<h3>{{.Attributes.label}}{{if .Validations.required}}<label class="required"></label>{{end}}</h3> <h3>{{.item.Attributes.label}}{{if .item.Validations.required}}<label class="required"></label>{{end}}</h3>
{{end}} {{end}}
{{if .Attributes.description}} {{if .item.Attributes.description}}
<span class="help">{{RenderMarkdownToHtml .Attributes.description}}</span> <span class="help">{{RenderMarkdownToHtml .Context .item.Attributes.description}}</span>
{{end}} {{end}}

View File

@ -1,4 +1,4 @@
<div class="field"> <div class="field">
{{template "repo/issue/fields/header" .}} {{template "repo/issue/fields/header" .}}
<input type="{{if .Validations.is_number}}number{{else}}text{{end}}" name="form-field-{{.ID}}" placeholder="{{.Attributes.placeholder}}" value="{{.Attributes.value}}" {{if .Validations.required}}required{{end}} {{if .Validations.regex}}pattern="{{.Validations.regex}}" title="{{.Validations.regex}}"{{end}}> <input type="{{if .item.Validations.is_number}}number{{else}}text{{end}}" name="form-field-{{.item.ID}}" placeholder="{{.item.Attributes.placeholder}}" value="{{.item.Attributes.value}}" {{if .item.Validations.required}}required{{end}} {{if .item.Validations.regex}}pattern="{{.item.Validations.regex}}" title="{{.item.Validations.regex}}"{{end}}>
</div> </div>

View File

@ -1,3 +1,3 @@
<div class="field"> <div class="field">
<div>{{RenderMarkdownToHtml .Attributes.value}}</div> <div>{{RenderMarkdownToHtml .Context .item.Attributes.value}}</div>
</div> </div>

View File

@ -2,5 +2,5 @@
{{template "repo/issue/fields/header" .}} {{template "repo/issue/fields/header" .}}
{{/* FIXME: preview markdown result */}} {{/* FIXME: preview markdown result */}}
{{/* FIXME: required validation for markdown editor */}} {{/* FIXME: required validation for markdown editor */}}
<textarea name="form-field-{{.ID}}" placeholder="{{.Attributes.placeholder}}" class="edit_area {{if .Attributes.render}}no-easymde{{end}}" {{if and .Validations.required .Attributes.render}}required{{end}}>{{.Attributes.value}}</textarea> <textarea name="form-field-{{.item.ID}}" placeholder="{{.item.Attributes.placeholder}}" class="edit_area {{if .item.Attributes.render}}no-easymde{{end}}" {{if and .item.Validations.required .item.Attributes.render}}required{{end}}>{{.item.Attributes.value}}</textarea>
</div> </div>

View File

@ -7,7 +7,7 @@
<div class="header">{{.ctx.locale.Tr "repo.pick_reaction"}}</div> <div class="header">{{.ctx.locale.Tr "repo.pick_reaction"}}</div>
<div class="divider"></div> <div class="divider"></div>
{{range $value := AllowedReactions}} {{range $value := AllowedReactions}}
<div class="item reaction tooltip" data-content="{{$value}}">{{ReactionToEmoji $value}}</div> <a class="item reaction tooltip" data-content="{{$value}}">{{ReactionToEmoji $value}}</a>
{{end}} {{end}}
</div> </div>
</div> </div>

View File

@ -10,16 +10,16 @@
{{else}} {{else}}
{{$referenceUrl = Printf "%s/files#%s" .ctx.Issue.Link .item.HashTag}} {{$referenceUrl = Printf "%s/files#%s" .ctx.Issue.Link .item.HashTag}}
{{end}} {{end}}
<div class="item context" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</div> <a class="item context" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</a>
<div class="item context quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctx.locale.Tr "repo.issues.context.quote_reply"}}</div> <a class="item context quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctx.locale.Tr "repo.issues.context.quote_reply"}}</a>
{{if not .ctx.UnitIssuesGlobalDisabled}} {{if not .ctx.UnitIssuesGlobalDisabled}}
<div class="item context reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.reference_issue"}}</div> <a class="item context reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.reference_issue"}}</a>
{{end}} {{end}}
{{if or .ctx.Permission.IsAdmin .IsCommentPoster .ctx.HasIssuesOrPullsWritePermission}} {{if or .ctx.Permission.IsAdmin .IsCommentPoster .ctx.HasIssuesOrPullsWritePermission}}
<div class="divider"></div> <div class="divider"></div>
<div class="item context edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</div> <a class="item context edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</a>
{{if .delete}} {{if .delete}}
<div class="item context delete-comment" data-comment-id={{.item.HashTag}} data-url="{{.ctx.RepoLink}}/comments/{{.item.ID}}/delete" data-locale="{{.ctx.locale.Tr "repo.issues.delete_comment_confirm"}}">{{.ctx.locale.Tr "repo.issues.context.delete"}}</div> <a class="item context delete-comment" data-comment-id={{.item.HashTag}} data-url="{{.ctx.RepoLink}}/comments/{{.item.ID}}/delete" data-locale="{{.ctx.locale.Tr "repo.issues.delete_comment_confirm"}}">{{.ctx.locale.Tr "repo.issues.context.delete"}}</a>
{{end}} {{end}}
{{end}} {{end}}
</div> </div>

View File

@ -121,7 +121,7 @@
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}"> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}">
</div> </div>
{{end}} {{end}}
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_labels"}}</div> <a class="no-select item" href="#">{{.locale.Tr "repo.issues.new.clear_labels"}}</a>
{{if or .Labels .OrgLabels}} {{if or .Labels .OrgLabels}}
{{$previousExclusiveScope := "_no_scope"}} {{$previousExclusiveScope := "_no_scope"}}
{{range .Labels}} {{range .Labels}}

View File

@ -81,7 +81,8 @@ function attachOneDropdownAria($dropdown) {
$dropdown.on('keydown', (e) => { $dropdown.on('keydown', (e) => {
// here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler // here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler
if (e.key === 'Enter') { if (e.key === 'Enter') {
const $item = $dropdown.dropdown('get item', $dropdown.dropdown('get value')); let $item = $dropdown.dropdown('get item', $dropdown.dropdown('get value'));
if (!$item) $item = $menu.find('> .item.selected'); // when dropdown filters items by input, there is no "value", so query the "selected" item
// if the selected item is clickable, then trigger the click event. in the future there could be a special CSS class for it. // if the selected item is clickable, then trigger the click event. in the future there could be a special CSS class for it.
if ($item && $item.is('a')) $item[0].click(); if ($item && $item.is('a')) $item[0].click();
} }

View File

@ -29,6 +29,26 @@ import {hideElem, showElem} from '../utils/dom.js';
const {csrfToken} = window.config; const {csrfToken} = window.config;
// if there are draft comments (more than 20 chars), confirm before reloading, to avoid losing comments
function reloadConfirmDraftComment() {
const commentTextareas = [
document.querySelector('.edit-content-zone:not(.gt-hidden) textarea'),
document.querySelector('.edit_area'),
];
for (const textarea of commentTextareas) {
// Most users won't feel too sad if they lose a comment with 10 or 20 chars, they can re-type these in seconds.
// But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy.
if (textarea && textarea.value.trim().length > 20) {
textarea.parentElement.scrollIntoView();
if (!window.confirm('Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?')) {
return;
}
break;
}
}
window.location.reload();
}
export function initRepoCommentForm() { export function initRepoCommentForm() {
const $commentForm = $('.comment.form'); const $commentForm = $('.comment.form');
if ($commentForm.length === 0) { if ($commentForm.length === 0) {
@ -86,12 +106,15 @@ export function initRepoCommentForm() {
let hasUpdateAction = $listMenu.data('action') === 'update'; let hasUpdateAction = $listMenu.data('action') === 'update';
const items = {}; const items = {};
$(`.${selector}`).dropdown('setting', 'onHide', () => { $(`.${selector}`).dropdown({
'action': 'nothing', // do not hide the menu if user presses Enter
fullTextSearch: 'exact',
async onHide() {
hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
if (hasUpdateAction) { if (hasUpdateAction) {
// TODO: Add batch functionality and make this 1 network request. // TODO: Add batch functionality and make this 1 network request.
(async function() { const itemEntries = Object.entries(items);
for (const [elementId, item] of Object.entries(items)) { for (const [elementId, item] of itemEntries) {
await updateIssuesMeta( await updateIssuesMeta(
item['update-url'], item['update-url'],
item.action, item.action,
@ -99,9 +122,11 @@ export function initRepoCommentForm() {
elementId, elementId,
); );
} }
window.location.reload(); if (itemEntries.length) {
})(); reloadConfirmDraftComment();
} }
}
},
}); });
$listMenu.find('.item:not(.no-select)').on('click', function (e) { $listMenu.find('.item:not(.no-select)').on('click', function (e) {
@ -196,7 +221,7 @@ export function initRepoCommentForm() {
'clear', 'clear',
$listMenu.data('issue-id'), $listMenu.data('issue-id'),
'', '',
).then(() => window.location.reload()); ).then(reloadConfirmDraftComment);
} }
$(this).parent().find('.item').each(function () { $(this).parent().find('.item').each(function () {
@ -239,7 +264,7 @@ export function initRepoCommentForm() {
'', '',
$menu.data('issue-id'), $menu.data('issue-id'),
$(this).data('id'), $(this).data('id'),
).then(() => window.location.reload()); ).then(reloadConfirmDraftComment);
} }
let icon = ''; let icon = '';
@ -272,7 +297,7 @@ export function initRepoCommentForm() {
'', '',
$menu.data('issue-id'), $menu.data('issue-id'),
$(this).data('id'), $(this).data('id'),
).then(() => window.location.reload()); ).then(reloadConfirmDraftComment);
} }
$list.find('.selected').html(''); $list.find('.selected').html('');

View File

@ -3269,17 +3269,9 @@ td.blob-excerpt {
.ui.attached.header.diff-file-header { .ui.attached.header.diff-file-header {
&.sticky-2nd-row { &.sticky-2nd-row {
position: sticky; position: sticky;
top: 46px; top: 77px;
z-index: 7; z-index: 7;
@media @mediaMd {
top: 77px;
}
@media @mediaSm {
top: 77px;
}
@media (max-width: 480px) { @media (max-width: 480px) {
position: static; position: static;
} }

View File

@ -415,6 +415,7 @@
padding: .2em .4em; padding: .2em .4em;
margin: 0; margin: 0;
font-size: 85%; font-size: 85%;
white-space: break-spaces;
background-color: var(--color-markup-code-block); background-color: var(--color-markup-code-block);
border-radius: 4px; border-radius: 4px;
} }