diff --git a/packages/dashboard/src/locales/en.json b/packages/dashboard/src/locales/en.json index bfabd3df0f75..46fa7bbf29e4 100644 --- a/packages/dashboard/src/locales/en.json +++ b/packages/dashboard/src/locales/en.json @@ -113,8 +113,6 @@ "settings_appearance_desc": "Select the theme you would like to use", "settings_data_source_title": "Data Source", "settings_data_source_desc": "Fetch trending data from different platformse", - "settings_ancient_post_compatibility_mode_title": "Ancient Post Compatibility Mode", - "settings_ancient_post_compatibility_mode_desc": "Enable if you need to decrypt ancient posts (created before Jan 2020)", "settings_sync_with_mobile_title": "Sync With Mobile", "settings_sync_with_mobile_desc": "You can sync your accounts and information with your mobile device. Open the Mask Network mobile app, go to Settings and tap on Sync With Plug-ins", "settings_global_backup_title": "Global Backup", diff --git a/packages/dashboard/src/locales/zh.json b/packages/dashboard/src/locales/zh.json index 665bb8266f45..a3a81f4c8f76 100644 --- a/packages/dashboard/src/locales/zh.json +++ b/packages/dashboard/src/locales/zh.json @@ -68,8 +68,6 @@ "settings_appearance_desc": "Select the theme you would like to use", "settings_data_source_title": "Data Source", "settings_data_source_desc": "Fetch trending data from different platformse", - "settings_ancient_post_compatibility_mode_title": "Ancient Post Compatibility Mode", - "settings_ancient_post_compatibility_mode_desc": "Enable if you need to decrypt ancient posts (created before Jan 2020)", "settings_sync_with_mobile_title": "Sync With Mobile", "settings_sync_with_mobile_desc": "You can sync your accounts and information with your mobile device. Open the Mask Network mobile app, go to Settings and tap on Sync With Plug-ins", "settings_global_backup_title": "Global Backup", diff --git a/packages/dashboard/src/pages/Settings/api.ts b/packages/dashboard/src/pages/Settings/api.ts index 87763bd0f50d..44db39666118 100644 --- a/packages/dashboard/src/pages/Settings/api.ts +++ b/packages/dashboard/src/pages/Settings/api.ts @@ -8,8 +8,3 @@ export const [useLanguage] = createGlobalState(Services.Settings.getLanguage, (x export const [useTrendingDataSource] = createGlobalState(Services.Settings.getTrendingDataSource, (x) => Messages.events.currentTrendingDataProviderSettings.on(x), ) - -export const [useAncientPostsCompatibilityMode] = createGlobalState( - Services.Settings.getAncientPostsCompatibiltyMode, - (x) => Messages.events.disableOpenNewTabInBackgroundSettings.on(x), -) diff --git a/packages/dashboard/src/pages/Settings/components/AncientPostsSetting.tsx b/packages/dashboard/src/pages/Settings/components/AncientPostsSetting.tsx deleted file mode 100644 index 075f58f4a2a9..000000000000 --- a/packages/dashboard/src/pages/Settings/components/AncientPostsSetting.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import type { ChangeEvent } from 'react' -import { Services } from '../../../API' -import { useAncientPostsCompatibilityMode } from '../api' -import SettingSwitch from './SettingSwitch' - -export default function AncientPostsSetting() { - const checked = useAncientPostsCompatibilityMode() - - const handleChange = (event: ChangeEvent) => { - Services.Settings.setAncientPostsCompatibiltyMode(event.target.checked) - } - - return -} diff --git a/packages/dashboard/src/pages/Settings/index.tsx b/packages/dashboard/src/pages/Settings/index.tsx index 8ee45821841a..decef258722b 100644 --- a/packages/dashboard/src/pages/Settings/index.tsx +++ b/packages/dashboard/src/pages/Settings/index.tsx @@ -5,7 +5,6 @@ import SettingItem from './components/SettingItem' import LanguageIcon from '@material-ui/icons/Language' import PaletteIcon from '@material-ui/icons/Palette' import TrendingUpIcon from '@material-ui/icons/TrendingUp' -import DesktopWindowsIcon from '@material-ui/icons/DesktopWindows' import SyncIcon from '@material-ui/icons/Sync' import SaveIcon from '@material-ui/icons/Save' import SaveAltIcon from '@material-ui/icons/SaveAlt' @@ -18,7 +17,6 @@ import { useDashboardI18N } from '../../locales' import LanguageSetting from './components/LanguageSetting' import AppearanceSetting from './components/AppearanceSetting' -import AncientPostsSetting from './components/AncientPostsSetting' import BackupSetting from './components/BackupSetting' import RestoreSetting from './components/RestoreSetting' import MobileSyncSetting from './components/MobileSyncSetting' @@ -54,12 +52,6 @@ export default function Settings() { desc={t.settings_data_source_desc()}> - } - title={t.settings_ancient_post_compatibility_mode_title()} - desc={t.settings_ancient_post_compatibility_mode_desc()}> - - } title={t.settings_sync_with_mobile_title()} diff --git a/packages/maskbook/src/_locales/en/messages.json b/packages/maskbook/src/_locales/en/messages.json index 3e892213efa7..c8db5bad2dee 100644 --- a/packages/maskbook/src/_locales/en/messages.json +++ b/packages/maskbook/src/_locales/en/messages.json @@ -38,7 +38,6 @@ "connect_to": "Connect to", "connecting": "Connecting…", "contacts": "Contacts", - "groups": "Groups", "create": "Create", "gas_fee": "Transaction fee", "create_a_persona": "Create a Persona", @@ -68,9 +67,6 @@ "dashboard_email_address": "info@dimension.im", "dashboard_license": "GNU AGPL 3.0", "dashboard_no_collectible_found": "No collectible found", - "database_group_followers_name": "Followers of {{owner}}", - "database_group_following_name": "Followed by {{owner}}", - "database_group_friends_default_name": "Friends of {{owner}}", "decrypted_postbox_add_recipients": "Append recipients", "decrypted_postbox_decrypting": "Mask decrypting…", "decrypted_postbox_decoding": "Mask decoding…", @@ -137,10 +133,6 @@ "payload_not_found": "Payload not found.", "payload_throw_in_alpha41": "Support for Alpha41 is deprecated. Tell your friends to upgrade Mask!", "people_in_database": "All people recorded in the Mask database.", - "person_or_group_in_chip": "{{name}} ({{count}} in the group)", - "person_or_group_in_list_0": "This is an empty group", - "person_or_group_in_list_many": "{{people}}… {{count}} in total", - "person_or_group_in_list_many_no_preview": "{{count}} in total", "personas": "Personas", "browser_action_request_permission": "Grant Permission", "browser_action_notifications": "Notifications", @@ -236,8 +228,6 @@ "set_up_qr_scanner_fail": "Fail to scan QR code", "set_up_backup_fail": "Backup Failed", "settings": "Settings", - "settings_ancient_post_compatibility_mode": "Ancient Post Compatibility Mode", - "settings_ancient_post_compatibility_mode_desc": "Enable if you need to decrypt ancient posts (created before Jan 2020).", "settings_enable_debug": "Debug Mode", "settings_enable_debug_desc": "See additional information for debugging.", "settings_appearance": "Appearance", diff --git a/packages/maskbook/src/_locales/ja/messages.json b/packages/maskbook/src/_locales/ja/messages.json index eed9f3f852e3..41b50369e01e 100644 --- a/packages/maskbook/src/_locales/ja/messages.json +++ b/packages/maskbook/src/_locales/ja/messages.json @@ -38,7 +38,6 @@ "connect_to": "次に接続します:", "connecting": "接続中…", "contacts": "連絡先", - "groups": "グループ", "create": "作成", "gas_fee": "", "create_a_persona": "ペルソナを作成", @@ -68,9 +67,6 @@ "dashboard_email_address": "info@dimension.im", "dashboard_license": "GNU AGPL 3.0", "dashboard_no_collectible_found": "コレクションが見つかりません", - "database_group_followers_name": "{{owner}}のフォロワー", - "database_group_following_name": "{{owner}}にフォローされている人", - "database_group_friends_default_name": "{{owner}}の相互フォロー", "decrypted_postbox_add_recipients": "受信者を追加", "decrypted_postbox_decrypting": "Mask が復号しています。", "decrypted_postbox_decoding": "Mask が解読しています。", @@ -137,10 +133,6 @@ "payload_not_found": "ペイロードが見つかりませんでした。", "payload_throw_in_alpha41": "Alpha41方式は使用できません。Mask をアップデートしてください。", "people_in_database": "全ての人は Mask データベースに保存されます。", - "person_or_group_in_chip": "{{name}} (グループ {{count}}番目)", - "person_or_group_in_list_0": "空のグループです", - "person_or_group_in_list_many": "{{people}}… 全部で{{count}}", - "person_or_group_in_list_many_no_preview": "全部で{{count}}", "personas": "ペルソナ", "browser_action_request_permission": "権限を付与", "browser_action_notifications": "通知", @@ -236,8 +228,6 @@ "set_up_qr_scanner_fail": "QR コードのスキャンに失敗しました。", "set_up_backup_fail": "バックアップに失敗しました。", "settings": "設定", - "settings_ancient_post_compatibility_mode": "エンシェントポスト互換モード", - "settings_ancient_post_compatibility_mode_desc": "過去の投稿(2020年1月以前に作成)を復号する必要がある場合に有効にします。", "settings_enable_debug": "デバッグモード", "settings_enable_debug_desc": "デバッグのための追加情報を参照してください。", "settings_appearance": "テーマ", diff --git a/packages/maskbook/src/_locales/ko/messages.json b/packages/maskbook/src/_locales/ko/messages.json index 46397d4f14a8..0b3da46854f0 100644 --- a/packages/maskbook/src/_locales/ko/messages.json +++ b/packages/maskbook/src/_locales/ko/messages.json @@ -38,7 +38,6 @@ "connect_to": "연결", "connecting": "연결 중", "contacts": "연락처", - "groups": "그롭", "create": "만들기", "gas_fee": "", "create_a_persona": "페르소나 만들기", @@ -68,9 +67,6 @@ "dashboard_email_address": "info@dimension.im", "dashboard_license": "GNU AGPL 3.0", "dashboard_no_collectible_found": "수집품이 없습니다.", - "database_group_followers_name": "{{owner}}의 팔로워", - "database_group_following_name": "{{owner}}의 팔로잉", - "database_group_friends_default_name": "{{owner}}의 친구", "decrypted_postbox_add_recipients": "받는사람 추가", "decrypted_postbox_decrypting": "Mask 해독 중", "decrypted_postbox_decoding": "Mask 디코딩 중", @@ -137,10 +133,6 @@ "payload_not_found": "페이로드를 찾을 수 없습니다.", "payload_throw_in_alpha41": "Alpha41 이미 지원하 않습니다. 상대방에게 Mask를 업그레이드하라고 하세요!", "people_in_database": "모든 연락을 다 Mask의 데이터베이스에 기록되어 있습니다.", - "person_or_group_in_chip": "{{name}} ({{count}} 명", - "person_or_group_in_list_0": "비워 있는 그룹입니다", - "person_or_group_in_list_many": "{{people}}등 전체 {{count}} 명", - "person_or_group_in_list_many_no_preview": "전체 {{count}} 명", "personas": "페르소나", "browser_action_request_permission": "권한 받기", "browser_action_notifications": "알림", @@ -236,8 +228,6 @@ "set_up_qr_scanner_fail": "QR 코드 스캔 실패", "set_up_backup_fail": "백업 실패", "settings": "설정", - "settings_ancient_post_compatibility_mode": "오래된 게시물 호환성 모드", - "settings_ancient_post_compatibility_mode_desc": "오래된 게시물(2020 1월 전에 만든 것) 해독 가능", "settings_enable_debug": "디버그 모드", "settings_enable_debug_desc": "디버깅에 대한 추가 정보 보기", "settings_appearance": "화면", diff --git a/packages/maskbook/src/_locales/zh/messages.json b/packages/maskbook/src/_locales/zh/messages.json index 75446ec6b43f..3b1ed37fdbe0 100644 --- a/packages/maskbook/src/_locales/zh/messages.json +++ b/packages/maskbook/src/_locales/zh/messages.json @@ -38,7 +38,6 @@ "connect_to": "連接到", "connecting": "正在連接…", "contacts": "聯絡人", - "groups": "群組", "create": "建立", "gas_fee": "", "create_a_persona": "建立一個角色", @@ -68,9 +67,6 @@ "dashboard_email_address": "info@dimension.im", "dashboard_license": "GNU AGPL 3.0", "dashboard_no_collectible_found": "找不到收藏品", - "database_group_followers_name": "{{owner} 的追隨者", - "database_group_following_name": "被 {{owner}} 追隨", - "database_group_friends_default_name": "{{owner}} 的朋友", "decrypted_postbox_add_recipients": "新增收件人", "decrypted_postbox_decrypting": "Mask 正在解密…", "decrypted_postbox_decoding": "Mask 正在解碼…", @@ -137,10 +133,6 @@ "payload_not_found": "找不到貼文。", "payload_throw_in_alpha41": "不推薦使用 Alpha41。告訴你的朋友升級Mask!", "people_in_database": "被記錄在 Mask 資料庫裡的所有人。", - "person_or_group_in_chip": "{{name}} ({{count}} 人在群組中)", - "person_or_group_in_list_0": "這是一個空群組", - "person_or_group_in_list_many": "{{people}}… 總共 {{count}}", - "person_or_group_in_list_many_no_preview": "總共 {{count}}", "personas": "角色", "browser_action_request_permission": "授權", "browser_action_notifications": "通知", @@ -236,8 +228,6 @@ "set_up_qr_scanner_fail": "掃描 QR Code 失敗", "set_up_backup_fail": "備份失敗", "settings": "設定", - "settings_ancient_post_compatibility_mode": "遠古貼文相容模式", - "settings_ancient_post_compatibility_mode_desc": "開啟這個選項如果你需要解密遠古貼文 (在 2020 年一月前建立)。", "settings_enable_debug": "除錯模式", "settings_enable_debug_desc": "請參考有關除錯的附加資訊", "settings_appearance": "外觀", diff --git a/packages/maskbook/src/components/InjectedComponents/PostDialog.tsx b/packages/maskbook/src/components/InjectedComponents/PostDialog.tsx index 29bd62f39191..7db10a50047e 100644 --- a/packages/maskbook/src/components/InjectedComponents/PostDialog.tsx +++ b/packages/maskbook/src/components/InjectedComponents/PostDialog.tsx @@ -22,8 +22,8 @@ import { import { useValueRef } from '@masknet/shared' import { CompositionEvent, MaskMessage, useI18N, Flags } from '../../utils' import { isMinds } from '../../social-network-adaptor/minds.com/base' -import { useStylesExtends, or } from '../custom-ui-helper' -import type { Profile, Group } from '../../database' +import { or } from '../custom-ui-helper' +import type { Profile } from '../../database' import { useFriendsList, useCurrentIdentity, useMyIdentities } from '../DataSource/useActivatedUI' import { currentImagePayloadStatus, debugModeSetting } from '../../settings/settings' import { activatedSocialNetworkUI } from '../../social-network' @@ -66,15 +66,15 @@ const useStyles = makeStyles({ }, }) -export interface PostDialogUIProps extends withClasses { +export interface PostDialogUIProps { open: boolean onlyMyself: boolean shareToEveryone: boolean imagePayload: boolean imagePayloadUnchangeable: boolean maxLength?: number - availableShareTarget: Array - currentShareTarget: Array + availableShareTarget: Array + currentShareTarget: Array currentIdentity: Profile | null postContent: TypedMessage postBoxButtonDisabled: boolean @@ -86,11 +86,10 @@ export interface PostDialogUIProps extends withClasses { onCloseButtonClicked: () => void onSetSelected: SelectRecipientsUIProps['onSetSelected'] DialogProps?: Partial - SelectRecipientsUIProps?: Partial } export function PostDialogUI(props: PostDialogUIProps) { - const classes = useStylesExtends(useStyles(), props) + const classes = useStyles() const { t } = useI18N() const isDebug = useValueRef(debugModeSetting) const [showPostMetadata, setShowPostMetadata] = useState(false) @@ -154,8 +153,7 @@ export function PostDialogUI(props: PostDialogUIProps) { + onSetSelected={props.onSetSelected}> , 'open' open?: [boolean, (next: boolean) => void] reason?: 'timeline' | 'popup' identities?: Profile[] - onRequestPost?: (target: (Profile | Group)[], content: TypedMessage) => void + onRequestPost?: (target: Profile[], content: TypedMessage) => void onRequestReset?: () => void typedMessageMetadata?: ReadonlyMap } @@ -258,7 +256,7 @@ export function PostDialog({ reason: props_reason = 'timeline', ...props }: Post const people = useFriendsList() const availableShareTarget = props.availableShareTarget || people const currentIdentity = or(props.currentIdentity, useCurrentIdentity()) - const [currentShareTarget, setCurrentShareTarget] = useState<(Profile | Group)[]>(() => []) + const [currentShareTarget, setCurrentShareTarget] = useState(() => []) //#endregion //#region Image Based Payload Switch const imagePayloadStatus = useValueRef(currentImagePayloadStatus[activatedSocialNetworkUI.networkIdentifier]) @@ -274,7 +272,7 @@ export function PostDialog({ reason: props_reason = 'timeline', ...props }: Post const onRequestPost = or( props.onRequestPost, useCallback( - async (target: (Profile | Group)[], content: TypedMessage) => { + async (target: Profile[], content: TypedMessage) => { const [encrypted, token] = await Services.Crypto.encryptTo( content, target.map((x) => x.identifier), diff --git a/packages/maskbook/src/components/InjectedComponents/SelectPeopleDialog.tsx b/packages/maskbook/src/components/InjectedComponents/SelectPeopleDialog.tsx index 87cfd97250f2..0c3d247b86a7 100644 --- a/packages/maskbook/src/components/InjectedComponents/SelectPeopleDialog.tsx +++ b/packages/maskbook/src/components/InjectedComponents/SelectPeopleDialog.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from 'react' import { makeStyles } from '@material-ui/core/styles' import { Button, CircularProgress, DialogActions, DialogContent } from '@material-ui/core' import { useI18N } from '../../utils' -import { SelectProfileAndGroupsUI, SelectProfileAndGroupsUIProps } from '../shared/SelectPeopleAndGroups' +import { SelectProfileUI } from '../shared/SelectProfileUI' import type { Profile } from '../../database' import { useStylesExtends } from '../custom-ui-helper' import { InjectedDialog } from '../shared/InjectedDialog' @@ -13,7 +13,6 @@ export interface SelectProfileDialogProps extends withClasses { alreadySelectedPreviously: Profile[] onClose: () => void onSelect: (people: Profile[]) => Promise - SelectPeopleAndGroupsUIProps?: SelectProfileAndGroupsUIProps } const useStyles = makeStyles({ title: { paddingBottom: 0 }, @@ -41,13 +40,12 @@ export function SelectProfileDialog(props: SelectProfileDialogProps) { return ( - + {rejection && ( diff --git a/packages/maskbook/src/components/Welcomes/Banner.tsx b/packages/maskbook/src/components/Welcomes/Banner.tsx index 6c9bab2fe622..38464f3d4bcf 100644 --- a/packages/maskbook/src/components/Welcomes/Banner.tsx +++ b/packages/maskbook/src/components/Welcomes/Banner.tsx @@ -4,7 +4,6 @@ import { Theme, IconButton } from '@material-ui/core' import { useLastRecognizedIdentity, useMyIdentities } from '../DataSource/useActivatedUI' import Services from '../../extension/service' import { activatedSocialNetworkUI } from '../../social-network' -import { setStorage } from '../../utils/browser.storage' import { useStylesExtends } from '../custom-ui-helper' import { DashboardRoute } from '../../extension/options-page/Route' import { MaskbookSharpIcon } from '../../resources/MaskbookIcon' @@ -59,7 +58,6 @@ export function Banner(props: BannerProps) { nextStep ?? console.warn('You must provide one of networkIdentifier or nextStep.onClick') return } - setStorage(networkIdentifier, { forceDisplayWelcome: false }) Services.Welcome.openOptionsPage(DashboardRoute.Setup) }, [networkIdentifier, nextStep]) const defaultUserName = networkIdentifier diff --git a/packages/maskbook/src/components/shared/ChooseIdentity.tsx b/packages/maskbook/src/components/shared/ChooseIdentity.tsx index bd6c74f9fbfe..fd4b68ce5c1b 100644 --- a/packages/maskbook/src/components/shared/ChooseIdentity.tsx +++ b/packages/maskbook/src/components/shared/ChooseIdentity.tsx @@ -3,9 +3,9 @@ import { makeStyles } from '@material-ui/core/styles' import ExpandMoreIcon from '@material-ui/icons/ExpandMore' import type { Profile } from '../../database' import { List, Accordion, AccordionSummary } from '@material-ui/core' -import { ProfileOrGroupInList, ProfileOrGroupInListProps } from './SelectPeopleAndGroups' +import { ProfileInList } from './SelectProfileUI' import { activatedSocialNetworkUI } from '../../social-network' -import { useCurrentIdentity, useMyIdentities } from '../DataSource/useActivatedUI' +import { useCurrentIdentity } from '../DataSource/useActivatedUI' import { ProfileIdentifier } from '../../database/type' import { currentSelectedIdentity } from '../../settings/settings' import { useStylesExtends } from '../custom-ui-helper' @@ -55,20 +55,10 @@ const useAccordionSummaryStyle = makeStyles({ }) export interface ChooseIdentityProps extends withClasses { - /** - * Current selected identity - * @defaultValue the global selected identity - */ - current?: Profile /** All available identities * @defaultValue `useMyIdentities()` */ identities: readonly Profile[] - /** When user change the identity - * @defaultValue will change the global selected identity - */ - onChangeIdentity?(person: Profile): void - PersonOrGroupInListProps?: ProfileOrGroupInListProps } /** * Choose the current using identity. @@ -93,25 +83,24 @@ export function ChooseIdentity(props: ChooseIdentityProps) { 1 ? : null}> - {identities.length ? ( - {identities.map((person) => - person.identifier.equals(current.identifier) ? null : ( - + profile.identifier.equals(current.identifier) ? null : ( + { setExpanded(false) - currentSelectedIdentity[ui.networkIdentifier].value = person.identifier.toText() + currentSelectedIdentity[ui.networkIdentifier].value = + profile.identifier.toText() }} - {...props.PersonOrGroupInListProps} /> ), )} @@ -121,18 +110,3 @@ export function ChooseIdentity(props: ChooseIdentityProps) { ) } - -/** - * This hook allows use in a isolated scope without providing - * verbose information. - */ -export function useIsolatedChooseIdentity(): readonly [Profile | null, React.ReactNode] { - const all = useMyIdentities() - const whoami = useCurrentIdentity() - const [current, setCurrent] = useState() - const selected = current || whoami || undefined - return [ - selected || null, - , - ] as const -} diff --git a/packages/maskbook/src/components/shared/NotSetupYetPrompt.tsx b/packages/maskbook/src/components/shared/NotSetupYetPrompt.tsx index 2d48471094ac..f536e4e93935 100644 --- a/packages/maskbook/src/components/shared/NotSetupYetPrompt.tsx +++ b/packages/maskbook/src/components/shared/NotSetupYetPrompt.tsx @@ -1,4 +1 @@ -import type React from 'react' -import { Banner, BannerProps } from '../Welcomes/Banner' - -export const NotSetupYetPrompt = Banner as React.FC> +export { Banner as NotSetupYetPrompt } from '../Welcomes/Banner' diff --git a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/PersonOrGroupInChip.tsx b/packages/maskbook/src/components/shared/SelectPeopleAndGroups/PersonOrGroupInChip.tsx deleted file mode 100644 index 4e884db3eec1..000000000000 --- a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/PersonOrGroupInChip.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import MuiAvatar from '@material-ui/core/Avatar/Avatar' -import { Chip } from '@material-ui/core' -import type { ChipProps } from '@material-ui/core/Chip' -import { Avatar, useI18N } from '../../../utils' -import type { Profile, Group } from '../../../database' -import { isGroup, isPerson } from './SelectPeopleAndGroupsUI' -import { useResolveSpecialGroupName } from './resolveSpecialGroupName' - -export interface ProfileOrGroupInChipProps { - onDelete?(): void - disabled?: boolean - item: Profile | Group - ChipProps?: ChipProps -} -export function ProfileOrGroupInChip(props: ProfileOrGroupInChipProps) { - const { disabled, onDelete } = props - const { t } = useI18N() - let avatar: ReturnType | undefined = undefined - let displayName = '' - const groupName = useResolveSpecialGroupName(props.item) - if (isGroup(props.item)) { - const group = props.item - displayName = t('person_or_group_in_chip', { name: groupName, count: group.members.length }) - avatar = group.avatar ? : undefined - } else { - const person = props.item - displayName = person.nickname || person.identifier.userId - avatar = person.avatar ? : undefined - } - - return ( - - ) -} diff --git a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/PersonOrGroupInList.tsx b/packages/maskbook/src/components/shared/SelectPeopleAndGroups/PersonOrGroupInList.tsx deleted file mode 100644 index 84a9b92344e5..000000000000 --- a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/PersonOrGroupInList.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { memo, useMemo } from 'react' -import { ListItem, Theme, ListItemAvatar, ListItemText } from '@material-ui/core' -import type { DefaultComponentProps } from '@material-ui/core/OverridableComponent' -import type { ListItemTypeMap } from '@material-ui/core/ListItem' -import { makeStyles } from '@material-ui/core/styles' -import MuiAvatar from '@material-ui/core/Avatar/Avatar' -import GroupIcon from '@material-ui/icons/Group' -import { Avatar, useI18N, useIntlListFormat } from '../../../utils' -import { useFriendsList } from '../../DataSource/useActivatedUI' -import type { Profile, Group } from '../../../database' -import type { ProfileIdentifier } from '../../../database/type' -import { isGroup } from './SelectPeopleAndGroupsUI' -import { useResolveSpecialGroupName } from './resolveSpecialGroupName' -import { useStylesExtends } from '../../custom-ui-helper' - -export interface ProfileOrGroupInListProps extends withClasses { - item: Group | Profile - disabled?: boolean - showAtNetwork?: boolean - onClick?: () => void - ListItemProps?: Partial>> -} -const useStyle = makeStyles((theme: Theme) => ({ - // ? I want to let the children of this element have no change to - // ? extends the width of the parent element. - // ? Only `grid` or `inline-grid` works. but why?? - root: { display: 'inline-grid' }, - overflow: { - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - overflow: 'hidden', - }, - networkHint: { - color: theme.palette.grey[500], - }, -})) -/** - * Item in the list - */ -export const ProfileOrGroupInList = memo((props) => { - const classes = useStylesExtends(useStyle(), props) - - const { disabled, ListItemProps: listItemProps, onClick, showAtNetwork } = props - - if (isGroup(props.item)) - return ( - - - - ) - - const name = props.item.nickname || props.item.identifier.userId - - return ( - - - - - - {name} - @ {props.item.identifier.network} - - ) : ( - name - ) - } - secondary={props.item.linkedPersona?.fingerprint.toLowerCase()} - /> - - ) -}) - -function GroupListItemContent({ group, showAtNetwork }: { group: Group; showAtNetwork?: boolean }) { - const { t } = useI18N() - const classes = useStyle() - const nicknamePreviewsForGroup = useNickNamesFromList(group.members) - const listFormat = useIntlListFormat() - const groupName = useResolveSpecialGroupName(group) - - const joined = listFormat(nicknamePreviewsForGroup) - const groupSize = group.members.length - const data = { people: joined, count: groupSize } - - let secondaryText: string | undefined = undefined - if (groupSize === 0) { - secondaryText = t('person_or_group_in_list_0') - } else if (nicknamePreviewsForGroup.length === 0) { - secondaryText = t('person_or_group_in_list_many_no_preview', data) - } else if (groupSize > nicknamePreviewsForGroup.length) { - secondaryText = t('person_or_group_in_list_many', data) - } else { - secondaryText = joined - } - - return ( - <> - - - - - - - {groupName} - @ {group.identifier.network} - - ) : ( - groupName - ) - } - secondary={secondaryText} - /> - - ) -} - -function useNickNamesFromList(preview: readonly ProfileIdentifier[]) { - const profile = useFriendsList() - const userWithNames = useMemo(() => profile.filter((x) => x.nickname), [profile]) - - const [x, y, z] = preview - const [a] = useMemo(() => x && userWithNames.filter((w) => w.identifier.equals(x)), [userWithNames, x]) || [] - const [b] = useMemo(() => y && userWithNames.filter((w) => w.identifier.equals(y)), [userWithNames, y]) || [] - const [c] = useMemo(() => z && userWithNames.filter((w) => w.identifier.equals(z)), [userWithNames, z]) || [] - return useMemo(() => [a, b, c].filter((x) => x).map((x) => x.nickname!), [a, b, c]) -} diff --git a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/index.tsx b/packages/maskbook/src/components/shared/SelectPeopleAndGroups/index.tsx deleted file mode 100644 index d7dacbc46759..000000000000 --- a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export * from './SelectPeopleAndGroupsUI' -export * from './PersonOrGroupInList' -export * from './PersonOrGroupInChip' diff --git a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/resolveSpecialGroupName.ts b/packages/maskbook/src/components/shared/SelectPeopleAndGroups/resolveSpecialGroupName.ts deleted file mode 100644 index 781484ac8ee6..000000000000 --- a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/resolveSpecialGroupName.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useI18N, I18NFunction, I18NStrings } from '../../../utils' -import { PreDefinedVirtualGroupNames, GroupIdentifier } from '../../../database/type' -import type { Group, Profile } from '../../../database' -import { useFriendsList, useMyIdentities } from '../../DataSource/useActivatedUI' - -function resolveSpecialGroupName(t: I18NFunction, group: Group, knownPeople: Profile[]): string { - let owner: string = group.identifier.virtualGroupOwner || 'Unknown' - - for (const profile of knownPeople.filter((x) => x.identifier.equals(group.identifier.ownerIdentifier))) { - owner = profile.linkedPersona?.nickname || profile.nickname || owner - } - const data = { owner } - switch (group.groupName) { - case PreDefinedVirtualGroupNames.friends: - return t('database_group_friends_default_name', data) - case PreDefinedVirtualGroupNames.followers: - return t('database_group_followers_name', data) - case PreDefinedVirtualGroupNames.following: - return t('database_group_following_name', data) - default: - return t(group.groupName as keyof I18NStrings, data) - } -} - -export function useResolveSpecialGroupName(group: Group | Profile) { - const { t } = useI18N() - const x = useFriendsList() - const y = useMyIdentities() - if (!isGroup(group)) return '' - return resolveSpecialGroupName(t, group, [...x, ...y]) -} -function isGroup(group: Group | Profile): group is Group { - return group.identifier instanceof GroupIdentifier -} diff --git a/packages/maskbook/src/components/shared/SelectProfileUI/ProfileInChip.tsx b/packages/maskbook/src/components/shared/SelectProfileUI/ProfileInChip.tsx new file mode 100644 index 000000000000..08d66b925ca7 --- /dev/null +++ b/packages/maskbook/src/components/shared/SelectProfileUI/ProfileInChip.tsx @@ -0,0 +1,27 @@ +import { Chip } from '@material-ui/core' +import type { ChipProps } from '@material-ui/core/Chip' +import { Avatar } from '../../../utils' +import type { Profile } from '../../../database' + +export interface ProfileInChipProps { + onDelete?(): void + disabled?: boolean + item: Profile + ChipProps?: ChipProps +} +export function ProfileInChip(props: ProfileInChipProps) { + const { disabled, onDelete, item: profile } = props + const avatar = profile.avatar ? : undefined + const displayName = profile.nickname || profile.identifier.userId + + return ( + + ) +} diff --git a/packages/maskbook/src/components/shared/SelectProfileUI/ProfileInList.tsx b/packages/maskbook/src/components/shared/SelectProfileUI/ProfileInList.tsx new file mode 100644 index 000000000000..36c58b08625a --- /dev/null +++ b/packages/maskbook/src/components/shared/SelectProfileUI/ProfileInList.tsx @@ -0,0 +1,54 @@ +import { memo } from 'react' +import { ListItem, Theme, ListItemAvatar, ListItemText } from '@material-ui/core' +import type { DefaultComponentProps } from '@material-ui/core/OverridableComponent' +import type { ListItemTypeMap } from '@material-ui/core/ListItem' +import { makeStyles } from '@material-ui/core/styles' +import { Avatar } from '../../../utils' +import type { Profile } from '../../../database' + +export interface ProfileInListProps { + item: Profile + disabled?: boolean + onClick?: () => void + ListItemProps?: Partial>> +} +const useStyle = makeStyles((theme: Theme) => ({ + // ? I want to let the children of this element have no change to + // ? extends the width of the parent element. + // ? Only `grid` or `inline-grid` works. but why?? + root: { display: 'inline-grid' }, + overflow: { + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + }, + networkHint: { + color: theme.palette.grey[500], + }, +})) +/** + * Item in the list + */ +export const ProfileInList = memo((props) => { + const classes = useStyle() + + const { disabled, ListItemProps: listItemProps, onClick } = props + const name = props.item.nickname || props.item.identifier.userId + + return ( + + + + + + + ) +}) diff --git a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/SelectPeopleAndGroupsUI.tsx b/packages/maskbook/src/components/shared/SelectProfileUI/SelectProfileUI.tsx similarity index 50% rename from packages/maskbook/src/components/shared/SelectPeopleAndGroups/SelectPeopleAndGroupsUI.tsx rename to packages/maskbook/src/components/shared/SelectProfileUI/SelectProfileUI.tsx index a6a6ceedb17d..60be6928e440 100644 --- a/packages/maskbook/src/components/shared/SelectPeopleAndGroups/SelectPeopleAndGroupsUI.tsx +++ b/packages/maskbook/src/components/shared/SelectProfileUI/SelectProfileUI.tsx @@ -1,30 +1,18 @@ -import { useEffect, useState, useCallback, CSSProperties } from 'react' +import { useState, useCallback, CSSProperties } from 'react' import { makeStyles, ListItem, ListItemText, InputBase, Button, List, Box, Chip } from '@material-ui/core' import { useI18N } from '../../../utils' -import type { Profile, Group } from '../../../database' -import { useCurrentIdentity } from '../../DataSource/useActivatedUI' -import { ProfileOrGroupInList, ProfileOrGroupInListProps } from './PersonOrGroupInList' -import { ProfileOrGroupInChip, ProfileOrGroupInChipProps } from './PersonOrGroupInChip' -import { ProfileIdentifier, GroupIdentifier } from '../../../database/type' +import type { Profile } from '../../../database' +import { ProfileInList } from './ProfileInList' +import { ProfileInChip } from './ProfileInChip' import { useStylesExtends } from '../../custom-ui-helper' import { FixedSizeList } from 'react-window' -type ProfileOrGroup = Group | Profile -export interface SelectProfileAndGroupsUIProps - extends withClasses { - /** Omit myself in the UI and the selected result */ - ignoreMyself?: boolean - items: ServeType[] - selected: ServeType[] - frozenSelected: ServeType[] - onSetSelected(selected: ServeType[]): void +export interface SelectProfileUIProps extends withClasses<'root'> { + items: Profile[] + selected: Profile[] + frozenSelected: Profile[] + onSetSelected(selected: Profile[]): void disabled?: boolean - hideSelectAll?: boolean - hideSelectNone?: boolean - showAtNetwork?: boolean - maxSelection?: number - ProfileOrGroupInChipProps?: Partial - ProfileOrGroupInListProps?: Partial } const useStyles = makeStyles({ selectedArea: { @@ -36,48 +24,32 @@ const useStyles = makeStyles({ input: { flex: 1, minWidth: '10em' }, buttons: { marginLeft: 8, padding: '2px 6px' }, }) -export function SelectProfileAndGroupsUI( - props: SelectProfileAndGroupsUIProps, -) { +export function SelectProfileUI(props: SelectProfileUIProps) { const { t } = useI18N() const classes = useStylesExtends(useStyles(), props) - const myself = useCurrentIdentity() - const items: ProfileOrGroup[] = props.items - const selected: ProfileOrGroup[] = props.selected - const { frozenSelected, onSetSelected, disabled, ignoreMyself } = props - const { hideSelectAll, hideSelectNone, showAtNetwork, maxSelection } = props - - useEffect(() => { - if (myself && ignoreMyself) { - const filtered = selected.find((x) => x.identifier.equals(myself.identifier)) - if (filtered) onSetSelected(selected.filter((x) => x !== filtered) as ServeType[]) - } - }, [ignoreMyself, myself, onSetSelected, selected]) + const items: Profile[] = props.items + const selected: Profile[] = props.selected + const { frozenSelected, onSetSelected, disabled } = props const [search, setSearch] = useState('') const listBeforeSearch = items.filter((x) => { - if (ignoreMyself && myself && x.identifier.equals(myself.identifier)) return false if (selected.find((y) => x.identifier.equals(y.identifier))) return false return true }) const listAfterSearch = listBeforeSearch.filter((x) => { if (frozenSelected && frozenSelected.find((y) => x.identifier.equals(y.identifier))) return false if (search === '') return true - if (isPerson(x)) { - return ( - !!x.identifier.userId.toLowerCase().match(search.toLowerCase()) || - !!x.linkedPersona?.fingerprint.toLowerCase().match(search.toLowerCase()) || - !!(x.nickname || '').toLowerCase().match(search.toLowerCase()) - ) - } else { - return x.groupName.toLowerCase().match(search.toLowerCase()) - } + return ( + !!x.identifier.userId.toLowerCase().match(search.toLowerCase()) || + !!x.linkedPersona?.fingerprint.toLowerCase().match(search.toLowerCase()) || + !!(x.nickname || '').toLowerCase().match(search.toLowerCase()) + ) }) const SelectAllButton = ( ) @@ -86,8 +58,8 @@ export function SelectProfileAndGroupsUI ) - const showSelectAll = !hideSelectAll && listAfterSearch.length > 0 && typeof maxSelection === 'undefined' - const showSelectNone = !hideSelectNone && selected.length > 0 + const showSelectAll = listAfterSearch.length > 0 + const showSelectNone = selected.length > 0 return (
@@ -106,22 +78,19 @@ export function SelectProfileAndGroupsUI ) : ( <> - {frozenSelected.map((x) => FrozenChip(x, props.ProfileOrGroupInChipProps))} + {frozenSelected.map((x) => FrozenChip(x))} {selected - .filter((item) => !frozenSelected.includes(item as ServeType)) + .filter((item) => !frozenSelected.includes(item as Profile)) .map((item) => ( - onSetSelected( - selected.filter( - (x) => !x.identifier.equals(item.identifier), - ) as ServeType[], + selected.filter((x) => !x.identifier.equals(item.identifier)) as Profile[], ) } - {...props.ProfileOrGroupInChipProps} /> ))} @@ -132,7 +101,7 @@ export function SelectProfileAndGroupsUI setSearch(e.target.value), [])} onKeyDown={(e) => { if (search === '' && e.key === 'Backspace') { - onSetSelected(selected.slice(0, selected.length - 1) as ServeType[]) + onSetSelected(selected.slice(0, selected.length - 1) as Profile[]) } }} placeholder={disabled ? '' : t('search_box_placeholder')} @@ -163,47 +132,31 @@ export function SelectProfileAndGroupsUI {({ index, style }) => - listAfterSearch[index] ? PeopleListItem(listAfterSearch[index], style) : null + listAfterSearch[index] ? ProfileListItem(listAfterSearch[index], style) : null }
) - function PeopleListItem(item: ProfileOrGroup, style: CSSProperties) { - if (ignoreMyself && myself && item.identifier.equals(myself.identifier)) return null + function ProfileListItem(item: Profile, style: CSSProperties) { return ( - = 2 && - frozenSelected.length + selected.length >= maxSelection) - } + disabled={disabled} onClick={() => { - if (maxSelection === 1) onSetSelected([item as ServeType]) - else onSetSelected(selected.concat(item) as ServeType[]) + onSetSelected(selected.concat(item) as Profile[]) setSearch('') }} - ListItemProps={{ ...props.ProfileOrGroupInListProps?.ListItemProps, style }} - {...props.ProfileOrGroupInListProps} + ListItemProps={{ style }} /> ) } } -SelectProfileAndGroupsUI.defaultProps = { +SelectProfileUI.defaultProps = { frozenSelected: [], } -function FrozenChip(item: ProfileOrGroup, additionalProps?: Partial) { - return -} - -export function isPerson(x: ProfileOrGroup): x is Profile { - return x.identifier instanceof ProfileIdentifier -} -export function isGroup(x: ProfileOrGroup): x is Group { - return x.identifier instanceof GroupIdentifier +function FrozenChip(item: Profile) { + return } diff --git a/packages/maskbook/src/components/shared/SelectProfileUI/index.tsx b/packages/maskbook/src/components/shared/SelectProfileUI/index.tsx new file mode 100644 index 000000000000..d261112c0b18 --- /dev/null +++ b/packages/maskbook/src/components/shared/SelectProfileUI/index.tsx @@ -0,0 +1,3 @@ +export * from './SelectProfileUI' +export * from './ProfileInList' +export * from './ProfileInChip' diff --git a/packages/maskbook/src/components/shared/SelectRecipients/GroupInChip.tsx b/packages/maskbook/src/components/shared/SelectRecipients/GroupInChip.tsx deleted file mode 100644 index c00f1452bf79..000000000000 --- a/packages/maskbook/src/components/shared/SelectRecipients/GroupInChip.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import classNames from 'classnames' -import Chip, { ChipProps } from '@material-ui/core/Chip' -import type { Group } from '../../../database' -import DoneIcon from '@material-ui/icons/Done' -import { useResolveSpecialGroupName } from '../SelectPeopleAndGroups/resolveSpecialGroupName' -import { makeStyles } from '@material-ui/core/styles' -import { useCallback, ChangeEvent } from 'react' - -export interface GroupInChipProps { - item: Group - checked?: boolean - disabled?: boolean - onChange?(ev: ChangeEvent, checked: boolean): void - ChipProps?: ChipProps -} - -const useStyles = makeStyles({ - root: { - marginRight: 6, - marginBottom: 6, - cursor: 'pointer', - }, - icon: { - backgroundColor: 'transparent !important', - }, -}) - -export function GroupInChip(props: GroupInChipProps) { - const classes = useStyles() - const onClick = useCallback( - (ev) => { - if (props.onChange) { - props.onChange(ev, !props.checked) - } - }, - [props], - ) - - return ( - : undefined} - color={props.checked ? 'primary' : 'default'} - disabled={props.disabled ?? false} - onClick={onClick} - label={useResolveSpecialGroupName(props.item)} - {...props.ChipProps} - className={classNames(classes.root, props.ChipProps?.className)} - data-testid={props.item.groupName} - /> - ) -} diff --git a/packages/maskbook/src/components/shared/SelectRecipients/SelectRecipients.tsx b/packages/maskbook/src/components/shared/SelectRecipients/SelectRecipients.tsx index a4b3c55cbcd6..bb15b80539cd 100644 --- a/packages/maskbook/src/components/shared/SelectRecipients/SelectRecipients.tsx +++ b/packages/maskbook/src/components/shared/SelectRecipients/SelectRecipients.tsx @@ -1,15 +1,11 @@ import { makeStyles, Box, Chip } from '@material-ui/core' import AddIcon from '@material-ui/icons/Add' -import { useMemo, useState } from 'react' +import { useState } from 'react' import { difference } from 'lodash-es' import { useI18N } from '../../../utils' -import type { ProfileOrGroupInListProps } from '../SelectPeopleAndGroups' -import { GroupInChipProps, GroupInChip } from './GroupInChip' -import { ProfileIdentifier, GroupIdentifier } from '../../../database/type' -import type { Group, Profile } from '../../../database' -import { SelectRecipientsDialogUIProps, SelectRecipientsDialogUI } from './SelectRecipientsDialog' +import type { Profile } from '../../../database' +import { SelectRecipientsDialogUI } from './SelectRecipientsDialog' import { useCurrentIdentity } from '../../DataSource/useActivatedUI' -import { useStylesExtends } from '../../custom-ui-helper' const useStyles = makeStyles({ root: { @@ -18,58 +14,33 @@ const useStyles = makeStyles({ }, }) -export interface SelectRecipientsUIProps extends withClasses { - items: T[] - selected: T[] - frozenSelected: T[] +export interface SelectRecipientsUIProps { + items: Profile[] + selected: Profile[] + frozenSelected: Profile[] disabled?: boolean hideSelectAll?: boolean hideSelectNone?: boolean - showAtNetwork?: boolean - onSetSelected(selected: T[]): void - GroupInChipProps?: Partial - PersonOrGroupInListProps?: Partial - SelectRecipientsDialogUIProps?: Partial + onSetSelected(selected: Profile[]): void children?: React.ReactNode } -export function SelectRecipientsUI(props: SelectRecipientsUIProps) { +export function SelectRecipientsUI(props: SelectRecipientsUIProps) { const { t } = useI18N() - const classes = useStylesExtends(useStyles(), props) + const classes = useStyles() const { items, selected, onSetSelected, children } = props const currentIdentity = useCurrentIdentity() - const groupItems = items.filter((x) => isGroup(x)) as Group[] const profileItems = items.filter( - (x) => isProfile(x) && !x.identifier.equals(currentIdentity?.identifier) && x.linkedPersona?.fingerprint, - ) as Profile[] - const [open, setOpen] = useState(false) - const selectedProfiles = selected.filter((x) => isProfile(x)) as Profile[] - const selectedGroups = selected.filter((x) => isGroup(x)) as Group[] - const selectedGroupMembers = useMemo( - () => selectedGroups.flatMap((group) => group.members).map((identifier) => identifier.toText()), - [selectedGroups], + (x) => !x.identifier.equals(currentIdentity?.identifier) && x.linkedPersona?.fingerprint, ) + const [open, setOpen] = useState(false) return ( - {groupItems.map((item) => ( - x.identifier.equals(item.identifier))} - disabled={props.disabled} - onChange={(_, checked) => { - if (checked) onSetSelected([...selected, item]) - else onSetSelected(difference(selected, [item])) - }} - {...props.GroupInChipProps} - /> - ))} {children} x.identifier.toText())]) - .size, + selected: new Set([...selected.map((x) => x.identifier.toText())]).size, })} avatar={} disabled={props.disabled || profileItems.length === 0} @@ -79,14 +50,12 @@ export function SelectRecipientsUI( open={open} items={profileItems} selected={profileItems.filter((x) => selected.includes(x))} - disabledItems={profileItems.filter((x) => selectedGroupMembers.includes(x.identifier.toText()))} disabled={false} submitDisabled={false} onSubmit={() => setOpen(false)} onClose={() => setOpen(false)} onSelect={(item) => onSetSelected([...selected, item])} onDeselect={(item) => onSetSelected(difference(selected, [item]))} - {...props.SelectRecipientsDialogUIProps} /> ) @@ -95,10 +64,3 @@ export function SelectRecipientsUI( SelectRecipientsUI.defaultProps = { frozenSelected: [], } - -export function isProfile(x: Profile | Group): x is Profile { - return x.identifier instanceof ProfileIdentifier -} -export function isGroup(x: Profile | Group): x is Group { - return x.identifier instanceof GroupIdentifier -} diff --git a/packages/maskbook/src/content-script.ts b/packages/maskbook/src/content-script.ts index bf337b1956c2..269a1802014b 100644 --- a/packages/maskbook/src/content-script.ts +++ b/packages/maskbook/src/content-script.ts @@ -1,10 +1,6 @@ import './extension/content-script/hmr' import Services from './extension/service' -import { status } from './setup.ui' - -status.then((loaded) => { - loaded && import('./extension/content-script/tasks') -}) +import './setup.ui' // The scope should be the ./ of the web page Services.ThirdPartyPlugin.isSDKEnabled(new URL('./', location.href).href).then((result) => { diff --git a/packages/maskbook/src/database/__tests__/avatar.ts b/packages/maskbook/src/database/__tests__/avatar.ts index d72b4d0d3272..79c08308195d 100644 --- a/packages/maskbook/src/database/__tests__/avatar.ts +++ b/packages/maskbook/src/database/__tests__/avatar.ts @@ -7,7 +7,7 @@ import { isAvatarOutdatedDB, deleteAvatarsDB, } from '../avatar' -import { ProfileIdentifier, GroupIdentifier } from '../type' +import { ProfileIdentifier } from '../type' function createBeforeDate(beforeDays: number) { return new Date(Date.now() - 1000 * 60 * 60 * 24 * beforeDays) @@ -17,10 +17,6 @@ function createProfileIdentifier() { return new ProfileIdentifier(uuid(), uuid()) } -function createGroupIdentifier() { - return new GroupIdentifier(uuid(), uuid(), uuid()) -} - function createArrayBuffer(length: number) { return new Uint8Array(new Array(length).fill(0).map(() => Math.round(Math.random() * 256))).buffer } @@ -28,13 +24,9 @@ function createArrayBuffer(length: number) { test('storeAvatarDB & queryAvatarDB', async () => { const ab = createArrayBuffer(20) const profileIdentifier = createProfileIdentifier() - const groupIdentifier = createGroupIdentifier() await storeAvatarDB(profileIdentifier, ab) expect(await queryAvatarDB(profileIdentifier)).toEqual(ab) - - await storeAvatarDB(groupIdentifier, ab) - expect(await queryAvatarDB(groupIdentifier)).toEqual(ab) }) test('updateAvatarMetaDB & queryAvatarOutdatedDB', async () => { diff --git a/packages/maskbook/src/database/__tests__/group.ts b/packages/maskbook/src/database/__tests__/group.ts deleted file mode 100644 index c8be263e689b..000000000000 --- a/packages/maskbook/src/database/__tests__/group.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { v4 as uuid } from 'uuid' -import { - GroupRecord, - createUserGroupDatabase, - queryUserGroupDatabase, - queryUserGroupsDatabase, - createOrUpdateUserGroupDatabase, - deleteUserGroupDatabase, - updateUserGroupDatabase, -} from '../group' -import { GroupIdentifier } from '../type' - -function createGroupRecord() { - const identifier = new GroupIdentifier(uuid(), uuid(), uuid()) - return { - groupName: uuid(), - identifier, - members: [], - banned: undefined, - } as GroupRecord -} - -function groupRecordToDB(groupRecord: GroupRecord) { - return { - ...groupRecord, - network: groupRecord.identifier.network, - } -} - -test('createUserGroupDatabase & queryUserGroupDatabase', async () => { - const groupRecord = createGroupRecord() - await createUserGroupDatabase(groupRecord.identifier, groupRecord.groupName) - expect(await queryUserGroupDatabase(groupRecord.identifier)).toEqual(groupRecordToDB(groupRecord)) -}) - -test('createOrUpdateUserGroupDatabase', async () => { - const groupRecord = createGroupRecord() - const groupRecordNew = createGroupRecord() - groupRecordNew.identifier = groupRecord.identifier - - expect(await queryUserGroupDatabase(groupRecord.identifier)).toBe(null) - - await createOrUpdateUserGroupDatabase(groupRecord, 'replace') - expect(await queryUserGroupDatabase(groupRecord.identifier)).toEqual(groupRecordToDB(groupRecord)) - - await createOrUpdateUserGroupDatabase(groupRecordNew, 'replace') - expect(await queryUserGroupDatabase(groupRecord.identifier)).toEqual(groupRecordToDB(groupRecordNew)) -}) - -test('deleteUserGroupDatabase', async () => { - const groupRecord = createGroupRecord() - expect(await queryUserGroupDatabase(groupRecord.identifier)).toBe(null) - - await createUserGroupDatabase(groupRecord.identifier, groupRecord.groupName) - expect(await queryUserGroupDatabase(groupRecord.identifier)).toEqual(groupRecordToDB(groupRecord)) - - await deleteUserGroupDatabase(groupRecord.identifier) - expect(await queryUserGroupDatabase(groupRecord.identifier)).toBe(null) -}) - -test('updateUserGroupDatabase', async () => { - const groupRecord = createGroupRecord() - const groupRecordNew = createGroupRecord() - groupRecordNew.identifier = groupRecord.identifier - - await createUserGroupDatabase(groupRecord.identifier, groupRecord.groupName) - expect(await queryUserGroupDatabase(groupRecord.identifier)).toEqual(groupRecordToDB(groupRecord)) - - await updateUserGroupDatabase(groupRecordNew, 'replace') - expect(await queryUserGroupDatabase(groupRecord.identifier)).toEqual(groupRecordToDB(groupRecordNew)) -}) - -test('queryUserGroupsDatabase', async () => { - const groupRecordA = createGroupRecord() - const groupRecordB = createGroupRecord() - const networks = [groupRecordA.identifier.network, groupRecordB.identifier.network] - await createUserGroupDatabase(groupRecordA.identifier, groupRecordA.groupName) - await createUserGroupDatabase(groupRecordB.identifier, groupRecordB.groupName) - expect(await queryUserGroupsDatabase({ network: groupRecordA.identifier.network })).toEqual([ - groupRecordToDB(groupRecordA), - ]) - expect(await queryUserGroupsDatabase({ network: groupRecordB.identifier.network })).toEqual([ - groupRecordToDB(groupRecordB), - ]) - expect(await queryUserGroupsDatabase((key) => networks.includes(key.network))).toBeTruthy() -}) diff --git a/packages/maskbook/src/database/__tests__/helpers/group.ts b/packages/maskbook/src/database/__tests__/helpers/group.ts deleted file mode 100644 index 3424f9833aac..000000000000 --- a/packages/maskbook/src/database/__tests__/helpers/group.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { v4 as uuid } from 'uuid' -import { ProfileIdentifier, GroupIdentifier, PreDefinedVirtualGroupNames } from '../../type' -import { - createFriendsGroup, - queryUserGroups, - addProfileToFriendsGroup, - removeProfileFromFriendsGroup, -} from '../../helpers/group' -import { queryUserGroupDatabase } from '../../group' - -function createProfileIdentifier(network = uuid(), userId = uuid()) { - return new ProfileIdentifier(network, userId) -} - -function createGroupIdentifier(network = uuid(), owner = uuid(), groupId = uuid()) { - return new GroupIdentifier(network, owner, groupId) -} - -test('createFriendsGroup', async () => { - const groupId = uuid() - const profileIdentifier = createProfileIdentifier() - const groupIdentifier = createGroupIdentifier(profileIdentifier.network, profileIdentifier.userId, groupId) - await createFriendsGroup(profileIdentifier, groupId) - expect(await queryUserGroupDatabase(groupIdentifier)).toBeTruthy() -}) - -test('createDefaultFriendsGroup', async () => { - const groupId = PreDefinedVirtualGroupNames.friends - const profileIdentifier = createProfileIdentifier() - const groupIdentifier = createGroupIdentifier(profileIdentifier.network, profileIdentifier.userId, groupId) - await createFriendsGroup(profileIdentifier, groupId) - expect(await queryUserGroupDatabase(groupIdentifier)).toBeTruthy() -}) - -test('addProfileToFriendsGroup & removeProfileFromFriendsGroup', async () => { - const groupId = uuid() - const ownerIdentifier = createProfileIdentifier() - const profileIdentifierA = createProfileIdentifier() - const profileIdentifierB = createProfileIdentifier() - const groupIdentifier = createGroupIdentifier(ownerIdentifier.network, ownerIdentifier.userId, groupId) - - await createFriendsGroup(ownerIdentifier, groupId) - expect((await queryUserGroupDatabase(groupIdentifier))?.members.length).toBe(0) - - await addProfileToFriendsGroup(groupIdentifier, [profileIdentifierA, profileIdentifierB]) - expect((await queryUserGroupDatabase(groupIdentifier))?.members.length).toBe(2) - - await removeProfileFromFriendsGroup(groupIdentifier, [profileIdentifierA, profileIdentifierB]) - expect((await queryUserGroupDatabase(groupIdentifier))?.members.length).toBe(0) -}) - -test('queryUserGroups', async () => { - const groupId = uuid() - const profileIdentifier = createProfileIdentifier() - await createFriendsGroup(profileIdentifier, groupId) - expect((await queryUserGroups(profileIdentifier.network)).length).toBe(1) -}) diff --git a/packages/maskbook/src/database/__tests__/types.ts b/packages/maskbook/src/database/__tests__/types.ts index 124e600a136a..a1b5d63f8d1a 100644 --- a/packages/maskbook/src/database/__tests__/types.ts +++ b/packages/maskbook/src/database/__tests__/types.ts @@ -5,7 +5,6 @@ import { PostIVIdentifier, PostIdentifier, ProfileIdentifier, - PreDefinedVirtualGroupNames, ECKeyIdentifierFromJsonWebKey, } from '../type' import type { EC_Public_JsonWebKey } from '../../modules/CryptoAlgorithm/interfaces/utils' @@ -64,8 +63,8 @@ test('ECKeyIdentifier', async () => { }) test('GroupIdentifier', () => { - const virtualG = new GroupIdentifier('facebook.com', 'owner', PreDefinedVirtualGroupNames.friends) - const realG = new GroupIdentifier('facebook.com', null, PreDefinedVirtualGroupNames.followers) + const virtualG = new GroupIdentifier('facebook.com', 'owner', '_default_friends_group_') + const realG = new GroupIdentifier('facebook.com', null, '_followers_group_') expect(virtualG.toText()).toBe('group:facebook.com/owner/_default_friends_group_') expect(virtualG.groupID).toBe('_default_friends_group_') @@ -86,12 +85,6 @@ test('GroupIdentifier', () => { expect(() => new GroupIdentifier('facebook.com', 'owner', '/')).toThrowError() }) -test('Stable PreDefinedVirtualGroupNames', () => { - expect(PreDefinedVirtualGroupNames.followers).toBe('_followers_group_') - expect(PreDefinedVirtualGroupNames.following).toBe('_following_group_') - expect(PreDefinedVirtualGroupNames.friends).toBe('_default_friends_group_') -}) - test('Static Identifier method', () => { const normal = new ProfileIdentifier('facebook.com', 'user_id') const normal2 = new ProfileIdentifier('facebook.com', 'user_id2') @@ -106,8 +99,8 @@ test('Static Identifier method', () => { ) expect(Identifier.IdentifiersToString([normal, normal2])).toBe(Identifier.IdentifiersToString([normal2, normal])) - const virtualG = new GroupIdentifier('facebook.com', 'owner', PreDefinedVirtualGroupNames.friends) - const realG = new GroupIdentifier('facebook.com', null, PreDefinedVirtualGroupNames.followers) + const virtualG = new GroupIdentifier('facebook.com', 'owner', '_default_friends_group_') + const realG = new GroupIdentifier('facebook.com', null, '_followers_group_') expect(virtualG.equals(realG)).toBe(false) expect(virtualG.equals(virtualG)).toBe(true) diff --git a/packages/maskbook/src/database/avatar.ts b/packages/maskbook/src/database/avatar.ts index 41b5d0098503..7e0c50498045 100644 --- a/packages/maskbook/src/database/avatar.ts +++ b/packages/maskbook/src/database/avatar.ts @@ -1,10 +1,10 @@ /// import { openDB, DBSchema } from 'idb/with-async-ittr-cjs' -import { Identifier, ProfileIdentifier, GroupIdentifier } from './type' +import { Identifier, ProfileIdentifier } from './type' import { createDBAccess, IDBPSafeTransaction, createTransaction } from './helpers/openDB' //#region Schema -type IdentityWithAvatar = ProfileIdentifier | GroupIdentifier +type IdentityWithAvatar = ProfileIdentifier export type AvatarRecord = ArrayBuffer interface AvatarMetadataRecord { identifier: string @@ -29,8 +29,8 @@ const db = createDBAccess(() => { return openDB('maskbook-avatar-cache', 1, { upgrade(db, oldVersion, newVersion, transaction) { // Out line keys - const avatarStore = db.createObjectStore('avatars') - const metadataStore = db.createObjectStore('metadata', { keyPath: 'identifier' }) + db.createObjectStore('avatars') + db.createObjectStore('metadata', { keyPath: 'identifier' }) }, }) }) @@ -102,7 +102,7 @@ export async function queryAvatarOutdatedDB( * defaults to 7 days for lastUpdateTime */ export async function isAvatarOutdatedDB( - identifier: ProfileIdentifier | GroupIdentifier, + identifier: ProfileIdentifier, attribute: 'lastUpdateTime' | 'lastAccessTime', deadline: Date = new Date(Date.now() - 1000 * 60 * 60 * 24 * (attribute === 'lastAccessTime' ? 30 : 7)), ): Promise { diff --git a/packages/maskbook/src/database/group.ts b/packages/maskbook/src/database/group.ts deleted file mode 100644 index 64702c4f082b..000000000000 --- a/packages/maskbook/src/database/group.ts +++ /dev/null @@ -1,201 +0,0 @@ -/// -import { DBSchema, openDB, IDBPTransaction } from 'idb/with-async-ittr-cjs' -import { GroupIdentifier, Identifier, ProfileIdentifier } from './type' -import { MaskMessage } from '../utils/messages' -import { PrototypeLess, restorePrototypeArray } from '../utils/type' -import { createDBAccess } from './helpers/openDB' -import type { Ok } from 'ts-results' - -//#region Schema -interface GroupRecordBase { - groupName: string -} -interface GroupRecordInDatabase extends GroupRecordBase { - /** Index */ - network: string - identifier: string - members: PrototypeLess[] - /** - * Ban list of this group. - * Only used for virtual group currently - * - * Used to remember if user clicks - * > they is not my friend, don't add them to my auto-share list again! - */ - banned?: PrototypeLess[] -} -export interface GroupRecord extends GroupRecordBase { - identifier: GroupIdentifier - members: ProfileIdentifier[] - banned?: ProfileIdentifier[] -} -interface GroupDB extends DBSchema { - /** Key is value.identifier */ - groups: { - value: GroupRecordInDatabase - key: string - indexes: { - // Use `network` field as index - network: string - } - } -} -//#endregion - -const db = createDBAccess(() => { - return openDB('maskbook-user-groups', 1, { - upgrade(db, oldVersion, newVersion, transaction) { - // Out line keys - db.createObjectStore('groups', { keyPath: 'identifier' }) - transaction.objectStore('groups').createIndex('network', 'network', { unique: false }) - }, - }) -}) -export const GroupDBAccess = db -type GroupTransaction = IDBPTransaction - -/** - * This function create a new user group - * It will return a GroupIdentifier - * @param group GroupIdentifier - * @param groupName - */ -export async function createUserGroupDatabase( - group: GroupIdentifier, - groupName: string, - t?: GroupTransaction, -): Promise { - t = t || (await db()).transaction('groups', 'readwrite') - await t.objectStore('groups').put({ - groupName, - identifier: group.toText(), - members: [], - network: group.network, - }) -} - -export async function createOrUpdateUserGroupDatabase( - group: Partial & Pick, - type: 'append' | 'replace' | ((record: GroupRecord) => GroupRecord | void), - t?: GroupTransaction, -) { - t = t || (await db()).transaction('groups', 'readwrite') - if (await queryUserGroupDatabase(group.identifier, t)) return updateUserGroupDatabase(group, type, t) - else return createUserGroupDatabase(group.identifier, group.groupName, t) -} - -/** - * Delete a user group that stored in the Mask - * @param group Group ID - */ -export async function deleteUserGroupDatabase(group: GroupIdentifier, t?: GroupTransaction): Promise { - t = t || (await db()).transaction('groups', 'readwrite') - await t.objectStore('groups').delete(group.toText()) -} - -/** - * Update a user group that stored in the Mask - * @param group Group ID - * @param type - */ -export async function updateUserGroupDatabase( - group: Partial & Pick, - type: 'append' | 'replace' | ((record: GroupRecord) => GroupRecord | void), - t?: GroupTransaction, -): Promise { - t = t || (await db()).transaction('groups', 'readwrite') - - const orig = await queryUserGroupDatabase(group.identifier, t) - if (!orig) throw new TypeError('User group not found') - - let nextRecord: GroupRecord - const nonDuplicateNewMembers: ProfileIdentifier[] = [] - if (type === 'replace') { - nextRecord = { ...orig, ...group } - } else if (type === 'append') { - const nextMembers = new Set() - for (const i of orig.members) { - nextMembers.add(i.toText()) - } - for (const i of group.members || []) { - if (!nextMembers.has(i.toText())) { - nextMembers.add(i.toText()) - nonDuplicateNewMembers.push(i) - } - } - nextRecord = { - identifier: group.identifier, - banned: !orig.banned && !group.banned ? undefined : [...(orig.banned || []), ...(group.banned || [])], - groupName: group.groupName || orig.groupName, - members: Array.from(nextMembers) - .map((x) => Identifier.fromString(x, ProfileIdentifier)) - .filter((x): x is Ok => x.ok) - .map((x) => x.val), - } - } else { - nextRecord = type(orig) || orig - } - await t.objectStore('groups').put(GroupRecordIntoDB(nextRecord)) - - if (process.env.NODE_ENV !== 'test' && nonDuplicateNewMembers.length) { - MaskMessage.events.profileJoinedGroup.sendToAll({ - group: group.identifier, - newMembers: nonDuplicateNewMembers, - }) - } -} - -/** - * Query a user group that stored in the Mask - * @param group Group ID - */ -export async function queryUserGroupDatabase( - group: GroupIdentifier, - t?: GroupTransaction, -): Promise { - t = t || (await db()).transaction('groups', 'readonly') - const result = await t.objectStore('groups').get(group.toText()) - if (!result) return null - return GroupRecordOutDB(result) -} - -/** - * Query user groups that stored in the Mask - * @param query Query ID - */ -export async function queryUserGroupsDatabase( - query: ((key: GroupIdentifier, record: GroupRecordInDatabase) => boolean) | { network: string }, - t?: GroupTransaction, -): Promise { - t = t || (await db()).transaction('groups') - const result: GroupRecordInDatabase[] = [] - if (typeof query === 'function') { - for await (const { value, key } of t.store) { - const identifier = Identifier.fromString(key, GroupIdentifier) - if (identifier.err) { - console.warn('Invalid identifier', key) - continue - } - if (query(identifier.val, value)) result.push(value) - } - } else { - result.push(...(await t.objectStore('groups').index('network').getAll(IDBKeyRange.only(query.network)))) - } - return result.map(GroupRecordOutDB) -} - -function GroupRecordOutDB(x: GroupRecordInDatabase): GroupRecord { - return { - ...x, - identifier: Identifier.fromString(x.identifier, GroupIdentifier).unwrap(), - members: restorePrototypeArray(x.members, ProfileIdentifier.prototype), - banned: restorePrototypeArray(x.banned, ProfileIdentifier.prototype), - } -} -function GroupRecordIntoDB(x: GroupRecord): GroupRecordInDatabase { - return { - ...x, - identifier: x.identifier.toText(), - network: x.identifier.network, - } -} diff --git a/packages/maskbook/src/database/helpers/avatar.ts b/packages/maskbook/src/database/helpers/avatar.ts index b56cb3945539..8c4d98b09b7b 100644 --- a/packages/maskbook/src/database/helpers/avatar.ts +++ b/packages/maskbook/src/database/helpers/avatar.ts @@ -1,4 +1,4 @@ -import { ProfileIdentifier, GroupIdentifier } from '../type' +import { ProfileIdentifier } from '../type' import { queryAvatarDB, isAvatarOutdatedDB, storeAvatarDB } from '../avatar' import { memoizePromise } from '../../utils/memoize' import { MaskMessage } from '../../utils/messages' @@ -10,7 +10,7 @@ import { blobToArrayBuffer, blobToDataURL } from '@dimensiondev/kit' * ? Because of cross-origin restrictions, we cannot use blob url here. sad :( */ export const queryAvatarDataURL = memoizePromise( - async function (identifier: ProfileIdentifier | GroupIdentifier): Promise { + async function (identifier: ProfileIdentifier): Promise { const buffer = await queryAvatarDB(identifier) if (!buffer) throw new Error('Avatar not found') return blobToDataURL(new Blob([buffer], { type: 'image/png' })) @@ -26,7 +26,7 @@ export const queryAvatarDataURL = memoizePromise( */ export async function storeAvatar( - identifier: ProfileIdentifier | GroupIdentifier, + identifier: ProfileIdentifier, avatar: ArrayBuffer | string, force?: boolean, ): Promise { diff --git a/packages/maskbook/src/database/helpers/group.ts b/packages/maskbook/src/database/helpers/group.ts deleted file mode 100644 index b50ca4cc1945..000000000000 --- a/packages/maskbook/src/database/helpers/group.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { GroupRecord, createUserGroupDatabase, updateUserGroupDatabase, queryUserGroupsDatabase } from '../group' -import { ProfileIdentifier, GroupIdentifier, PreDefinedVirtualGroupNames } from '../type' -import type { Profile } from '..' - -export interface Group extends GroupRecord { - avatar?: string -} - -export function createFriendsGroup(who: ProfileIdentifier, groupId: string) { - return createUserGroupDatabase( - GroupIdentifier.getFriendsGroupIdentifier(who, groupId), - // Put the raw special name in, then UI can display in their own language. - groupId, - ) -} - -export function createDefaultFriendsGroup(who: ProfileIdentifier) { - return createFriendsGroup(who, PreDefinedVirtualGroupNames.friends) -} - -export async function addProfileToFriendsGroup(group: GroupIdentifier, newMembers: (Profile | ProfileIdentifier)[]) { - const memberList = newMembers.map((x) => (x instanceof ProfileIdentifier ? x : x.identifier)) - await updateUserGroupDatabase({ identifier: group, members: memberList }, 'append') -} -export function removeProfileFromFriendsGroup(group: GroupIdentifier, removedFriend: (Profile | ProfileIdentifier)[]) { - const friendList = removedFriend.map((x) => (x instanceof ProfileIdentifier ? x : x.identifier)) - return updateUserGroupDatabase({ identifier: group }, (r) => { - r.members = r.members.filter((x) => !friendList.some((y) => y.equals(x))) - }) -} - -export function queryUserGroups(network: string): Promise { - return queryUserGroupsDatabase((r) => r.network === network) -} diff --git a/packages/maskbook/src/database/index.ts b/packages/maskbook/src/database/index.ts index 26904b50ae78..4ac9d5a59f86 100644 --- a/packages/maskbook/src/database/index.ts +++ b/packages/maskbook/src/database/index.ts @@ -1,5 +1,4 @@ export * from './helpers/avatar' -export * from './helpers/group' export * from './Persona/helpers' export * from './Persona/types' export * from './Plugin' diff --git a/packages/maskbook/src/database/post.ts b/packages/maskbook/src/database/post.ts index 7f8005bcac51..a7cd89422c17 100644 --- a/packages/maskbook/src/database/post.ts +++ b/packages/maskbook/src/database/post.ts @@ -17,7 +17,7 @@ const db = createDBAccessWithAsyncUpgrade( type Version2PostRecord = { postBy: PrototypeLess identifier: string - recipientGroups: PrototypeLess[] + recipientGroups?: unknown recipients?: ProfileIdentifier[] foundAt: Date postCryptoKey?: CryptoKey @@ -174,7 +174,6 @@ export async function updatePostDB( recipients: new IdentifierMap(new Map()), postBy: ProfileIdentifier.unknown, foundAt: new Date(), - recipientGroups: [], } const currentRecord = (await queryPostDB(updateRecord.identifier, t)) || emptyRecord const nextRecord: PostRecord = { ...currentRecord, ...updateRecord } @@ -238,13 +237,12 @@ export async function deletePostCryptoKeyDB(record: PostIVIdentifier, t?: PostTr //#region db in and out function postOutDB(db: PostDBRecord): PostRecord { - const { identifier, foundAt, postBy, recipientGroups, recipients, postCryptoKey } = db + const { identifier, foundAt, postBy, recipients, postCryptoKey } = db for (const detail of recipients.values()) { detail.reason.forEach((x) => x.type === 'group' && restorePrototype(x.group, GroupIdentifier.prototype)) } return { identifier: Identifier.fromString(identifier, PostIVIdentifier).unwrap(), - recipientGroups: restorePrototypeArray(recipientGroups, GroupIdentifier.prototype), postBy: restorePrototype(postBy, ProfileIdentifier.prototype), recipients: new IdentifierMap(recipients, ProfileIdentifier), foundAt: foundAt, @@ -291,10 +289,8 @@ export interface PostRecord { * Receivers */ recipients: IdentifierMap - /** - * This post shared with these groups. - */ - recipientGroups: GroupIdentifier[] + /** @deprecated */ + recipientGroups?: unknown /** * When does Mask find this post. * For your own post, it is when Mask created this post. @@ -303,11 +299,10 @@ export interface PostRecord { foundAt: Date } -interface PostDBRecord extends Omit { +interface PostDBRecord extends Omit { postBy: PrototypeLess identifier: string recipients: Map - recipientGroups: PrototypeLess[] } interface PostDB extends DBSchema { diff --git a/packages/maskbook/src/database/type.ts b/packages/maskbook/src/database/type.ts index 7dc05d76bef0..27e2f9b89a10 100644 --- a/packages/maskbook/src/database/type.ts +++ b/packages/maskbook/src/database/type.ts @@ -10,7 +10,6 @@ export { PersonaIdentifier, PostIVIdentifier, PostIdentifier, - PreDefinedVirtualGroupNames, ProfileIdentifier, } from '@masknet/shared' export function ECKeyIdentifierFromJsonWebKey(key: EC_JsonWebKey) { diff --git a/packages/maskbook/src/extension/background-script/CryptoService.ts b/packages/maskbook/src/extension/background-script/CryptoService.ts index e760c9dfb00a..631bf559959f 100644 --- a/packages/maskbook/src/extension/background-script/CryptoService.ts +++ b/packages/maskbook/src/extension/background-script/CryptoService.ts @@ -7,4 +7,9 @@ export { appendShareTarget } from './CryptoServices/appendShareTarget' export { getSharedListOfPost } from './CryptoServices/getSharedListOfPost' export { verifyOthersProve } from './CryptoServices/verifyOthersProve' export { getMyProveBio } from './CryptoServices/getMyProveBio' -export { debugShowAllPossibleHashForPost } from './CryptoServices/debugShowAllPossibleHashForPost' + +import type { debugShowAllPossibleHashForPost as orig } from './CryptoServices/debugShowAllPossibleHashForPost' +// This module requires lazy loading otherwise it will load gun +export async function debugShowAllPossibleHashForPost(...args: Parameters) { + return (await import('./CryptoServices/debugShowAllPossibleHashForPost')).debugShowAllPossibleHashForPost(...args) +} diff --git a/packages/maskbook/src/extension/background-script/CryptoServices/addPerson.ts b/packages/maskbook/src/extension/background-script/CryptoServices/addPerson.ts deleted file mode 100644 index 94ef8572eec4..000000000000 --- a/packages/maskbook/src/extension/background-script/CryptoServices/addPerson.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { GunAPI as Gun2 } from '../../../network/gun' -import { ProfileIdentifier, PostIdentifier } from '../../../database/type' -import { getNetworkWorker } from '../../../social-network' -import { verifyOthersProve } from './verifyOthersProve' -import { memoizePromise } from '../../../utils/memoize' -import { queryPersonaRecord } from '../../../database' -import type { PersonaRecord } from '../../../database/Persona/Persona.db' -import { i18n } from '../../../utils/i18n-next' - -async function getUserPublicKeyFromBio(user: ProfileIdentifier) { - const profile = await (await getNetworkWorker(user)).tasks.fetchProfile!(user) - if (!(await verifyOthersProve(profile.bioContent, user))) { - throw new Error('Not in bio!') - } -} -async function getUserPublicKeyFromProvePost(user: ProfileIdentifier) { - const profile = await Gun2.queryPersonFromGun2(user) - const proverPostID = profile?.provePostId as string | '' | undefined - if (!proverPostID?.length) { - throw new Error('Not in gun!') - } - const postId = new PostIdentifier(user, proverPostID) - const post = await (await getNetworkWorker(user)).tasks.fetchPostContent!(postId) - if ((await verifyOthersProve(post, user)) === false) throw new Error('Not in prove post!') -} -async function getUserPublicKeyFromNetwork(user: ProfileIdentifier) { - let bioRejected = false - let proveRejected = false - const errors: Error[] = [] - const [bio, post] = [getUserPublicKeyFromBio(user), getUserPublicKeyFromProvePost(user)] - try { - await bio - } catch (e) { - errors.push(e) - bioRejected = true - } - try { - await post - } catch (e) { - errors.push(e) - proveRejected = true - } - const person = await queryPersonaRecord(user) - if ((bioRejected && proveRejected) || !person?.publicKey) { - throw new Error(i18n.t('service_others_key_not_found', { name: user.userId })) - } - return person -} -Object.assign(globalThis, { getUserPublicKeyFromBio, getUserPublicKeyFromProvePost, getUserPublicKeyFromNetwork }) - -/** - * Fetch a user's public key from bio or prove post - * @param user Identifier - */ -export const addPerson = memoizePromise( - async function (user: ProfileIdentifier): Promise { - const person = await queryPersonaRecord(user) - if (!person?.publicKey) return getUserPublicKeyFromNetwork(user) - return person - }, - (id) => id.toText(), -) diff --git a/packages/maskbook/src/extension/background-script/CryptoServices/decryptFrom.ts b/packages/maskbook/src/extension/background-script/CryptoServices/decryptFrom.ts index 7943a7ad1cc0..99d3bfe8671b 100644 --- a/packages/maskbook/src/extension/background-script/CryptoServices/decryptFrom.ts +++ b/packages/maskbook/src/extension/background-script/CryptoServices/decryptFrom.ts @@ -7,7 +7,6 @@ import { i18n } from '../../../utils/i18n-next' import { queryPersonaRecord, queryLocalKey } from '../../../database' import { ProfileIdentifier, PostIVIdentifier } from '../../../database/type' import { queryPostDB, updatePostDB } from '../../../database/post' -import { addPerson } from './addPerson' import { getNetworkWorker, getNetworkWorkerUninitialized } from '../../../social-network/worker' import { cryptoProviderTable } from './cryptoProviderTable' import type { PersonaRecord } from '../../../database/Persona/Persona.db' @@ -23,7 +22,6 @@ import stringify from 'json-stable-stringify' import type { SharedAESKeyGun2 } from '../../../network/gun/version.2' import { MaskMessage } from '../../../utils/messages' import { GunAPI } from '../../../network/gun' -import { calculatePostKeyPartition } from '../../../network/gun/version.2/hash' import { Err, Ok, Result } from 'ts-results' import { decodeTextPayloadWorker } from '../../../social-network/utils/text-payload-worker' @@ -189,7 +187,9 @@ async function* decryptFromPayloadWithProgress_raw( try { if (version === -40) throw '' const gunNetworkHint = networkWorker!.gunNetworkHint - const { keyHash, postHash } = await calculatePostKeyPartition(version, iv, minePublic, gunNetworkHint) + const { keyHash, postHash } = await ( + await import('../../../network/gun/version.2/hash') + ).calculatePostKeyPartition(version, iv, minePublic, gunNetworkHint) yield { type: 'debug', debug: 'debug_finding_hash', hash: [postHash, keyHash] } } catch {} if (cachedPostResult) return makeSuccessResult(cachedPostResult, ['post_key_cached']) @@ -359,22 +359,11 @@ async function* findAuthorPublicKey( if (iterations < maxIteration) yield makeProgress('finding_person_public_key') else return 'out of chance' as const - author = await addPerson(by).catch(() => null) + author = await queryPersonaRecord(by).catch(() => null) if (!author?.publicKey) { if (hasCache) return 'use cache' as const const abort = new AbortController() - const gunPromise = (async () => { - const subscription = Gun2Subscribe.subscribeProfileFromGun2(by) - const undo = () => subscription.return?.(void 0) - abort.signal.addEventListener('abort', undo) - GunWorker?.onTerminated(undo) - for await (const data of subscription) { - const provePostID = String(data?.provePostId || '') - if (provePostID.length > 0) return - } - throw new Error() - })() const databasePromise = new Promise((resolve, reject) => { abort.signal.addEventListener('abort', () => { undo() @@ -391,7 +380,7 @@ async function* findAuthorPublicKey( } }) }) - await Promise.race([gunPromise, databasePromise]) + await Promise.race([databasePromise]) .then(() => abort.abort()) .catch(() => null) } diff --git a/packages/maskbook/src/extension/background-script/CryptoServices/encryptTo.ts b/packages/maskbook/src/extension/background-script/CryptoServices/encryptTo.ts index 9ecc1e0f8a98..b6818c2f9a17 100644 --- a/packages/maskbook/src/extension/background-script/CryptoServices/encryptTo.ts +++ b/packages/maskbook/src/extension/background-script/CryptoServices/encryptTo.ts @@ -3,7 +3,7 @@ import { GunAPI as Gun2 } from '../../../network/gun' import { encodeArrayBuffer } from '../../../utils/type-transform/String-ArrayBuffer' import { constructAlpha38, PayloadLatest } from '../../../utils/type-transform/Payload' import { queryPrivateKey, queryLocalKey } from '../../../database' -import { ProfileIdentifier, PostIVIdentifier, GroupIdentifier } from '../../../database/type' +import { ProfileIdentifier, PostIVIdentifier } from '../../../database/type' import { prepareRecipientDetail } from './prepareRecipientDetail' import { getNetworkWorker } from '../../../social-network/worker' import { createPostDB } from '../../../database/post' @@ -34,7 +34,7 @@ const OthersAESKeyEncryptedMap = new Map< */ export async function encryptTo( content: TypedMessage, - to: (ProfileIdentifier | GroupIdentifier)[], + to: ProfileIdentifier[], whoAmI: ProfileIdentifier, publicShared: boolean, ): Promise<[EncryptedText, OthersAESKeyEncryptedToken]> { @@ -83,7 +83,6 @@ export async function encryptTo( postCryptoKey: postAESKey, recipients: recipients, foundAt: new Date(), - recipientGroups: to.filter((x) => x instanceof GroupIdentifier) as GroupIdentifier[], }) const postAESKeyToken = encodeArrayBuffer(iv) diff --git a/packages/maskbook/src/extension/background-script/CryptoServices/prepareRecipientDetail.ts b/packages/maskbook/src/extension/background-script/CryptoServices/prepareRecipientDetail.ts index 68255b46b2e2..809dce3ac24f 100644 --- a/packages/maskbook/src/extension/background-script/CryptoServices/prepareRecipientDetail.ts +++ b/packages/maskbook/src/extension/background-script/CryptoServices/prepareRecipientDetail.ts @@ -1,27 +1,17 @@ -import { GroupIdentifier, ProfileIdentifier } from '../../../database/type' +import { ProfileIdentifier } from '../../../database/type' import { queryPublicKey } from '../../../database' import { IdentifierMap } from '../../../database/IdentifierMap' import type { RecipientDetail, RecipientReason } from '../../../database/post' -import { queryUserGroup } from '../UserGroupService' import type { EC_Public_JsonWebKey } from '../../../modules/CryptoAlgorithm/interfaces/utils' -import { unreachable } from '@dimensiondev/kit' -export async function prepareRecipientDetail(to: (ProfileIdentifier | GroupIdentifier)[]) { +export async function prepareRecipientDetail(to: ProfileIdentifier[]) { const recipients = new IdentifierMap(new Map(), ProfileIdentifier) const keys = new IdentifierMap(new Map(), ProfileIdentifier) await Promise.all( to.map(async function self(who, _, __, detail: RecipientReason = { at: new Date(), type: 'direct' }) { - if (who instanceof ProfileIdentifier) { - const pub = await queryPublicKey(who) - if (pub) keys.set(who, pub) - append(who, detail) - } else if (who instanceof GroupIdentifier) { - const group = await queryUserGroup(who) - if (!group) return // ? should we throw? - await Promise.all( - group.members.map((x) => self(x, _, __, { type: 'group', at: new Date(), group: who })), - ) - } else unreachable(who) + const pub = await queryPublicKey(who) + if (pub) keys.set(who, pub) + append(who, detail) }), ) function append(who: ProfileIdentifier, reason: RecipientReason) { diff --git a/packages/maskbook/src/extension/background-script/Jobs/AutoShareToFriends.ts b/packages/maskbook/src/extension/background-script/Jobs/AutoShareToFriends.ts deleted file mode 100644 index cbeeca085166..000000000000 --- a/packages/maskbook/src/extension/background-script/Jobs/AutoShareToFriends.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { MaskMessage } from '../../../utils/messages' -import type { ProfileIdentifier } from '../../../database/type' -import { queryPostsDB, PostRecord, RecipientReason } from '../../../database/post' -import { currentImportingBackup } from '../../../settings/settings' -import { Services } from '../../service' - -interface ShareTarget { - share: ProfileIdentifier[] - whoAmI: ProfileIdentifier - reason: RecipientReason -} - -async function appendShare( - postFilter: Parameters[0], - shareTargets: (postRec: PostRecord) => ShareTarget, -) { - const posts = await queryPostsDB(postFilter) - for (const post of posts) { - const postID = post.identifier.toText() - const { share, whoAmI, reason } = shareTargets(post) - if (share.length === 0) return - - console.log('Sharing post ', postID, '...') - if (!post.postCryptoKey) { - console.warn('Post ', postID, ' have no CryptoKey, skipping') - return - } - - Services.Crypto.appendShareTarget(-38, post.postCryptoKey, post.identifier.postIV, share, whoAmI, reason).then( - () => { - console.log('Post ', postID, ' shared') - }, - console.error, - ) - } -} - -export default function (signal: AbortSignal) { - const undo1 = MaskMessage.events.profileJoinedGroup.on(async (data) => { - if (currentImportingBackup.value) return - if (data.group.isReal) return - const group = await Services.UserGroup.queryUserGroup(data.group) - if (!group) return - // if (group.groupName !== PreDefinedVirtualGroupNames.friends) return - - const whoAmI = data.group.ownerIdentifier - if (!whoAmI) return - - console.groupCollapsed( - '[Auto share to friends] New friends', - data.newMembers, - ' to group ', - data.group, - ' share old posts to them.', - ) - appendShare( - (record, id) => { - if (id.network !== data.group.network) return false - if (record.recipientGroups.some((x) => data.group.equals(x))) return true - return false - }, - (post) => { - const notSharedBefore: ProfileIdentifier[] = [] - data.newMembers.forEach((x) => { - if (post.recipients.has(x)) { - } else notSharedBefore.push(x) - }) - return { - share: notSharedBefore, - whoAmI, - reason: { type: 'group', at: new Date(), group: group.identifier }, - } - }, - ) - console.groupEnd() - }) - const undo2 = MaskMessage.events.linkedProfilesChanged.on(async (events) => { - if (currentImportingBackup.value) return - const mine = await Services.Identity.queryMyProfiles() - for (const e of events) { - appendShare( - (rec) => !!e.after && mine.some((q) => rec.postBy.equals(q.identifier)) && rec.recipients.has(e.of), - (rec) => ({ share: [e.of], whoAmI: rec.postBy, reason: { type: 'auto-share', at: new Date() } }), - ) - } - }) - signal.addEventListener('abort', undo1) - signal.addEventListener('abort', undo2) -} diff --git a/packages/maskbook/src/extension/background-script/Jobs/DatabaseCleanup/CleanProfileAndAvatar.ts b/packages/maskbook/src/extension/background-script/Jobs/DatabaseCleanup/CleanProfileAndAvatar.ts index 14d69b4b2749..cfda1fe5ab72 100644 --- a/packages/maskbook/src/extension/background-script/Jobs/DatabaseCleanup/CleanProfileAndAvatar.ts +++ b/packages/maskbook/src/extension/background-script/Jobs/DatabaseCleanup/CleanProfileAndAvatar.ts @@ -1,11 +1,11 @@ import { createAvatarDBAccess, queryAvatarOutdatedDB, deleteAvatarsDB } from '../../../../database/avatar' import { createTransaction } from '../../../../database/helpers/openDB' import { consistentPersonaDBWriteAccess } from '../../../../database/Persona/Persona.db' -import { Identifier, ProfileIdentifier, GroupIdentifier } from '../../../../database/type' +import { Identifier, ProfileIdentifier } from '../../../../database/type' import { IdentifierMap } from '../../../../database/IdentifierMap' import { untilDocumentReady } from '../../../../utils' -async function cleanAvatarDB(anotherList: IdentifierMap) { +async function cleanAvatarDB(anotherList: IdentifierMap) { const t = createTransaction(await createAvatarDBAccess(), 'readwrite')('avatars', 'metadata') const outdated = await queryAvatarOutdatedDB('lastAccessTime', t) for (const each of outdated) { @@ -19,11 +19,7 @@ export default async function cleanProfileWithNoLinkedPersona(signal: AbortSigna const timeout = setTimeout(cleanProfileWithNoLinkedPersona, 1000 * 60 * 60 * 24 /** 1 day */) signal.addEventListener('abort', () => clearTimeout(timeout)) - const cleanedList = new IdentifierMap( - new Map(), - ProfileIdentifier, - GroupIdentifier, - ) + const cleanedList = new IdentifierMap(new Map(), ProfileIdentifier) const expired = new Date(Date.now() - 1000 * 60 * 60 * 24 * 14 /** days */) await consistentPersonaDBWriteAccess(async (t) => { if (signal.aborted) throw new Error('Abort') diff --git a/packages/maskbook/src/extension/background-script/Jobs/DatabaseCleanup/DropOldDatabase.ts b/packages/maskbook/src/extension/background-script/Jobs/DatabaseCleanup/DropOldDatabase.ts new file mode 100644 index 000000000000..2bf433659d1d --- /dev/null +++ b/packages/maskbook/src/extension/background-script/Jobs/DatabaseCleanup/DropOldDatabase.ts @@ -0,0 +1,10 @@ +import { deleteDB } from 'idb' +import { noop } from 'lodash-es' + +try { + deleteDB('maskbook-user-groups').catch(noop) +} catch {} + +try { + deleteDB('maskbook-people-v2').catch(noop) +} catch {} diff --git a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/_deprecated_people_db.ts b/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/_deprecated_people_db.ts deleted file mode 100644 index d5ddd50b4fb2..000000000000 --- a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/_deprecated_people_db.ts +++ /dev/null @@ -1,170 +0,0 @@ -/* eslint-disable import/no-deprecated */ -/** - * @deprecated - * This database is deprecated since Mask 1.8.10 - * Do not store new data in it. - */ -/** - * Database structure: - * - * # ObjectStore `people`: - * @description Store Other people. - * @type {PersonRecordInDatabase} - * @keys inline, {@link Person.identifier} - * @index network - * - * # ObjectStore `myself`: - * @description Store my identities. - * @type {PersonRecordInDatabase} - * @keys inline, {@link Person.identifier} - * - * # ObjectStore `localKeys`: - * @description Store local AES keys. - * @type {Record} Record of - * @keys outline, string, which means network. - */ -import { GroupIdentifier, Identifier, ProfileIdentifier } from '../../../../database/type' -import { DBSchema, openDB } from 'idb/with-async-ittr-cjs' -import { JsonWebKeyToCryptoKey, getKeyParameter } from '../../../../utils/type-transform/CryptoKey-JsonWebKey' -import { assertEnvironment, Environment } from '@dimensiondev/holoflows-kit' -import { createDBAccess } from '../../../../database/helpers/openDB' -import type { AESJsonWebKey } from '../../../../modules/CryptoAlgorithm/interfaces/utils' - -assertEnvironment(Environment.ManifestBackground) -//#region Type and utils -/** - * Transform data out of database - */ -async function outDb({ identifier, publicKey, privateKey, ...rest }: PersonRecordInDatabase): Promise { - // Restore prototype - rest.previousIdentifiers && - rest.previousIdentifiers.forEach((y) => Object.setPrototypeOf(y, ProfileIdentifier.prototype)) - rest.groups.forEach((y) => Object.setPrototypeOf(y, GroupIdentifier.prototype)) - const result: PersonRecord = { - ...rest, - identifier: Identifier.fromString(identifier, ProfileIdentifier).unwrap(), - } - if (publicKey) result.publicKey = await JsonWebKeyToCryptoKey(publicKey, ...getKeyParameter('ecdh')) - if (privateKey) result.privateKey = await JsonWebKeyToCryptoKey(privateKey, ...getKeyParameter('ecdh')) - return result -} -interface Base { - identifier: IF - publicKey?: IF - privateKey?: IF -} -/** - * @deprecated - */ -export interface PersonRecord extends Omit | 'network'>, Base {} -/** - * @deprecated - */ -export type PersonRecordPublic = PersonRecord & Required> -/** - * @deprecated - */ -export type PersonRecordPublicPrivate = PersonRecord & Required> -interface PersonRecordInDatabase extends Base { - network: string - previousIdentifiers?: ProfileIdentifier[] - nickname?: string - groups: GroupIdentifier[] -} -type LocalKeys = Record -interface PeopleDB extends DBSchema { - /** Use inline keys */ - people: { - value: PersonRecordInDatabase - key: string - indexes: { - // Use `network` field as index - network: string - } - } - /** Use inline keys */ - myself: { - value: PersonRecordInDatabase - key: string - } - /** Use `network` as out-of-line keys */ - localKeys: { - value: LocalKeys - key: string - } -} -const db = createDBAccess(() => - openDB('maskbook-people-v2', 1, { - upgrade(db, oldVersion, _newVersion, transaction) { - function v0_v1() { - // inline keys - db.createObjectStore('people', { keyPath: 'identifier' }) - // inline keys - db.createObjectStore('myself', { keyPath: 'identifier' }) - db.createObjectStore('localKeys') - - transaction.objectStore('people').createIndex('network', 'network', { unique: false }) - } - if (oldVersion < 1) v0_v1() - }, - }), -) -//#endregion -//#region Other people -/** - * Query person with an identifier - * @deprecated - */ -export async function queryPeopleDB( - query: ((key: ProfileIdentifier, record: PersonRecordInDatabase) => boolean) | { network: string } = () => true, -): Promise { - const t = (await db()).transaction('people') - const result: PersonRecordInDatabase[] = [] - if (typeof query === 'function') { - // eslint-disable-next-line @typescript-eslint/await-thenable - for await (const { value, key } of t.store) { - const id = Identifier.fromString(key, ProfileIdentifier) - if (id.err) { - console.warn('Found invalid identifier', id.val.message) - continue - } - if (query(id.val, value)) result.push(value) - } - } else { - result.push(...(await t.objectStore('people').index('network').getAll(IDBKeyRange.only(query.network)))) - } - return Promise.all(result.map(outDb)) -} - -//#endregion -//#region Myself -/** - * Get all my identities. - * @deprecated - */ -export async function getMyIdentitiesDB(): Promise { - const t = (await db()).transaction('myself') - const result = await t.objectStore('myself').getAll() - return Promise.all(result.map(outDb)) as Promise -} -//#region LocalKeys - -/** - * - * @param network - * @deprecated - */ -export async function queryLocalKeyDB(network: string): Promise -export async function queryLocalKeyDB(identifier: ProfileIdentifier): Promise -export async function queryLocalKeyDB( - identifier: string | ProfileIdentifier, -): Promise { - const t = (await db()).transaction('localKeys') - if (typeof identifier === 'string') { - const result = await t.objectStore('localKeys').get(identifier) - return result || {} - } else { - const store = await queryLocalKeyDB(identifier.network) - return store[identifier.userId] || null - } -} diff --git a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/create.user.group.for.old.users.ts b/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/create.user.group.for.old.users.ts deleted file mode 100644 index 3cabb44abfc2..000000000000 --- a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/create.user.group.for.old.users.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { queryUserGroupDatabase } from '../../../../database/group' -import { GroupIdentifier } from '@masknet/shared' -import { createDefaultFriendsGroup } from '../../UserGroupService' -import { queryMyProfiles } from '../../IdentityService' - -/** - * If an identity has no default user group, create one - */ -export default async function createUserGroupForOldUsers() { - const ids = await queryMyProfiles() - for (const id of ids) { - const g = await queryUserGroupDatabase(GroupIdentifier.getDefaultFriendsGroupIdentifier(id.identifier)) - if (!g) createDefaultFriendsGroup(id.identifier) - } -} diff --git a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/fix.qr.private.key.bug.ts b/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/fix.qr.private.key.bug.ts deleted file mode 100644 index f5d24a82981e..000000000000 --- a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/fix.qr.private.key.bug.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { setStorage } from '../../../../utils/browser.storage' -import { queryPersonasWithPrivateKey } from '../../../../database/Persona/Persona.db' -import { deletePersona } from '../../../../database/Persona/helpers' - -/** - * There is a bug that when use QR to import key, the private ket lost its secret. - * If the JsonWebKey has no "d" field, remove the key and set mask as not setup. - * - * remove this after Mar 1 2020 - */ -export default async function () { - const ids = await queryPersonasWithPrivateKey() - let hasBug = false - for (const id of ids) { - const key = id.privateKey - if (!key.d) { - deletePersona(id.identifier, 'delete even with private').catch(() => {}) - hasBug = true - } - } - - if (hasBug) { - setStorage('facebook.com', { forceDisplayWelcome: true, userIgnoredWelcome: false }) - setStorage('twitter.com', { forceDisplayWelcome: true, userIgnoredWelcome: false }) - } -} diff --git a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/index.ts b/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/index.ts deleted file mode 100644 index 8903fef71ee2..000000000000 --- a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import createUserGroupForOldUsers from './create.user.group.for.old.users' -import fixQrPrivateKeyBug from './fix.qr.private.key.bug' -import migratePeopleToPersona from './people.to.persona' -import { sideEffect, untilDocumentReady } from '../../../../utils' - -sideEffect.then(untilDocumentReady).then(run) -function run() { - createUserGroupForOldUsers() - migratePeopleToPersona() - fixQrPrivateKeyBug() -} diff --git a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/people.to.persona.ts b/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/people.to.persona.ts deleted file mode 100644 index 25500c59b6fb..000000000000 --- a/packages/maskbook/src/extension/background-script/Jobs/MigrateDatabase/people.to.persona.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* eslint-disable import/no-deprecated */ -import { - getMyIdentitiesDB, - queryLocalKeyDB, - queryPeopleDB, - PersonRecord, - PersonRecordPublic, - PersonRecordPublicPrivate, -} from './_deprecated_people_db' -import * as persona from '../../../../database/Persona/Persona.db' -import { ECKeyIdentifier, ProfileIdentifier, IdentifierMap, AESJsonWebKey } from '@masknet/shared' -import { ECKeyIdentifierFromCryptoKey } from '../../../../database/type' -import { CryptoKeyToJsonWebKey } from '../../../../utils/type-transform/CryptoKey-JsonWebKey' - -export default async function migratePeopleToPersona() { - const myIDs = await getMyIdentitiesDB() - const otherIDs = await queryPeopleDB(() => true) - await migrateHelper_operateDB(myIDs, otherIDs, queryLocalKeyDB) -} -async function migrateHelper_operateDB( - myIDs: PersonRecordPublicPrivate[], - otherIDs: PersonRecord[], - getLocalKey: (identifier: ProfileIdentifier) => Promise, -) { - const [personaMap, profilesMap, attachRelationMap] = await migrateHelper_importPersonaFromPersonRecord( - myIDs, - otherIDs, - getLocalKey, - ) - - await persona.consistentPersonaDBWriteAccess(async (t) => { - for (const [v, incomingRecord] of personaMap) { - const currentRecord = await persona.queryPersonaDB(incomingRecord.identifier, t) - if (!currentRecord) { - await persona.createPersonaDB(incomingRecord, t) - } - } - for (const [v, incomingRecord] of profilesMap) { - const currentRecord = await persona.queryProfileDB(incomingRecord.identifier, t) - if (!currentRecord) { - // remove the linkedPersona, call attachProfileDB to keep consistency - const { linkedPersona, ...rec } = incomingRecord - await persona.createProfileDB(rec, t) - } - } - for (const [profileID, personaID] of attachRelationMap) { - const currentRecord = await persona.queryPersonaDB(personaID, t) - const data: persona.LinkedProfileDetails = currentRecord!.linkedProfiles.get(profileID) ?? { - connectionConfirmState: 'pending', - } - await persona.attachProfileDB(profileID, personaID, data, t) - } - }) - indexedDB.deleteDatabase('maskbook-people-v2') -} -async function migrateHelper_importPersonaFromPersonRecord( - myIDs: PersonRecordPublicPrivate[], - otherIDs: PersonRecord[], - getLocalKey: (identifier: ProfileIdentifier) => Promise, -) { - const jwkMap = new Map() - const attachRelationMap = new IdentifierMap(new Map(), ProfileIdentifier) - const localKeysMap = new IdentifierMap(new Map(), ProfileIdentifier) - const personaMap = new IdentifierMap(new Map(), ECKeyIdentifier) - const profilesMap = new IdentifierMap(new Map(), ProfileIdentifier) - - await Promise.all( - otherIDs.concat(myIDs).map(async (value) => { - if (value.publicKey) jwkMap.set(value.publicKey, await CryptoKeyToJsonWebKey(value.publicKey)) - if (value.privateKey) jwkMap.set(value.privateKey, await CryptoKeyToJsonWebKey(value.privateKey)) - - if (value.publicKey) { - attachRelationMap.set(value.identifier, await ECKeyIdentifierFromCryptoKey(value.publicKey)) - } - }), - ) - await Promise.all( - myIDs.map(async (value) => { - const key = await getLocalKey(value.identifier) - key && localKeysMap.set(value.identifier, key) - }), - ) - for (const profile of otherIDs.concat(myIDs)) { - const ec_id = attachRelationMap.get(profile.identifier)! - if (profile.publicKey) { - updateOrCreatePersonaRecord(personaMap, ec_id, profile as PersonRecordPublic, jwkMap) - } - updateOrCreateProfileRecord(profilesMap, attachRelationMap, localKeysMap, profile) - } - - return [personaMap, profilesMap, attachRelationMap] as const -} - -function updateOrCreatePersonaRecord( - map: IdentifierMap, - ec_id: ECKeyIdentifier, - profile: PersonRecordPublic, - cryptoKeyMap: Map, -) { - const rec = map.get(ec_id) - if (rec) { - if (profile.privateKey) { - // @ts-ignore - rec.privateKey = cryptoKeyMap.get(profile.privateKey)! - } - rec.linkedProfiles.set(profile.identifier, { - connectionConfirmState: 'pending', - }) - } else { - map.set(ec_id, { - // @ts-ignore - privateKey: cryptoKeyMap.get(profile.privateKey!)!, - // @ts-ignore - publicKey: cryptoKeyMap.get(profile.publicKey)!, - createdAt: new Date(0), - updatedAt: new Date(), - linkedProfiles: new IdentifierMap(new Map(), ProfileIdentifier), - identifier: ec_id, - }) - map.get(ec_id)!.linkedProfiles.set(profile.identifier, { - connectionConfirmState: 'pending', - }) - } -} -function updateOrCreateProfileRecord( - map: IdentifierMap, - ec_idMap: IdentifierMap, - localKeyMap: IdentifierMap, - profile: PersonRecord, -) { - const rec = map.get(profile.identifier) - if (rec) { - rec.nickname = rec.nickname ?? profile.nickname - } else { - map.set(profile.identifier, { - createdAt: new Date(0), - updatedAt: new Date(), - identifier: profile.identifier, - nickname: profile.nickname, - localKey: localKeyMap.get(profile.identifier), - linkedPersona: ec_idMap.get(profile.identifier), - }) - } -} diff --git a/packages/maskbook/src/extension/background-script/Jobs/index.ts b/packages/maskbook/src/extension/background-script/Jobs/index.ts index 5e6964583136..b7e835fbedde 100644 --- a/packages/maskbook/src/extension/background-script/Jobs/index.ts +++ b/packages/maskbook/src/extension/background-script/Jobs/index.ts @@ -1,4 +1,4 @@ import './index_hmr' // Add and execute your non-cancelable jobs here. It won't compatible to HMR. import './PrintBuildFlags' -import './MigrateDatabase' +import './DatabaseCleanup/DropOldDatabase' diff --git a/packages/maskbook/src/extension/background-script/Jobs/index_hmr.ts b/packages/maskbook/src/extension/background-script/Jobs/index_hmr.ts index 1f39957cb2b7..4006a494d5bc 100644 --- a/packages/maskbook/src/extension/background-script/Jobs/index_hmr.ts +++ b/packages/maskbook/src/extension/background-script/Jobs/index_hmr.ts @@ -1,4 +1,3 @@ -import * as AutoShareToFriends from './AutoShareToFriends' import * as IsolatedDashboardBridge from './IsolatedDashboardBridge' import * as InjectContentScripts from './InjectContentScripts' import * as NewInstalled from './NewInstalled' @@ -10,7 +9,6 @@ type CancelableJob = { default: (signal: AbortSignal) => void } const CancelableJobs: CancelableJob[] = [ InjectContentScripts, NewInstalled, - AutoShareToFriends, IsolatedDashboardBridge, PluginWorker, SettingListeners, diff --git a/packages/maskbook/src/extension/background-script/SettingsService.ts b/packages/maskbook/src/extension/background-script/SettingsService.ts index f14367f80248..9fc3d3305c41 100644 --- a/packages/maskbook/src/extension/background-script/SettingsService.ts +++ b/packages/maskbook/src/extension/background-script/SettingsService.ts @@ -5,7 +5,6 @@ import { appearanceSettings, currentPersonaIdentifier, languageSettings, - disableOpenNewTabInBackgroundSettings, currentPluginEnabledStatus, } from '../../settings/settings' import { currentDataProviderSettings } from '../../plugins/Trader/settings' @@ -43,10 +42,6 @@ export const [getBlockNumber, setBlockNumber] = create(currentBlockNumberSetting export const [getEtherPrice, setEtherPrice] = create(currentEtherPriceSettings) export const [getGasNow, setGasNow] = create(currentGasNowSettings) export const [getTrendingDataSource, setTrendingDataSource] = create(currentDataProviderSettings) -export const [getAncientPostsCompatibiltyMode, setAncientPostsCompatibiltyMode] = create( - disableOpenNewTabInBackgroundSettings, -) - export const [getCurrentSelectedWalletProvider, setCurrentSelectedWalletProvider] = create(currentProviderSettings) export const [getCurrentSelectedWalletNetwork, setCurrentSelectedWalletNetwork] = create(currentNetworkSettings) diff --git a/packages/maskbook/src/extension/background-script/UserGroupService.ts b/packages/maskbook/src/extension/background-script/UserGroupService.ts deleted file mode 100644 index 49c45a0856fb..000000000000 --- a/packages/maskbook/src/extension/background-script/UserGroupService.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { assertEnvironment, Environment } from '@dimensiondev/holoflows-kit' -assertEnvironment(Environment.ManifestBackground) - -export { - addProfileToFriendsGroup, - createDefaultFriendsGroup, - removeProfileFromFriendsGroup, - createFriendsGroup, - queryUserGroups, -} from '../../database/helpers/group' -export { queryUserGroupDatabase as queryUserGroup } from '../../database/group' diff --git a/packages/maskbook/src/extension/background-script/WelcomeService.ts b/packages/maskbook/src/extension/background-script/WelcomeService.ts index efaa33e66fd7..59bd76cc5906 100644 --- a/packages/maskbook/src/extension/background-script/WelcomeService.ts +++ b/packages/maskbook/src/extension/background-script/WelcomeService.ts @@ -6,7 +6,6 @@ import { attachProfileDB, LinkedProfileDetails } from '../../database/Persona/Pe import { deriveLocalKeyFromECDHKey } from '../../utils/mnemonic-code/localKeyGenerate' import type { ProfileIdentifier, PersonaIdentifier } from '../../database/type' import { generateBackupJSON, BackupOptions } from './WelcomeServices/generateBackupJSON' -import { exclusiveTasks } from '../content-script/tasks' import type { AESJsonWebKey } from '../../modules/CryptoAlgorithm/interfaces/utils' import { saveAsFileFromBuffer } from './HelperService' import type { DashboardRoute } from '../options-page/Route' @@ -98,9 +97,10 @@ async function createBackupInfo(obj: T) { } export async function openOptionsPage(route?: DashboardRoute, search?: string) { - return exclusiveTasks( - browser.runtime.getURL(route ? `/index.html#${route}${search ? `?${search}` : ''}` : '/index.html'), - ).noop() + return browser.tabs.create({ + active: true, + url: browser.runtime.getURL(route ? `/index.html#${route}${search ? `?${search}` : ''}` : '/index.html'), + }) } export { createPersonaByMnemonic } from '../../database' diff --git a/packages/maskbook/src/extension/background-script/WelcomeServices/generateBackupJSON.ts b/packages/maskbook/src/extension/background-script/WelcomeServices/generateBackupJSON.ts index 2b793b24f895..7f84e64b453c 100644 --- a/packages/maskbook/src/extension/background-script/WelcomeServices/generateBackupJSON.ts +++ b/packages/maskbook/src/extension/background-script/WelcomeServices/generateBackupJSON.ts @@ -5,11 +5,9 @@ import { getBackupPreviewInfo, } from '../../../utils/type-transform/BackupFormat/JSON/latest' import { queryPersonasDB, queryProfilesDB } from '../../../database/Persona/Persona.db' -import { queryUserGroupsDatabase } from '../../../database/group' import { queryPostsDB } from '../../../database/post' import { PersonaRecordToJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/PersonaRecord' import { ProfileRecordToJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/ProfileRecord' -import { GroupRecordToJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/GroupRecord' import { PostRecordToJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/PostRecord' import { ProfileIdentifier, PersonaIdentifier, Identifier } from '../../../database/type' import { getWallets } from '../../../plugins/Wallet/services' @@ -18,7 +16,6 @@ import { WalletRecordToJSONFormat } from '../../../utils/type-transform/BackupFo export type { BackupPreview } from '../../../utils/type-transform/BackupFormat/JSON/latest' export interface BackupOptions { noPosts: boolean - noUserGroups: boolean noWallets: boolean noPersonas: boolean noProfiles: boolean @@ -30,7 +27,6 @@ export async function generateBackupJSON(opts: Partial = {}): Pro const posts: BackupJSONFileLatest['posts'] = [] const wallets: BackupJSONFileLatest['wallets'] = [] const profiles: BackupJSONFileLatest['profiles'] = [] - const userGroups: BackupJSONFileLatest['userGroups'] = [] if (!opts.filter) { if (!opts.noPersonas) await backupPersonas() @@ -46,7 +42,6 @@ export async function generateBackupJSON(opts: Partial = {}): Pro ) if (!opts.noProfiles) await backProfiles(wantedProfiles) } - if (!opts.noUserGroups) await backupAllUserGroups() if (!opts.noPosts) await backupAllPosts() if (!opts.noWallets) await backupAllWallets() @@ -62,17 +57,13 @@ export async function generateBackupJSON(opts: Partial = {}): Pro posts, wallets, profiles, - userGroups, + userGroups: [], } async function backupAllPosts() { posts.push(...(await queryPostsDB(() => true)).map(PostRecordToJSONFormat)) } - async function backupAllUserGroups() { - userGroups.push(...(await queryUserGroupsDatabase(() => true)).map(GroupRecordToJSONFormat)) - } - async function backProfiles(of?: ProfileIdentifier[]) { const data = ( await queryProfilesDB((p) => { diff --git a/packages/maskbook/src/extension/background-script/WelcomeServices/restoreBackup.ts b/packages/maskbook/src/extension/background-script/WelcomeServices/restoreBackup.ts index d778935b4a08..4aaacbb0c3ec 100644 --- a/packages/maskbook/src/extension/background-script/WelcomeServices/restoreBackup.ts +++ b/packages/maskbook/src/extension/background-script/WelcomeServices/restoreBackup.ts @@ -10,8 +10,6 @@ import { PersonaRecordFromJSONFormat } from '../../../utils/type-transform/Backu import { ProfileRecordFromJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/ProfileRecord' import { PostRecordFromJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/PostRecord' import { createOrUpdatePostDB } from '../../../database/post' -import { GroupRecordFromJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/GroupRecord' -import { createOrUpdateUserGroupDatabase } from '../../../database/group' import { i18n } from '../../../utils/i18n-next' import { currentImportingBackup } from '../../../settings/settings' import { WalletRecordFromJSONFormat } from '../../../utils/type-transform/BackupFormat/JSON/DBRecord-JSON/WalletRecord' @@ -59,11 +57,6 @@ export async function restoreBackup(json: object, whoAmI?: ProfileIdentifier) { for (const x of data.posts) { await createOrUpdatePostDB(PostRecordFromJSONFormat(x), 'append') } - - for (const x of data.userGroups) { - const rec = GroupRecordFromJSONFormat(x) - await createOrUpdateUserGroupDatabase(rec, 'append') - } } finally { currentImportingBackup.value = false } diff --git a/packages/maskbook/src/extension/content-script/index.tsx b/packages/maskbook/src/extension/content-script/index.tsx deleted file mode 100644 index b61d2123e31c..000000000000 --- a/packages/maskbook/src/extension/content-script/index.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import './tasks' // ? AutomatedTabTask Run tasks when invoked by background page - -import * as HoloflowsKit from '@dimensiondev/holoflows-kit' -Object.assign(globalThis, HoloflowsKit) diff --git a/packages/maskbook/src/extension/content-script/tasks.ts b/packages/maskbook/src/extension/content-script/tasks.ts deleted file mode 100644 index 40b80cbd037f..000000000000 --- a/packages/maskbook/src/extension/content-script/tasks.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { AutomatedTabTask, AutomatedTabTaskRuntimeOptions } from '@dimensiondev/holoflows-kit' -import { disableOpenNewTabInBackgroundSettings } from '../../settings/settings' -import type { SocialNetworkUI } from '../../social-network' -import { memoizePromise } from '../../utils/memoize' -import Serialization from '../../utils/type-transform/Serialization' -import { delay } from '../../utils/utils' -import { Flags } from '../../utils/flags' -import { activatedSocialNetworkUI } from '../../social-network' - -const _tasks = { - getPostContent: async () => (await activatedSocialNetworkUI.collecting.getPostContent?.()) || '', - /** - * Access profile page - * Get Profile - */ - getProfile: async () => (await activatedSocialNetworkUI.collecting.getProfile?.()) || { bioContent: '' }, - /** - * Access main page - * Paste text into PostBox - */ - pasteIntoPostBox: async ( - text: string, - options: SocialNetworkUI.AutomationCapabilities.NativeCompositionAttachTextOptions, - ) => activatedSocialNetworkUI.automation.nativeCompositionDialog?.appendText?.(text, options), - /** - * Fetch a url in the current context - */ - async fetch(...args: Parameters) { - return fetch(...args).then((x) => x.text()) - }, - memoizeFetch: memoizePromise( - (url: string): Promise => { - return fetch(url).then((x) => x.text()) - }, - (x) => x, - ), - async noop() {}, -} -const realTasks = AutomatedTabTask(_tasks, { - memorable: true, - AsyncCallOptions: { serializer: Serialization, strict: false }, -})! -// console.log('To debug tasks, use globalThis.tasks, sleep fn is also available') -Object.assign(globalThis, { tasks: _tasks, sleep: delay }) -export default function tasks(...args: Parameters) { - const [tabIdOrUri, options] = args - if (disableOpenNewTabInBackgroundSettings.value && Number.isNaN(Number(tabIdOrUri))) { - if (!options || !options.active) - throw new Error( - `You have disabled "Disable fetching public keys in the background" in the settings so Mask can not perform this action`, - ) - } - // in the background - if (realTasks) return realTasks(...args) - // for debug purpose - return _tasks -} - -const uriCanDoTask = (tabUri?: string, targetUri?: any) => { - if (tabUri?.startsWith(targetUri)) return true - return false -} - -/** - * ! For mobile standalone app - * This function will open/switch tabs (and start tasks) - * It tries to check if a tab being able to do the target task already opened before opening it - * For Chrome, `browser.tabs.query` won't work for chrome-extension: schema, so this function is not useful - */ - -export function exclusiveTasks(...args: Parameters) { - const [uri, options = {}, ...others] = args - const updatedOptions: Partial = { - active: true, - memorable: false, - autoClose: false, - } - if (!Flags.has_no_browser_tab_ui) return tasks(uri, { ...updatedOptions, ...options }, ...others) - let _key: keyof typeof _tasks - let _args: any[] - async function p() { - const tabs = await browser.tabs.query({}) - const target = uri.toString().replace(/\/.+$/, '') - const [tab] = tabs.filter((tab) => tab.url?.startsWith(target)) - if (tab) { - Object.assign(updatedOptions, { - runAtTabID: tab.id, - needRedirect: !uriCanDoTask(tab.url, uri), - url: uri, - }) - } - Object.assign(updatedOptions, options) - const task = tasks(uri, updatedOptions, ...others) - // @ts-ignore - if (_key in task) return task[_key](..._args) - return task - } - const promise = p() - return new Proxy({} as typeof _tasks, { - get(_, key: keyof typeof _tasks | 'then') { - if (key === 'then') return undefined - _key = key - return (...args: any) => { - _args = args - return promise - } - }, - }) -} diff --git a/packages/maskbook/src/extension/options-page/DashboardComponents/DatabasePreviewCard.tsx b/packages/maskbook/src/extension/options-page/DashboardComponents/DatabasePreviewCard.tsx index 9996c7301092..18ea6c043fab 100644 --- a/packages/maskbook/src/extension/options-page/DashboardComponents/DatabasePreviewCard.tsx +++ b/packages/maskbook/src/extension/options-page/DashboardComponents/DatabasePreviewCard.tsx @@ -36,7 +36,6 @@ export enum DatabaseRecordType { Persona, Profile, Post, - Group, Wallet, } @@ -62,8 +61,6 @@ export function DatabasePreviewCard(props: DatabasePreviewCardProps) { return t('profiles') case DatabaseRecordType.Post: return t('posts') - case DatabaseRecordType.Group: - return t('groups') case DatabaseRecordType.Wallet: return t('wallets') default: diff --git a/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/Backup.tsx b/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/Backup.tsx index 3482489ddae3..9bdeb3379182 100644 --- a/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/Backup.tsx +++ b/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/Backup.tsx @@ -24,7 +24,6 @@ export function DashboardBackupDialog(props: WrappedDialogProps) { { type: DatabaseRecordType.Persona, length: value?.personas.length ?? 0, checked: false }, { type: DatabaseRecordType.Profile, length: value?.profiles.length ?? 0, checked: false }, { type: DatabaseRecordType.Post, length: value?.posts.length ?? 0, checked: false }, - { type: DatabaseRecordType.Group, length: value?.userGroups.length ?? 0, checked: false }, { type: DatabaseRecordType.Wallet, length: value?.wallets.length ?? 0, checked: false }, ] diff --git a/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/ConfirmBackup.tsx b/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/ConfirmBackup.tsx index a524177b7414..ffff595e7acb 100644 --- a/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/ConfirmBackup.tsx +++ b/packages/maskbook/src/extension/options-page/DashboardDialogs/Backup/ConfirmBackup.tsx @@ -48,7 +48,6 @@ export function ConfirmBackup({ restoreId, date, backup, onDone }: ConfirmBackup { type: DatabaseRecordType.Persona, length: backup?.personas.length ?? 0, checked: imported === true }, { type: DatabaseRecordType.Profile, length: backup?.profiles.length ?? 0, checked: imported === true }, { type: DatabaseRecordType.Post, length: backup?.posts.length ?? 0, checked: imported === true }, - { type: DatabaseRecordType.Group, length: backup?.userGroups.length ?? 0, checked: imported === true }, { type: DatabaseRecordType.Wallet, length: backup?.wallets.length ?? 0, checked: imported === true }, ] diff --git a/packages/maskbook/src/extension/options-page/DashboardDialogs/Persona/Backup.tsx b/packages/maskbook/src/extension/options-page/DashboardDialogs/Persona/Backup.tsx index 2a78c5f46db0..68579a67515b 100644 --- a/packages/maskbook/src/extension/options-page/DashboardDialogs/Persona/Backup.tsx +++ b/packages/maskbook/src/extension/options-page/DashboardDialogs/Persona/Backup.tsx @@ -17,7 +17,6 @@ export function DashboardPersonaBackupDialog(props: WrappedDialogProps { Services.Welcome.generateBackupJSON({ noPosts: true, - noUserGroups: true, filter: { type: 'persona', wanted: [persona.identifier] }, }).then((file) => { setBase64Value(encodeArrayBuffer(encodeText(JSON.stringify(file)))) diff --git a/packages/maskbook/src/extension/options-page/DashboardRouters/Settings.tsx b/packages/maskbook/src/extension/options-page/DashboardRouters/Settings.tsx index c979984e700f..6d1c09c7df99 100644 --- a/packages/maskbook/src/extension/options-page/DashboardRouters/Settings.tsx +++ b/packages/maskbook/src/extension/options-page/DashboardRouters/Settings.tsx @@ -9,11 +9,9 @@ import { useMatchXS, extendsTheme, useI18N, Flags, useValueRef } from '../../../ import { SettingsUI, SettingsUIEnum, SettingsUIDummy } from '../../../components/shared-settings/useSettingsUI' import { debugModeSetting, - disableOpenNewTabInBackgroundSettings, languageSettings, allPostReplacementSettings, appearanceSettings, - enableGroupSharingSettings, launchPageSettings, newDashboardConnection, } from '../../../settings/settings' @@ -25,9 +23,7 @@ import SwapHorizIcon from '@material-ui/icons/SwapHoriz' import MemoryOutlinedIcon from '@material-ui/icons/MemoryOutlined' import ArchiveOutlinedIcon from '@material-ui/icons/ArchiveOutlined' import UnarchiveOutlinedIcon from '@material-ui/icons/UnarchiveOutlined' -import ShareIcon from '@material-ui/icons/ShareOutlined' import FlipToFrontIcon from '@material-ui/icons/FlipToFront' -import TabIcon from '@material-ui/icons/Tab' import PaletteIcon from '@material-ui/icons/Palette' import LanguageIcon from '@material-ui/icons/Language' import WifiIcon from '@material-ui/icons/Wifi' @@ -272,11 +268,6 @@ export default function DashboardSettingsRouter() { - } - value={disableOpenNewTabInBackgroundSettings} - /> } @@ -287,11 +278,6 @@ export default function DashboardSettingsRouter() { icon={} value={allPostReplacementSettings} /> - } - value={enableGroupSharingSettings} - /> {process.env.NODE_ENV === 'development' || process.env.build !== 'stable' ? ( () - const { enqueueSnackbar, closeSnackbar } = useSnackbar() + const { enqueueSnackbar } = useSnackbar() const { uuid } = useQueryParams(['uuid']) const [imported, setImported] = useState(false) @@ -753,13 +753,11 @@ export function RestoreDatabaseConfirmation() { const personas = backup?.personas.length ?? 0 const profiles = backup?.profiles.length ?? 0 const posts = backup?.posts.length ?? 0 - const contacts = backup?.userGroups.length ?? 0 const wallets = backup?.wallets.length ?? 0 const records = [ { type: DatabaseRecordType.Persona, length: personas, checked: imported === true }, { type: DatabaseRecordType.Profile, length: profiles, checked: imported === true }, { type: DatabaseRecordType.Post, length: posts, checked: imported === true }, - { type: DatabaseRecordType.Group, length: contacts, checked: imported === true }, { type: DatabaseRecordType.Wallet, length: wallets, checked: imported === true }, ] diff --git a/packages/maskbook/src/extension/options-page/DeveloperComponents/AddProve.tsx b/packages/maskbook/src/extension/options-page/DeveloperComponents/AddProve.tsx deleted file mode 100644 index 128c726c1e57..000000000000 --- a/packages/maskbook/src/extension/options-page/DeveloperComponents/AddProve.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useState } from 'react' -import { makeStyles } from '@material-ui/core/styles' -import Card from '@material-ui/core/Card' -import CardActions from '@material-ui/core/CardActions' -import CardContent from '@material-ui/core/CardContent' -import Button from '@material-ui/core/Button' -import Typography from '@material-ui/core/Typography' -import Services from '../../service' -import { ProfileIdentifier } from '../../../database/type' -import { useTextField } from '../../../utils/hooks/useForms' - -const useStyles = makeStyles((theme) => ({ - success: { - color: 'green', - }, - error: { - color: theme.palette.error.main, - }, -})) - -export function AddProve() { - const classes = useStyles() - const [network, networkInput] = useTextField('Network', { defaultValue: 'facebook.com', required: true }) - const [userID, userIDInput] = useTextField('User ID (not nickname)', { required: true }) - const [provePost, provePostInput] = useTextField('Prove Post', { - placeholder: '🔒A+7HqsU+9p3fFIQvLSFiqrsdC1k/nXu7A1UmKjuLyyZy🔒', - required: true, - }) - // Error: Error - // false: Verify failed - // true: Success - const [lastVerifyResult, setResult] = useState() - return ( - - - - Add your friend manually - - {networkInput} - {userIDInput} - {provePostInput} - - - - - Result:{' '} - {((r) => { - if (r === undefined) return '' - if (r === false) return ❌ Failed - if (r === true) return ✔ Added - if (r instanceof Error) - return ( - - Exception: {r.message} - {console.error(r)} - - ) - return r - })(lastVerifyResult)} - - - - ) -} diff --git a/packages/maskbook/src/extension/options-page/DeveloperComponents/DecryptPost.tsx b/packages/maskbook/src/extension/options-page/DeveloperComponents/DecryptPost.tsx deleted file mode 100644 index bf842bbdd37f..000000000000 --- a/packages/maskbook/src/extension/options-page/DeveloperComponents/DecryptPost.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useMemo, useState } from 'react' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import Typography from '@material-ui/core/Typography' -import { ProfileIdentifier } from '../../../database/type' -import { useTextField } from '../../../utils/hooks/useForms' -import { DecryptPost } from '../../../components/InjectedComponents/DecryptedPost/DecryptedPost' -import { useIsolatedChooseIdentity } from '../../../components/shared/ChooseIdentity' -import { FormControlLabel, Checkbox } from '@material-ui/core' - -export function DecryptPostDeveloperMode() { - const [whoAmI, chooseIdentity] = useIsolatedChooseIdentity() - const [postByMyself, setPostByMyself] = useState(false) - const [author, authorInput] = useTextField('Author ID of this post', { - required: !postByMyself, - disabled: postByMyself, - }) - const [encryptedText, encryptedTextInput] = useTextField('Encrypted post', { - placeholder: '🎼3/4|ownersAESKeyEncrypted|iv|encryptedText|signature|publicShared|author:||', - required: true, - }) - const network = whoAmI ? whoAmI.identifier.network : 'localhost' - const authorIdentifier = useMemo(() => new ProfileIdentifier(network, author), [network, author]) - const whoAmIIdentifier = whoAmI ? whoAmI.identifier : ProfileIdentifier.unknown - return ( - - - - Decrypt post manually - - - Your identity? - - {chooseIdentity} - {/* {networkInput} */} - setPostByMyself(e.target.checked)} - /> - } - label="Post by myself" - /> - {authorInput} - {encryptedTextInput} -
- {}} - profiles={[]} - whoAmI={whoAmIIdentifier} - /> -
-
-
- ) -} diff --git a/packages/maskbook/src/extension/options-page/DeveloperComponents/SeeMyProvePost.tsx b/packages/maskbook/src/extension/options-page/DeveloperComponents/SeeMyProvePost.tsx deleted file mode 100644 index 08528dec2210..000000000000 --- a/packages/maskbook/src/extension/options-page/DeveloperComponents/SeeMyProvePost.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useState, useMemo } from 'react' -import { makeStyles } from '@material-ui/core/styles' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import Typography from '@material-ui/core/Typography' -import Services from '../../service' -import { useIsolatedChooseIdentity } from '../../../components/shared/ChooseIdentity' - -const useStyles = makeStyles({ - prove: { - wordBreak: 'break-all', - }, -}) - -export function SeeMyProvePost() { - const classes = useStyles() - const [whoAmI, chooseIdentity] = useIsolatedChooseIdentity() - const [provePost, setProvePost] = useState('') - useMemo(() => { - if (!whoAmI) return - Services.Crypto.getMyProveBio(whoAmI.identifier).then(setProvePost) - }, [whoAmI]) - return ( - - - See my prove post - {chooseIdentity} - Your prove post is: - - {provePost} - - - - ) -} diff --git a/packages/maskbook/src/extension/service.ts b/packages/maskbook/src/extension/service.ts index ec4394e39e26..a5fff5db4448 100644 --- a/packages/maskbook/src/extension/service.ts +++ b/packages/maskbook/src/extension/service.ts @@ -26,7 +26,6 @@ const log: AsyncCallOptions['log'] = { export const Services = { Crypto: add(() => import('./background-script/CryptoService'), 'Crypto'), Identity: add(() => import('./background-script/IdentityService'), 'Identity'), - UserGroup: add(() => import('./background-script/UserGroupService'), 'UserGroup'), Welcome: add(() => import('./background-script/WelcomeService'), 'Welcome'), Steganography: add(() => import('./background-script/SteganographyService'), 'Steganography'), Helper: add(() => import('./background-script/HelperService'), 'Helper'), @@ -44,7 +43,6 @@ if (import.meta.webpackHot && isEnvironment(Environment.ManifestBackground)) { [ './background-script/CryptoService', './background-script/IdentityService', - './background-script/UserGroupService', './background-script/WelcomeService', './background-script/SteganographyService', './background-script/HelperService', diff --git a/packages/maskbook/src/network/gun/version.1/index.ts b/packages/maskbook/src/network/gun/version.1/index.ts index 6774cf121859..a1f5946ec360 100644 --- a/packages/maskbook/src/network/gun/version.1/index.ts +++ b/packages/maskbook/src/network/gun/version.1/index.ts @@ -13,13 +13,13 @@ import { gun2 } from '../version.2' */ interface ApplicationStateInGunVersion1 { maskbook: { - users: { - // User ID - [userID: string]: { - /** @deprecated if you want to read it, cast it to string */ - provePostId: never - } - } + // This section is no longer used but we leave the type here to avoid future collisions + // users: { + // [userID: string]: { + // /** @deprecated if you want to read it, cast it to string */ + // provePostId: never + // } + // } posts: { // Post salt [salt: string]: { diff --git a/packages/maskbook/src/network/gun/version.2/hash.ts b/packages/maskbook/src/network/gun/version.2/hash.ts index feb9c8493dc0..42cdf43bf569 100644 --- a/packages/maskbook/src/network/gun/version.2/hash.ts +++ b/packages/maskbook/src/network/gun/version.2/hash.ts @@ -2,7 +2,7 @@ /** * @see https://github.com/DimensionDev/Maskbook/wiki/Data-structure-on-Gun-version-2 */ -import type { EC_Public_JsonWebKey, ProfileIdentifier } from '@masknet/shared-base' +import type { EC_Public_JsonWebKey } from '@masknet/shared-base' import Gun from 'gun' import 'gun/sea' import { memoizePromise } from '../../../utils/memoize' @@ -24,15 +24,6 @@ export async function calculatePostKeyPartition( const keyHash = await (version <= -39 ? hashCryptoKeyUnstable : hashCryptoKey)(partitionByCryptoKey) return { postHash, keyHash } } -export const hashProfileIdentifier = memoizePromise( - async function hashProfileIdentifier(id: ProfileIdentifier) { - const hashPair = `f67a6a2c-fe66-4f47-bd1f-00a5603d1010` - - const hash = await Gun.SEA.work(id.toText(), hashPair)! - return hash! - }, - (id) => id.toText(), -) export const hashPostSalt = memoizePromise( async function (postSalt: string, networkHint: string) { diff --git a/packages/maskbook/src/network/gun/version.2/index.ts b/packages/maskbook/src/network/gun/version.2/index.ts index a9ea7675f428..9517744fb5a6 100644 --- a/packages/maskbook/src/network/gun/version.2/index.ts +++ b/packages/maskbook/src/network/gun/version.2/index.ts @@ -7,15 +7,13 @@ import 'gun/lib/rindexed' import { gunServers } from '../../gun-servers' import type { EC_Public_JsonWebKey } from '@masknet/shared-base' -export * from './people' export * from './post' -export type PersonOnGun2 = - | { - /** @deprecated if you want to use it, cast it to string. */ - provePostId?: unknown - } - | undefined +export type PersonOnGun2 = { + /** @deprecated if you want to use it, cast it to string. */ + // provePostId?: unknown + __notInGunButABrand__DO_NOT_USE_THIS__?: never +} export type SharedAESKeyGun2 = { encryptedKey: string salt: string diff --git a/packages/maskbook/src/network/gun/version.2/people.ts b/packages/maskbook/src/network/gun/version.2/people.ts deleted file mode 100644 index e893c8068d01..000000000000 --- a/packages/maskbook/src/network/gun/version.2/people.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { ProfileIdentifier } from '@masknet/shared-base' -import { gun2, PersonOnGun2 as ProfileOnGun2 } from '.' -import { hashProfileIdentifier } from './hash' -import { EventIterator } from 'event-iterator' - -/** - * Query person from gun by once. - * @param user Identifier - */ -export async function queryPersonFromGun2(user: ProfileIdentifier): Promise { - const result = await gun2.get(await hashProfileIdentifier(user)).then!() - return result || {} -} - -// ? Gun's off have bugs. -// ? See: https://github.com/amark/gun/issues/541 -/** - * Subscribe changes of this person on gun. - * @param user Identifier - * @param callback Callback - */ -export async function* subscribeProfileFromGun2(user: ProfileIdentifier) { - const iter = new EventIterator(({ push: callback }) => { - hashProfileIdentifier(user).then((hash) => - gun2.get(hash).on((data: ProfileOnGun2) => { - const data2 = Object.assign({}, data) - // @ts-ignore - delete data2._ - callback(data2) - }), - ) - }) - yield* iter -} -/** - * Note: unused. - * Write person to gun. - * @param user Identifier - * @param data Data needs to write - */ -async function writeProfileOnGun(user: ProfileIdentifier, data: NonNullable) { - return gun2.get(await hashProfileIdentifier(user)).put(data).then!() -} diff --git a/packages/maskbook/src/network/gun/version.2/post.ts b/packages/maskbook/src/network/gun/version.2/post.ts index f8447056b09d..f926aba1ea4f 100644 --- a/packages/maskbook/src/network/gun/version.2/post.ts +++ b/packages/maskbook/src/network/gun/version.2/post.ts @@ -54,6 +54,7 @@ export async function* subscribePostKeysOnGun2( // @ts-ignore .get(keyHash) .map() + // @ts-ignore .on((data: SharedAESKeyGun2) => { // @ts-ignore const { _, ...data2 } = Object.assign({}, data) diff --git a/packages/maskbook/src/network/gun/worker-implementation-subscribe.ts b/packages/maskbook/src/network/gun/worker-implementation-subscribe.ts index 7891e3a42b9b..e9d7cf1b21e3 100644 --- a/packages/maskbook/src/network/gun/worker-implementation-subscribe.ts +++ b/packages/maskbook/src/network/gun/worker-implementation-subscribe.ts @@ -1,2 +1 @@ export { subscribePostKeysOnGun2 } from './version.2/post' -export { subscribeProfileFromGun2 } from './version.2/people' diff --git a/packages/maskbook/src/network/gun/worker-implementation.ts b/packages/maskbook/src/network/gun/worker-implementation.ts index 6c6a5d45dadb..3f8ff74076c4 100644 --- a/packages/maskbook/src/network/gun/worker-implementation.ts +++ b/packages/maskbook/src/network/gun/worker-implementation.ts @@ -1,2 +1,2 @@ export { queryVersion1PostAESKey, getVersion1PostByHash } from './version.1' -export { publishPostAESKeyOnGun2, queryPersonFromGun2, queryPostKeysOnGun2 } from './version.2' +export { publishPostAESKeyOnGun2, queryPostKeysOnGun2 } from './version.2' diff --git a/packages/maskbook/src/plugins/Polls/Services.ts b/packages/maskbook/src/plugins/Polls/Services.ts index b9e2592195d0..1e6a8b989bd2 100644 --- a/packages/maskbook/src/plugins/Polls/Services.ts +++ b/packages/maskbook/src/plugins/Polls/Services.ts @@ -71,6 +71,7 @@ export async function vote(props: voteProps) { PollGun // @ts-ignore .get(poll.key) + // @ts-ignore .get('results') .on((item) => { // @ts-ignore @@ -86,6 +87,7 @@ export async function vote(props: voteProps) { PollGun // @ts-ignore .get(poll.key) + // @ts-ignore .get('results') // @ts-ignore .put(newResults) @@ -107,6 +109,7 @@ export async function getPollByKey(props: { key: string }) { PollGun // @ts-ignore .get(props.key) + // @ts-ignore .on((data: PollGunDB) => { poll = { ...poll, @@ -119,6 +122,7 @@ export async function getPollByKey(props: { key: string }) { PollGun // @ts-ignore .get(props.key) + // @ts-ignore .get('options') .on((options) => { // @ts-ignore @@ -130,6 +134,7 @@ export async function getPollByKey(props: { key: string }) { PollGun // @ts-ignore .get(props.key) + // @ts-ignore .get('results') .on((results) => { // @ts-ignore diff --git a/packages/maskbook/src/protocols/friendship-discovery/__tests__/friendship-discover.ts b/packages/maskbook/src/protocols/friendship-discovery/__tests__/friendship-discover.ts deleted file mode 100644 index 949a2f04b399..000000000000 --- a/packages/maskbook/src/protocols/friendship-discovery/__tests__/friendship-discover.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { packFriendshipCertificate, unpackFriendshipCertificate, issueFriendshipCertificate } from '../friendship-pack' -import { ProfileIdentifier } from '../../../database/type' -import { CryptoWorker } from '../../../modules/workers' - -const aliceID = new ProfileIdentifier('localhost', 'alice.test') -async function testFriendshipDiscover() { - // Alice don't need a keypair during the process - const bob = await CryptoWorker.generate_ec_k256_pair() - - const rawCert = await issueFriendshipCertificate( - aliceID, - await CryptoWorker.generate_aes_gcm(), - Math.random().toString(), - ) - // Alice to bob - const encryptedCert = await packFriendshipCertificate(rawCert, bob.publicKey) - - // Bob verify the cert - const unpacked = await unpackFriendshipCertificate(encryptedCert, bob.privateKey) - if (!unpacked) throw new Error('Unpack cert failed') - if (!unpacked.certificateIssuer.equals(aliceID)) throw new Error('???') - - // Bob should verify their friendship by other channel. - return true -} - -test('Friendship certificate', () => testFriendshipDiscover()) diff --git a/packages/maskbook/src/protocols/friendship-discovery/friendship-cert.d.ts b/packages/maskbook/src/protocols/friendship-discovery/friendship-cert.d.ts deleted file mode 100644 index b4b8c47042bb..000000000000 --- a/packages/maskbook/src/protocols/friendship-discovery/friendship-cert.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ProfileIdentifier } from '../../database/type' -import type { EC_Public_JsonWebKey, AESJsonWebKey } from '../../modules/CryptoAlgorithm/interfaces/utils' - -/** - * Server guide: - * - On receive `packed`: - * 1. If `packed.cryptoKey` is used before, drop it. - * 2. Set `packed.timestamp` to current time. - * 3. Drop this object after a period of time. e.g. 31days - */ -/** - * @remarks - * ! This object should be encrypted with a NEW RANDOM crypto key ! - */ -export interface FriendshipCertificateDecryptedV1 { - /** - * Who declared they issued this certificate - * ! DO NOT trust this field ! - */ - certificateIssuer: ProfileIdentifier - /** - * ? This key is used to join and decrypt the message in the private channel - */ - channelCryptoKey: AESJsonWebKey - /** - * ? This seed is used to generate the deterministic channel ID - */ - channelSeed: string -} -export interface FriendshipCertificateEncryptedV1 { - version: 1 - /** - * ! A NEW RANDOM crypto key ! - * ! If a server receive a cert with a cryptoKey previously received, reject it ! - */ - cryptoKey: EC_Public_JsonWebKey - /** - * This is encrypted {@link FriendshipCertificate} by {@link FriendshipCertificatePacked.cryptoKey} - */ - payload: string - /** - * timestamp - * ! Server should overwrite it ! - * ? If server is some kind of decentralized instance message service, - * ? use the message timestamp - */ - timestamp: number - /** iv used to decrypt the payload */ - iv: string -} diff --git a/packages/maskbook/src/protocols/friendship-discovery/friendship-client.ts b/packages/maskbook/src/protocols/friendship-discovery/friendship-client.ts deleted file mode 100644 index efd1d221f15b..000000000000 --- a/packages/maskbook/src/protocols/friendship-discovery/friendship-client.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { FriendshipCertificateEncryptedV1 } from './friendship-cert' - -declare function publishCertificate(cert: FriendshipCertificateEncryptedV1): Promise -declare function pullCertificates( - afterTimeStamp: number, - partition: string, -): Promise diff --git a/packages/maskbook/src/protocols/friendship-discovery/friendship-pack.ts b/packages/maskbook/src/protocols/friendship-discovery/friendship-pack.ts deleted file mode 100644 index a726e30d6834..000000000000 --- a/packages/maskbook/src/protocols/friendship-discovery/friendship-pack.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { FriendshipCertificateDecryptedV1, FriendshipCertificateEncryptedV1 } from './friendship-cert' -import { encryptWithAES, decryptWithAES } from '../../crypto/crypto-alpha-40' -import { encodeArrayBuffer, decodeText } from '../../utils/type-transform/String-ArrayBuffer' -import { ProfileIdentifier } from '../../database/type' -import { CryptoWorker } from '../../modules/workers' -import type { - EC_Public_JsonWebKey, - AESJsonWebKey, - EC_Private_JsonWebKey, -} from '../../modules/CryptoAlgorithm/interfaces/utils' -import { derive_AES_GCM_256_Key_From_ECDH_256k1_Keys } from '../../modules/CryptoAlgorithm/helper' - -export async function issueFriendshipCertificate( - whoAmI: ProfileIdentifier, - channelCryptoKey: AESJsonWebKey, - channelSeed: string, -): Promise { - return { - certificateIssuer: whoAmI, - channelCryptoKey, - channelSeed: channelSeed, - } -} - -/** - * Pack steps for {@link FriendshipCertificateV1} - * @remakrs - * Pack Steps: - * 1. let `cert` = New {@link FriendshipCertificateV1} - * 2. let `key` = A new random CryptoKey. algorithm = 'ECDH', curve: 'K-256' - * 3. Derive an AES key by key of `cert target` and `key`, let it be `aes` - * 4. Encrypt `cert` with `aes`, let it be `payload`, store the `iv` - * 5. let `packed` = @{link FriendshipCertificatePackedV1} (version: `1`, payload: `payload`, cryptoKey: `key`, iv: `iv`) - * 6. Send `packed` to server - */ -export async function packFriendshipCertificate( - cert: FriendshipCertificateDecryptedV1, - targetKey: EC_Public_JsonWebKey, -): Promise { - const key = await CryptoWorker.generate_ec_k256_pair() - const aes = await CryptoWorker.derive_aes_from_ecdh_k256(key.privateKey, targetKey, 'AES-GCM', 256) - const { content: payload, iv } = await encryptWithAES({ - aesKey: aes, - content: JSON.stringify(cert), - }) - return { - cryptoKey: key.publicKey, - iv: encodeArrayBuffer(iv), - payload: encodeArrayBuffer(payload), - timestamp: Date.now(), - version: 1, - } -} -/** - * Verify Steps: - * For each `packed` ({@link FriendshipCertificatePackedV1}): - * 1. Derive an AES key by `packed.cryptoKey` and your own key, let it be `aes` - * 2. Decrypt `packed.payload`, if failed, drop it; else, let it be `cert` - * 3. Manual or automatically verify friendship of `cert.myId` on network `cert.network` - */ -export async function unpackFriendshipCertificate( - packed: FriendshipCertificateEncryptedV1, - privateKey: EC_Private_JsonWebKey, -): Promise { - const packedCryptoKey = packed.cryptoKey - const aes = await derive_AES_GCM_256_Key_From_ECDH_256k1_Keys(privateKey, packedCryptoKey) - try { - const certBuffer = await decryptWithAES({ aesKey: aes, encrypted: packed.payload, iv: packed.iv }) - const certString = decodeText(certBuffer) - const cert: FriendshipCertificateDecryptedV1 = JSON.parse(certString) - if (!cert.certificateIssuer || !cert.channelCryptoKey || !cert.channelSeed) - throw new TypeError('Not a valid cert.') - // Recover prototype - Object.setPrototypeOf(cert.certificateIssuer, ProfileIdentifier.prototype) - return cert - } catch { - return null - } -} diff --git a/packages/maskbook/src/protocols/friendship-discovery/index.md b/packages/maskbook/src/protocols/friendship-discovery/index.md deleted file mode 100644 index 73e2cb051e17..000000000000 --- a/packages/maskbook/src/protocols/friendship-discovery/index.md +++ /dev/null @@ -1,11 +0,0 @@ -# This folder is implementing - -## Cooperative Friendship Discovery Mechanism - -- - - diff --git a/packages/maskbook/src/settings/listener.ts b/packages/maskbook/src/settings/listener.ts index 4b6b9db1e96d..69fd87637195 100644 --- a/packages/maskbook/src/settings/listener.ts +++ b/packages/maskbook/src/settings/listener.ts @@ -4,7 +4,6 @@ import { appearanceSettings, languageSettings, debugModeSetting, - disableOpenNewTabInBackgroundSettings, currentPersonaIdentifier, } from './settings' import { @@ -31,7 +30,6 @@ export function ToBeListened() { currentBlockNumberSettings, currentEtherPriceSettings, currentTrendingDataProviderSettings: currentDataProviderSettings, - disableOpenNewTabInBackgroundSettings, currentProviderSettings, currentNetworkSettings, currentAccountSettings, diff --git a/packages/maskbook/src/settings/settings.ts b/packages/maskbook/src/settings/settings.ts index 33fb303c6606..6361d15d25b9 100644 --- a/packages/maskbook/src/settings/settings.ts +++ b/packages/maskbook/src/settings/settings.ts @@ -12,18 +12,6 @@ export const debugModeSetting = createGlobalSettings('debugMode', false secondary: () => i18n.t('settings_enable_debug_desc'), }) -/** - * Never open a new tab in the background - */ -export const disableOpenNewTabInBackgroundSettings = createGlobalSettings( - 'disable automated tab task open new tab', - true, - { - primary: () => i18n.t('settings_ancient_post_compatibility_mode'), - secondary: () => i18n.t('settings_ancient_post_compatibility_mode_desc'), - }, -) - /** * Whether if create substitute post for all posts */ @@ -48,11 +36,6 @@ export const languageSettings = createGlobalSettings( ) //#endregion -export const enableGroupSharingSettings = createGlobalSettings('experimental/group-sharing@sept2020', false, { - primary: () => 'Experimental: Enable group sharing', - secondary: () => '(Unstable) Automatically share posts to a group', -}) - //#region network setting /** diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/getPostContent.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/getPostContent.ts deleted file mode 100644 index bc90b147a515..000000000000 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/getPostContent.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { LiveSelector, MutationObserverWatcher } from '@dimensiondev/holoflows-kit' -import { timeout } from '../../../utils/utils' - -const postContent = new LiveSelector().querySelector('[data-ad-preview="message"]') -export async function getPostContentFacebook(): Promise { - return get(postContent) -} -async function get(post: LiveSelector) { - const [data] = await timeout(new MutationObserverWatcher(post), 10000) - return data.innerText -} diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/getProfile.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/getProfile.ts deleted file mode 100644 index e3f7be70e35d..000000000000 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/getProfile.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { timeout } from '../../../utils/utils' -import { MutationObserverWatcher, LiveSelector } from '@dimensiondev/holoflows-kit' - -const bioCard = new LiveSelector().querySelector('#profile_timeline_intro_card') -export async function getProfileFacebook() { - const [data] = await timeout(new MutationObserverWatcher(bioCard), 10000) - return { bioContent: data.innerText } -} diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/profiles.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/profiles.ts deleted file mode 100644 index 820e48c523af..000000000000 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/collecting/profiles.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { LiveSelector, MutationObserverWatcher } from '@dimensiondev/holoflows-kit' -import { getProfileIdentifierAtFacebook } from '../utils/getProfileIdentifier' -import Services from '../../../extension/service' -import { GroupIdentifier, ProfileIdentifier } from '../../../database/type' -import { currentSelectedIdentity } from '../../../settings/settings' -export function profilesCollectorFacebook(signal: AbortSignal) { - const whoAmI = currentSelectedIdentity['facebook.com'] - // TODO: support mobile - const bio = new LiveSelector().querySelector('#profile_timeline_intro_card').enableSingleMode() - const watcher = new MutationObserverWatcher(bio) - /** - * @var node: bio in the side of user page - */ - .useForeach((node) => { - function tryFindBioKey() { - /** - * @var text - * @example - * "...r Xoogler, MaskBook: A80TOj...eW9yqf Google - Softwa..." - */ - const text = node.innerText - - /** - * Also collect 'identifier' | 'nickname' | 'avatar' - */ - const a = document.querySelector('#fb-timeline-cover-name a') - // side effect: save to service - const id = getProfileIdentifierAtFacebook(a, true) - if (!id) return - Services.Crypto.verifyOthersProve(text, id.identifier) - return id - } - function parseFriendship() { - const thisPerson = tryFindBioKey() - if (!thisPerson || !whoAmI.ready) return - const [isFriendNow] = isFriend.evaluate() - const myFriends = GroupIdentifier.getDefaultFriendsGroupIdentifier( - ProfileIdentifier.fromString(whoAmI.value, ProfileIdentifier).unwrap(), - ) - if (isFriendNow === Status.Friend) { - Services.UserGroup.addProfileToFriendsGroup(myFriends, [thisPerson.identifier]) - console.log('Adding friend', thisPerson.identifier, 'to', myFriends) - } else if (isFriendNow === Status.NonFriend) { - Services.UserGroup.removeProfileFromFriendsGroup(myFriends, [thisPerson.identifier]) - console.log('Removing friend', thisPerson.identifier, 'from', myFriends) - } - } - whoAmI.addListener(parseFriendship) - parseFriendship() - return { - onNodeMutation: parseFriendship, - onTargetChanged: parseFriendship, - } - }) - watcher.startWatch({ - childList: true, - subtree: true, - }) - signal.addEventListener('abort', () => watcher.stopWatch()) -} -enum Status { - NonFriend = 1, - Friend = 2, - Unknown = 3, -} -/** - * Ack: - * If `#pagelet_timeline_profile_actions button:not(.hidden_elem)` have 3 nodes, they are friend. - * If have 2 children, they are not friend. - */ -const isFriend = new LiveSelector() - .querySelectorAll('#pagelet_timeline_profile_actions button:not(.hidden_elem)') - .replace((arr) => { - if (arr.length === 3) return [Status.Friend] - else if (arr.length === 2) return [Status.NonFriend] - return [Status.Unknown] - }) diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/ui-provider.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/ui-provider.ts index 09c336c8d5c2..c3a426fc9be8 100644 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/ui-provider.ts +++ b/packages/maskbook/src/social-network-adaptor/facebook.com/ui-provider.ts @@ -3,8 +3,6 @@ import { stateCreator } from '../../social-network/utils' import { facebookBase } from './base' import { facebookShared } from './shared' import { getProfilePageUrlAtFacebook } from './utils/parse-username' -import { getPostContentFacebook } from './collecting/getPostContent' -import { getProfileFacebook } from './collecting/getProfile' import { taskOpenComposeBoxFacebook } from './automation/openComposeBox' import { pasteTextToCompositionFacebook } from './automation/pasteTextToComposition' import { IdentityProviderFacebook } from './collecting/identity' @@ -16,7 +14,6 @@ import { injectPostCommentsDefault } from '../../social-network/defaults/inject/ import { pasteToCommentBoxFacebook } from './automation/pasteToCommentBoxFacebook' import { injectCommentBoxDefaultFactory } from '../../social-network/defaults/inject/CommentBox' import { injectPostInspectorFacebook } from './injection/PostInspector' -import { profilesCollectorFacebook } from './collecting/profiles' import { PostProviderFacebook } from './collecting/posts' import { pasteImageToCompositionDefault } from '../../social-network/defaults/automation/AttachImageToComposition' import { injectPageInspectorDefault } from '../../social-network/defaults/inject/PageInspector' @@ -59,9 +56,6 @@ const facebookUI: SocialNetworkUI.Definition = { }, }, collecting: { - getPostContent: getPostContentFacebook, - getProfile: getProfileFacebook, - profilesCollector: profilesCollectorFacebook, identityProvider: IdentityProviderFacebook, postsProvider: PostProviderFacebook, }, diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/utils/parse-html.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/utils/parse-html.ts deleted file mode 100644 index 699d3e80b315..000000000000 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/utils/parse-html.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Parse static result from fb - */ -export function parseFacebookStaticHTML(html: string): (HTMLElement | Document)[] { - const parser = new DOMParser() - const doc1 = parser.parseFromString(html, 'text/html') - const codeDom = doc1.body.querySelector('code') - const rootDom = doc1.body.querySelector('#root') - if (codeDom) { - const nodes = Array.from(doc1.body.querySelectorAll('code')) - .map((x) => { - const comment = x.childNodes[0] - if (!comment) return null - return parser.parseFromString(comment.textContent || '', 'text/html') - }) - .filter((x) => x) - return nodes as Document[] - } - - // node is absent in old version profile page since use timeline node instead - if (rootDom) return [rootDom] - return [] -} diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/utils/parse-username.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/utils/parse-username.ts index 30e55f5761b6..81485b2ff3e9 100644 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/utils/parse-username.ts +++ b/packages/maskbook/src/social-network-adaptor/facebook.com/utils/parse-username.ts @@ -1,4 +1,4 @@ -import { ProfileIdentifier, GroupIdentifier, PostIdentifier } from '../../../database/type' +import type { ProfileIdentifier, PostIdentifier } from '../../../database/type' import { isMobileFacebook } from './isMobile' import { i18n } from '../../../utils/i18n-next' @@ -32,8 +32,7 @@ export function getPostUrlAtFacebook(post: PostIdentifier) { /** * Normalize profile url */ -export function getProfilePageUrlAtFacebook(user: ProfileIdentifier | GroupIdentifier) { - if (user instanceof GroupIdentifier) throw new Error('Not implemented') +export function getProfilePageUrlAtFacebook(user: ProfileIdentifier) { if (user.network !== 'facebook.com') throw new Error('Wrong origin') const host = getFacebookHostName() diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/worker-provider.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/worker-provider.ts index e18f80f932ee..b522baf96cfe 100644 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/worker-provider.ts +++ b/packages/maskbook/src/social-network-adaptor/facebook.com/worker-provider.ts @@ -1,15 +1,10 @@ import type { SocialNetworkWorker } from '../../social-network' import { facebookWorkerBase } from './base' import { facebookShared } from './shared' -import { fetchPostContentFacebook } from './worker/fetchPostContent' -import { fetchProfileFacebook } from './worker/fetchProfile' const facebookWorker: SocialNetworkWorker.Definition = { ...facebookWorkerBase, ...facebookShared, - tasks: { - fetchPostContent: fetchPostContentFacebook, - fetchProfile: fetchProfileFacebook, - }, + tasks: {}, } export default facebookWorker diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/worker/fetchPostContent.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/worker/fetchPostContent.ts deleted file mode 100644 index 780778b8f929..000000000000 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/worker/fetchPostContent.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { ProfileIdentifier, PostIdentifier } from '../../../database/type' -import { parseFacebookStaticHTML } from '../utils/parse-html' -import { getPostUrlAtFacebook } from '../utils/parse-username' -import tasks from '../../../extension/content-script/tasks' -import { timeout } from '../../../utils/utils' -import { getActiveTabFacebook } from '../../../utils/tabs' -import { isDocument } from '../../../utils/dom' - -// ? We now always run fetch request from an active tab. -// ? If failed, we will fallback to open a new tab to do this. -export async function fetchPostContentFacebook(post: PostIdentifier): Promise { - const activeTabID = await getActiveTabFacebook() - if (activeTabID !== undefined) { - // Path 1: fetch by http req - const url = getPostUrlAtFacebook(post) - const { memoizeFetch } = tasks(activeTabID) - const html = await timeout(memoizeFetch(url), 10000).catch((_) => null) - if (html !== null) { - try { - const doc = parseFacebookStaticHTML(html) - if (doc.length) return doc.map((x) => (isDocument(x) ? x.body : x).innerText).join('') - } catch (e) { - console.warn(e) - memoizeFetch.cache?.delete(url) - } - } - } - - // Path 2: fetch by tab task - return tasks(getPostUrlAtFacebook(post)).getPostContent() -} diff --git a/packages/maskbook/src/social-network-adaptor/facebook.com/worker/fetchProfile.ts b/packages/maskbook/src/social-network-adaptor/facebook.com/worker/fetchProfile.ts deleted file mode 100644 index d251a4da1fbe..000000000000 --- a/packages/maskbook/src/social-network-adaptor/facebook.com/worker/fetchProfile.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { ProfileIdentifier } from '../../../database/type' -import { getProfilePageUrlAtFacebook } from '../utils/parse-username' -import { parseFacebookStaticHTML } from '../utils/parse-html' -import type { SocialNetworkUI } from '../../../social-network' -import tasks from '../../../extension/content-script/tasks' -import { timeout } from '../../../utils/utils' -import { getActiveTabFacebook } from '../../../utils/tabs' - -// ? We now always run fetch request from an active tab. -// ? If failed, we will fallback to open a new tab to do this. -export async function fetchProfileFacebook( - who: ProfileIdentifier, -): Promise { - const activeTabID = await getActiveTabFacebook() - if (activeTabID) { - const url = getProfilePageUrlAtFacebook(who) - const { memoizeFetch } = tasks(activeTabID) - const html = await timeout(memoizeFetch(url), 10000).catch((_) => null) - if (html !== null) { - // Path 1: fetch by http req - try { - const doc = parseFacebookStaticHTML(html) - if (!doc.length) throw new Error("Can't parse the page") - const bio = doc - .map( - (doc) => - doc.querySelector('#intro_container_id') || - doc.querySelector('#bio') || - doc.querySelector('div > div > div:nth-child(2) > div:nth-child(2)'), - ) - .map((x) => x && x.innerText) - .join('') - return { bioContent: bio } - } catch (e) { - console.warn(e) - memoizeFetch.cache?.delete(url) - } - } - } - - // Path 2: fetch by tab task - return tasks(getProfilePageUrlAtFacebook(who)).getProfile() -} diff --git a/packages/maskbook/src/social-network-adaptor/minds.com/ui-provider.ts b/packages/maskbook/src/social-network-adaptor/minds.com/ui-provider.ts index c06ec1007cf0..e58e89e7e240 100644 --- a/packages/maskbook/src/social-network-adaptor/minds.com/ui-provider.ts +++ b/packages/maskbook/src/social-network-adaptor/minds.com/ui-provider.ts @@ -47,7 +47,6 @@ const mindsUI: SocialNetworkUI.Definition = { collecting: { identityProvider: IdentityProviderMinds, postsProvider: PostProviderMinds, - profilesCollector: undefined, getSearchedKeyword: getSearchedKeywordAtMinds, }, customization: { diff --git a/packages/maskbook/src/social-network-adaptor/minds.com/utils/url.ts b/packages/maskbook/src/social-network-adaptor/minds.com/utils/url.ts deleted file mode 100644 index ed7b95717761..000000000000 --- a/packages/maskbook/src/social-network-adaptor/minds.com/utils/url.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { PostIdentifier, ProfileIdentifier } from '../../../database/type' -import { i18n } from '../../../utils/i18n-next' -import { usernameValidator } from './user' - -export const mindsUrl = { - hostIdentifier: 'minds.com', - hostLeadingUrl: 'https://minds.com', -} - -export const getPostUrlAtMinds = (post: PostIdentifier, isMobile: boolean = false) => { - if (!usernameValidator(post.identifier.userId)) throw new Error(i18n.t('service_username_invalid')) - return `${mindsUrl.hostLeadingUrl}/newsfeed/${post.postId}` -} - -export const getProfileUrlAtMinds = (self: ProfileIdentifier, isMobile: boolean = false) => { - return `${mindsUrl.hostLeadingUrl}/${self.userId}` -} diff --git a/packages/maskbook/src/social-network-adaptor/minds.com/worker-provider.ts b/packages/maskbook/src/social-network-adaptor/minds.com/worker-provider.ts index 815e3b971306..6c7661db09e5 100644 --- a/packages/maskbook/src/social-network-adaptor/minds.com/worker-provider.ts +++ b/packages/maskbook/src/social-network-adaptor/minds.com/worker-provider.ts @@ -1,14 +1,10 @@ import type { SocialNetworkWorker } from '../../social-network' import { mindsWorkerBase } from './base' import { mindsShared } from './shared' -import { fetchPostContent, fetchProfile } from './worker/fetch' const mindsWorker: SocialNetworkWorker.Definition = { ...mindsWorkerBase, ...mindsShared, - tasks: { - fetchPostContent, - fetchProfile, - }, + tasks: {}, } export default mindsWorker diff --git a/packages/maskbook/src/social-network-adaptor/minds.com/worker/fetch.ts b/packages/maskbook/src/social-network-adaptor/minds.com/worker/fetch.ts deleted file mode 100644 index 402f30cb532b..000000000000 --- a/packages/maskbook/src/social-network-adaptor/minds.com/worker/fetch.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { PostIdentifier, ProfileIdentifier } from '../../../database/type' -import tasks from '../../../extension/content-script/tasks' -import type { SocialNetworkUI } from '../../../social-network' -import { getPostUrlAtMinds, getProfileUrlAtMinds } from '../utils/url' - -export function fetchPostContent(post: PostIdentifier): Promise { - return tasks(getPostUrlAtMinds(post)).getPostContent() -} - -export function fetchProfile(self: ProfileIdentifier): Promise { - return tasks(getProfileUrlAtMinds(self, false), {}).getProfile() -} diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/getPostContent.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/getPostContent.ts deleted file mode 100644 index 00b55c294a00..000000000000 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/getPostContent.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { timeout } from '../../../utils/utils' -import { postsSelector } from '../utils/selector' -import type { SocialNetworkUI } from '../../../social-network' -import { postContentParser } from '../utils/fetch' -import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit' - -export const getPostContentTwitter: SocialNetworkUI.CollectingCapabilities.Define['getPostContent'] = async () => { - const contentNode = (await timeout(new MutationObserverWatcher(postsSelector()), 10000))[0] - return contentNode ? postContentParser(contentNode) : '' -} diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/getProfile.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/getProfile.ts deleted file mode 100644 index a641177a9e2d..000000000000 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/getProfile.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { timeout } from '../../../utils/utils' -import { bioCardSelector } from '../utils/selector' -import { bioCardParser } from '../utils/fetch' -import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit' -import { twitterEncoding } from '../encoding' - -export async function getProfileTwitter() { - const { publicKeyEncoder, publicKeyDecoder } = twitterEncoding - const cardNode = (await timeout(new MutationObserverWatcher(bioCardSelector(false)), 10000))[0] - const bio = cardNode ? bioCardParser(cardNode).bio : '' - return { - bioContent: publicKeyEncoder(publicKeyDecoder(bio)[0] || ''), - } -} diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/profiles.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/profiles.ts deleted file mode 100644 index a38415f42dda..000000000000 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/collecting/profiles.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { bioCardSelector } from '../utils/selector' -import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit' -import { GroupIdentifier, PreDefinedVirtualGroupNames } from '../../../database/type' -import type { SocialNetworkUI } from '../../../social-network' -import { bioCardParser } from '../utils/fetch' -import Services from '../../../extension/service' -import { IdentityProviderTwitter } from './identity' - -export function profilesCollectorTwitter(signal: AbortSignal) { - registerUserCollectorInner(IdentityProviderTwitter.lastRecognized, signal) -} -function registerUserCollectorInner( - ref: SocialNetworkUI.CollectingCapabilities.IdentityResolveProvider['lastRecognized'], - signal: AbortSignal, -) { - const watcher = new MutationObserverWatcher(bioCardSelector()) - .useForeach((cardNode: HTMLDivElement) => { - const resolve = async () => { - if (!cardNode) return - const { isFollower, isFollowing, identifier } = bioCardParser(cardNode) - const myIdentity = await Services.Identity.queryProfile(ref.value.identifier) - if (signal?.aborted) return - const myFriends = GroupIdentifier.getFriendsGroupIdentifier( - myIdentity.identifier, - PreDefinedVirtualGroupNames.friends, - ) - const myFollowers = GroupIdentifier.getFriendsGroupIdentifier( - myIdentity.identifier, - PreDefinedVirtualGroupNames.followers, - ) - const myFollowing = GroupIdentifier.getFriendsGroupIdentifier( - myIdentity.identifier, - PreDefinedVirtualGroupNames.following, - ) - if (isFollower || isFollowing) { - if (isFollower) Services.UserGroup.addProfileToFriendsGroup(myFollowers, [identifier]) - if (isFollowing) Services.UserGroup.addProfileToFriendsGroup(myFollowing, [identifier]) - if (isFollower && isFollowing) Services.UserGroup.addProfileToFriendsGroup(myFriends, [identifier]) - } else Services.UserGroup.removeProfileFromFriendsGroup(myFriends, [identifier]) - } - resolve() - return { - onNodeMutation: resolve, - onTargetChanged: resolve, - } - }) - .startWatch({ - childList: true, - subtree: true, - }) - signal.addEventListener('abort', () => watcher.stopWatch()) -} diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/ui-provider.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/ui-provider.ts index a4fce07965c4..29e70f34f217 100644 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/ui-provider.ts +++ b/packages/maskbook/src/social-network-adaptor/twitter.com/ui-provider.ts @@ -8,11 +8,8 @@ import { openComposeBoxTwitter } from './automation/openComposeBox' import { pasteTextToCompositionTwitter } from './automation/pasteTextToComposition' import { gotoNewsFeedPageTwitter } from './automation/gotoNewsFeedPage' import { gotoProfilePageTwitter } from './automation/gotoProfilePage' -import { getPostContentTwitter } from './collecting/getPostContent' -import { getProfileTwitter } from './collecting/getProfile' import { IdentityProviderTwitter } from './collecting/identity' import { PostProviderTwitter } from './collecting/post' -import { profilesCollectorTwitter } from './collecting/profiles' import { PaletteModeProviderTwitter, useInjectedDialogClassesOverwriteTwitter, @@ -52,11 +49,8 @@ const twitterUI: SocialNetworkUI.Definition = { }, }, collecting: { - getPostContent: getPostContentTwitter, - getProfile: getProfileTwitter, identityProvider: IdentityProviderTwitter, postsProvider: PostProviderTwitter, - profilesCollector: profilesCollectorTwitter, getSearchedKeyword: getSearchedKeywordAtTwitter, }, customization: { diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/utils/fetch.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/utils/fetch.ts index a7415fe27b59..af14b1a261da 100644 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/utils/fetch.ts +++ b/packages/maskbook/src/social-network-adaptor/twitter.com/utils/fetch.ts @@ -2,8 +2,7 @@ import { regexMatch } from '../../../utils/utils' import { notNullable } from '../../../utils/assert' import { defaultTo, flattenDeep } from 'lodash-es' import { nthChild } from '../../../utils/dom' -import { ProfileIdentifier } from '../../../database/type' -import { twitterUrl, canonifyImgUrl } from './url' +import { canonifyImgUrl } from './url' import { makeTypedMessageText, makeTypedMessageAnchor, @@ -60,47 +59,6 @@ const isMobilePost = (node: HTMLElement) => { return node.classList.contains('tweet') ?? node.classList.contains('main-tweet') } -export const bioCardParser = (cardNode: HTMLDivElement) => { - if (cardNode.classList.contains('profile')) { - const avatarElement = cardNode.querySelector('.avatar img') - const { name, handle } = parseNameArea( - [ - notNullable(cardNode.querySelector('.user-info .fullname')).innerText, - notNullable(cardNode.querySelector('.user-info .screen-name')).innerText, - ].join('@'), - ) - const bio = notNullable(cardNode.querySelector('.details') as HTMLTableCellElement).innerText - const isFollower = !!cardNode.querySelector('.follows-you') - const isFollowing = - notNullable(cardNode.querySelector('.profile-actions form')).action.indexOf('unfollow') > - -1 - return { - avatar: avatarElement ? avatarElement.src : undefined, - name, - handle, - identifier: new ProfileIdentifier(twitterUrl.hostIdentifier, handle), - bio, - isFollower, - isFollowing, - } - } else { - const avatarElement = cardNode.querySelector('img') - const { name, handle } = parseNameArea(notNullable(cardNode.children[1] as HTMLDivElement).innerText) - const bio = notNullable(cardNode.children[2] as HTMLDivElement).innerHTML - const isFollower = !!nthChild(cardNode, 1, 0, 0, 1, 1, 0) - const isFollowing = !!cardNode.querySelector('[data-testid*="unfollow"]') - return { - avatar: avatarElement ? avatarElement.src : undefined, - name, - handle, - identifier: new ProfileIdentifier(twitterUrl.hostIdentifier, handle), - bio, - isFollower, - isFollowing, - } - } -} - export const postIdParser = (node: HTMLElement) => { if (isMobilePost(node)) { const idNode = node.querySelector('.tweet-text') @@ -165,30 +123,6 @@ export const postAvatarParser = (node: HTMLElement) => { } } -export const postContentParser = (node: HTMLElement) => { - if (isMobilePost(node)) { - const containerNode = node.querySelector('.tweet-text > div') - if (!containerNode) return '' - return Array.from(containerNode.childNodes) - .map((node) => { - if (node.nodeType === Node.TEXT_NODE) return node.nodeValue - if (node.nodeName === 'A') return (node as HTMLAnchorElement).getAttribute('title') - return '' - }) - .join(',') - } else { - const select = (selectors: string) => { - const lang = node.parentElement!.querySelector('[lang]') - return lang ? Array.from(lang.querySelectorAll(selectors)) : [] - } - const sto = [ - ...select('a').map((x) => x.textContent), - ...select('span').map((x) => x.innerText), - ] - return sto.filter(Boolean).join(' ') - } -} - export const postContentMessageParser = (node: HTMLElement) => { function resolve(content: string) { if (content.startsWith('@')) return 'user' diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/utils/selector.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/utils/selector.ts index 30529c54fe41..98c9c665efcc 100644 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/utils/selector.ts +++ b/packages/maskbook/src/social-network-adaptor/twitter.com/utils/selector.ts @@ -61,23 +61,6 @@ export const floatingBioCardSelector = () => querySelector( `[style~="left:"] a[role=link] > div:first-child > div:first-child > div:first-child[dir="auto"]`, ) -export const bioCardSelector = (singleMode = true) => - querySelector( - [ - '.profile', // legacy twitter - 'a[href*="header_photo"] ~ div', // new twitter - 'div[data-testid="primaryColumn"] > div > div:last-child > div > div > div > div ~ div', // new twitter without header photo - ].join(), - singleMode, - ) - -export const postsSelector = () => - querySelectorAll( - [ - '#main_content .timeline .tweet', // legacy twitter - '[data-testid="tweet"]', // new twitter - ].join(), - ) export const postsImageSelector = (node: HTMLElement) => new LiveSelector([node]).querySelectorAll( diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/utils/url.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/utils/url.ts index 03e38fd02754..38d5015e2e90 100644 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/utils/url.ts +++ b/packages/maskbook/src/social-network-adaptor/twitter.com/utils/url.ts @@ -1,7 +1,3 @@ -import type { ProfileIdentifier, PostIdentifier } from '../../../database/type' -import { usernameValidator } from './user' -import { i18n } from '../../../utils/i18n-next' - export const twitterUrl = { hostIdentifier: 'twitter.com', hostLeadingUrl: 'https://twitter.com', @@ -11,17 +7,6 @@ export const twitterUrl = { export const hostLeadingUrlAutoTwitter = (isMobile: boolean) => isMobile ? twitterUrl.hostLeadingUrlMobile : twitterUrl.hostLeadingUrl -export const getPostUrlAtTwitter = (post: PostIdentifier, isMobile: boolean = false) => { - if (!usernameValidator(post.identifier.userId)) throw new Error(i18n.t('service_username_invalid')) - return `${hostLeadingUrlAutoTwitter(isMobile)}/${post.identifier.userId}/status/${post.postId}` -} - -export const getProfileUrlAtTwitter = (self: ProfileIdentifier, isMobile: boolean = false) => { - return isMobile - ? `${hostLeadingUrlAutoTwitter(isMobile)}/account` - : `${hostLeadingUrlAutoTwitter(isMobile)}/${self.userId}` -} - // more about twitter photo url formating: https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/entities-object#photo_format export const canonifyImgUrl = (url: string) => { const parsed = new URL(url) diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/worker-provider.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/worker-provider.ts index ba24204f04d4..9df85b058764 100644 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/worker-provider.ts +++ b/packages/maskbook/src/social-network-adaptor/twitter.com/worker-provider.ts @@ -1,14 +1,10 @@ import type { SocialNetworkWorker } from '../../social-network' import { twitterWorkerBase } from './base' import { twitterShared } from './shared' -import { fetchPostContent, fetchProfile } from './worker/fetch' const twitterWorker: SocialNetworkWorker.Definition = { ...twitterWorkerBase, ...twitterShared, - tasks: { - fetchPostContent, - fetchProfile, - }, + tasks: {}, } export default twitterWorker diff --git a/packages/maskbook/src/social-network-adaptor/twitter.com/worker/fetch.ts b/packages/maskbook/src/social-network-adaptor/twitter.com/worker/fetch.ts deleted file mode 100644 index eb935e789eab..000000000000 --- a/packages/maskbook/src/social-network-adaptor/twitter.com/worker/fetch.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ProfileIdentifier, PostIdentifier } from '../../../database/type' -import { getPostUrlAtTwitter, getProfileUrlAtTwitter } from '../utils/url' -import tasks from '../../../extension/content-script/tasks' -import { isMobileTwitter } from '../utils/isMobile' -import type { SocialNetworkUI } from '../../../social-network' - -/** - * get things at server side with legacy twitter - * seems not possible since we cannot access the - * legacy twitter with only a fetch. - * resolve this problem when you can. - */ - -export function fetchPostContent(post: PostIdentifier): Promise { - return tasks(getPostUrlAtTwitter(post)).getPostContent() -} - -export function fetchProfile(self: ProfileIdentifier): Promise { - return tasks(getProfileUrlAtTwitter(self, isMobileTwitter as boolean), {}).getProfile() -} diff --git a/packages/maskbook/src/social-network/types.ts b/packages/maskbook/src/social-network/types.ts index 65ef795ad513..c57fa2a89b1c 100644 --- a/packages/maskbook/src/social-network/types.ts +++ b/packages/maskbook/src/social-network/types.ts @@ -178,18 +178,12 @@ export namespace SocialNetworkUI { } export namespace CollectingCapabilities { export interface Define { - /** Collect all profiles seen on the network */ - profilesCollector?(signal: AbortSignal): void /** Resolve the information of who am I on the current network. */ identityProvider?: IdentityResolveProvider /** Maintain all the posts up-to-date. */ postsProvider?: PostsProvider /** Get searched keyword */ getSearchedKeyword?(): string - /** @deprecated Seems we don't use it anymore. */ - getPostContent?(): Promise - /** @deprecated Seems we don't use it anymore. */ - getProfile?(): Promise } export type ProfileUI = { bioContent: string } export type IdentityResolved = Pick @@ -302,30 +296,5 @@ export namespace SocialNetworkWorker { export interface Definition extends SocialNetwork.Base, SocialNetwork.Shared, WorkerBase { tasks: Tasks } - export interface Tasks { - /** - * This function should fetch the given post by `fetch`, `AutomatedTabTask` or anything - * @deprecated - * @pseudoCode - * fetchPostContent(post) { - * let tab = get_tab_with_same_origin_and_not_pinned() - * if (!isUndefined(tab)) { - * // tab available, let them to fetch. - * // this process should not visible to user. - * return tasks(tab).fetch(url) - * } - * - * // no tab available for now, call foreground to do so. - * return tasks(getPostURL(post)).getPostContent() - * } - * @param postIdentifier The post id - */ - fetchPostContent?(postIdentifier: PostIdentifier): Promise - /** - * This function should fetch the given post by `fetch`, `AutomatedTabTask` or anything - * @param identifier The post id - * @deprecated - */ - fetchProfile?(identifier: ProfileIdentifier): Promise - } + export interface Tasks {} } diff --git a/packages/maskbook/src/social-network/ui.ts b/packages/maskbook/src/social-network/ui.ts index 338ba8c3b562..d3f9de7b3e22 100644 --- a/packages/maskbook/src/social-network/ui.ts +++ b/packages/maskbook/src/social-network/ui.ts @@ -71,7 +71,6 @@ export async function activateSocialNetworkUIInner(ui_deferred: SocialNetworkUI. ui.collecting.postsProvider?.start(signal) startPostListener() - ui.collecting.profilesCollector?.(signal) ui.injection.pageInspector?.(signal) if (Flags.toolbox_enabled) ui.injection.toolBoxInNavBar?.(signal) ui.injection.setupPrompt?.(signal) diff --git a/packages/maskbook/src/utils/__tests__/browser.storage.ts b/packages/maskbook/src/utils/__tests__/browser.storage.ts deleted file mode 100644 index 1b1072917c40..000000000000 --- a/packages/maskbook/src/utils/__tests__/browser.storage.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getStorage, setStorage } from '../browser.storage' - -test('get and set storage', async () => { - const before = await getStorage('network_id') - expect(before.forceDisplayWelcome).toBeUndefined() - expect(before.userIgnoredWelcome).toBeUndefined() - - await setStorage('network_id', { - forceDisplayWelcome: true, - userIgnoredWelcome: true, - }) - - const after = await getStorage('network_id') - expect(after.forceDisplayWelcome).toBeTruthy() - expect(after.userIgnoredWelcome).toBeTruthy() -}) diff --git a/packages/maskbook/src/utils/browser.storage.ts b/packages/maskbook/src/utils/browser.storage.ts deleted file mode 100644 index 2ce35b45d5e0..000000000000 --- a/packages/maskbook/src/utils/browser.storage.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { merge } from 'lodash-es' - -interface Storage { - forceDisplayWelcome: boolean - userIgnoredWelcome: boolean -} -export async function getStorage(network: string): Promise> { - if (typeof browser === 'undefined' || !browser.storage) return {} - const storage = await browser.storage.local.get(network) - return ((storage ?? {})[network] ?? {}) as Partial -} - -export async function setStorage(network: string, item: Partial) { - if (typeof browser === 'undefined' || !browser.storage) return - const storage = await getStorage(network) - return browser.storage.local.set({ - [network]: merge(storage, item), - }) -} diff --git a/packages/maskbook/src/utils/comparer.ts b/packages/maskbook/src/utils/comparer.ts index aa5cfa352042..2f9e4b0f90b7 100644 --- a/packages/maskbook/src/utils/comparer.ts +++ b/packages/maskbook/src/utils/comparer.ts @@ -1,4 +1,4 @@ -import type { Group, Profile, Persona } from '../database' +import type { Profile, Persona } from '../database' export function PersonaComparer(a: Persona, b: Persona): boolean { if (a.createdAt.getTime() !== b.createdAt.getTime()) return false @@ -33,17 +33,3 @@ export function ProfileArrayComparer(a: Profile[], b: Profile[]) { return true }) } - -export function GroupArrayComparer(a: Group[], b: Group[]) { - if (a.length !== b.length) return false - return a.every((group, index) => { - const target = b[index] - if (!group.identifier.equals(target.identifier)) return false - if (group.avatar !== target.avatar) return false - if (group.banned !== target.banned) return false - if (group.groupName !== target.groupName) return false - if (group.members.length !== target.members.length) return false - if (!group.members.every((person, index) => target.members[index].equals(person))) return false - return true - }) -} diff --git a/packages/maskbook/src/utils/hooks/index.ts b/packages/maskbook/src/utils/hooks/index.ts index 5f8dd1988e36..ef6aee837924 100644 --- a/packages/maskbook/src/utils/hooks/index.ts +++ b/packages/maskbook/src/utils/hooks/index.ts @@ -1,6 +1,3 @@ -export * from './useAsyncTimes' -export * from './useForms' -export * from './useIsWindowVisible' export * from './useMatchXS' export * from './useMenu' export * from './useQRCodeImageScan' diff --git a/packages/maskbook/src/utils/hooks/useAsyncTimes.ts b/packages/maskbook/src/utils/hooks/useAsyncTimes.ts deleted file mode 100644 index 28f360540761..000000000000 --- a/packages/maskbook/src/utils/hooks/useAsyncTimes.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { useState, useEffect, useMemo, useRef } from 'react' -import { useMountedState } from 'react-use' -import { delay as sleep } from '../utils' - -export interface AsyncTimesOptions { - delay?: number - times?: number - timeout?: number - done?: () => boolean - ignoreError?: boolean -} - -/** - * Invoke callback n times - * @param callback - * @param options - */ -export function useAsyncTimes(callback: () => Promise, options?: AsyncTimesOptions) { - const { - times = 30, - delay = 30 /* seconds */ * 1000 /* milliseconds */, - timeout = 60 /* seconds*/ * 1000 /* milliseconds */, - ignoreError = true, - done = () => false, - } = options ?? {} - - //#region only callback is reactive - const savedCallback = useRef<(() => Promise) | null>(null) - useEffect(() => { - savedCallback.current = callback - }) - //#endregion - - // create ticker - const isMounted = useMountedState() - const [result, setResult] = useState(undefined) - const ticker = useMemo(() => { - return async (rest: number) => { - try { - const r = await Promise.race([ - savedCallback.current?.(), - new Promise((_, reject) => { - setTimeout(() => reject(new Error('timeout')), timeout) - }), - ]) - if (isMounted()) setResult(r) - } catch (e) { - if (!ignoreError) setResult(undefined) - } finally { - if (rest <= 0 || done()) return - await sleep(delay) - if (isMounted()) ticker(rest - 1) - } - } - }, [delay, timeout, ignoreError, done]) - - // invoke ticker at the first time - useEffect(() => { - ticker(times) - }, []) - return result -} diff --git a/packages/maskbook/src/utils/hooks/useForms.tsx b/packages/maskbook/src/utils/hooks/useForms.tsx deleted file mode 100644 index eeebbd0d6295..000000000000 --- a/packages/maskbook/src/utils/hooks/useForms.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import type { TextFieldProps } from '@material-ui/core/TextField' -import { useState } from 'react' -import { TextField } from '@material-ui/core' - -export function useTextField(label: string, props?: TextFieldProps) { - const { defaultValue = '', value: _, ..._props } = props || {} - const [value, setValue] = useState(String(defaultValue)) - return [ - value, - setValue(e.currentTarget.value)} - margin="normal" - {..._props} - variant="outlined" - />, - ] as const -} diff --git a/packages/maskbook/src/utils/hooks/useIsWindowVisible.ts b/packages/maskbook/src/utils/hooks/useIsWindowVisible.ts deleted file mode 100644 index fe2824ed6e02..000000000000 --- a/packages/maskbook/src/utils/hooks/useIsWindowVisible.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useState, useCallback, useEffect } from 'react' - -export function useIsWindowVisible() { - const [visible, setVisible] = useState(document.visibilityState === 'visible') - const listener = useCallback(() => { - setVisible(document.visibilityState === 'visible') - }, [setVisible]) - - useEffect(() => { - document.addEventListener('visibilitychange', listener) - return () => { - document.removeEventListener('visibilitychange', listener) - } - }, [listener]) - return visible -} diff --git a/packages/maskbook/src/utils/i18n-next-ui.ts b/packages/maskbook/src/utils/i18n-next-ui.ts index 54fe4f5b8e07..6124a9b115f9 100644 --- a/packages/maskbook/src/utils/i18n-next-ui.ts +++ b/packages/maskbook/src/utils/i18n-next-ui.ts @@ -2,7 +2,7 @@ import type { i18n } from 'i18next' import { initReactI18next, useTranslation as useTranslation_, UseTranslationOptions } from 'react-i18next' import type { TOptions } from 'i18next' -import { useMemo, useCallback, useEffect } from 'react' +import { useEffect } from 'react' import { useUpdate } from 'react-use' import type en from '../_locales/en/messages.json' import i18nNextInstance from './i18n-next' @@ -10,12 +10,8 @@ import { languageSettings } from '../settings/settings' i18nNextInstance.use(initReactI18next) -// const test = { -// my_test: 'string', -// } as const type Namespaces = { default: typeof en - // test: typeof test } export type I18NFunction = ( @@ -54,10 +50,3 @@ export function useI18N( languageSettings.addListener((next) => { i18nNextInstance.changeLanguage(next) }) - -export function useIntlListFormat() { - const formatter = useMemo(() => { - return new Intl.ListFormat({ type: 'conjunction' }) - }, []) - return useCallback((list: string[]) => formatter.format(list), [formatter]) -} diff --git a/packages/maskbook/src/utils/index.ts b/packages/maskbook/src/utils/index.ts index 42a70613b9cb..94ff8893d6b4 100644 --- a/packages/maskbook/src/utils/index.ts +++ b/packages/maskbook/src/utils/index.ts @@ -6,7 +6,6 @@ export * from './shadow-root' export * from './suspends' export * from './type-transform' export * from './assert' -export * from './browser.storage' export * from './comparer' export * from './constants' export * from './dom' @@ -24,7 +23,6 @@ export * from './permissions' export * from './promiEvent' export * from './side-effects' export * from './SSRRenderer' -export * from './tabs' export * from './theme-tools' export * from './theme' export * from './type' diff --git a/packages/maskbook/src/utils/messages.ts b/packages/maskbook/src/utils/messages.ts index dd00861b86ee..b53aa33a0759 100644 --- a/packages/maskbook/src/utils/messages.ts +++ b/packages/maskbook/src/utils/messages.ts @@ -3,7 +3,7 @@ if (import.meta.webpackHot) import.meta.webpackHot.accept() import { WebExtensionMessage } from '@dimensiondev/holoflows-kit' import Serialization from './type-transform/Serialization' -import type { ProfileIdentifier, GroupIdentifier, PersonaIdentifier } from '../database/type' +import type { ProfileIdentifier, PersonaIdentifier } from '../database/type' import type { TypedMessage } from '../protocols/typed-message' import type { ThirdPartyPopupContextIdentifier } from '../plugins/External/popup-context' import type { SettingsEvents } from '../settings/listener' @@ -43,7 +43,6 @@ export interface MaskMessages extends SettingsEvents { createInternalSettingsChanged: SettingsUpdateEvent /** emit when the settings finished syncing with storage. */ createInternalSettingsUpdated: SettingsUpdateEvent - profileJoinedGroup: { group: GroupIdentifier; newMembers: ProfileIdentifier[] } /** emit when compose status updated. */ compositionUpdated: CompositionEvent personaChanged: (UpdateEvent & { owned: boolean })[] diff --git a/packages/maskbook/src/utils/tabs.ts b/packages/maskbook/src/utils/tabs.ts deleted file mode 100644 index aeb9bef23029..000000000000 --- a/packages/maskbook/src/utils/tabs.ts +++ /dev/null @@ -1,8 +0,0 @@ -export async function getActiveTabFacebook() { - const [tab] = await browser.tabs.query({ - url: ['https://www.facebook.com/*', 'https://m.facebook.com/*'], - pinned: false, - }) - if (tab) return tab.id - return undefined -} diff --git a/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/DBRecord-JSON/GroupRecord.ts b/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/DBRecord-JSON/GroupRecord.ts deleted file mode 100644 index c0840ed3c58c..000000000000 --- a/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/DBRecord-JSON/GroupRecord.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { GroupRecord } from '../../../../../database/group' -import type { BackupJSONFileLatest } from '../latest' -import { Identifier, GroupIdentifier, ProfileIdentifier } from '../../../../../database/type' - -export function GroupRecordToJSONFormat(group: GroupRecord): BackupJSONFileLatest['userGroups'][0] { - return { - groupName: group.groupName, - identifier: group.identifier.toText(), - members: group.members.map((x) => x.toText()), - banned: group.banned?.map((x) => x.toText()), - } -} - -export function GroupRecordFromJSONFormat(group: BackupJSONFileLatest['userGroups'][0]): GroupRecord { - return { - groupName: group.groupName, - identifier: Identifier.fromString(group.identifier, GroupIdentifier).unwrap(), - members: group.members.map((x) => Identifier.fromString(x, ProfileIdentifier).unwrap()), - banned: group.banned?.map((x) => Identifier.fromString(x, ProfileIdentifier).unwrap()), - } -} diff --git a/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/DBRecord-JSON/PostRecord.ts b/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/DBRecord-JSON/PostRecord.ts index ab39487bf96a..350477e77723 100644 --- a/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/DBRecord-JSON/PostRecord.ts +++ b/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/DBRecord-JSON/PostRecord.ts @@ -11,7 +11,7 @@ export function PostRecordToJSONFormat(post: PostRecord): BackupJSONFileLatest[' foundAt: post.foundAt.getTime(), identifier: post.identifier.toText(), postBy: post.postBy.toText(), - recipientGroups: post.recipientGroups.map((x) => x.toText()), + recipientGroups: [], recipients: Array.from(post.recipients).map( ([identifier, detail]): [string, { reason: RecipientReasonJSON[] }] => [ identifier.toText(), @@ -29,7 +29,6 @@ export function PostRecordFromJSONFormat(post: BackupJSONFileLatest['posts'][0]) foundAt: new Date(post.foundAt), identifier: Identifier.fromString(post.identifier, PostIVIdentifier).unwrap(), postBy: Identifier.fromString(post.postBy, ProfileIdentifier).unwrap(), - recipientGroups: post.recipientGroups.map((x) => Identifier.fromString(x, GroupIdentifier).unwrap()), recipients: new IdentifierMap( new Map( post.recipients.map(([x, y]) => [ diff --git a/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/version-2.ts b/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/version-2.ts index 9344eb73b977..983500ccef52 100644 --- a/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/version-2.ts +++ b/packages/maskbook/src/utils/type-transform/BackupFormat/JSON/version-2.ts @@ -1,7 +1,7 @@ /* eslint-disable import/no-deprecated */ import type { LinkedProfileDetails } from '../../../../database/Persona/Persona.db' import type { BackupJSONFileVersion1 } from './version-1' -import { ProfileIdentifier, GroupIdentifier, ECKeyIdentifierFromJsonWebKey } from '../../../../database/type' +import { ProfileIdentifier, ECKeyIdentifierFromJsonWebKey } from '../../../../database/type' import type { AESJsonWebKey, EC_Public_JsonWebKey, @@ -53,18 +53,15 @@ export interface BackupJSONFileVersion2 { createdAt: number // Unix timestamp updatedAt: number // Unix timestamp }> - userGroups: Array<{ - groupName: string - identifier: string // GroupIdentifier.toText() - members: string[] // Array - banned?: string[] // Array - }> + /** @deprecated */ + userGroups: never[] posts: Array<{ postBy: string // ProfileIdentifier.toText() identifier: string // PostIVIdentifier.toText() postCryptoKey?: AESJsonWebKey recipients: [/** ProfileIdentifier.toText() */ string, { reason: RecipientReasonJSON[] }][] - recipientGroups: string[] // Array + /** @deprecated */ + recipientGroups: never[] // Array foundAt: number // Unix timestamp }> wallets: Array<{ @@ -93,7 +90,6 @@ export function isBackupJSONFileVersion2(obj: object): obj is BackupJSONFileVers export function upgradeFromBackupJSONFileVersion1(json: BackupJSONFileVersion1): BackupJSONFileVersion2 { const personas: BackupJSONFileVersion2['personas'] = [] const profiles: BackupJSONFileVersion2['profiles'] = [] - const userGroups: BackupJSONFileVersion2['userGroups'] = [] function addPersona(record: Omit) { const prev = personas.find((x) => x.identifier === record.identifier) @@ -110,19 +106,6 @@ export function upgradeFromBackupJSONFileVersion1(json: BackupJSONFileVersion1): } else profiles.push({ ...record, updatedAt: 0, createdAt: 0 }) } - function addProfileToGroup( - member: ProfileIdentifier, - detail: NonNullable[0]['groups']>[0], - ) { - const groupId = new GroupIdentifier(detail.network, detail.virtualGroupOwner, detail.groupID).toText() - const prev = userGroups.find((x) => x.identifier === groupId) - if (prev) { - prev.members.push(member.toText()) - } else { - userGroups.push({ groupName: '', identifier: groupId, members: [] }) - } - } - for (const x of json.whoami) { const profile = new ProfileIdentifier(x.network, x.userId).toText() const persona = ECKeyIdentifierFromJsonWebKey(x.publicKey).toText() @@ -156,13 +139,8 @@ export function upgradeFromBackupJSONFileVersion1(json: BackupJSONFileVersion1): publicKey: x.publicKey, nickname: x.nickname, }) - x.groups?.forEach((y) => addProfileToGroup(profile, y)) } - userGroups.forEach((x) => { - x.members = Array.from(new Set(x.members)) - }) - return { _meta_: { version: 2, @@ -174,7 +152,7 @@ export function upgradeFromBackupJSONFileVersion1(json: BackupJSONFileVersion1): wallets: [], personas, profiles, - userGroups, + userGroups: [], grantedHostPermissions: json.grantedHostPermissions, } } diff --git a/packages/shared-base/src/Identifier/type.ts b/packages/shared-base/src/Identifier/type.ts index 0aacfccd68a0..b5d3bc7de73c 100644 --- a/packages/shared-base/src/Identifier/type.ts +++ b/packages/shared-base/src/Identifier/type.ts @@ -114,19 +114,11 @@ export class ProfileIdentifier extends Identifier { return new ProfileIdentifier(network, userId) } } -export enum PreDefinedVirtualGroupNames { - friends = '_default_friends_group_', - followers = '_followers_group_', - following = '_following_group_', -} @serialize('GroupIdentifier') export class GroupIdentifier extends Identifier { static getFriendsGroupIdentifier(who: ProfileIdentifier, groupId: string) { return new GroupIdentifier(who.network, who.userId, groupId) } - static getDefaultFriendsGroupIdentifier(who: ProfileIdentifier) { - return new GroupIdentifier(who.network, who.userId, PreDefinedVirtualGroupNames.friends) - } constructor( public readonly network: string, public readonly virtualGroupOwner: string | null,