ronantakizawa/codereview-bench huggingface dataset:
Repository: uber-go/zap
File: internal/ztest/clock.go (Go)

Code before:
}

// runAt schedules the given function to be run at the given time.
// The function runs without a lock held, so it may schedule more work.
func (c *MockClock) runAt(t time.Time, fn func()) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.waiters.Push(waiter{until: t, fn: fn})
}

type waiter struct {
	until time.Time
	fn    func()
}

// waiters is a thread-safe collection of waiters
// with the next waiter to be resolved at the front.
//
// Use the methods on this type to manipulate the collection.
// Do not modify the slice directly.
type waiters struct{ heap waiterHeap }

// Push adds a new waiter to the collection.
func (w *waiters) Push(v waiter) {
	heap.Push(&w.heap, v)
}

// PopLTE removes and returns the next waiter to be resolved
// if it is scheduled to be resolved at or before the given time.
//
// Returns false if there are no waiters in range.
func (w *waiters) PopLTE(t time.Time) (_ waiter, ok bool) {
	if len(w.heap) == 0 || w.heap[0].until.After(t) {
		return waiter{}, false
	}

	return heap.Pop(&w.heap).(waiter), true
}

// waiterHeap implements a min-heap of waiters based on their 'until' time.
//
// This is separate from the waiters type so that we can implement heap.Interface
// while still exposing a type-safe API on waiters.
type waiterHeap []waiter

var _ heap.Interface = (*waiterHeap)(nil)

func (h waiterHeap) Len() int      { return len(h) }
func (h waiterHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }

func (h waiterHeap) Less(i, j int) bool {

Review comment (performance):
just to understand this better, this is to make sure any asynchronous side-effects of waiters are run right? I'm guessing the main case is after writing to a ticker channel, we want to make sure the other side reads from it.

I guess if this sleep is too low, the ticker consumer may not have finished processing, and will miss ticks. If it's too high, we're slowing down tests. May be worth calling out that this is a best-effort.

Code after:
	})

	newTime := c.now.Add(d)
	// newTime won't be recorded until the end of this method.
	// This ensures that any waiters that are resolved
	// are resolved at the time they were expecting.

	for len(c.waiters) > 0 {
		w := c.waiters[0]
		if w.until.After(newTime) {
			break
		}
		c.waiters[0] = waiter{} // avoid memory leak
		c.waiters = c.waiters[1:]

		// The waiter is within range.
		// Travel to the time of the waiter and resolve it.
		c.now = w.until

		// The waiter may schedule more work
		// so we must release the lock.
		c.mu.Unlock()
		w.fn()
		// Sleeping here is necessary to let the side effects of waiters
		// take effect before we continue.
		time.Sleep(1 * time.Millisecond)
		c.mu.Lock()
	}

	c.now = newTime
}
Repository: uber-go/zap
File: buffer/buffer.go (Go)

Code before:
// package's zero-allocation formatters.
package buffer // import "go.uber.org/zap/buffer"

import (
	"strconv"
	"time"
)

const _size = 1024 // by default, create 1 KiB buffers

// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so
// the only way to construct one is via a Pool.
type Buffer struct {
	bs   []byte
	pool Pool
}

// AppendByte writes a single byte to the Buffer.
func (b *Buffer) AppendByte(v byte) {
	b.bs = append(b.bs, v)
}

// AppendByteV writes a single byte to the Buffer.
func (b *Buffer) AppendByteV(v ...byte) {
	b.bs = append(b.bs, v...)
}

// AppendString writes a string to the Buffer.
func (b *Buffer) AppendString(s string) {
	b.bs = append(b.bs, s...)
}

// AppendInt appends an integer to the underlying buffer (assuming base 10).
func (b *Buffer) AppendInt(i int64) {
	b.bs = strconv.AppendInt(b.bs, i, 10)
}

// AppendTime appends the time formatted using the specified layout.
func (b *Buffer) AppendTime(t time.Time, layout string) {
	b.bs = t.AppendFormat(b.bs, layout)
}

// AppendUint appends an unsigned integer to the underlying buffer (assuming
// base 10).
func (b *Buffer) AppendUint(i uint64) {
	b.bs = strconv.AppendUint(b.bs, i, 10)
}

// AppendBool appends a bool to the underlying buffer.
func (b *Buffer) AppendBool(v bool) {
	b.bs = strconv.AppendBool(b.bs, v)

Review comment (suggestion):
This should probably be named `AppendBytes` and take a `[]byte`, not a vararg. (Also, the docstring is inaccurate.)

(FYI, there's also Buffer.Write which does the same, while satisfying io.Writer, but there's no problem with also having this method. However, if we have both, maybe Write should call AppendBytes.)

Code after:
// package's zero-allocation formatters.
package buffer // import "go.uber.org/zap/buffer"

import (
	"strconv"
	"time"
)

const _size = 1024 // by default, create 1 KiB buffers

// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so
// the only way to construct one is via a Pool.
type Buffer struct {
	bs   []byte
	pool Pool
}

// AppendByte writes a single byte to the Buffer.
func (b *Buffer) AppendByte(v byte) {
	b.bs = append(b.bs, v)
}

// AppendBytes writes a single byte to the Buffer.
func (b *Buffer) AppendBytes(v []byte) {
	b.bs = append(b.bs, v...)
}

// AppendString writes a string to the Buffer.
func (b *Buffer) AppendString(s string) {
	b.bs = append(b.bs, s...)
}

// AppendInt appends an integer to the underlying buffer (assuming base 10).
func (b *Buffer) AppendInt(i int64) {
	b.bs = strconv.AppendInt(b.bs, i, 10)
}

// AppendTime appends the time formatted using the specified layout.
func (b *Buffer) AppendTime(t time.Time, layout string) {
	b.bs = t.AppendFormat(b.bs, layout)
}

// AppendUint appends an unsigned integer to the underlying buffer (assuming
// base 10).
func (b *Buffer) AppendUint(i uint64) {
	b.bs = strconv.AppendUint(b.bs, i, 10)
}

// AppendBool appends a bool to the underlying buffer.
func (b *Buffer) AppendBool(v bool) {
	b.bs = strconv.AppendBool(b.bs, v)
Repository: uber-go/zap
File: sugar_test.go (Go)

Code before:
func TestSugarWithCaptures(t *testing.T) {
	type withAny func(*SugaredLogger, ...interface{}) *SugaredLogger

	tests := []struct {
		name        string
		withMethods []withAny
		wantJSON    []string
	}{
		{
			name:        "with captures arguments at time of With",
			withMethods: []withAny{(*SugaredLogger).With},
			wantJSON: []string{
				`{
					"m": "hello 0",
					"a0": [0],
					"b0": [1]
				}`,
				`{
					"m": "world 0",
					"a0": [0],
					"c0": [2]
				}`,
			},
		},
		{
			name:        "lazy with captures arguments at time of With or Logging",
			withMethods: []withAny{(*SugaredLogger).WithLazy},
			wantJSON: []string{
				`{
					"m": "hello 0",
					"a0": [1],
					"b0": [1]
				}`,
				`{
					"m": "world 0",
					"a0": [1],
					"c0": [2]
				}`,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
				MessageKey: "m",
			})

			var bs ztest.Buffer
			logger := New(zapcore.NewCore(enc, &bs, DebugLevel)).Sugar()


Review comment (suggestion):
I suspect that this may be a typo in the original zap.Logger PR, but I think the test case name should be "lazy with captures arguments at time of Logging"

Code after:
func TestSugarWithCaptures(t *testing.T) {
	type withAny func(*SugaredLogger, ...interface{}) *SugaredLogger

	tests := []struct {
		name        string
		withMethods []withAny
		wantJSON    []string
	}{
		{
			name:        "with captures arguments at time of With",
			withMethods: []withAny{(*SugaredLogger).With},
			wantJSON: []string{
				`{
					"m": "hello 0",
					"a0": [0],
					"b0": [1]
				}`,
				`{
					"m": "world 0",
					"a0": [0],
					"c0": [2]
				}`,
			},
		},
		{
			name:        "lazy with captures arguments at time of Logging",
			withMethods: []withAny{(*SugaredLogger).WithLazy},
			wantJSON: []string{
				`{
					"m": "hello 0",
					"a0": [1],
					"b0": [1]
				}`,
				`{
					"m": "world 0",
					"a0": [1],
					"c0": [2]
				}`,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
				MessageKey: "m",
			})

			var bs ztest.Buffer
			logger := New(zapcore.NewCore(enc, &bs, DebugLevel)).Sugar()
Repository: uber-go/zap
File: exp/zapslog/handler.go (Go)

Code before:
	}

	fields := make([]zapcore.Field, 0, record.NumAttrs()+1)
	record.Attrs(func(attr slog.Attr) bool {
		f := convertAttrToField(attr)
		if h.holdGroup != "" && f != zap.Skip() {
			fields = append(fields, zap.Namespace(h.holdGroup))
			h.holdGroup = ""
		}
		fields = append(fields, f)
		return true
	})

	ce.Write(fields...)
	return nil
}

// WithAttrs returns a new Handler whose attributes consist of
// both the receiver's attributes and the arguments.
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
	fields := make([]zapcore.Field, 0, len(attrs)+1)
	for _, attr := range attrs {
		f := convertAttrToField(attr)
		if h.holdGroup != "" && f != zap.Skip() {
			fields = append(fields, zap.Namespace(h.holdGroup))
			h.holdGroup = ""
		}
		fields = append(fields, f)
	}
	return h.withFields(fields...)
}

// WithGroup returns a new Handler with the given group appended to
// the receiver's existing groups.
func (h *Handler) WithGroup(group string) slog.Handler {
	cloned := *h
	if h.holdGroup != "" {
		cloned.core = h.core.With([]zapcore.Field{zap.Namespace(group)})
	}
	cloned.holdGroup = group
	return &cloned
}

// withFields returns a cloned Handler with the given fields.
func (h *Handler) withFields(fields ...zapcore.Field) *Handler {
	cloned := *h
	cloned.core = h.core.With(fields)
	return &cloned
}


Review comment (suggestion):
Same here. Whether or not the namespace was added should be considered a concern local to this WithAttrs call.

After that, in the child handler constructed and returned below—that's where we unset the group because it's already been "used" here.

Code after:
	if record.Level >= h.addStackAt {
		// Skipping 3:
		// zapslog/handler log/slog.(*Logger).log
		// slog/logger log/slog.(*Logger).log
		// slog/logger log/slog.(*Logger).<level>
		ce.Stack = stacktrace.Take(3 + h.callerSkip)
	}

	fields := make([]zapcore.Field, 0, record.NumAttrs()+len(h.groups))

	var addedNamespace bool
	record.Attrs(func(attr slog.Attr) bool {
		f := convertAttrToField(attr)
		if !addedNamespace && len(h.groups) > 0 && f != zap.Skip() {
			// Namespaces are added only if at least one field is present
			// to avoid creating empty groups.
			fields = h.appendGroups(fields)
			addedNamespace = true
		}
		fields = append(fields, f)
		return true
	})

	ce.Write(fields...)
	return nil
}

func (h *Handler) appendGroups(fields []zapcore.Field) []zapcore.Field {
	for _, g := range h.groups {
		fields = append(fields, zap.Namespace(g))
	}
	return fields
}

// WithAttrs returns a new Handler whose attributes consist of
// both the receiver's attributes and the arguments.
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
	fields := make([]zapcore.Field, 0, len(attrs)+len(h.groups))
	var addedNamespace bool
	for _, attr := range attrs {
		f := convertAttrToField(attr)
		if !addedNamespace && len(h.groups) > 0 && f != zap.Skip() {
			// Namespaces are added only if at least one field is present
			// to avoid creating empty groups.
			fields = h.appendGroups(fields)
			addedNamespace = true
		}
		fields = append(fields, f)
	}

	cloned := *h
Repository: uber-go/zap
File: zapcore/lazy_with.go (Go)

Code before:
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import "sync"

type lazyWithCore struct {
	core         Core
	originalCore Core
	sync.Once
	fields []Field
}

// NewLazyWith wraps a Core with a "lazy" Core that will only encode fields if
// the logger is written to (or is further chained in a lon-lazy manner).
func NewLazyWith(core Core, fields []Field) Core {
	return &lazyWithCore{
		core:         core,
		originalCore: core,
		fields:       fields,
	}
}

func (d *lazyWithCore) initOnce() {
	d.Once.Do(func() {
		d.core = d.core.With(d.fields)
	})
}

func (d *lazyWithCore) With(fields []Field) Core {
	d.initOnce()
	return d.core.With(fields)
}

func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {
	if !d.originalCore.Enabled(e.Level) {
		return ce
	}
	d.initOnce()
	return d.core.Check(e, ce)
}

func (d *lazyWithCore) Enabled(level Level) bool {
	return d.originalCore.Enabled(level)

Review comment (suggestion):
this is confusing, only set `originalCore`, and add a comment that `core` is set in `initOnce` (and update `initOnce` to do `d.core = d.originalCore.With(d.fields)`

Code after:
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import "sync"

type lazyWithCore struct {
	core         Core
	originalCore Core
	sync.Once
	fields []Field
}

// NewLazyWith wraps a Core with a "lazy" Core that will only encode fields if
// the logger is written to (or is further chained in a lon-lazy manner).
func NewLazyWith(core Core, fields []Field) Core {
	return &lazyWithCore{
		core:         nil, // core is allocated once `initOnce` is called.
		originalCore: core,
		fields:       fields,
	}
}

func (d *lazyWithCore) initOnce() {
	d.Once.Do(func() {
		d.core = d.originalCore.With(d.fields)
	})
}

func (d *lazyWithCore) With(fields []Field) Core {
	d.initOnce()
	return d.core.With(fields)
}

func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {
	// This is safe because `lazyWithCore` doesn't change the level.
	// So we can delagate the level check, any not `initOnce`
	// just for the check.
	if !d.originalCore.Enabled(e.Level) {
		return ce
	}
	d.initOnce()
	return d.core.Check(e, ce)
}
Repository: uber-go/zap
File: zapcore/lazy_with.go (Go)

Code before:
func NewLazyWith(core Core, fields []Field) Core {
	return &lazyWithCore{
		core:         core,
		originalCore: core,
		fields:       fields,
	}
}

func (d *lazyWithCore) initOnce() {
	d.Once.Do(func() {
		d.core = d.core.With(d.fields)
	})
}

func (d *lazyWithCore) With(fields []Field) Core {
	d.initOnce()
	return d.core.With(fields)
}

func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {
	if !d.originalCore.Enabled(e.Level) {
		return ce
	}
	d.initOnce()
	return d.core.Check(e, ce)
}

func (d *lazyWithCore) Enabled(level Level) bool {
	return d.originalCore.Enabled(level)
}

func (d *lazyWithCore) Write(e Entry, fields []Field) error {
	d.initOnce()
	return d.core.Write(e, fields)
}

func (d *lazyWithCore) Sync() error {
	d.initOnce()
	return d.core.Sync()
}


Review comment (suggestion):
this seems reasonable, though I am wondering whether we can do a more thorough check here,
```
if ce := d.originalCore.Check(e, nil); ce == nil {
  return ce
}
```

we pass `nil` since we don't want the underlying core to add itself to the `CheckedEntry`, but we do want to know what it would have done.

that should do more than the level check, but if the entry is supposed to be logged, we'll end up allocating an additional `CheckedEntry`

Code after:
func NewLazyWith(core Core, fields []Field) Core {
	return &lazyWithCore{
		core:         nil, // core is allocated once `initOnce` is called.
		originalCore: core,
		fields:       fields,
	}
}

func (d *lazyWithCore) initOnce() {
	d.Once.Do(func() {
		d.core = d.originalCore.With(d.fields)
	})
}

func (d *lazyWithCore) With(fields []Field) Core {
	d.initOnce()
	return d.core.With(fields)
}

func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {
	// This is safe because `lazyWithCore` doesn't change the level.
	// So we can delagate the level check, any not `initOnce`
	// just for the check.
	if !d.originalCore.Enabled(e.Level) {
		return ce
	}
	d.initOnce()
	return d.core.Check(e, ce)
}

func (d *lazyWithCore) Enabled(level Level) bool {
	// Like above, this is safe because `lazyWithCore` doesn't change the level.
	return d.originalCore.Enabled(level)
}

func (d *lazyWithCore) Write(e Entry, fields []Field) error {
	d.initOnce()
	return d.core.Write(e, fields)
}

func (d *lazyWithCore) Sync() error {
	d.initOnce()
	return d.core.Sync()
}
Repository: usememos/memos
File: web/src/labs/highlighter/index.ts (TypeScript)

Code before:
const escapeRegExp = (str: string): string => {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};

function dfsAndHighlight(node: HTMLElement, keyword: string) {
  if (node.nodeType === 3) {
    const span = document.createElement("span");
    span.innerHTML = node.nodeValue?.replace(new RegExp(keyword, "g"), `<mark>${keyword}</mark>`) ?? "";
    node.parentNode?.insertBefore(span, node);
    node.parentNode?.removeChild(node);
  }
  for (const child of Array.from(node.childNodes)) {
    dfsAndHighlight(<HTMLElement>child, keyword);
  }
  return node.innerHTML;
}

const highlightWithWord = (html: string, keyword: string | undefined): string => {
  if (!keyword) {
    return html;
  }
  keyword = escapeRegExp(keyword);
  const wrap = document.createElement("div");
  wrap.innerHTML = html;
  return dfsAndHighlight(wrap, keyword);
};

export default highlightWithWord;


Review comment (suggestion):
```suggestion
const highlightWithWord = (html: string, keyword?: string): string => {
```

Code after:
const escapeRegExp = (str: string): string => {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};

const walkthroughNodeWithKeyword = (node: HTMLElement, keyword: string) => {
  if (node.nodeType === 3) {
    const span = document.createElement("span");
    span.innerHTML = node.nodeValue?.replace(new RegExp(keyword, "g"), `<mark>${keyword}</mark>`) ?? "";
    node.parentNode?.insertBefore(span, node);
    node.parentNode?.removeChild(node);
  }
  for (const child of Array.from(node.childNodes)) {
    walkthroughNodeWithKeyword(<HTMLElement>child, keyword);
  }
  return node.innerHTML;
};

export const highlightWithWord = (html: string, keyword?: string): string => {
  if (!keyword) {
    return html;
  }
  keyword = escapeRegExp(keyword);
  const wrap = document.createElement("div");
  wrap.innerHTML = html;
  return walkthroughNodeWithKeyword(wrap, keyword);
};
Repository: usememos/memos
File: web/src/components/ChangeMemberPasswordDialog.tsx (TypeScript)

Code before:
        />
        <p className="text-sm mb-1 mt-2">{t("common.repeat-new-password")}</p>
        <input
          type="password"
          className="input-text"
          placeholder={t("common.repeat-new-password")}
          value={newPasswordAgain}
          onChange={handleNewPasswordAgainChanged}
        />
        <div className="mt-4 w-full flex flex-row justify-end items-center space-x-2">
          <span className="btn-text" onClick={handleCloseBtnClick}>
            {t("common.cancel")}
          </span>
          <span className="btn-primary" onClick={handleSaveBtnClick}>
            {t("common.save")}
          </span>
        </div>
      </div>
    </>
  );
};

function showChangeMemberPasswordDialog(user: User) {
  generateDialog(
    {
      className: "change-password-dialog",
    },
    ChangeMemberPasswordDialog,
    { user }
  );
}

export default showChangeMemberPasswordDialog;


Review comment (suggestion):
```suggestion
      className: "change-member-password-dialog",
```

Code after:
        />
        <p className="text-sm mb-1 mt-2">{t("common.repeat-new-password")}</p>
        <input
          type="password"
          className="input-text"
          placeholder={t("common.repeat-new-password")}
          value={newPasswordAgain}
          onChange={handleNewPasswordAgainChanged}
        />
        <div className="mt-4 w-full flex flex-row justify-end items-center space-x-2">
          <span className="btn-text" onClick={handleCloseBtnClick}>
            {t("common.cancel")}
          </span>
          <span className="btn-primary" onClick={handleSaveBtnClick}>
            {t("common.save")}
          </span>
        </div>
      </div>
    </>
  );
};

function showChangeMemberPasswordDialog(user: User) {
  generateDialog(
    {
      className: "change-member-password-dialog",
    },
    ChangeMemberPasswordDialog,
    { user }
  );
}

export default showChangeMemberPasswordDialog;
Repository: usememos/memos
File: web/src/components/ShareMemoImageDialog.tsx (TypeScript)

Code before:
          setState((state) => {
            return {
              ...state,
              shortcutImgUrl: url,
            };
          });
        })
        .catch((err) => {
          console.error(err);
        });
    }, ANIMATION_DURATION);
  }, [loadingState.isLoading]);

  const handleCloseBtnClick = () => {
    destroy();
  };

  const handleDownloadBtnClick = () => {
    const a = document.createElement("a");
    a.href = state.shortcutImgUrl;
    a.download = `memos-${utils.getDateTimeString(Date.now())}.png`;
    a.click();
  };

  const handleCopyLinkBtnClick = () => {
    copy(`${window.location.origin}m/${memo.id}`);
    toastHelper.success(t("message.succeed-copy-content"));
  };

  const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => {
    return {
      value: item.value,
      text: t(`memo.visibility.${toLower(item.value)}`),
    };
  });

  const handleMemoVisibilityOptionChanged = async (value: string) => {
    const visibilityValue = value as Visibility;
    setState({
      ...state,
      memoVisibility: visibilityValue,
    });
    await memoService.patchMemo({
      id: memo.id,
      visibility: visibilityValue,
    });
  };

  return (
    <>
      <div className="dialog-header-container">

Review comment (suggestion):
```suggestion
    copy(`${window.location.origin}/m/${memo.id}`);
```

Code after:
          setState((state) => {
            return {
              ...state,
              shortcutImgUrl: url,
            };
          });
        })
        .catch((err) => {
          console.error(err);
        });
    }, ANIMATION_DURATION);
  }, [loadingState.isLoading]);

  const handleCloseBtnClick = () => {
    destroy();
  };

  const handleDownloadBtnClick = () => {
    const a = document.createElement("a");
    a.href = state.shortcutImgUrl;
    a.download = `memos-${utils.getDateTimeString(Date.now())}.png`;
    a.click();
  };

  const handleCopyLinkBtnClick = () => {
    copy(`${window.location.origin}/m/${memo.id}`);
    toastHelper.success(t("message.succeed-copy-content"));
  };

  const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => {
    return {
      value: item.value,
      text: t(`memo.visibility.${toLower(item.value)}`),
    };
  });

  const handleMemoVisibilityOptionChanged = async (value: string) => {
    const visibilityValue = value as Visibility;
    setState({
      ...state,
      memoVisibility: visibilityValue,
    });
    await memoService.patchMemo({
      id: memo.id,
      visibility: visibilityValue,
    });
  };

  return (
    <>
      <div className="dialog-header-container">