Commit 707e5caf authored by Steven's avatar Steven

chore: update workspace setting store

parent 17e8fc54
...@@ -93,9 +93,9 @@ message WorkspaceStorageSetting { ...@@ -93,9 +93,9 @@ message WorkspaceStorageSetting {
StorageType storage_type = 1; StorageType storage_type = 1;
// The id of actived external storage. // The id of actived external storage.
optional int32 actived_external_storage_id = 2; optional int32 actived_external_storage_id = 2;
// The local storage path for STORAGE_TYPE_LOCAL. // The template of local storage path.
// e.g. assets/{timestamp}_{filename} // e.g. assets/{timestamp}_{filename}
string local_storage_path = 3; string local_storage_path_template = 3;
// The max upload size in megabytes. // The max upload size in megabytes.
int64 upload_size_limit_mb = 4; int64 upload_size_limit_mb = 4;
......
...@@ -3084,7 +3084,7 @@ Used internally for obfuscating the page token. ...@@ -3084,7 +3084,7 @@ Used internally for obfuscating the page token.
| ----- | ---- | ----- | ----------- | | ----- | ---- | ----- | ----------- |
| storage_type | [WorkspaceStorageSetting.StorageType](#memos-api-v2-WorkspaceStorageSetting-StorageType) | | storage_type is the storage type. | | storage_type | [WorkspaceStorageSetting.StorageType](#memos-api-v2-WorkspaceStorageSetting-StorageType) | | storage_type is the storage type. |
| actived_external_storage_id | [int32](#int32) | optional | The id of actived external storage. | | actived_external_storage_id | [int32](#int32) | optional | The id of actived external storage. |
| local_storage_path | [string](#string) | | The local storage path for STORAGE_TYPE_LOCAL. e.g. assets/{timestamp}_{filename} | | local_storage_path_template | [string](#string) | | The template of local storage path. e.g. assets/{timestamp}_{filename} |
| upload_size_limit_mb | [int64](#int64) | | The max upload size in megabytes. | | upload_size_limit_mb | [int64](#int64) | | The max upload size in megabytes. |
......
...@@ -636,7 +636,7 @@ ...@@ -636,7 +636,7 @@
| ----- | ---- | ----- | ----------- | | ----- | ---- | ----- | ----------- |
| storage_type | [WorkspaceStorageSetting.StorageType](#memos-store-WorkspaceStorageSetting-StorageType) | | storage_type is the storage type. | | storage_type | [WorkspaceStorageSetting.StorageType](#memos-store-WorkspaceStorageSetting-StorageType) | | storage_type is the storage type. |
| actived_external_storage_id | [int32](#int32) | optional | The id of actived external storage. | | actived_external_storage_id | [int32](#int32) | optional | The id of actived external storage. |
| local_storage_path | [string](#string) | | The local storage path for STORAGE_TYPE_LOCAL. e.g. assets/{timestamp}_{filename} | | local_storage_path_template | [string](#string) | | The template of local storage path. e.g. assets/{timestamp}_{filename} |
| upload_size_limit_mb | [int64](#int64) | | The max upload size in megabytes. | | upload_size_limit_mb | [int64](#int64) | | The max upload size in megabytes. |
......
This diff is collapsed.
...@@ -62,9 +62,9 @@ message WorkspaceStorageSetting { ...@@ -62,9 +62,9 @@ message WorkspaceStorageSetting {
StorageType storage_type = 1; StorageType storage_type = 1;
// The id of actived external storage. // The id of actived external storage.
optional int32 actived_external_storage_id = 2; optional int32 actived_external_storage_id = 2;
// The local storage path for STORAGE_TYPE_LOCAL. // The template of local storage path.
// e.g. assets/{timestamp}_{filename} // e.g. assets/{timestamp}_{filename}
string local_storage_path = 3; string local_storage_path_template = 3;
// The max upload size in megabytes. // The max upload size in megabytes.
int64 upload_size_limit_mb = 4; int64 upload_size_limit_mb = 4;
......
...@@ -2069,10 +2069,10 @@ definitions: ...@@ -2069,10 +2069,10 @@ definitions:
type: integer type: integer
format: int32 format: int32
description: The id of actived external storage. description: The id of actived external storage.
localStoragePath: localStoragePathTemplate:
type: string type: string
title: |- title: |-
The local storage path for STORAGE_TYPE_LOCAL. The template of local storage path.
e.g. assets/{timestamp}_{filename} e.g. assets/{timestamp}_{filename}
uploadSizeLimitMb: uploadSizeLimitMb:
type: string type: string
......
...@@ -260,8 +260,8 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc ...@@ -260,8 +260,8 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc
if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_LOCAL { if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_LOCAL {
localStoragePath := "assets/{timestamp}_{filename}" localStoragePath := "assets/{timestamp}_{filename}"
if workspaceStorageSetting.LocalStoragePath != "" { if workspaceStorageSetting.LocalStoragePathTemplate != "" {
localStoragePath = workspaceStorageSetting.LocalStoragePath localStoragePath = workspaceStorageSetting.LocalStoragePathTemplate
} }
internalPath := localStoragePath internalPath := localStoragePath
......
...@@ -176,9 +176,10 @@ func convertWorkspaceStorageSettingFromStore(setting *storepb.WorkspaceStorageSe ...@@ -176,9 +176,10 @@ func convertWorkspaceStorageSettingFromStore(setting *storepb.WorkspaceStorageSe
return nil return nil
} }
return &apiv2pb.WorkspaceStorageSetting{ return &apiv2pb.WorkspaceStorageSetting{
StorageType: apiv2pb.WorkspaceStorageSetting_StorageType(setting.StorageType), StorageType: apiv2pb.WorkspaceStorageSetting_StorageType(setting.StorageType),
LocalStoragePath: setting.LocalStoragePath, LocalStoragePathTemplate: setting.LocalStoragePathTemplate,
UploadSizeLimitMb: setting.UploadSizeLimitMb, UploadSizeLimitMb: setting.UploadSizeLimitMb,
ActivedExternalStorageId: setting.ActivedExternalStorageId,
} }
} }
...@@ -187,9 +188,10 @@ func convertWorkspaceStorageSettingToStore(setting *apiv2pb.WorkspaceStorageSett ...@@ -187,9 +188,10 @@ func convertWorkspaceStorageSettingToStore(setting *apiv2pb.WorkspaceStorageSett
return nil return nil
} }
return &storepb.WorkspaceStorageSetting{ return &storepb.WorkspaceStorageSetting{
StorageType: storepb.WorkspaceStorageSetting_StorageType(setting.StorageType), StorageType: storepb.WorkspaceStorageSetting_StorageType(setting.StorageType),
LocalStoragePath: setting.LocalStoragePath, LocalStoragePathTemplate: setting.LocalStoragePathTemplate,
UploadSizeLimitMb: setting.UploadSizeLimitMb, UploadSizeLimitMb: setting.UploadSizeLimitMb,
ActivedExternalStorageId: setting.ActivedExternalStorageId,
} }
} }
......
package store package store
import "google.golang.org/protobuf/encoding/protojson"
var (
protojsonUnmarshaler = protojson.UnmarshalOptions{
AllowPartial: true,
DiscardUnknown: true,
}
)
// RowStatus is the status for a row. // RowStatus is the status for a row.
type RowStatus string type RowStatus string
......
...@@ -5,42 +5,19 @@ import ( ...@@ -5,42 +5,19 @@ import (
"database/sql" "database/sql"
"strings" "strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) (*storepb.UserSetting, error) { func (d *DB) UpsertUserSetting(ctx context.Context, upsert *store.UserSetting) (*store.UserSetting, error) {
stmt := "INSERT INTO `user_setting` (`user_id`, `key`, `value`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `value` = ?" stmt := "INSERT INTO `user_setting` (`user_id`, `key`, `value`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `value` = ?"
var valueString string if _, err := d.db.ExecContext(ctx, stmt, upsert.UserID, upsert.Key.String(), upsert.Value, upsert.Value); err != nil {
if upsert.Key == storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS {
valueBytes, err := protojson.Marshal(upsert.GetAccessTokens())
if err != nil {
return nil, err
}
valueString = string(valueBytes)
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_LOCALE {
valueString = upsert.GetLocale()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_APPEARANCE {
valueString = upsert.GetAppearance()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY {
valueString = upsert.GetMemoVisibility()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
valueString = upsert.GetTelegramUserId()
} else {
return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String())
}
if _, err := d.db.ExecContext(ctx, stmt, upsert.UserId, upsert.Key.String(), valueString, valueString); err != nil {
return nil, err return nil, err
} }
return upsert, nil return upsert, nil
} }
func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ([]*storepb.UserSetting, error) { func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ([]*store.UserSetting, error) {
where, args := []string{"1 = 1"}, []any{} where, args := []string{"1 = 1"}, []any{}
if v := find.Key; v != storepb.UserSettingKey_USER_SETTING_KEY_UNSPECIFIED { if v := find.Key; v != storepb.UserSettingKey_USER_SETTING_KEY_UNSPECIFIED {
...@@ -57,46 +34,18 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ...@@ -57,46 +34,18 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
} }
defer rows.Close() defer rows.Close()
userSettingList := make([]*storepb.UserSetting, 0) userSettingList := make([]*store.UserSetting, 0)
for rows.Next() { for rows.Next() {
userSetting := &storepb.UserSetting{} userSetting := &store.UserSetting{}
var keyString, valueString string var keyString string
if err := rows.Scan( if err := rows.Scan(
&userSetting.UserId, &userSetting.UserID,
&keyString, &keyString,
&valueString, &userSetting.Value,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
userSetting.Key = storepb.UserSettingKey(storepb.UserSettingKey_value[keyString]) userSetting.Key = storepb.UserSettingKey(storepb.UserSettingKey_value[keyString])
if userSetting.Key == storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS {
accessTokensUserSetting := &storepb.AccessTokensUserSetting{}
if err := protojson.Unmarshal([]byte(valueString), accessTokensUserSetting); err != nil {
return nil, err
}
userSetting.Value = &storepb.UserSetting_AccessTokens{
AccessTokens: accessTokensUserSetting,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_LOCALE {
userSetting.Value = &storepb.UserSetting_Locale{
Locale: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_APPEARANCE {
userSetting.Value = &storepb.UserSetting_Appearance{
Appearance: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY {
userSetting.Value = &storepb.UserSetting_MemoVisibility{
MemoVisibility: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
userSetting.Value = &storepb.UserSetting_TelegramUserId{
TelegramUserId: valueString,
}
} else {
// Skip unknown user setting key.
continue
}
userSettingList = append(userSettingList, userSetting) userSettingList = append(userSettingList, userSetting)
} }
......
...@@ -5,14 +5,11 @@ import ( ...@@ -5,14 +5,11 @@ import (
"database/sql" "database/sql"
"strings" "strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) (*storepb.UserSetting, error) { func (d *DB) UpsertUserSetting(ctx context.Context, upsert *store.UserSetting) (*store.UserSetting, error) {
stmt := ` stmt := `
INSERT INTO user_setting ( INSERT INTO user_setting (
user_id, key, value user_id, key, value
...@@ -21,33 +18,13 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) ...@@ -21,33 +18,13 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting)
ON CONFLICT(user_id, key) DO UPDATE ON CONFLICT(user_id, key) DO UPDATE
SET value = EXCLUDED.value SET value = EXCLUDED.value
` `
var valueString string if _, err := d.db.ExecContext(ctx, stmt, upsert.UserID, upsert.Key.String(), upsert.Value); err != nil {
if upsert.Key == storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS {
valueBytes, err := protojson.Marshal(upsert.GetAccessTokens())
if err != nil {
return nil, err
}
valueString = string(valueBytes)
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_LOCALE {
valueString = upsert.GetLocale()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_APPEARANCE {
valueString = upsert.GetAppearance()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY {
valueString = upsert.GetMemoVisibility()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
valueString = upsert.GetTelegramUserId()
} else {
return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String())
}
if _, err := d.db.ExecContext(ctx, stmt, upsert.UserId, upsert.Key.String(), valueString); err != nil {
return nil, err return nil, err
} }
return upsert, nil return upsert, nil
} }
func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ([]*storepb.UserSetting, error) { func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ([]*store.UserSetting, error) {
where, args := []string{"1 = 1"}, []any{} where, args := []string{"1 = 1"}, []any{}
if v := find.Key; v != storepb.UserSettingKey_USER_SETTING_KEY_UNSPECIFIED { if v := find.Key; v != storepb.UserSettingKey_USER_SETTING_KEY_UNSPECIFIED {
...@@ -70,46 +47,18 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ...@@ -70,46 +47,18 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
} }
defer rows.Close() defer rows.Close()
userSettingList := make([]*storepb.UserSetting, 0) userSettingList := make([]*store.UserSetting, 0)
for rows.Next() { for rows.Next() {
userSetting := &storepb.UserSetting{} userSetting := &store.UserSetting{}
var keyString, valueString string var keyString string
if err := rows.Scan( if err := rows.Scan(
&userSetting.UserId, &userSetting.UserID,
&keyString, &keyString,
&valueString, &userSetting.Value,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
userSetting.Key = storepb.UserSettingKey(storepb.UserSettingKey_value[keyString]) userSetting.Key = storepb.UserSettingKey(storepb.UserSettingKey_value[keyString])
if userSetting.Key == storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS {
accessTokensUserSetting := &storepb.AccessTokensUserSetting{}
if err := protojson.Unmarshal([]byte(valueString), accessTokensUserSetting); err != nil {
return nil, err
}
userSetting.Value = &storepb.UserSetting_AccessTokens{
AccessTokens: accessTokensUserSetting,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_LOCALE {
userSetting.Value = &storepb.UserSetting_Locale{
Locale: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_APPEARANCE {
userSetting.Value = &storepb.UserSetting_Appearance{
Appearance: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY {
userSetting.Value = &storepb.UserSetting_MemoVisibility{
MemoVisibility: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
userSetting.Value = &storepb.UserSetting_TelegramUserId{
TelegramUserId: valueString,
}
} else {
// Skip unknown user setting key.
continue
}
userSettingList = append(userSettingList, userSetting) userSettingList = append(userSettingList, userSetting)
} }
......
...@@ -5,14 +5,11 @@ import ( ...@@ -5,14 +5,11 @@ import (
"database/sql" "database/sql"
"strings" "strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) (*storepb.UserSetting, error) { func (d *DB) UpsertUserSetting(ctx context.Context, upsert *store.UserSetting) (*store.UserSetting, error) {
stmt := ` stmt := `
INSERT INTO user_setting ( INSERT INTO user_setting (
user_id, key, value user_id, key, value
...@@ -21,33 +18,13 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) ...@@ -21,33 +18,13 @@ func (d *DB) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting)
ON CONFLICT(user_id, key) DO UPDATE ON CONFLICT(user_id, key) DO UPDATE
SET value = EXCLUDED.value SET value = EXCLUDED.value
` `
var valueString string if _, err := d.db.ExecContext(ctx, stmt, upsert.UserID, upsert.Key.String(), upsert.Value); err != nil {
if upsert.Key == storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS {
valueBytes, err := protojson.Marshal(upsert.GetAccessTokens())
if err != nil {
return nil, err
}
valueString = string(valueBytes)
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_LOCALE {
valueString = upsert.GetLocale()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_APPEARANCE {
valueString = upsert.GetAppearance()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY {
valueString = upsert.GetMemoVisibility()
} else if upsert.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
valueString = upsert.GetTelegramUserId()
} else {
return nil, errors.Errorf("unknown user setting key: %s", upsert.Key.String())
}
if _, err := d.db.ExecContext(ctx, stmt, upsert.UserId, upsert.Key.String(), valueString); err != nil {
return nil, err return nil, err
} }
return upsert, nil return upsert, nil
} }
func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ([]*storepb.UserSetting, error) { func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ([]*store.UserSetting, error) {
where, args := []string{"1 = 1"}, []any{} where, args := []string{"1 = 1"}, []any{}
if v := find.Key; v != storepb.UserSettingKey_USER_SETTING_KEY_UNSPECIFIED { if v := find.Key; v != storepb.UserSettingKey_USER_SETTING_KEY_UNSPECIFIED {
...@@ -70,46 +47,18 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting) ...@@ -70,46 +47,18 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
} }
defer rows.Close() defer rows.Close()
userSettingList := make([]*storepb.UserSetting, 0) userSettingList := make([]*store.UserSetting, 0)
for rows.Next() { for rows.Next() {
userSetting := &storepb.UserSetting{} userSetting := &store.UserSetting{}
var keyString, valueString string var keyString string
if err := rows.Scan( if err := rows.Scan(
&userSetting.UserId, &userSetting.UserID,
&keyString, &keyString,
&valueString, &userSetting.Value,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
userSetting.Key = storepb.UserSettingKey(storepb.UserSettingKey_value[keyString]) userSetting.Key = storepb.UserSettingKey(storepb.UserSettingKey_value[keyString])
if userSetting.Key == storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS {
accessTokensUserSetting := &storepb.AccessTokensUserSetting{}
if err := protojson.Unmarshal([]byte(valueString), accessTokensUserSetting); err != nil {
return nil, err
}
userSetting.Value = &storepb.UserSetting_AccessTokens{
AccessTokens: accessTokensUserSetting,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_LOCALE {
userSetting.Value = &storepb.UserSetting_Locale{
Locale: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_APPEARANCE {
userSetting.Value = &storepb.UserSetting_Appearance{
Appearance: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY {
userSetting.Value = &storepb.UserSetting_MemoVisibility{
MemoVisibility: valueString,
}
} else if userSetting.Key == storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID {
userSetting.Value = &storepb.UserSetting_TelegramUserId{
TelegramUserId: valueString,
}
} else {
// Skip unknown user setting key.
continue
}
userSettingList = append(userSettingList, userSetting) userSettingList = append(userSettingList, userSetting)
} }
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
......
...@@ -61,8 +61,8 @@ type Driver interface { ...@@ -61,8 +61,8 @@ type Driver interface {
DeleteUser(ctx context.Context, delete *DeleteUser) error DeleteUser(ctx context.Context, delete *DeleteUser) error
// UserSetting model related methods. // UserSetting model related methods.
UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) (*storepb.UserSetting, error) UpsertUserSetting(ctx context.Context, upsert *UserSetting) (*UserSetting, error)
ListUserSettings(ctx context.Context, find *FindUserSetting) ([]*storepb.UserSetting, error) ListUserSettings(ctx context.Context, find *FindUserSetting) ([]*UserSetting, error)
// IdentityProvider model related methods. // IdentityProvider model related methods.
CreateIdentityProvider(ctx context.Context, create *IdentityProvider) (*IdentityProvider, error) CreateIdentityProvider(ctx context.Context, create *IdentityProvider) (*IdentityProvider, error)
......
...@@ -3,34 +3,56 @@ package store ...@@ -3,34 +3,56 @@ package store
import ( import (
"context" "context"
"github.com/pkg/errors"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
"google.golang.org/protobuf/encoding/protojson"
) )
type UserSetting struct {
UserID int32
Key storepb.UserSettingKey
Value string
}
type FindUserSetting struct { type FindUserSetting struct {
UserID *int32 UserID *int32
Key storepb.UserSettingKey Key storepb.UserSettingKey
} }
func (s *Store) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) (*storepb.UserSetting, error) { func (s *Store) UpsertUserSetting(ctx context.Context, upsert *storepb.UserSetting) (*storepb.UserSetting, error) {
userSettingMessage, err := s.driver.UpsertUserSetting(ctx, upsert) userSettingRaw, err := convertUserSettingToRaw(upsert)
if err != nil {
return nil, err
}
userSettingRaw, err = s.driver.UpsertUserSetting(ctx, userSettingRaw)
if err != nil { if err != nil {
return nil, err return nil, err
} }
s.userSettingCache.Store(getUserSettingV1CacheKey(userSettingMessage.UserId, userSettingMessage.Key.String()), userSettingMessage) userSetting, err := convertUserSettingFromRaw(userSettingRaw)
return userSettingMessage, nil if err != nil {
return nil, err
}
s.userSettingCache.Store(getUserSettingV1CacheKey(userSetting.UserId, userSetting.Key.String()), userSetting)
return userSetting, nil
} }
func (s *Store) ListUserSettings(ctx context.Context, find *FindUserSetting) ([]*storepb.UserSetting, error) { func (s *Store) ListUserSettings(ctx context.Context, find *FindUserSetting) ([]*storepb.UserSetting, error) {
userSettingList, err := s.driver.ListUserSettings(ctx, find) userSettingRawList, err := s.driver.ListUserSettings(ctx, find)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, userSetting := range userSettingList { userSettings := []*storepb.UserSetting{}
for _, userSettingRaw := range userSettingRawList {
userSetting, err := convertUserSettingFromRaw(userSettingRaw)
if err != nil {
return nil, err
}
s.userSettingCache.Store(getUserSettingV1CacheKey(userSetting.UserId, userSetting.Key.String()), userSetting) s.userSettingCache.Store(getUserSettingV1CacheKey(userSetting.UserId, userSetting.Key.String()), userSetting)
userSettings = append(userSettings, userSetting)
} }
return userSettingList, nil return userSettings, nil
} }
func (s *Store) GetUserSetting(ctx context.Context, find *FindUserSetting) (*storepb.UserSetting, error) { func (s *Store) GetUserSetting(ctx context.Context, find *FindUserSetting) (*storepb.UserSetting, error) {
...@@ -44,10 +66,12 @@ func (s *Store) GetUserSetting(ctx context.Context, find *FindUserSetting) (*sto ...@@ -44,10 +66,12 @@ func (s *Store) GetUserSetting(ctx context.Context, find *FindUserSetting) (*sto
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(list) == 0 { if len(list) == 0 {
return nil, nil return nil, nil
} }
if len(list) > 1 {
return nil, errors.Errorf("expected 1 user setting, but got %d", len(list))
}
userSetting := list[0] userSetting := list[0]
s.userSettingCache.Store(getUserSettingV1CacheKey(userSetting.UserId, userSetting.Key.String()), userSetting) s.userSettingCache.Store(getUserSettingV1CacheKey(userSetting.UserId, userSetting.Key.String()), userSetting)
...@@ -97,3 +121,58 @@ func (s *Store) RemoveUserAccessToken(ctx context.Context, userID int32, token s ...@@ -97,3 +121,58 @@ func (s *Store) RemoveUserAccessToken(ctx context.Context, userID int32, token s
return err return err
} }
func convertUserSettingFromRaw(raw *UserSetting) (*storepb.UserSetting, error) {
userSetting := &storepb.UserSetting{
UserId: raw.UserID,
Key: raw.Key,
}
switch raw.Key {
case storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS:
accessTokensUserSetting := &storepb.AccessTokensUserSetting{}
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), accessTokensUserSetting); err != nil {
return nil, err
}
userSetting.Value = &storepb.UserSetting_AccessTokens{AccessTokens: accessTokensUserSetting}
case storepb.UserSettingKey_USER_SETTING_LOCALE:
userSetting.Value = &storepb.UserSetting_Locale{Locale: raw.Value}
case storepb.UserSettingKey_USER_SETTING_APPEARANCE:
userSetting.Value = &storepb.UserSetting_Appearance{Appearance: raw.Value}
case storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY:
userSetting.Value = &storepb.UserSetting_MemoVisibility{MemoVisibility: raw.Value}
case storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID:
userSetting.Value = &storepb.UserSetting_TelegramUserId{TelegramUserId: raw.Value}
default:
return nil, errors.Errorf("unsupported user setting key: %v", raw.Key)
}
return userSetting, nil
}
func convertUserSettingToRaw(userSetting *storepb.UserSetting) (*UserSetting, error) {
raw := &UserSetting{
UserID: userSetting.UserId,
Key: userSetting.Key,
}
switch userSetting.Key {
case storepb.UserSettingKey_USER_SETTING_ACCESS_TOKENS:
accessTokensUserSetting := userSetting.GetAccessTokens()
value, err := protojson.Marshal(accessTokensUserSetting)
if err != nil {
return nil, err
}
raw.Value = string(value)
case storepb.UserSettingKey_USER_SETTING_LOCALE:
raw.Value = userSetting.GetLocale()
case storepb.UserSettingKey_USER_SETTING_APPEARANCE:
raw.Value = userSetting.GetAppearance()
case storepb.UserSettingKey_USER_SETTING_MEMO_VISIBILITY:
raw.Value = userSetting.GetMemoVisibility()
case storepb.UserSettingKey_USER_SETTING_TELEGRAM_USER_ID:
raw.Value = userSetting.GetTelegramUserId()
default:
return nil, errors.Errorf("unsupported user setting key: %v", userSetting.Key)
}
return raw, nil
}
...@@ -186,6 +186,12 @@ func (s *Store) GetWorkspaceMemoRelatedSetting(ctx context.Context) (*storepb.Wo ...@@ -186,6 +186,12 @@ func (s *Store) GetWorkspaceMemoRelatedSetting(ctx context.Context) (*storepb.Wo
return workspaceMemoRelatedSetting, nil return workspaceMemoRelatedSetting, nil
} }
const (
defaultWorkspaceStorageType = storepb.WorkspaceStorageSetting_STORAGE_TYPE_DATABASE
defaultWorkspaceUploadSizeLimitMb = 30
defaultWorkspaceLocalStoragePathTemplate = "assets/{timestamp}_{filename}"
)
func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.WorkspaceStorageSetting, error) { func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.WorkspaceStorageSetting, error) {
workspaceSetting, err := s.GetWorkspaceSettingV1(ctx, &FindWorkspaceSetting{ workspaceSetting, err := s.GetWorkspaceSettingV1(ctx, &FindWorkspaceSetting{
Name: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_STORAGE.String(), Name: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_STORAGE.String(),
...@@ -198,6 +204,15 @@ func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.Worksp ...@@ -198,6 +204,15 @@ func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.Worksp
if workspaceSetting != nil { if workspaceSetting != nil {
workspaceStorageSetting = workspaceSetting.GetStorageSetting() workspaceStorageSetting = workspaceSetting.GetStorageSetting()
} }
if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_UNSPECIFIED {
workspaceStorageSetting.StorageType = defaultWorkspaceStorageType
}
if workspaceStorageSetting.UploadSizeLimitMb == 0 {
workspaceStorageSetting.UploadSizeLimitMb = defaultWorkspaceUploadSizeLimitMb
}
if workspaceStorageSetting.LocalStoragePathTemplate == "" {
workspaceStorageSetting.LocalStoragePathTemplate = defaultWorkspaceLocalStoragePathTemplate
}
return workspaceStorageSetting, nil return workspaceStorageSetting, nil
} }
...@@ -208,31 +223,31 @@ func convertWorkspaceSettingFromRaw(workspaceSettingRaw *WorkspaceSetting) (*sto ...@@ -208,31 +223,31 @@ func convertWorkspaceSettingFromRaw(workspaceSettingRaw *WorkspaceSetting) (*sto
switch workspaceSettingRaw.Name { switch workspaceSettingRaw.Name {
case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_BASIC.String(): case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_BASIC.String():
basicSetting := &storepb.WorkspaceBasicSetting{} basicSetting := &storepb.WorkspaceBasicSetting{}
if err := protojson.Unmarshal([]byte(workspaceSettingRaw.Value), basicSetting); err != nil { if err := protojsonUnmarshaler.Unmarshal([]byte(workspaceSettingRaw.Value), basicSetting); err != nil {
return nil, err return nil, err
} }
workspaceSetting.Value = &storepb.WorkspaceSetting_BasicSetting{BasicSetting: basicSetting} workspaceSetting.Value = &storepb.WorkspaceSetting_BasicSetting{BasicSetting: basicSetting}
case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_GENERAL.String(): case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_GENERAL.String():
generalSetting := &storepb.WorkspaceGeneralSetting{} generalSetting := &storepb.WorkspaceGeneralSetting{}
if err := protojson.Unmarshal([]byte(workspaceSettingRaw.Value), generalSetting); err != nil { if err := protojsonUnmarshaler.Unmarshal([]byte(workspaceSettingRaw.Value), generalSetting); err != nil {
return nil, err return nil, err
} }
workspaceSetting.Value = &storepb.WorkspaceSetting_GeneralSetting{GeneralSetting: generalSetting} workspaceSetting.Value = &storepb.WorkspaceSetting_GeneralSetting{GeneralSetting: generalSetting}
case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_STORAGE.String(): case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_STORAGE.String():
storageSetting := &storepb.WorkspaceStorageSetting{} storageSetting := &storepb.WorkspaceStorageSetting{}
if err := protojson.Unmarshal([]byte(workspaceSettingRaw.Value), storageSetting); err != nil { if err := protojsonUnmarshaler.Unmarshal([]byte(workspaceSettingRaw.Value), storageSetting); err != nil {
return nil, err return nil, err
} }
workspaceSetting.Value = &storepb.WorkspaceSetting_StorageSetting{StorageSetting: storageSetting} workspaceSetting.Value = &storepb.WorkspaceSetting_StorageSetting{StorageSetting: storageSetting}
case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_MEMO_RELATED.String(): case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_MEMO_RELATED.String():
memoRelatedSetting := &storepb.WorkspaceMemoRelatedSetting{} memoRelatedSetting := &storepb.WorkspaceMemoRelatedSetting{}
if err := protojson.Unmarshal([]byte(workspaceSettingRaw.Value), memoRelatedSetting); err != nil { if err := protojsonUnmarshaler.Unmarshal([]byte(workspaceSettingRaw.Value), memoRelatedSetting); err != nil {
return nil, err return nil, err
} }
workspaceSetting.Value = &storepb.WorkspaceSetting_MemoRelatedSetting{MemoRelatedSetting: memoRelatedSetting} workspaceSetting.Value = &storepb.WorkspaceSetting_MemoRelatedSetting{MemoRelatedSetting: memoRelatedSetting}
case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_TELEGRAM_INTEGRATION.String(): case storepb.WorkspaceSettingKey_WORKSPACE_SETTING_TELEGRAM_INTEGRATION.String():
telegramIntegrationSetting := &storepb.WorkspaceTelegramIntegrationSetting{} telegramIntegrationSetting := &storepb.WorkspaceTelegramIntegrationSetting{}
if err := protojson.Unmarshal([]byte(workspaceSettingRaw.Value), telegramIntegrationSetting); err != nil { if err := protojsonUnmarshaler.Unmarshal([]byte(workspaceSettingRaw.Value), telegramIntegrationSetting); err != nil {
return nil, err return nil, err
} }
workspaceSetting.Value = &storepb.WorkspaceSetting_TelegramIntegrationSetting{TelegramIntegrationSetting: telegramIntegrationSetting} workspaceSetting.Value = &storepb.WorkspaceSetting_TelegramIntegrationSetting{TelegramIntegrationSetting: telegramIntegrationSetting}
......
...@@ -2,7 +2,6 @@ import { ...@@ -2,7 +2,6 @@ import {
Button, Button,
Divider, Divider,
Dropdown, Dropdown,
IconButton,
Input, Input,
List, List,
ListItem, ListItem,
...@@ -11,9 +10,12 @@ import { ...@@ -11,9 +10,12 @@ import {
MenuItem, MenuItem,
Radio, Radio,
RadioGroup, RadioGroup,
Select,
Tooltip, Tooltip,
Option,
} from "@mui/joy"; } from "@mui/joy";
import { useEffect, useState } from "react"; import { isEqual } from "lodash-es";
import { useEffect, useMemo, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import * as api from "@/helpers/api"; import * as api from "@/helpers/api";
...@@ -25,7 +27,6 @@ import showCreateStorageServiceDialog from "../CreateStorageServiceDialog"; ...@@ -25,7 +27,6 @@ import showCreateStorageServiceDialog from "../CreateStorageServiceDialog";
import { showCommonDialog } from "../Dialog/CommonDialog"; import { showCommonDialog } from "../Dialog/CommonDialog";
import Icon from "../Icon"; import Icon from "../Icon";
import LearnMore from "../LearnMore"; import LearnMore from "../LearnMore";
import showUpdateLocalStorageDialog from "../UpdateLocalStorageDialog";
const StorageSection = () => { const StorageSection = () => {
const t = useTranslate(); const t = useTranslate();
...@@ -37,6 +38,26 @@ const StorageSection = () => { ...@@ -37,6 +38,26 @@ const StorageSection = () => {
), ),
); );
const allowSaveStorageSetting = useMemo(() => {
if (workspaceStorageSetting.uploadSizeLimitMb <= 0) {
return false;
}
const origin = WorkspaceStorageSetting.fromPartial(
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE)?.storageSetting || {},
);
if (workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL) {
if (workspaceStorageSetting.localStoragePathTemplate.length === 0) {
return false;
}
} else if (workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_EXTERNAL) {
if (!workspaceStorageSetting.activedExternalStorageId || workspaceStorageSetting.activedExternalStorageId === 0) {
return false;
}
}
return !isEqual(origin, workspaceStorageSetting);
}, [workspaceStorageSetting, workspaceSettingStore.getState()]);
useEffect(() => { useEffect(() => {
fetchStorageList(); fetchStorageList();
}, []); }, []);
...@@ -56,10 +77,14 @@ const StorageSection = () => { ...@@ -56,10 +77,14 @@ const StorageSection = () => {
uploadSizeLimitMb: num, uploadSizeLimitMb: num,
}; };
setWorkspaceStorageSetting(update); setWorkspaceStorageSetting(update);
workspaceSettingStore.setWorkspaceSetting({ };
name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE}`,
storageSetting: update, const handleLocalStoragePathTemplateChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
}); const update: WorkspaceStorageSetting = {
...workspaceStorageSetting,
localStoragePathTemplate: event.target.value,
};
setWorkspaceStorageSetting(update);
}; };
const handleStorageTypeChanged = async (storageType: WorkspaceStorageSetting_StorageType) => { const handleStorageTypeChanged = async (storageType: WorkspaceStorageSetting_StorageType) => {
...@@ -68,10 +93,6 @@ const StorageSection = () => { ...@@ -68,10 +93,6 @@ const StorageSection = () => {
storageType: storageType, storageType: storageType,
}; };
setWorkspaceStorageSetting(update); setWorkspaceStorageSetting(update);
await workspaceSettingStore.setWorkspaceSetting({
name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE}`,
storageSetting: update,
});
}; };
const handleActivedExternalStorageIdChanged = async (activedExternalStorageId: number) => { const handleActivedExternalStorageIdChanged = async (activedExternalStorageId: number) => {
...@@ -80,10 +101,14 @@ const StorageSection = () => { ...@@ -80,10 +101,14 @@ const StorageSection = () => {
activedExternalStorageId: activedExternalStorageId, activedExternalStorageId: activedExternalStorageId,
}; };
setWorkspaceStorageSetting(update); setWorkspaceStorageSetting(update);
};
const saveWorkspaceStorageSetting = async () => {
await workspaceSettingStore.setWorkspaceSetting({ await workspaceSettingStore.setWorkspaceSetting({
name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE}`, name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE}`,
storageSetting: update, storageSetting: workspaceStorageSetting,
}); });
toast.success("Updated");
}; };
const handleDeleteStorage = (storage: ObjectStorage) => { const handleDeleteStorage = (storage: ObjectStorage) => {
...@@ -106,10 +131,9 @@ const StorageSection = () => { ...@@ -106,10 +131,9 @@ const StorageSection = () => {
return ( return (
<div className="w-full flex flex-col gap-2 pt-2 pb-4"> <div className="w-full flex flex-col gap-2 pt-2 pb-4">
<div className="w-full flex flex-row justify-start items-center"> <div className="font-medium text-gray-700 dark:text-gray-500">{t("setting.storage-section.current-storage")}</div>
<span className="font-mono text-sm text-gray-400 mr-2 dark:text-gray-500">{t("setting.storage-section.current-storage")}</span>
</div>
<RadioGroup <RadioGroup
orientation="horizontal"
className="w-full" className="w-full"
value={workspaceStorageSetting.storageType} value={workspaceStorageSetting.storageType}
onChange={(event) => { onChange={(event) => {
...@@ -117,28 +141,12 @@ const StorageSection = () => { ...@@ -117,28 +141,12 @@ const StorageSection = () => {
}} }}
> >
<Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_DATABASE} label={t("setting.storage-section.type-database")} /> <Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_DATABASE} label={t("setting.storage-section.type-database")} />
<div> <Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL} label={t("setting.storage-section.type-local")} />
<Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL} label={t("setting.storage-section.type-local")} /> <Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_EXTERNAL} disabled={storageList.length === 0} label={"S3"} />
<IconButton size="sm" onClick={() => showUpdateLocalStorageDialog()}>
<Icon.PenBox className="w-4 h-auto" />
</IconButton>
</div>
<Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_EXTERNAL} label={"S3"} />
</RadioGroup>
<RadioGroup
className="w-full"
value={workspaceStorageSetting.activedExternalStorageId}
onChange={(event) => {
handleActivedExternalStorageIdChanged(Number(event.target.value));
}}
>
{storageList.map((storage) => (
<Radio key={storage.id} value={storage.id} label={storage.name} />
))}
</RadioGroup> </RadioGroup>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<span className="mr-1">{t("setting.system-section.max-upload-size")}</span> <span className="text-gray-700 dark:text-gray-500 mr-1">{t("setting.system-section.max-upload-size")}</span>
<Tooltip title={t("setting.system-section.max-upload-size-hint")} placement="top"> <Tooltip title={t("setting.system-section.max-upload-size-hint")} placement="top">
<Icon.HelpCircle className="w-4 h-auto" /> <Icon.HelpCircle className="w-4 h-auto" />
</Tooltip> </Tooltip>
...@@ -152,6 +160,36 @@ const StorageSection = () => { ...@@ -152,6 +160,36 @@ const StorageSection = () => {
onChange={handleMaxUploadSizeChanged} onChange={handleMaxUploadSizeChanged}
/> />
</div> </div>
{workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL && (
<div className="w-full flex flex-row justify-between items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">Local file path template</span>
<Input
defaultValue={workspaceStorageSetting.localStoragePathTemplate}
placeholder="assets/{timestamp}_{filename}"
onChange={handleLocalStoragePathTemplateChanged}
/>
</div>
)}
{workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_EXTERNAL && (
<div className="w-full flex flex-row justify-between items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">Actived storage</span>
<Select
onChange={(_, value) => handleActivedExternalStorageIdChanged(value as number)}
defaultValue={workspaceStorageSetting.activedExternalStorageId}
>
{storageList.map((storage) => (
<Option key={storage.id} value={storage.id}>
{storage.name}
</Option>
))}
</Select>
</div>
)}
<div>
<Button disabled={!allowSaveStorageSetting} onClick={saveWorkspaceStorageSetting}>
{t("common.save")}
</Button>
</div>
<Divider className="!my-2" /> <Divider className="!my-2" />
<div className="mb-2 w-full flex flex-row justify-between items-center gap-1"> <div className="mb-2 w-full flex flex-row justify-between items-center gap-1">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
......
import { Button, IconButton, Input } from "@mui/joy";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { WorkspaceSettingPrefix, useWorkspaceSettingStore } from "@/store/v1";
import { WorkspaceSettingKey, WorkspaceStorageSetting } from "@/types/proto/store/workspace_setting";
import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog";
import Icon from "./Icon";
import LearnMore from "./LearnMore";
interface Props extends DialogProps {
confirmCallback?: () => void;
}
const UpdateLocalStorageDialog: React.FC<Props> = (props: Props) => {
const t = useTranslate();
const { destroy, confirmCallback } = props;
const workspaceSettingStore = useWorkspaceSettingStore();
const [workspaceStorageSetting, setWorkspaceStorageSetting] = useState<WorkspaceStorageSetting>(
WorkspaceStorageSetting.fromPartial(
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE)?.storageSetting || {},
),
);
const handleCloseBtnClick = () => {
destroy();
};
const handleConfirmBtnClick = async () => {
try {
await workspaceSettingStore.setWorkspaceSetting({
name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE}`,
storageSetting: workspaceStorageSetting,
});
} catch (error: any) {
console.error(error);
toast.error(error.details);
}
if (confirmCallback) {
confirmCallback();
}
destroy();
};
return (
<>
<div className="dialog-header-container">
<p className="title-text">{t("setting.storage-section.update-local-path")}</p>
<IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" />
</IconButton>
</div>
<div className="dialog-content-container max-w-xs">
<p className="text-sm break-words mb-1">{t("setting.storage-section.update-local-path-description")}</p>
<div className="flex flex-row items-center mb-2 gap-x-2">
<span className="text-sm text-gray-400 break-all">e.g. {"assets/{timestamp}_{filename}"}</span>
<LearnMore url="https://usememos.com/docs/advanced-settings/local-storage" />
</div>
<Input
className="mb-2"
placeholder={t("setting.storage-section.local-storage-path")}
fullWidth
value={workspaceStorageSetting.localStoragePath}
onChange={(e) => setWorkspaceStorageSetting({ ...workspaceStorageSetting, localStoragePath: e.target.value })}
/>
<div className="mt-2 w-full flex flex-row justify-end items-center space-x-1">
<Button variant="plain" color="neutral" onClick={handleCloseBtnClick}>
{t("common.cancel")}
</Button>
<Button onClick={handleConfirmBtnClick}>{t("common.update")}</Button>
</div>
</div>
</>
);
};
function showUpdateLocalStorageDialog(confirmCallback?: () => void) {
generateDialog(
{
className: "update-local-storage-dialog",
dialogName: "update-local-storage-dialog",
},
UpdateLocalStorageDialog,
{ confirmCallback },
);
}
export default showUpdateLocalStorageDialog;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment