Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
f494f70003 | |||
fb2a55a909 | |||
4edfa7fd50 | |||
d699cac9b1 | |||
c0428e12c1 | |||
55f434ff05 | |||
f2b3bdda2d | |||
1f6bf33b0e | |||
e2027b1a32 | |||
2b3a58b55e | |||
6ac536412a | |||
52f8ffe4e4 | |||
aca81431aa | |||
1fadd850b7 | |||
ed2a9a21b6 | |||
57279eb3e4 | |||
c403a2914a | |||
bcb176344c | |||
ecf362cffc | |||
f4ab7671d8 | |||
a2a3018917 | |||
0bdb664000 | |||
9c3b61ce57 | |||
d06df3d278 | |||
547ba19e61 | |||
cb05ff2e9e | |||
f614da7918 | |||
a3c8dafff9 | |||
fa978a7cd1 | |||
aaa0a562b4 | |||
590a4ce2a6 | |||
f26edce071 | |||
603799ea32 | |||
a32baf7798 | |||
498c9af663 | |||
202dbff6d3 | |||
96fd64d85d | |||
e236b7f98b | |||
5c7929e618 | |||
7ba5260246 | |||
a6d4947a23 |
@ -7,11 +7,7 @@ meta {
|
|||||||
post {
|
post {
|
||||||
url: {{endpoint}}/cgi/uc/boosts/1/activate
|
url: {{endpoint}}/cgi/uc/boosts/1/activate
|
||||||
body: none
|
body: none
|
||||||
auth: bearer
|
auth: inherit
|
||||||
}
|
|
||||||
|
|
||||||
auth:bearer {
|
|
||||||
token: {{atk}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
|
19
api/Paperclip/Stickers/Create Sticker Pack.bru
Normal file
19
api/Paperclip/Stickers/Create Sticker Pack.bru
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
meta {
|
||||||
|
name: Create Sticker Pack
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{endpoint}}/cgi/uc/stickers/packs
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"prefix": "cat",
|
||||||
|
"name": "Solar Network full of Cats!",
|
||||||
|
"description": "The sticker packs is full of stickers which related with cats!"
|
||||||
|
}
|
||||||
|
}
|
20
api/Paperclip/Stickers/Create Sticker.bru
Normal file
20
api/Paperclip/Stickers/Create Sticker.bru
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
meta {
|
||||||
|
name: Create Sticker
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{endpoint}}/cgi/uc/stickers
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"alias": "AteChip",
|
||||||
|
"name": "Cat ate chips",
|
||||||
|
"attachment_id": "d0b692cc64054463",
|
||||||
|
"pack_id": 2
|
||||||
|
}
|
||||||
|
}
|
@ -7,11 +7,7 @@ meta {
|
|||||||
post {
|
post {
|
||||||
url: {{endpoint}}/cgi/id/dev/notify/all
|
url: {{endpoint}}/cgi/id/dev/notify/all
|
||||||
body: json
|
body: json
|
||||||
auth: bearer
|
auth: inherit
|
||||||
}
|
|
||||||
|
|
||||||
auth:bearer {
|
|
||||||
token: {{atk}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
|
7
api/collection.bru
Normal file
7
api/collection.bru
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
auth {
|
||||||
|
mode: bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
auth:bearer {
|
||||||
|
token: {{atk}}
|
||||||
|
}
|
@ -181,6 +181,8 @@
|
|||||||
"settingsAppearance": "Appearance",
|
"settingsAppearance": "Appearance",
|
||||||
"settingsAppBarTransparent": "Transparent App Bar",
|
"settingsAppBarTransparent": "Transparent App Bar",
|
||||||
"settingsAppBarTransparentDescription": "Enable transparent effect for the app bar.",
|
"settingsAppBarTransparentDescription": "Enable transparent effect for the app bar.",
|
||||||
|
"settingsDrawerPreferCollapse": "Prefer Drawer Collapse",
|
||||||
|
"settingsDrawerPreferCollapseDescription": "Make the drawer to collapse even when the screen is wide enough.",
|
||||||
"settingsBackgroundImage": "Background Image",
|
"settingsBackgroundImage": "Background Image",
|
||||||
"settingsBackgroundImageDescription": "Set the background image that will be applied globally.",
|
"settingsBackgroundImageDescription": "Set the background image that will be applied globally.",
|
||||||
"settingsBackgroundImageClear": "Clear Existing Background Image",
|
"settingsBackgroundImageClear": "Clear Existing Background Image",
|
||||||
@ -281,7 +283,12 @@
|
|||||||
"one": "{} attachment",
|
"one": "{} attachment",
|
||||||
"other": "{} attachments"
|
"other": "{} attachments"
|
||||||
},
|
},
|
||||||
|
"messageTyping": {
|
||||||
|
"one": "{} is typing...",
|
||||||
|
"other": "{} are typing..."
|
||||||
|
},
|
||||||
"fieldAttachmentRandomId": "Random ID",
|
"fieldAttachmentRandomId": "Random ID",
|
||||||
|
"fieldAttachmentAlt": "Alternative text",
|
||||||
"addAttachmentFromAlbum": "Add from album",
|
"addAttachmentFromAlbum": "Add from album",
|
||||||
"addAttachmentFromClipboard": "Paste file",
|
"addAttachmentFromClipboard": "Paste file",
|
||||||
"addAttachmentFromCameraPhoto": "Take photo",
|
"addAttachmentFromCameraPhoto": "Take photo",
|
||||||
@ -293,6 +300,7 @@
|
|||||||
"attachmentUnsetAsPostThumbnail": "Unset as post thumbnail",
|
"attachmentUnsetAsPostThumbnail": "Unset as post thumbnail",
|
||||||
"attachmentCompressVideo": "Re-encode video",
|
"attachmentCompressVideo": "Re-encode video",
|
||||||
"attachmentSetThumbnail": "Set thumbnail",
|
"attachmentSetThumbnail": "Set thumbnail",
|
||||||
|
"attachmentSetAlt": "Set alternative text",
|
||||||
"attachmentCopyRandomId": "Copy RID",
|
"attachmentCopyRandomId": "Copy RID",
|
||||||
"attachmentUpload": "Upload",
|
"attachmentUpload": "Upload",
|
||||||
"attachmentInputDialog": "Upload attachments",
|
"attachmentInputDialog": "Upload attachments",
|
||||||
@ -408,6 +416,9 @@
|
|||||||
"celebrateBirthday": "Happy birthday, {}!",
|
"celebrateBirthday": "Happy birthday, {}!",
|
||||||
"celebrateMerryXmas": "Merry christmas, {}!",
|
"celebrateMerryXmas": "Merry christmas, {}!",
|
||||||
"celebrateNewYear": "Happy new year, {}!",
|
"celebrateNewYear": "Happy new year, {}!",
|
||||||
|
"celebrateLunarNewYear": "Happy lunar new year, {}!",
|
||||||
|
"celebrateMidAutumn": "Happy mid-autumn festival, {}!",
|
||||||
|
"celebrateDragonBoat": "Happy dragon boat festival, {}!",
|
||||||
"celebrateValentineDay": "Today is valentine's day, {}!",
|
"celebrateValentineDay": "Today is valentine's day, {}!",
|
||||||
"celebrateLaborDay": "Today is labor day, {}.",
|
"celebrateLaborDay": "Today is labor day, {}.",
|
||||||
"celebrateMotherDay": "Today is mother's day, {}.",
|
"celebrateMotherDay": "Today is mother's day, {}.",
|
||||||
@ -417,6 +428,9 @@
|
|||||||
"celebrateThanksgiving": "Today is thanksgiving day, {}!",
|
"celebrateThanksgiving": "Today is thanksgiving day, {}!",
|
||||||
"pendingBirthday": "Birthday in {}",
|
"pendingBirthday": "Birthday in {}",
|
||||||
"pendingMerryXmas": "Christmas in {}",
|
"pendingMerryXmas": "Christmas in {}",
|
||||||
|
"pendingLunarNewYear": "Lunar new year in {}",
|
||||||
|
"pendingMidAutumn": "Mid-autumn festival in {}",
|
||||||
|
"pendingDragonBoat": "Dragon boat festival in {}",
|
||||||
"pendingNewYear": "New year in {}",
|
"pendingNewYear": "New year in {}",
|
||||||
"pendingValentineDay": "Valentine's day in {}",
|
"pendingValentineDay": "Valentine's day in {}",
|
||||||
"pendingLaborDay": "Labor day in {}",
|
"pendingLaborDay": "Labor day in {}",
|
||||||
@ -467,6 +481,7 @@
|
|||||||
"accountStatusLastSeen": "Last seen at {}",
|
"accountStatusLastSeen": "Last seen at {}",
|
||||||
"postArticle": "Article on the Solar Network",
|
"postArticle": "Article on the Solar Network",
|
||||||
"postStory": "Story on the Solar Network",
|
"postStory": "Story on the Solar Network",
|
||||||
|
"postLocalDraftRestored": "Restored from device",
|
||||||
"articleWrittenAt": "Written at {}",
|
"articleWrittenAt": "Written at {}",
|
||||||
"articleEditedAt": "Edited at {}",
|
"articleEditedAt": "Edited at {}",
|
||||||
"attachmentSaved": "Saved to album",
|
"attachmentSaved": "Saved to album",
|
||||||
|
@ -185,6 +185,8 @@
|
|||||||
"settingsThemeMaterial3Description": "将应用主题设置为 Material 3 设计范式的主题。",
|
"settingsThemeMaterial3Description": "将应用主题设置为 Material 3 设计范式的主题。",
|
||||||
"settingsAppBarTransparent": "透明顶栏",
|
"settingsAppBarTransparent": "透明顶栏",
|
||||||
"settingsAppBarTransparentDescription": "为顶栏启用透明效果。",
|
"settingsAppBarTransparentDescription": "为顶栏启用透明效果。",
|
||||||
|
"settingsDrawerPreferCollapse": "侧边栏偏好折叠",
|
||||||
|
"settingsDrawerPreferCollapseDescription": "将侧边栏优先折叠,即使屏幕宽度足够大去放下整个侧边栏。",
|
||||||
"settingsColorScheme": "主题色",
|
"settingsColorScheme": "主题色",
|
||||||
"settingsColorSchemeDescription": "设置应用主题色。",
|
"settingsColorSchemeDescription": "设置应用主题色。",
|
||||||
"settingsColorSeed": "预设色彩主题",
|
"settingsColorSeed": "预设色彩主题",
|
||||||
@ -279,7 +281,12 @@
|
|||||||
"one": "{} 个附件",
|
"one": "{} 个附件",
|
||||||
"other": "{} 个附件"
|
"other": "{} 个附件"
|
||||||
},
|
},
|
||||||
|
"messageTyping": {
|
||||||
|
"one": "{} 正在输入",
|
||||||
|
"other": "{} 正在输入"
|
||||||
|
},
|
||||||
"fieldAttachmentRandomId": "访问 ID",
|
"fieldAttachmentRandomId": "访问 ID",
|
||||||
|
"fieldAttachmentAlt": "概述文字",
|
||||||
"addAttachmentFromAlbum": "从相册中添加附件",
|
"addAttachmentFromAlbum": "从相册中添加附件",
|
||||||
"addAttachmentFromClipboard": "粘贴附件",
|
"addAttachmentFromClipboard": "粘贴附件",
|
||||||
"addAttachmentFromCameraPhoto": "拍摄照片",
|
"addAttachmentFromCameraPhoto": "拍摄照片",
|
||||||
@ -291,6 +298,7 @@
|
|||||||
"attachmentUnsetAsPostThumbnail": "取消设置为帖子缩略图",
|
"attachmentUnsetAsPostThumbnail": "取消设置为帖子缩略图",
|
||||||
"attachmentCompressVideo": "重新编码视频",
|
"attachmentCompressVideo": "重新编码视频",
|
||||||
"attachmentSetThumbnail": "设置缩略图",
|
"attachmentSetThumbnail": "设置缩略图",
|
||||||
|
"attachmentSetAlt": "设置概述文字",
|
||||||
"attachmentCopyRandomId": "复制访问 ID",
|
"attachmentCopyRandomId": "复制访问 ID",
|
||||||
"attachmentUpload": "上传",
|
"attachmentUpload": "上传",
|
||||||
"attachmentInputDialog": "上传附件",
|
"attachmentInputDialog": "上传附件",
|
||||||
@ -404,6 +412,9 @@
|
|||||||
"dailyCheckNegativeHint6": "出门",
|
"dailyCheckNegativeHint6": "出门",
|
||||||
"dailyCheckNegativeHint6Description": "忘带伞遇上大雨",
|
"dailyCheckNegativeHint6Description": "忘带伞遇上大雨",
|
||||||
"celebrateBirthday": "生日快乐,{}!",
|
"celebrateBirthday": "生日快乐,{}!",
|
||||||
|
"celebrateLunarNewYear": "春节快乐,{}!",
|
||||||
|
"celebrateMidAutumn": "中秋节快乐,{}!",
|
||||||
|
"celebrateDragonBoat": "端午节快乐,{}!",
|
||||||
"celebrateMerryXmas": "圣诞快乐,{}!",
|
"celebrateMerryXmas": "圣诞快乐,{}!",
|
||||||
"celebrateNewYear": "新年快乐,{}!",
|
"celebrateNewYear": "新年快乐,{}!",
|
||||||
"celebrateValentineDay": "今天是情人节,{}!",
|
"celebrateValentineDay": "今天是情人节,{}!",
|
||||||
@ -413,6 +424,9 @@
|
|||||||
"celebrateFatherDay": "今天是父亲节,{}。",
|
"celebrateFatherDay": "今天是父亲节,{}。",
|
||||||
"celebrateHalloween": "快乐在圣诞节,{}!",
|
"celebrateHalloween": "快乐在圣诞节,{}!",
|
||||||
"celebrateThanksgiving": "今天是感恩节,{}!",
|
"celebrateThanksgiving": "今天是感恩节,{}!",
|
||||||
|
"pendingLunarNewYear": "{} 过春节",
|
||||||
|
"pendingMidAutumn": "{} 过中秋节",
|
||||||
|
"pendingDragonBoat": "{} 过端午节",
|
||||||
"pendingBirthday": "{} 过生日",
|
"pendingBirthday": "{} 过生日",
|
||||||
"pendingMerryXmas": "{} 过圣诞节",
|
"pendingMerryXmas": "{} 过圣诞节",
|
||||||
"pendingNewYear": "{} 跨年",
|
"pendingNewYear": "{} 跨年",
|
||||||
@ -465,6 +479,7 @@
|
|||||||
"accountStatusLastSeen": "最后一次上线于 {}",
|
"accountStatusLastSeen": "最后一次上线于 {}",
|
||||||
"postArticle": "Solar Network 上的文章",
|
"postArticle": "Solar Network 上的文章",
|
||||||
"postStory": "Solar Network 上的故事",
|
"postStory": "Solar Network 上的故事",
|
||||||
|
"postLocalDraftRestored": "从本地恢复草稿",
|
||||||
"articleWrittenAt": "发表于 {}",
|
"articleWrittenAt": "发表于 {}",
|
||||||
"articleEditedAt": "编辑于 {}",
|
"articleEditedAt": "编辑于 {}",
|
||||||
"attachmentSaved": "已保存到相册",
|
"attachmentSaved": "已保存到相册",
|
||||||
|
@ -185,6 +185,8 @@
|
|||||||
"settingsThemeMaterial3Description": "將應用主題設置為 Material 3 設計範式的主題。",
|
"settingsThemeMaterial3Description": "將應用主題設置為 Material 3 設計範式的主題。",
|
||||||
"settingsAppBarTransparent": "透明頂欄",
|
"settingsAppBarTransparent": "透明頂欄",
|
||||||
"settingsAppBarTransparentDescription": "為頂欄啓用透明效果。",
|
"settingsAppBarTransparentDescription": "為頂欄啓用透明效果。",
|
||||||
|
"settingsDrawerPreferCollapse": "側邊欄偏好摺疊",
|
||||||
|
"settingsDrawerPreferCollapseDescription": "將側邊欄優先摺疊,即使屏幕寬度足夠大去放下整個側邊欄。",
|
||||||
"settingsColorScheme": "主題色",
|
"settingsColorScheme": "主題色",
|
||||||
"settingsColorSchemeDescription": "設置應用主題色。",
|
"settingsColorSchemeDescription": "設置應用主題色。",
|
||||||
"settingsColorSeed": "預設色彩主題",
|
"settingsColorSeed": "預設色彩主題",
|
||||||
@ -279,7 +281,12 @@
|
|||||||
"one": "{} 個附件",
|
"one": "{} 個附件",
|
||||||
"other": "{} 個附件"
|
"other": "{} 個附件"
|
||||||
},
|
},
|
||||||
|
"messageTyping": {
|
||||||
|
"one": "{} 正在輸入",
|
||||||
|
"other": "{} 正在輸入"
|
||||||
|
},
|
||||||
"fieldAttachmentRandomId": "訪問 ID",
|
"fieldAttachmentRandomId": "訪問 ID",
|
||||||
|
"fieldAttachmentAlt": "概述文字",
|
||||||
"addAttachmentFromAlbum": "從相冊中添加附件",
|
"addAttachmentFromAlbum": "從相冊中添加附件",
|
||||||
"addAttachmentFromClipboard": "粘貼附件",
|
"addAttachmentFromClipboard": "粘貼附件",
|
||||||
"addAttachmentFromCameraPhoto": "拍攝照片",
|
"addAttachmentFromCameraPhoto": "拍攝照片",
|
||||||
@ -291,6 +298,7 @@
|
|||||||
"attachmentUnsetAsPostThumbnail": "取消設置為帖子縮略圖",
|
"attachmentUnsetAsPostThumbnail": "取消設置為帖子縮略圖",
|
||||||
"attachmentCompressVideo": "重新編碼視頻",
|
"attachmentCompressVideo": "重新編碼視頻",
|
||||||
"attachmentSetThumbnail": "設置縮略圖",
|
"attachmentSetThumbnail": "設置縮略圖",
|
||||||
|
"attachmentSetAlt": "設置概述文字",
|
||||||
"attachmentCopyRandomId": "複製訪問 ID",
|
"attachmentCopyRandomId": "複製訪問 ID",
|
||||||
"attachmentUpload": "上傳",
|
"attachmentUpload": "上傳",
|
||||||
"attachmentInputDialog": "上傳附件",
|
"attachmentInputDialog": "上傳附件",
|
||||||
@ -404,6 +412,9 @@
|
|||||||
"dailyCheckNegativeHint6": "出門",
|
"dailyCheckNegativeHint6": "出門",
|
||||||
"dailyCheckNegativeHint6Description": "忘帶傘遇上大雨",
|
"dailyCheckNegativeHint6Description": "忘帶傘遇上大雨",
|
||||||
"celebrateBirthday": "生日快樂,{}!",
|
"celebrateBirthday": "生日快樂,{}!",
|
||||||
|
"celebrateLunarNewYear": "春節快樂,{}!",
|
||||||
|
"celebrateMidAutumn": "中秋節快樂,{}!",
|
||||||
|
"celebrateDragonBoat": "端午節快樂,{}!",
|
||||||
"celebrateMerryXmas": "聖誕快樂,{}!",
|
"celebrateMerryXmas": "聖誕快樂,{}!",
|
||||||
"celebrateNewYear": "新年快樂,{}!",
|
"celebrateNewYear": "新年快樂,{}!",
|
||||||
"celebrateValentineDay": "今天是情人節,{}!",
|
"celebrateValentineDay": "今天是情人節,{}!",
|
||||||
@ -413,6 +424,9 @@
|
|||||||
"celebrateFatherDay": "今天是父親節,{}。",
|
"celebrateFatherDay": "今天是父親節,{}。",
|
||||||
"celebrateHalloween": "快樂在聖誕節,{}!",
|
"celebrateHalloween": "快樂在聖誕節,{}!",
|
||||||
"celebrateThanksgiving": "今天是感恩節,{}!",
|
"celebrateThanksgiving": "今天是感恩節,{}!",
|
||||||
|
"pendingLunarNewYear": "{} 過春節",
|
||||||
|
"pendingMidAutumn": "{} 過中秋節",
|
||||||
|
"pendingDragonBoat": "{} 過端午節",
|
||||||
"pendingBirthday": "{} 過生日",
|
"pendingBirthday": "{} 過生日",
|
||||||
"pendingMerryXmas": "{} 過聖誕節",
|
"pendingMerryXmas": "{} 過聖誕節",
|
||||||
"pendingNewYear": "{} 跨年",
|
"pendingNewYear": "{} 跨年",
|
||||||
@ -456,6 +470,7 @@
|
|||||||
"accountJoinedAt": "加入於 {}",
|
"accountJoinedAt": "加入於 {}",
|
||||||
"accountBirthday": "出生於 {}",
|
"accountBirthday": "出生於 {}",
|
||||||
"accountBadge": "徽章",
|
"accountBadge": "徽章",
|
||||||
|
"accountCheckInNoRecords": "暫無運勢記錄",
|
||||||
"badgeCompanyStaff": "索爾辛茨士大夫 · 員工",
|
"badgeCompanyStaff": "索爾辛茨士大夫 · 員工",
|
||||||
"badgeSiteMigration": "Solar Network 原住民",
|
"badgeSiteMigration": "Solar Network 原住民",
|
||||||
"accountStatus": "狀態",
|
"accountStatus": "狀態",
|
||||||
@ -464,6 +479,7 @@
|
|||||||
"accountStatusLastSeen": "最後一次上線於 {}",
|
"accountStatusLastSeen": "最後一次上線於 {}",
|
||||||
"postArticle": "Solar Network 上的文章",
|
"postArticle": "Solar Network 上的文章",
|
||||||
"postStory": "Solar Network 上的故事",
|
"postStory": "Solar Network 上的故事",
|
||||||
|
"postLocalDraftRestored": "從本地恢復草稿",
|
||||||
"articleWrittenAt": "發表於 {}",
|
"articleWrittenAt": "發表於 {}",
|
||||||
"articleEditedAt": "編輯於 {}",
|
"articleEditedAt": "編輯於 {}",
|
||||||
"attachmentSaved": "已保存到相冊",
|
"attachmentSaved": "已保存到相冊",
|
||||||
|
@ -185,6 +185,8 @@
|
|||||||
"settingsThemeMaterial3Description": "將應用主題設置為 Material 3 設計範式的主題。",
|
"settingsThemeMaterial3Description": "將應用主題設置為 Material 3 設計範式的主題。",
|
||||||
"settingsAppBarTransparent": "透明頂欄",
|
"settingsAppBarTransparent": "透明頂欄",
|
||||||
"settingsAppBarTransparentDescription": "為頂欄啟用透明效果。",
|
"settingsAppBarTransparentDescription": "為頂欄啟用透明效果。",
|
||||||
|
"settingsDrawerPreferCollapse": "側邊欄偏好摺疊",
|
||||||
|
"settingsDrawerPreferCollapseDescription": "將側邊欄優先摺疊,即使屏幕寬度足夠大去放下整個側邊欄。",
|
||||||
"settingsColorScheme": "主題色",
|
"settingsColorScheme": "主題色",
|
||||||
"settingsColorSchemeDescription": "設置應用主題色。",
|
"settingsColorSchemeDescription": "設置應用主題色。",
|
||||||
"settingsColorSeed": "預設色彩主題",
|
"settingsColorSeed": "預設色彩主題",
|
||||||
@ -279,7 +281,12 @@
|
|||||||
"one": "{} 個附件",
|
"one": "{} 個附件",
|
||||||
"other": "{} 個附件"
|
"other": "{} 個附件"
|
||||||
},
|
},
|
||||||
|
"messageTyping": {
|
||||||
|
"one": "{} 正在輸入",
|
||||||
|
"other": "{} 正在輸入"
|
||||||
|
},
|
||||||
"fieldAttachmentRandomId": "訪問 ID",
|
"fieldAttachmentRandomId": "訪問 ID",
|
||||||
|
"fieldAttachmentAlt": "概述文字",
|
||||||
"addAttachmentFromAlbum": "從相冊中添加附件",
|
"addAttachmentFromAlbum": "從相冊中添加附件",
|
||||||
"addAttachmentFromClipboard": "粘貼附件",
|
"addAttachmentFromClipboard": "粘貼附件",
|
||||||
"addAttachmentFromCameraPhoto": "拍攝照片",
|
"addAttachmentFromCameraPhoto": "拍攝照片",
|
||||||
@ -291,6 +298,7 @@
|
|||||||
"attachmentUnsetAsPostThumbnail": "取消設置為帖子縮略圖",
|
"attachmentUnsetAsPostThumbnail": "取消設置為帖子縮略圖",
|
||||||
"attachmentCompressVideo": "重新編碼視頻",
|
"attachmentCompressVideo": "重新編碼視頻",
|
||||||
"attachmentSetThumbnail": "設置縮略圖",
|
"attachmentSetThumbnail": "設置縮略圖",
|
||||||
|
"attachmentSetAlt": "設置概述文字",
|
||||||
"attachmentCopyRandomId": "複製訪問 ID",
|
"attachmentCopyRandomId": "複製訪問 ID",
|
||||||
"attachmentUpload": "上傳",
|
"attachmentUpload": "上傳",
|
||||||
"attachmentInputDialog": "上傳附件",
|
"attachmentInputDialog": "上傳附件",
|
||||||
@ -404,6 +412,9 @@
|
|||||||
"dailyCheckNegativeHint6": "出門",
|
"dailyCheckNegativeHint6": "出門",
|
||||||
"dailyCheckNegativeHint6Description": "忘帶傘遇上大雨",
|
"dailyCheckNegativeHint6Description": "忘帶傘遇上大雨",
|
||||||
"celebrateBirthday": "生日快樂,{}!",
|
"celebrateBirthday": "生日快樂,{}!",
|
||||||
|
"celebrateLunarNewYear": "春節快樂,{}!",
|
||||||
|
"celebrateMidAutumn": "中秋節快樂,{}!",
|
||||||
|
"celebrateDragonBoat": "端午節快樂,{}!",
|
||||||
"celebrateMerryXmas": "聖誕快樂,{}!",
|
"celebrateMerryXmas": "聖誕快樂,{}!",
|
||||||
"celebrateNewYear": "新年快樂,{}!",
|
"celebrateNewYear": "新年快樂,{}!",
|
||||||
"celebrateValentineDay": "今天是情人節,{}!",
|
"celebrateValentineDay": "今天是情人節,{}!",
|
||||||
@ -413,6 +424,9 @@
|
|||||||
"celebrateFatherDay": "今天是父親節,{}。",
|
"celebrateFatherDay": "今天是父親節,{}。",
|
||||||
"celebrateHalloween": "快樂在聖誕節,{}!",
|
"celebrateHalloween": "快樂在聖誕節,{}!",
|
||||||
"celebrateThanksgiving": "今天是感恩節,{}!",
|
"celebrateThanksgiving": "今天是感恩節,{}!",
|
||||||
|
"pendingLunarNewYear": "{} 過春節",
|
||||||
|
"pendingMidAutumn": "{} 過中秋節",
|
||||||
|
"pendingDragonBoat": "{} 過端午節",
|
||||||
"pendingBirthday": "{} 過生日",
|
"pendingBirthday": "{} 過生日",
|
||||||
"pendingMerryXmas": "{} 過聖誕節",
|
"pendingMerryXmas": "{} 過聖誕節",
|
||||||
"pendingNewYear": "{} 跨年",
|
"pendingNewYear": "{} 跨年",
|
||||||
@ -456,6 +470,7 @@
|
|||||||
"accountJoinedAt": "加入於 {}",
|
"accountJoinedAt": "加入於 {}",
|
||||||
"accountBirthday": "出生於 {}",
|
"accountBirthday": "出生於 {}",
|
||||||
"accountBadge": "徽章",
|
"accountBadge": "徽章",
|
||||||
|
"accountCheckInNoRecords": "暫無運勢記錄",
|
||||||
"badgeCompanyStaff": "索爾辛茨士大夫 · 員工",
|
"badgeCompanyStaff": "索爾辛茨士大夫 · 員工",
|
||||||
"badgeSiteMigration": "Solar Network 原住民",
|
"badgeSiteMigration": "Solar Network 原住民",
|
||||||
"accountStatus": "狀態",
|
"accountStatus": "狀態",
|
||||||
@ -464,6 +479,7 @@
|
|||||||
"accountStatusLastSeen": "最後一次上線於 {}",
|
"accountStatusLastSeen": "最後一次上線於 {}",
|
||||||
"postArticle": "Solar Network 上的文章",
|
"postArticle": "Solar Network 上的文章",
|
||||||
"postStory": "Solar Network 上的故事",
|
"postStory": "Solar Network 上的故事",
|
||||||
|
"postLocalDraftRestored": "從本地恢復草稿",
|
||||||
"articleWrittenAt": "發表於 {}",
|
"articleWrittenAt": "發表於 {}",
|
||||||
"articleEditedAt": "編輯於 {}",
|
"articleEditedAt": "編輯於 {}",
|
||||||
"attachmentSaved": "已保存到相冊",
|
"attachmentSaved": "已保存到相冊",
|
||||||
|
@ -380,7 +380,7 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
|
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
|
||||||
flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a
|
flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
|
||||||
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab
|
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab
|
||||||
flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563
|
flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563
|
||||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
@ -11,6 +12,7 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/providers/user_directory.dart';
|
import 'package:surface/providers/user_directory.dart';
|
||||||
import 'package:surface/providers/websocket.dart';
|
import 'package:surface/providers/websocket.dart';
|
||||||
import 'package:surface/types/chat.dart';
|
import 'package:surface/types/chat.dart';
|
||||||
|
import 'package:surface/types/websocket.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class ChatMessageController extends ChangeNotifier {
|
class ChatMessageController extends ChangeNotifier {
|
||||||
@ -36,8 +38,7 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
|
|
||||||
int? messageTotal;
|
int? messageTotal;
|
||||||
|
|
||||||
bool get isAllLoaded =>
|
bool get isAllLoaded => messageTotal != null && messages.length >= messageTotal!;
|
||||||
messageTotal != null && messages.length >= messageTotal!;
|
|
||||||
|
|
||||||
String? _boxKey;
|
String? _boxKey;
|
||||||
SnChannel? channel;
|
SnChannel? channel;
|
||||||
@ -50,8 +51,10 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
/// Stored as a list of nonce to provide the loading state
|
/// Stored as a list of nonce to provide the loading state
|
||||||
final List<String> unconfirmedMessages = List.empty(growable: true);
|
final List<String> unconfirmedMessages = List.empty(growable: true);
|
||||||
|
|
||||||
Box<SnChatMessage>? get _box =>
|
Box<SnChatMessage>? get _box => (_boxKey == null || isPending) ? null : Hive.box<SnChatMessage>(_boxKey!);
|
||||||
(_boxKey == null || isPending) ? null : Hive.box<SnChatMessage>(_boxKey!);
|
|
||||||
|
final List<SnChannelMember> typingMembers = List.empty(growable: true);
|
||||||
|
final Map<int, Timer> typingInactiveTimer = {};
|
||||||
|
|
||||||
Future<void> initialize(SnChannel chan) async {
|
Future<void> initialize(SnChannel chan) async {
|
||||||
channel = chan;
|
channel = chan;
|
||||||
@ -78,22 +81,16 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
if (event.payload?['channel_id'] != channel?.id) break;
|
if (event.payload?['channel_id'] != channel?.id) break;
|
||||||
final member = SnChannelMember.fromJson(event.payload!['member']);
|
final member = SnChannelMember.fromJson(event.payload!['member']);
|
||||||
if (member.id == profile?.id) break;
|
if (member.id == profile?.id) break;
|
||||||
// TODO impl typing users
|
if (!typingMembers.any((x) => x.id == member.id)) {
|
||||||
// if (!_typingUsers.any((x) => x.id == member.id)) {
|
typingMembers.add(member);
|
||||||
// setState(() {
|
notifyListeners();
|
||||||
// _typingUsers.add(member);
|
}
|
||||||
// });
|
typingInactiveTimer[member.id]?.cancel();
|
||||||
// }
|
typingInactiveTimer[member.id] = Timer(const Duration(seconds: 3), () {
|
||||||
// _typingInactiveTimer[member.id]?.cancel();
|
typingMembers.removeWhere((x) => x.id == member.id);
|
||||||
// _typingInactiveTimer[member.id] = Timer(
|
typingInactiveTimer.remove(member.id);
|
||||||
// const Duration(seconds: 3),
|
notifyListeners();
|
||||||
// () {
|
});
|
||||||
// setState(() {
|
|
||||||
// _typingUsers.removeWhere((x) => x.id == member.id);
|
|
||||||
// _typingInactiveTimer.remove(member.id);
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -101,6 +98,35 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer? _typingNotifyTimer;
|
||||||
|
bool _typingStatus = false;
|
||||||
|
|
||||||
|
Future<void> _sendTypingStatusPackage() async {
|
||||||
|
_ws.conn?.sink.add(jsonEncode(
|
||||||
|
WebSocketPackage(
|
||||||
|
method: 'status.typing',
|
||||||
|
endpoint: 'im',
|
||||||
|
payload: {
|
||||||
|
'channel_id': channel!.id,
|
||||||
|
},
|
||||||
|
).toJson(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pingTypingStatus() {
|
||||||
|
if (!_typingStatus) {
|
||||||
|
_sendTypingStatusPackage();
|
||||||
|
_typingStatus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_typingNotifyTimer == null || !_typingNotifyTimer!.isActive) {
|
||||||
|
_typingNotifyTimer?.cancel();
|
||||||
|
_typingNotifyTimer = Timer(const Duration(milliseconds: 1850), () {
|
||||||
|
_typingStatus = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async {
|
Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async {
|
||||||
if (_box == null) return;
|
if (_box == null) return;
|
||||||
await _box!.putAll({
|
await _box!.putAll({
|
||||||
@ -167,8 +193,7 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case 'messages.edit':
|
case 'messages.edit':
|
||||||
if (message.relatedEventId != null) {
|
if (message.relatedEventId != null) {
|
||||||
final idx =
|
final idx = messages.indexWhere((x) => x.id == message.relatedEventId);
|
||||||
messages.indexWhere((x) => x.id == message.relatedEventId);
|
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
final newBody = message.body;
|
final newBody = message.body;
|
||||||
newBody.remove('related_event');
|
newBody.remove('related_event');
|
||||||
@ -207,8 +232,7 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
'algorithm': 'plain',
|
'algorithm': 'plain',
|
||||||
if (quoteId != null) 'quote_event': quoteId,
|
if (quoteId != null) 'quote_event': quoteId,
|
||||||
if (relatedId != null) 'related_event': relatedId,
|
if (relatedId != null) 'related_event': relatedId,
|
||||||
if (attachments != null && attachments.isNotEmpty)
|
if (attachments != null && attachments.isNotEmpty) 'attachments': attachments,
|
||||||
'attachments': attachments,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock the message locally
|
// Mock the message locally
|
||||||
@ -305,8 +329,7 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
|
|
||||||
if (out == null) {
|
if (out == null) {
|
||||||
try {
|
try {
|
||||||
final resp = await _sn.client
|
final resp = await _sn.client.get('/cgi/im/channels/${channel!.keyPath}/events/$id');
|
||||||
.get('/cgi/im/channels/${channel!.keyPath}/events/$id');
|
|
||||||
out = SnChatMessage.fromJson(resp.data);
|
out = SnChatMessage.fromJson(resp.data);
|
||||||
_saveMessageToLocal([out]);
|
_saveMessageToLocal([out]);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
@ -341,9 +364,7 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
bool forceRemote = false,
|
bool forceRemote = false,
|
||||||
}) async {
|
}) async {
|
||||||
late List<SnChatMessage> out;
|
late List<SnChatMessage> out;
|
||||||
if (_box != null &&
|
if (_box != null && (_box!.length >= take + offset || forceLocal) && !forceRemote) {
|
||||||
(_box!.length >= take + offset || forceLocal) &&
|
|
||||||
!forceRemote) {
|
|
||||||
out = _box!.keys
|
out = _box!.keys
|
||||||
.toList()
|
.toList()
|
||||||
.cast<int>()
|
.cast<int>()
|
||||||
@ -386,8 +407,7 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
quoteEvent: quoteEvent,
|
quoteEvent: quoteEvent,
|
||||||
attachments: attachments
|
attachments: attachments
|
||||||
.where(
|
.where(
|
||||||
(ele) =>
|
(ele) => out[i].body['attachments']?.contains(ele?.rid) ?? false,
|
||||||
out[i].body['attachments']?.contains(ele?.rid) ?? false,
|
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
@ -395,10 +415,7 @@ class ChatMessageController extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Preload sender accounts
|
// Preload sender accounts
|
||||||
final accountId = out
|
final accountId = out.where((ele) => ele.sender.accountId >= 0).map((ele) => ele.sender.accountId).toSet();
|
||||||
.where((ele) => ele.sender.accountId >= 0)
|
|
||||||
.map((ele) => ele.sender.accountId)
|
|
||||||
.toSet();
|
|
||||||
await _ud.listAccount(accountId);
|
await _ud.listAccount(accountId);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
@ -8,6 +11,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:mime/mime.dart';
|
import 'package:mime/mime.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:surface/providers/post.dart';
|
import 'package:surface/providers/post.dart';
|
||||||
import 'package:surface/providers/sn_attachment.dart';
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
@ -100,7 +104,7 @@ class PostWriteMedia {
|
|||||||
if (attachment != null) {
|
if (attachment != null) {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final ImageProvider provider = UniversalImage.provider(sn.getAttachmentUrl(attachment!.rid));
|
final ImageProvider provider = UniversalImage.provider(sn.getAttachmentUrl(attachment!.rid));
|
||||||
if (width != null && height != null) {
|
if (width != null && height != null && !kIsWeb) {
|
||||||
return ResizeImage(
|
return ResizeImage(
|
||||||
provider,
|
provider,
|
||||||
width: width,
|
width: width,
|
||||||
@ -150,9 +154,22 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
final TextEditingController descriptionController = TextEditingController();
|
final TextEditingController descriptionController = TextEditingController();
|
||||||
final TextEditingController aliasController = TextEditingController();
|
final TextEditingController aliasController = TextEditingController();
|
||||||
|
|
||||||
PostWriteController() {
|
bool _temporarySaveActive = false;
|
||||||
titleController.addListener(() => notifyListeners());
|
|
||||||
descriptionController.addListener(() => notifyListeners());
|
PostWriteController({bool doLoadFromTemporary = true}) {
|
||||||
|
_temporarySaveActive = doLoadFromTemporary;
|
||||||
|
titleController.addListener(() {
|
||||||
|
_temporaryPlanSave();
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
descriptionController.addListener(() {
|
||||||
|
_temporaryPlanSave();
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
contentController.addListener(() {
|
||||||
|
_temporaryPlanSave();
|
||||||
|
});
|
||||||
|
if (doLoadFromTemporary) _temporaryLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
String mode = kTitleMap.keys.first;
|
String mode = kTitleMap.keys.first;
|
||||||
@ -199,11 +216,11 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
aliasController.text = post.alias ?? '';
|
aliasController.text = post.alias ?? '';
|
||||||
publishedAt = post.publishedAt;
|
publishedAt = post.publishedAt;
|
||||||
publishedUntil = post.publishedUntil;
|
publishedUntil = post.publishedUntil;
|
||||||
visibleUsers = List.from(post.visibleUsersList ?? []);
|
visibleUsers = List.from(post.visibleUsersList ?? [], growable: true);
|
||||||
invisibleUsers = List.from(post.invisibleUsersList ?? []);
|
invisibleUsers = List.from(post.invisibleUsersList ?? [], growable: true);
|
||||||
visibility = post.visibility;
|
visibility = post.visibility;
|
||||||
tags = List.from(post.tags.map((ele) => ele.alias));
|
tags = List.from(post.tags.map((ele) => ele.alias), growable: true);
|
||||||
categories = List.from(post.categories.map((ele) => ele.alias));
|
categories = List.from(post.categories.map((ele) => ele.alias), growable: true);
|
||||||
attachments.addAll(post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []);
|
attachments.addAll(post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []);
|
||||||
|
|
||||||
if (post.preload?.thumbnail != null && (post.preload?.thumbnail?.rid.isNotEmpty ?? false)) {
|
if (post.preload?.thumbnail != null && (post.preload?.thumbnail?.rid.isNotEmpty ?? false)) {
|
||||||
@ -298,6 +315,83 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
return compressedAttachment;
|
return compressedAttachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const kTemporaryStorageKey = 'int_draft_post';
|
||||||
|
|
||||||
|
Timer? _temporarySaveTimer;
|
||||||
|
|
||||||
|
void _temporaryPlanSave() {
|
||||||
|
if (!_temporarySaveActive) return;
|
||||||
|
_temporarySaveTimer?.cancel();
|
||||||
|
_temporarySaveTimer = Timer(const Duration(seconds: 1), () {
|
||||||
|
_temporarySave();
|
||||||
|
log("[PostWriter] Temporary save saved.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _temporarySave() {
|
||||||
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
|
if (titleController.text.isEmpty &&
|
||||||
|
descriptionController.text.isEmpty &&
|
||||||
|
contentController.text.isEmpty &&
|
||||||
|
thumbnail == null &&
|
||||||
|
attachments.isEmpty) {
|
||||||
|
prefs.remove(kTemporaryStorageKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs.setString(
|
||||||
|
kTemporaryStorageKey,
|
||||||
|
jsonEncode({
|
||||||
|
'publisher': publisher,
|
||||||
|
'content': contentController.text,
|
||||||
|
if (aliasController.text.isNotEmpty) 'alias': aliasController.text,
|
||||||
|
if (titleController.text.isNotEmpty) 'title': titleController.text,
|
||||||
|
if (descriptionController.text.isNotEmpty) 'description': descriptionController.text,
|
||||||
|
if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.toJson(),
|
||||||
|
'attachments':
|
||||||
|
attachments.where((e) => e.attachment != null).map((e) => e.attachment!.toJson()).toList(growable: true),
|
||||||
|
'tags': tags.map((ele) => {'alias': ele}).toList(growable: true),
|
||||||
|
'categories': categories.map((ele) => {'alias': ele}).toList(growable: true),
|
||||||
|
'visibility': visibility,
|
||||||
|
'visible_users_list': visibleUsers,
|
||||||
|
'invisible_users_list': invisibleUsers,
|
||||||
|
if (publishedAt != null) 'published_at': publishedAt!.toUtc().toIso8601String(),
|
||||||
|
if (publishedUntil != null) 'published_until': publishedAt!.toUtc().toIso8601String(),
|
||||||
|
if (replyingPost != null) 'reply_to': replyingPost!.toJson(),
|
||||||
|
if (repostingPost != null) 'repost_to': repostingPost!.toJson(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool temporaryRestored = false;
|
||||||
|
|
||||||
|
void _temporaryLoad() {
|
||||||
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
|
final raw = prefs.getString(kTemporaryStorageKey);
|
||||||
|
if (raw == null) return;
|
||||||
|
final data = jsonDecode(raw);
|
||||||
|
contentController.text = data['content'];
|
||||||
|
aliasController.text = data['alias'] ?? '';
|
||||||
|
titleController.text = data['title'] ?? '';
|
||||||
|
descriptionController.text = data['description'] ?? '';
|
||||||
|
if (data['thumbnail'] != null) thumbnail = PostWriteMedia(SnAttachment.fromJson(data['thumbnail']));
|
||||||
|
attachments
|
||||||
|
.addAll(data['attachments'].map((ele) => PostWriteMedia(SnAttachment.fromJson(ele))).cast<PostWriteMedia>());
|
||||||
|
tags = List.from(data['tags'].map((ele) => ele['alias']));
|
||||||
|
categories = List.from(data['categories'].map((ele) => ele['alias']));
|
||||||
|
visibility = data['visibility'];
|
||||||
|
visibleUsers = List.from(data['visible_users_list'] ?? []);
|
||||||
|
invisibleUsers = List.from(data['invisible_users_list'] ?? []);
|
||||||
|
if (data['published_at'] != null) publishedAt = DateTime.tryParse(data['published_at'])?.toLocal();
|
||||||
|
if (data['published_until'] != null) publishedUntil = DateTime.tryParse(data['published_until'])?.toLocal();
|
||||||
|
replyingPost = data['reply_to'] != null ? SnPost.fromJson(data['reply_to']) : null;
|
||||||
|
repostingPost = data['repost_to'] != null ? SnPost.fromJson(data['repost_to']) : null;
|
||||||
|
temporaryRestored = true;
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> uploadSingleAttachment(BuildContext context, int idx) async {
|
Future<void> uploadSingleAttachment(BuildContext context, int idx) async {
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
|
|
||||||
@ -354,9 +448,11 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final compressedAttachment = await _tryCompressVideoCopy(context, media);
|
if (context.mounted) {
|
||||||
if (compressedAttachment != null) {
|
final compressedAttachment = await _tryCompressVideoCopy(context, media);
|
||||||
item = await attach.updateOne(item, compressedId: compressedAttachment.id);
|
if (compressedAttachment != null) {
|
||||||
|
item = await attach.updateOne(item, compressedId: compressedAttachment.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (context.mounted) context.showErrorDialog(err);
|
if (context.mounted) context.showErrorDialog(err);
|
||||||
@ -415,6 +511,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
method: editingPost != null ? 'PUT' : 'POST',
|
method: editingPost != null ? 'PUT' : 'POST',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -463,73 +560,88 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
|
|
||||||
void setPublisher(SnPublisher? item) {
|
void setPublisher(SnPublisher? item) {
|
||||||
publisher = item;
|
publisher = item;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPublishedAt(DateTime? value) {
|
void setPublishedAt(DateTime? value) {
|
||||||
publishedAt = value;
|
publishedAt = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPublishedUntil(DateTime? value) {
|
void setPublishedUntil(DateTime? value) {
|
||||||
publishedUntil = value;
|
publishedUntil = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTags(List<String> value) {
|
void setTags(List<String> value) {
|
||||||
tags = value;
|
tags = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCategories(List<String> value) {
|
void setCategories(List<String> value) {
|
||||||
categories = value;
|
categories = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVisibility(int value) {
|
void setVisibility(int value) {
|
||||||
visibility = value;
|
visibility = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVisibleUsers(List<int> value) {
|
void setVisibleUsers(List<int> value) {
|
||||||
visibleUsers = value;
|
visibleUsers = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInvisibleUsers(List<int> value) {
|
void setInvisibleUsers(List<int> value) {
|
||||||
invisibleUsers = value;
|
invisibleUsers = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setProgress(double? value) {
|
void setProgress(double? value) {
|
||||||
progress = value;
|
progress = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setIsBusy(bool value) {
|
void setIsBusy(bool value) {
|
||||||
isBusy = value;
|
isBusy = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMode(String value) {
|
void setMode(String value) {
|
||||||
mode = value;
|
mode = value;
|
||||||
|
_temporaryPlanSave();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
publishedAt = null;
|
publishedAt = null;
|
||||||
publishedUntil = null;
|
publishedUntil = null;
|
||||||
|
thumbnail = null;
|
||||||
|
visibility = 0;
|
||||||
titleController.clear();
|
titleController.clear();
|
||||||
descriptionController.clear();
|
descriptionController.clear();
|
||||||
contentController.clear();
|
contentController.clear();
|
||||||
aliasController.clear();
|
aliasController.clear();
|
||||||
tags.clear();
|
tags = List.empty(growable: true);
|
||||||
categories.clear();
|
categories = List.empty(growable: true);
|
||||||
attachments.clear();
|
attachments = List.empty(growable: true);
|
||||||
editingPost = null;
|
editingPost = null;
|
||||||
replyingPost = null;
|
replyingPost = null;
|
||||||
repostingPost = null;
|
repostingPost = null;
|
||||||
mode = kTitleMap.keys.first;
|
mode = kTitleMap.keys.first;
|
||||||
|
temporaryRestored = false;
|
||||||
|
SharedPreferences.getInstance().then((prefs) => prefs.remove(kTemporaryStorageKey));
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import 'package:easy_localization_loader/easy_localization_loader.dart';
|
|||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gap/gap.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
@ -18,7 +17,6 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:relative_time/relative_time.dart';
|
import 'package:relative_time/relative_time.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
|
||||||
import 'package:surface/firebase_options.dart';
|
import 'package:surface/firebase_options.dart';
|
||||||
import 'package:surface/providers/channel.dart';
|
import 'package:surface/providers/channel.dart';
|
||||||
import 'package:surface/providers/chat_call.dart';
|
import 'package:surface/providers/chat_call.dart';
|
||||||
@ -30,6 +28,7 @@ import 'package:surface/providers/post.dart';
|
|||||||
import 'package:surface/providers/relationship.dart';
|
import 'package:surface/providers/relationship.dart';
|
||||||
import 'package:surface/providers/sn_attachment.dart';
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/sn_sticker.dart';
|
||||||
import 'package:surface/providers/special_day.dart';
|
import 'package:surface/providers/special_day.dart';
|
||||||
import 'package:surface/providers/theme.dart';
|
import 'package:surface/providers/theme.dart';
|
||||||
import 'package:surface/providers/user_directory.dart';
|
import 'package:surface/providers/user_directory.dart';
|
||||||
@ -41,7 +40,6 @@ import 'package:surface/types/chat.dart';
|
|||||||
import 'package:surface/types/realm.dart';
|
import 'package:surface/types/realm.dart';
|
||||||
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/version_label.dart';
|
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
import 'package:workmanager/workmanager.dart';
|
import 'package:workmanager/workmanager.dart';
|
||||||
import 'package:in_app_review/in_app_review.dart';
|
import 'package:in_app_review/in_app_review.dart';
|
||||||
@ -144,6 +142,7 @@ class SolianApp extends StatelessWidget {
|
|||||||
Provider(create: (ctx) => SnPostContentProvider(ctx)),
|
Provider(create: (ctx) => SnPostContentProvider(ctx)),
|
||||||
Provider(create: (ctx) => SnRelationshipProvider(ctx)),
|
Provider(create: (ctx) => SnRelationshipProvider(ctx)),
|
||||||
Provider(create: (ctx) => SnLinkPreviewProvider(ctx)),
|
Provider(create: (ctx) => SnLinkPreviewProvider(ctx)),
|
||||||
|
Provider(create: (ctx) => SnStickerProvider(ctx)),
|
||||||
ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)),
|
ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)),
|
||||||
ChangeNotifierProvider(create: (ctx) => WebSocketProvider(ctx)),
|
ChangeNotifierProvider(create: (ctx) => WebSocketProvider(ctx)),
|
||||||
ChangeNotifierProvider(create: (ctx) => NotificationProvider(ctx)),
|
ChangeNotifierProvider(create: (ctx) => NotificationProvider(ctx)),
|
||||||
@ -208,8 +207,6 @@ class _AppSplashScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AppSplashScreenState extends State<_AppSplashScreen> {
|
class _AppSplashScreenState extends State<_AppSplashScreen> {
|
||||||
bool _isReady = false;
|
|
||||||
|
|
||||||
void _tryRequestRating() async {
|
void _tryRequestRating() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
if (prefs.containsKey('first_boot_time')) {
|
if (prefs.containsKey('first_boot_time')) {
|
||||||
@ -261,6 +258,10 @@ class _AppSplashScreenState extends State<_AppSplashScreen> {
|
|||||||
|
|
||||||
Future<void> _initialize() async {
|
Future<void> _initialize() async {
|
||||||
try {
|
try {
|
||||||
|
final cfg = context.read<ConfigProvider>();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
cfg.calcDrawerSize(context);
|
||||||
|
});
|
||||||
final home = context.read<HomeWidgetProvider>();
|
final home = context.read<HomeWidgetProvider>();
|
||||||
await home.initialize();
|
await home.initialize();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@ -282,8 +283,6 @@ class _AppSplashScreenState extends State<_AppSplashScreen> {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
await context.showErrorDialog(err);
|
await context.showErrorDialog(err);
|
||||||
} finally {
|
|
||||||
setState(() => _isReady = true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,32 +302,17 @@ class _AppSplashScreenState extends State<_AppSplashScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (!_isReady) {
|
final cfg = context.read<ConfigProvider>();
|
||||||
return Scaffold(
|
return NotificationListener<SizeChangedLayoutNotification>(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
onNotification: (notification) {
|
||||||
body: Container(
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
constraints: const BoxConstraints(maxWidth: 180),
|
cfg.calcDrawerSize(context);
|
||||||
child: Column(
|
});
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
return false;
|
||||||
mainAxisSize: MainAxisSize.min,
|
},
|
||||||
children: [
|
child: SizeChangedLayoutNotifier(
|
||||||
if (MediaQuery.of(context).platformBrightness == Brightness.dark)
|
child: widget.child,
|
||||||
Image.asset("assets/icon/icon-dark.png", width: 64, height: 64)
|
),
|
||||||
else
|
);
|
||||||
Image.asset("assets/icon/icon.png", width: 64, height: 64),
|
|
||||||
const Gap(6),
|
|
||||||
LinearProgressIndicator(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
|
||||||
),
|
|
||||||
const Gap(20),
|
|
||||||
Text('appInitializing'.tr(), textAlign: TextAlign.center),
|
|
||||||
AppVersionLabel(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
).center(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return widget.child;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:surface/providers/widget.dart';
|
import 'package:surface/providers/widget.dart';
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ const kNetworkServerStoreKey = 'app_server_url';
|
|||||||
const kAppbarTransparentStoreKey = 'app_bar_transparent';
|
const kAppbarTransparentStoreKey = 'app_bar_transparent';
|
||||||
const kAppBackgroundStoreKey = 'app_has_background';
|
const kAppBackgroundStoreKey = 'app_has_background';
|
||||||
const kAppColorSchemeStoreKey = 'app_color_scheme';
|
const kAppColorSchemeStoreKey = 'app_color_scheme';
|
||||||
|
const kAppDrawerPreferCollapse = 'app_drawer_prefer_collapse';
|
||||||
|
|
||||||
const Map<String, FilterQuality> kImageQualityLevel = {
|
const Map<String, FilterQuality> kImageQualityLevel = {
|
||||||
'settingsImageQualityLowest': FilterQuality.none,
|
'settingsImageQualityLowest': FilterQuality.none,
|
||||||
@ -33,6 +35,24 @@ class ConfigProvider extends ChangeNotifier {
|
|||||||
prefs = await SharedPreferences.getInstance();
|
prefs = await SharedPreferences.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool drawerIsCollapsed = false;
|
||||||
|
bool drawerIsExpanded = false;
|
||||||
|
|
||||||
|
void calcDrawerSize(BuildContext context) {
|
||||||
|
final rpb = ResponsiveBreakpoints.of(context);
|
||||||
|
final newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE);
|
||||||
|
final newDrawerIsExpanded = rpb.largerThan(TABLET)
|
||||||
|
? (prefs.getBool(kAppDrawerPreferCollapse) ?? false)
|
||||||
|
? false
|
||||||
|
: true
|
||||||
|
: false;
|
||||||
|
if (newDrawerIsExpanded != drawerIsExpanded || newDrawerIsCollapsed != drawerIsCollapsed) {
|
||||||
|
drawerIsExpanded = newDrawerIsExpanded;
|
||||||
|
drawerIsCollapsed = newDrawerIsCollapsed;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FilterQuality get imageQuality {
|
FilterQuality get imageQuality {
|
||||||
return kImageQualityLevel.values.elementAtOrNull(prefs.getInt('app_image_quality') ?? 3) ?? FilterQuality.high;
|
return kImageQualityLevel.values.elementAtOrNull(prefs.getInt('app_image_quality') ?? 3) ?? FilterQuality.high;
|
||||||
}
|
}
|
||||||
|
38
lib/providers/sn_sticker.dart
Normal file
38
lib/providers/sn_sticker.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/types/attachment.dart';
|
||||||
|
|
||||||
|
class SnStickerProvider {
|
||||||
|
late final SnNetworkProvider _sn;
|
||||||
|
final Map<String, SnSticker?> _cache = {};
|
||||||
|
|
||||||
|
SnStickerProvider(BuildContext context) {
|
||||||
|
_sn = context.read<SnNetworkProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNotSticker(String alias) {
|
||||||
|
return _cache.containsKey(alias) && _cache[alias] == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SnSticker?> lookupSticker(String alias) async {
|
||||||
|
if (_cache.containsKey(alias)) {
|
||||||
|
return _cache[alias];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final resp = await _sn.client.get('/cgi/uc/stickers/lookup/$alias');
|
||||||
|
final sticker = SnSticker.fromJson(resp.data);
|
||||||
|
_cache[alias] = sticker;
|
||||||
|
|
||||||
|
return sticker;
|
||||||
|
} catch (err) {
|
||||||
|
_cache[alias] = null;
|
||||||
|
log('[Sticker] Failed to lookup sticker $alias: $err');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,12 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
|
||||||
// Stored as key: month, day
|
// Stored as key: month, day
|
||||||
const Map<String, (int, int)> kSpecialDays = {
|
final Map<String, (int, int)> kSpecialDays = {
|
||||||
// Birthday is dynamically generated according to the user's profile
|
// Birthday is dynamically generated according to the user's profile
|
||||||
'NewYear': (1, 1),
|
'NewYear': (1, 1),
|
||||||
|
'LunarNewYear': (lunarToGregorian(null, 1, 1).month, lunarToGregorian(null, 1, 1).day),
|
||||||
|
'MidAutumn': (lunarToGregorian(null, 8, 15).month, lunarToGregorian(null, 8, 15).day),
|
||||||
|
'DragonBoat': (lunarToGregorian(null, 5, 5).month, lunarToGregorian(null, 5, 5).day),
|
||||||
'ValentineDay': (2, 14),
|
'ValentineDay': (2, 14),
|
||||||
'LaborDay': (5, 1),
|
'LaborDay': (5, 1),
|
||||||
'MotherDay': (5, 11),
|
'MotherDay': (5, 11),
|
||||||
@ -19,6 +22,9 @@ const Map<String, (int, int)> kSpecialDays = {
|
|||||||
const Map<String, String> kSpecialDaysSymbol = {
|
const Map<String, String> kSpecialDaysSymbol = {
|
||||||
'Birthday': '🎂',
|
'Birthday': '🎂',
|
||||||
'NewYear': '🎉',
|
'NewYear': '🎉',
|
||||||
|
'LunarNewYear': '🎉',
|
||||||
|
'MidAutumn': '🥮',
|
||||||
|
'DragonBoat': '🐲',
|
||||||
'MerryXmas': '🎄',
|
'MerryXmas': '🎄',
|
||||||
'ValentineDay': '💑',
|
'ValentineDay': '💑',
|
||||||
'LaborDay': '🏋️',
|
'LaborDay': '🏋️',
|
||||||
@ -134,3 +140,45 @@ class SpecialDayProvider {
|
|||||||
return (elapsedDuration / totalDuration).clamp(0.0, 1.0);
|
return (elapsedDuration / totalDuration).clamp(0.0, 1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Map<int, LunarYear> lunarYearData = {
|
||||||
|
2025: LunarYear(
|
||||||
|
startDate: DateTime(2025, 1, 29),
|
||||||
|
months: [29, 30, 30, 29, 30, 29, 29, 30, 30, 29, 30, 29],
|
||||||
|
leapMonth: 0,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
class LunarYear {
|
||||||
|
final DateTime startDate;
|
||||||
|
final List<int> months;
|
||||||
|
final int leapMonth;
|
||||||
|
|
||||||
|
LunarYear({required this.startDate, required this.months, required this.leapMonth});
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime lunarToGregorian(int? year, int month, int day, {bool isLeapMonth = false}) {
|
||||||
|
year = year ?? DateTime.now().year;
|
||||||
|
final lunarYear = lunarYearData[year];
|
||||||
|
if (lunarYear == null) {
|
||||||
|
throw Exception('Lunar data for year $year not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
int leapMonth = lunarYear.leapMonth;
|
||||||
|
if (isLeapMonth && (leapMonth == 0 || leapMonth != month)) {
|
||||||
|
throw Exception('Invalid leap month for year $year');
|
||||||
|
}
|
||||||
|
|
||||||
|
int daysFromStart = 0;
|
||||||
|
for (int i = 0; i < month - 1; i++) {
|
||||||
|
daysFromStart += lunarYear.months[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLeapMonth) {
|
||||||
|
daysFromStart += lunarYear.months[month - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
daysFromStart += day - 1;
|
||||||
|
|
||||||
|
return lunarYear.startDate.add(Duration(days: daysFromStart));
|
||||||
|
}
|
||||||
|
@ -35,7 +35,7 @@ class WebSocketProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> connect({noRetry = false}) async {
|
Future<void> connect({noRetry = false}) async {
|
||||||
if (!_ua.isAuthorized) return;
|
if (!_ua.isAuthorized) return;
|
||||||
if (isConnected) {
|
if (isConnected || conn != null) {
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ class WebSocketProvider extends ChangeNotifier {
|
|||||||
onError: (err) {
|
onError: (err) {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
Future.delayed(const Duration(seconds: 11), () => connect());
|
Future.delayed(const Duration(seconds: 1), () => connect());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
'alias': channel.alias,
|
'alias': channel.alias,
|
||||||
},
|
},
|
||||||
).then((value) {
|
).then((value) {
|
||||||
if (value == true) _refreshChannels();
|
if (mounted) _refreshChannels();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -87,7 +87,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
try {
|
try {
|
||||||
final resp = await sn.client.request(
|
final resp = await sn.client.request(
|
||||||
widget.editingChannelAlias != null
|
widget.editingChannelAlias != null
|
||||||
? '/cgi/im/channels/$scope/${widget.editingChannelAlias}'
|
? '/cgi/im/channels/$scope/${_editingChannel!.id}'
|
||||||
: '/cgi/im/channels/$scope',
|
: '/cgi/im/channels/$scope',
|
||||||
data: payload,
|
data: payload,
|
||||||
options: Options(
|
options: Options(
|
||||||
|
@ -17,6 +17,7 @@ import 'package:surface/types/chat.dart';
|
|||||||
import 'package:surface/widgets/chat/call/call_prejoin.dart';
|
import 'package:surface/widgets/chat/call/call_prejoin.dart';
|
||||||
import 'package:surface/widgets/chat/chat_message.dart';
|
import 'package:surface/widgets/chat/chat_message.dart';
|
||||||
import 'package:surface/widgets/chat/chat_message_input.dart';
|
import 'package:surface/widgets/chat/chat_message_input.dart';
|
||||||
|
import 'package:surface/widgets/chat/chat_typing_indicator.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||||
@ -280,11 +281,7 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: InfiniteList(
|
child: InfiniteList(
|
||||||
reverse: true,
|
reverse: true,
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(top: 12),
|
||||||
left: 12,
|
|
||||||
right: 12,
|
|
||||||
top: 12,
|
|
||||||
),
|
|
||||||
hasReachedMax: _messageController.isAllLoaded,
|
hasReachedMax: _messageController.isAllLoaded,
|
||||||
itemCount: _messageController.messages.length,
|
itemCount: _messageController.messages.length,
|
||||||
isLoading: _messageController.isLoading,
|
isLoading: _messageController.isLoading,
|
||||||
@ -310,23 +307,20 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
|||||||
|
|
||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Container(
|
child: ChatMessage(
|
||||||
constraints: BoxConstraints(maxWidth: 480),
|
data: message,
|
||||||
child: ChatMessage(
|
isMerged: canMerge,
|
||||||
data: message,
|
hasMerged: canMergePrevious,
|
||||||
isMerged: canMerge,
|
isPending: _messageController.unconfirmedMessages.contains(message.uuid),
|
||||||
hasMerged: canMergePrevious,
|
onReply: (value) {
|
||||||
isPending: _messageController.unconfirmedMessages.contains(message.uuid),
|
_inputGlobalKey.currentState?.setReply(value);
|
||||||
onReply: (value) {
|
},
|
||||||
_inputGlobalKey.currentState?.setReply(value);
|
onEdit: (value) {
|
||||||
},
|
_inputGlobalKey.currentState?.setEdit(value);
|
||||||
onEdit: (value) {
|
},
|
||||||
_inputGlobalKey.currentState?.setEdit(value);
|
onDelete: (value) {
|
||||||
},
|
_inputGlobalKey.currentState?.deleteMessage(value);
|
||||||
onDelete: (value) {
|
},
|
||||||
_inputGlobalKey.currentState?.deleteMessage(value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -335,11 +329,17 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
|||||||
if (!_messageController.isPending)
|
if (!_messageController.isPending)
|
||||||
Material(
|
Material(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
child: ChatMessageInput(
|
child: Column(
|
||||||
key: _inputGlobalKey,
|
children: [
|
||||||
otherMember: _otherMember,
|
ChatTypingIndicator(controller: _messageController),
|
||||||
controller: _messageController,
|
ChatMessageInput(
|
||||||
).padding(bottom: MediaQuery.of(context).padding.bottom),
|
key: _inputGlobalKey,
|
||||||
|
otherMember: _otherMember,
|
||||||
|
controller: _messageController,
|
||||||
|
),
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -153,9 +153,14 @@ class _HomeDashUpdateWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HomeDashSpecialDayWidget extends StatelessWidget {
|
class _HomeDashSpecialDayWidget extends StatefulWidget {
|
||||||
const _HomeDashSpecialDayWidget();
|
const _HomeDashSpecialDayWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_HomeDashSpecialDayWidget> createState() => _HomeDashSpecialDayWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomeDashSpecialDayWidgetState extends State<_HomeDashSpecialDayWidget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.watch<UserProvider>();
|
final ua = context.watch<UserProvider>();
|
||||||
@ -165,21 +170,20 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
|
|||||||
|
|
||||||
if (days.isNotEmpty) {
|
if (days.isNotEmpty) {
|
||||||
return Column(
|
return Column(
|
||||||
spacing: 8,
|
|
||||||
children: days.map((ele) {
|
children: days.map((ele) {
|
||||||
return Card(
|
return Card(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Text(kSpecialDaysSymbol[ele] ?? '🎉').fontSize(24),
|
leading: Text(kSpecialDaysSymbol[ele] ?? '🎉').fontSize(24),
|
||||||
title: Text('celebrate$ele').tr(args: [ua.user?.nick ?? 'user']),
|
title: Text('celebrate$ele').tr(args: [ua.user?.nick ?? 'user']),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
DateFormat('y/M/d').format(DateTime.now().copyWith(
|
DateFormat('y/M/d').format(DateTime.now().copyWith(
|
||||||
month: kSpecialDays[ele]!.$1,
|
month: kSpecialDays[ele]?.$1,
|
||||||
day: kSpecialDays[ele]!.$2,
|
day: kSpecialDays[ele]?.$2,
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).padding(bottom: 8);
|
).padding(bottom: 8);
|
||||||
}).toList());
|
}).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
final nextOne = dayz.getNextSpecialDay();
|
final nextOne = dayz.getNextSpecialDay();
|
||||||
@ -193,7 +197,7 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
|
|||||||
return Card(
|
return Card(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Text(kSpecialDaysSymbol[name] ?? '🎉').fontSize(24),
|
leading: Text(kSpecialDaysSymbol[name] ?? '🎉').fontSize(24),
|
||||||
title: Text('pending$name').tr(args: [RelativeTime(context).format(date)]),
|
title: Text('pending$name').tr(args: [RelativeTime(context).format(date).replaceFirst('in', '').trim()]),
|
||||||
subtitle: Row(
|
subtitle: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -204,6 +208,9 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
|
|||||||
separatorType: SeparatorType.symbol,
|
separatorType: SeparatorType.symbol,
|
||||||
decoration: BoxDecoration(),
|
decoration: BoxDecoration(),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
onDone: () {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -82,24 +82,15 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isSubmitting = true);
|
setState(() => _isSubmitting = true);
|
||||||
|
|
||||||
List<int> markList = List.empty(growable: true);
|
|
||||||
for (final element in _notifications) {
|
|
||||||
if (element.id <= 0) continue;
|
|
||||||
if (element.readAt != null) continue;
|
|
||||||
markList.add(element.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
await sn.client.put('/cgi/id/notifications/read', data: {
|
final resp = await sn.client.put('/cgi/id/notifications/read/all');
|
||||||
'messages': markList,
|
|
||||||
});
|
|
||||||
_notifications.clear();
|
_notifications.clear();
|
||||||
_fetchNotifications();
|
_fetchNotifications();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar(
|
context.showSnackbar(
|
||||||
'notificationMarkAllReadPrompt'.plural(markList.length),
|
'notificationMarkAllReadPrompt'.plural(resp.data['count']),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@ -215,10 +206,11 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
if (nty.subtitle != null) const Gap(4),
|
if (nty.subtitle != null) const Gap(4),
|
||||||
MarkdownTextContent(
|
SelectionArea(
|
||||||
content: nty.body,
|
child: MarkdownTextContent(
|
||||||
isAutoWarp: true,
|
content: nty.body,
|
||||||
isSelectable: true,
|
isAutoWarp: true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if ([
|
if ([
|
||||||
'interactive.feedback',
|
'interactive.feedback',
|
||||||
|
@ -96,6 +96,8 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
)
|
)
|
||||||
: Text('postDetail').tr(),
|
: Text('postDetail').tr(),
|
||||||
),
|
),
|
||||||
|
@ -54,7 +54,9 @@ class PostEditorScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PostEditorScreenState extends State<PostEditorScreen> {
|
class _PostEditorScreenState extends State<PostEditorScreen> {
|
||||||
final PostWriteController _writeController = PostWriteController();
|
late final PostWriteController _writeController = PostWriteController(
|
||||||
|
doLoadFromTemporary: widget.postEditId == null,
|
||||||
|
);
|
||||||
|
|
||||||
bool _isFetching = false;
|
bool _isFetching = false;
|
||||||
|
|
||||||
@ -364,6 +366,36 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
Container(
|
||||||
|
child: _writeController.temporaryRestored
|
||||||
|
? Container(
|
||||||
|
padding: const EdgeInsets.only(top: 4, bottom: 4, left: 28, right: 22),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.restore, size: 20),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(child: Text('postLocalDraftRestored').tr()),
|
||||||
|
InkWell(
|
||||||
|
child: Text('dialogDismiss').tr(),
|
||||||
|
onTap: () {
|
||||||
|
_writeController.reset();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
)
|
||||||
|
.height(_writeController.temporaryRestored ? 32 : 0, animate: true)
|
||||||
|
.animate(const Duration(milliseconds: 300), Curves.fastLinearToSlowEaseIn),
|
||||||
LoadingIndicator(isActive: _isLoading),
|
LoadingIndicator(isActive: _isLoading),
|
||||||
if (_writeController.isBusy && _writeController.progress != null)
|
if (_writeController.isBusy && _writeController.progress != null)
|
||||||
TweenAnimationBuilder<double>(
|
TweenAnimationBuilder<double>(
|
||||||
|
@ -88,9 +88,9 @@ class _RealmDetailScreenState extends State<RealmDetailScreen> {
|
|||||||
title: Text(_realm?.name ?? 'loading'.tr()),
|
title: Text(_realm?.name ?? 'loading'.tr()),
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
tabs: [
|
tabs: [
|
||||||
Tab(icon: const Icon(Symbols.home)),
|
Tab(icon: Icon(Symbols.home, color: Theme.of(context).appBarTheme.foregroundColor)),
|
||||||
Tab(icon: const Icon(Symbols.group)),
|
Tab(icon: Icon(Symbols.group, color: Theme.of(context).appBarTheme.foregroundColor)),
|
||||||
Tab(icon: const Icon(Symbols.settings)),
|
Tab(icon: Icon(Symbols.settings, color: Theme.of(context).appBarTheme.foregroundColor)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -240,6 +240,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
CheckboxListTile(
|
||||||
|
secondary: const Icon(Symbols.left_panel_close),
|
||||||
|
title: Text('settingsDrawerPreferCollapse').tr(),
|
||||||
|
subtitle: Text('settingsDrawerPreferCollapseDescription').tr(),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||||
|
value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false,
|
||||||
|
onChanged: (value) {
|
||||||
|
_prefs.setBool(kAppDrawerPreferCollapse, value ?? false);
|
||||||
|
final cfg = context.read<ConfigProvider>();
|
||||||
|
cfg.calcDrawerSize(context);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
|
@ -48,7 +48,7 @@ Future<ThemeData> createAppTheme(
|
|||||||
appBarTheme: AppBarTheme(
|
appBarTheme: AppBarTheme(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
elevation: hasAppBarBlurry ? 0 : null,
|
elevation: hasAppBarBlurry ? 0 : null,
|
||||||
backgroundColor: hasAppBarBlurry ? colorScheme.surfaceContainer.withAlpha(200) : colorScheme.primary,
|
backgroundColor: hasAppBarBlurry ? colorScheme.primary.withOpacity(0.3) : colorScheme.primary,
|
||||||
foregroundColor: hasAppBarBlurry ? colorScheme.onSurface : colorScheme.onPrimary,
|
foregroundColor: hasAppBarBlurry ? colorScheme.onSurface : colorScheme.onPrimary,
|
||||||
),
|
),
|
||||||
scaffoldBackgroundColor: Colors.transparent,
|
scaffoldBackgroundColor: Colors.transparent,
|
||||||
|
@ -141,3 +141,39 @@ class SnAttachmentBoost with _$SnAttachmentBoost {
|
|||||||
|
|
||||||
factory SnAttachmentBoost.fromJson(Map<String, Object?> json) => _$SnAttachmentBoostFromJson(json);
|
factory SnAttachmentBoost.fromJson(Map<String, Object?> json) => _$SnAttachmentBoostFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SnSticker with _$SnSticker {
|
||||||
|
const factory SnSticker({
|
||||||
|
required int id,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
required String alias,
|
||||||
|
required String name,
|
||||||
|
required int attachmentId,
|
||||||
|
required SnAttachment attachment,
|
||||||
|
required int packId,
|
||||||
|
required SnStickerPack pack,
|
||||||
|
required int accountId,
|
||||||
|
}) = _SnSticker;
|
||||||
|
|
||||||
|
factory SnSticker.fromJson(Map<String, Object?> json) => _$SnStickerFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SnStickerPack with _$SnStickerPack {
|
||||||
|
const factory SnStickerPack({
|
||||||
|
required int id,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
required String prefix,
|
||||||
|
required String name,
|
||||||
|
required String description,
|
||||||
|
required List<SnSticker>? stickers,
|
||||||
|
required int accountId,
|
||||||
|
}) = _SnStickerPack;
|
||||||
|
|
||||||
|
factory SnStickerPack.fromJson(Map<String, Object?> json) => _$SnStickerPackFromJson(json);
|
||||||
|
}
|
||||||
|
@ -2272,3 +2272,738 @@ abstract class _SnAttachmentBoost implements SnAttachmentBoost {
|
|||||||
_$$SnAttachmentBoostImplCopyWith<_$SnAttachmentBoostImpl> get copyWith =>
|
_$$SnAttachmentBoostImplCopyWith<_$SnAttachmentBoostImpl> get copyWith =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SnSticker _$SnStickerFromJson(Map<String, dynamic> json) {
|
||||||
|
return _SnSticker.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnSticker {
|
||||||
|
int get id => throw _privateConstructorUsedError;
|
||||||
|
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get deletedAt => throw _privateConstructorUsedError;
|
||||||
|
String get alias => throw _privateConstructorUsedError;
|
||||||
|
String get name => throw _privateConstructorUsedError;
|
||||||
|
int get attachmentId => throw _privateConstructorUsedError;
|
||||||
|
SnAttachment get attachment => throw _privateConstructorUsedError;
|
||||||
|
int get packId => throw _privateConstructorUsedError;
|
||||||
|
SnStickerPack get pack => throw _privateConstructorUsedError;
|
||||||
|
int get accountId => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this SnSticker to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$SnStickerCopyWith<SnSticker> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $SnStickerCopyWith<$Res> {
|
||||||
|
factory $SnStickerCopyWith(SnSticker value, $Res Function(SnSticker) then) =
|
||||||
|
_$SnStickerCopyWithImpl<$Res, SnSticker>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String alias,
|
||||||
|
String name,
|
||||||
|
int attachmentId,
|
||||||
|
SnAttachment attachment,
|
||||||
|
int packId,
|
||||||
|
SnStickerPack pack,
|
||||||
|
int accountId});
|
||||||
|
|
||||||
|
$SnAttachmentCopyWith<$Res> get attachment;
|
||||||
|
$SnStickerPackCopyWith<$Res> get pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnStickerCopyWithImpl<$Res, $Val extends SnSticker>
|
||||||
|
implements $SnStickerCopyWith<$Res> {
|
||||||
|
_$SnStickerCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? alias = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? attachmentId = null,
|
||||||
|
Object? attachment = null,
|
||||||
|
Object? packId = null,
|
||||||
|
Object? pack = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
createdAt: null == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
updatedAt: null == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
deletedAt: freezed == deletedAt
|
||||||
|
? _value.deletedAt
|
||||||
|
: deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
alias: null == alias
|
||||||
|
? _value.alias
|
||||||
|
: alias // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
attachmentId: null == attachmentId
|
||||||
|
? _value.attachmentId
|
||||||
|
: attachmentId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
attachment: null == attachment
|
||||||
|
? _value.attachment
|
||||||
|
: attachment // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAttachment,
|
||||||
|
packId: null == packId
|
||||||
|
? _value.packId
|
||||||
|
: packId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
pack: null == pack
|
||||||
|
? _value.pack
|
||||||
|
: pack // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnStickerPack,
|
||||||
|
accountId: null == accountId
|
||||||
|
? _value.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAttachmentCopyWith<$Res> get attachment {
|
||||||
|
return $SnAttachmentCopyWith<$Res>(_value.attachment, (value) {
|
||||||
|
return _then(_value.copyWith(attachment: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnStickerPackCopyWith<$Res> get pack {
|
||||||
|
return $SnStickerPackCopyWith<$Res>(_value.pack, (value) {
|
||||||
|
return _then(_value.copyWith(pack: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$SnStickerImplCopyWith<$Res>
|
||||||
|
implements $SnStickerCopyWith<$Res> {
|
||||||
|
factory _$$SnStickerImplCopyWith(
|
||||||
|
_$SnStickerImpl value, $Res Function(_$SnStickerImpl) then) =
|
||||||
|
__$$SnStickerImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String alias,
|
||||||
|
String name,
|
||||||
|
int attachmentId,
|
||||||
|
SnAttachment attachment,
|
||||||
|
int packId,
|
||||||
|
SnStickerPack pack,
|
||||||
|
int accountId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$SnAttachmentCopyWith<$Res> get attachment;
|
||||||
|
@override
|
||||||
|
$SnStickerPackCopyWith<$Res> get pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$SnStickerImplCopyWithImpl<$Res>
|
||||||
|
extends _$SnStickerCopyWithImpl<$Res, _$SnStickerImpl>
|
||||||
|
implements _$$SnStickerImplCopyWith<$Res> {
|
||||||
|
__$$SnStickerImplCopyWithImpl(
|
||||||
|
_$SnStickerImpl _value, $Res Function(_$SnStickerImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? alias = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? attachmentId = null,
|
||||||
|
Object? attachment = null,
|
||||||
|
Object? packId = null,
|
||||||
|
Object? pack = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$SnStickerImpl(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
createdAt: null == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
updatedAt: null == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
deletedAt: freezed == deletedAt
|
||||||
|
? _value.deletedAt
|
||||||
|
: deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
alias: null == alias
|
||||||
|
? _value.alias
|
||||||
|
: alias // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
attachmentId: null == attachmentId
|
||||||
|
? _value.attachmentId
|
||||||
|
: attachmentId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
attachment: null == attachment
|
||||||
|
? _value.attachment
|
||||||
|
: attachment // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAttachment,
|
||||||
|
packId: null == packId
|
||||||
|
? _value.packId
|
||||||
|
: packId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
pack: null == pack
|
||||||
|
? _value.pack
|
||||||
|
: pack // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnStickerPack,
|
||||||
|
accountId: null == accountId
|
||||||
|
? _value.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$SnStickerImpl implements _SnSticker {
|
||||||
|
const _$SnStickerImpl(
|
||||||
|
{required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.alias,
|
||||||
|
required this.name,
|
||||||
|
required this.attachmentId,
|
||||||
|
required this.attachment,
|
||||||
|
required this.packId,
|
||||||
|
required this.pack,
|
||||||
|
required this.accountId});
|
||||||
|
|
||||||
|
factory _$SnStickerImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$SnStickerImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int id;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime updatedAt;
|
||||||
|
@override
|
||||||
|
final DateTime? deletedAt;
|
||||||
|
@override
|
||||||
|
final String alias;
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final int attachmentId;
|
||||||
|
@override
|
||||||
|
final SnAttachment attachment;
|
||||||
|
@override
|
||||||
|
final int packId;
|
||||||
|
@override
|
||||||
|
final SnStickerPack pack;
|
||||||
|
@override
|
||||||
|
final int accountId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnSticker(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, attachmentId: $attachmentId, attachment: $attachment, packId: $packId, pack: $pack, accountId: $accountId)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$SnStickerImpl &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt) &&
|
||||||
|
(identical(other.deletedAt, deletedAt) ||
|
||||||
|
other.deletedAt == deletedAt) &&
|
||||||
|
(identical(other.alias, alias) || other.alias == alias) &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.attachmentId, attachmentId) ||
|
||||||
|
other.attachmentId == attachmentId) &&
|
||||||
|
(identical(other.attachment, attachment) ||
|
||||||
|
other.attachment == attachment) &&
|
||||||
|
(identical(other.packId, packId) || other.packId == packId) &&
|
||||||
|
(identical(other.pack, pack) || other.pack == pack) &&
|
||||||
|
(identical(other.accountId, accountId) ||
|
||||||
|
other.accountId == accountId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
deletedAt,
|
||||||
|
alias,
|
||||||
|
name,
|
||||||
|
attachmentId,
|
||||||
|
attachment,
|
||||||
|
packId,
|
||||||
|
pack,
|
||||||
|
accountId);
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$SnStickerImplCopyWith<_$SnStickerImpl> get copyWith =>
|
||||||
|
__$$SnStickerImplCopyWithImpl<_$SnStickerImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$SnStickerImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _SnSticker implements SnSticker {
|
||||||
|
const factory _SnSticker(
|
||||||
|
{required final int id,
|
||||||
|
required final DateTime createdAt,
|
||||||
|
required final DateTime updatedAt,
|
||||||
|
required final DateTime? deletedAt,
|
||||||
|
required final String alias,
|
||||||
|
required final String name,
|
||||||
|
required final int attachmentId,
|
||||||
|
required final SnAttachment attachment,
|
||||||
|
required final int packId,
|
||||||
|
required final SnStickerPack pack,
|
||||||
|
required final int accountId}) = _$SnStickerImpl;
|
||||||
|
|
||||||
|
factory _SnSticker.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$SnStickerImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get id;
|
||||||
|
@override
|
||||||
|
DateTime get createdAt;
|
||||||
|
@override
|
||||||
|
DateTime get updatedAt;
|
||||||
|
@override
|
||||||
|
DateTime? get deletedAt;
|
||||||
|
@override
|
||||||
|
String get alias;
|
||||||
|
@override
|
||||||
|
String get name;
|
||||||
|
@override
|
||||||
|
int get attachmentId;
|
||||||
|
@override
|
||||||
|
SnAttachment get attachment;
|
||||||
|
@override
|
||||||
|
int get packId;
|
||||||
|
@override
|
||||||
|
SnStickerPack get pack;
|
||||||
|
@override
|
||||||
|
int get accountId;
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$SnStickerImplCopyWith<_$SnStickerImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
SnStickerPack _$SnStickerPackFromJson(Map<String, dynamic> json) {
|
||||||
|
return _SnStickerPack.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnStickerPack {
|
||||||
|
int get id => throw _privateConstructorUsedError;
|
||||||
|
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get deletedAt => throw _privateConstructorUsedError;
|
||||||
|
String get prefix => throw _privateConstructorUsedError;
|
||||||
|
String get name => throw _privateConstructorUsedError;
|
||||||
|
String get description => throw _privateConstructorUsedError;
|
||||||
|
List<SnSticker>? get stickers => throw _privateConstructorUsedError;
|
||||||
|
int get accountId => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this SnStickerPack to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$SnStickerPackCopyWith<SnStickerPack> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $SnStickerPackCopyWith<$Res> {
|
||||||
|
factory $SnStickerPackCopyWith(
|
||||||
|
SnStickerPack value, $Res Function(SnStickerPack) then) =
|
||||||
|
_$SnStickerPackCopyWithImpl<$Res, SnStickerPack>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String prefix,
|
||||||
|
String name,
|
||||||
|
String description,
|
||||||
|
List<SnSticker>? stickers,
|
||||||
|
int accountId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnStickerPackCopyWithImpl<$Res, $Val extends SnStickerPack>
|
||||||
|
implements $SnStickerPackCopyWith<$Res> {
|
||||||
|
_$SnStickerPackCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? prefix = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? description = null,
|
||||||
|
Object? stickers = freezed,
|
||||||
|
Object? accountId = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
createdAt: null == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
updatedAt: null == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
deletedAt: freezed == deletedAt
|
||||||
|
? _value.deletedAt
|
||||||
|
: deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
prefix: null == prefix
|
||||||
|
? _value.prefix
|
||||||
|
: prefix // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
description: null == description
|
||||||
|
? _value.description
|
||||||
|
: description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
stickers: freezed == stickers
|
||||||
|
? _value.stickers
|
||||||
|
: stickers // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnSticker>?,
|
||||||
|
accountId: null == accountId
|
||||||
|
? _value.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$SnStickerPackImplCopyWith<$Res>
|
||||||
|
implements $SnStickerPackCopyWith<$Res> {
|
||||||
|
factory _$$SnStickerPackImplCopyWith(
|
||||||
|
_$SnStickerPackImpl value, $Res Function(_$SnStickerPackImpl) then) =
|
||||||
|
__$$SnStickerPackImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String prefix,
|
||||||
|
String name,
|
||||||
|
String description,
|
||||||
|
List<SnSticker>? stickers,
|
||||||
|
int accountId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$SnStickerPackImplCopyWithImpl<$Res>
|
||||||
|
extends _$SnStickerPackCopyWithImpl<$Res, _$SnStickerPackImpl>
|
||||||
|
implements _$$SnStickerPackImplCopyWith<$Res> {
|
||||||
|
__$$SnStickerPackImplCopyWithImpl(
|
||||||
|
_$SnStickerPackImpl _value, $Res Function(_$SnStickerPackImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? prefix = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? description = null,
|
||||||
|
Object? stickers = freezed,
|
||||||
|
Object? accountId = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$SnStickerPackImpl(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
createdAt: null == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
updatedAt: null == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
deletedAt: freezed == deletedAt
|
||||||
|
? _value.deletedAt
|
||||||
|
: deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
prefix: null == prefix
|
||||||
|
? _value.prefix
|
||||||
|
: prefix // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
description: null == description
|
||||||
|
? _value.description
|
||||||
|
: description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
stickers: freezed == stickers
|
||||||
|
? _value._stickers
|
||||||
|
: stickers // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnSticker>?,
|
||||||
|
accountId: null == accountId
|
||||||
|
? _value.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$SnStickerPackImpl implements _SnStickerPack {
|
||||||
|
const _$SnStickerPackImpl(
|
||||||
|
{required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.prefix,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required final List<SnSticker>? stickers,
|
||||||
|
required this.accountId})
|
||||||
|
: _stickers = stickers;
|
||||||
|
|
||||||
|
factory _$SnStickerPackImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$SnStickerPackImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int id;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime updatedAt;
|
||||||
|
@override
|
||||||
|
final DateTime? deletedAt;
|
||||||
|
@override
|
||||||
|
final String prefix;
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final String description;
|
||||||
|
final List<SnSticker>? _stickers;
|
||||||
|
@override
|
||||||
|
List<SnSticker>? get stickers {
|
||||||
|
final value = _stickers;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_stickers is EqualUnmodifiableListView) return _stickers;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int accountId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnStickerPack(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, prefix: $prefix, name: $name, description: $description, stickers: $stickers, accountId: $accountId)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$SnStickerPackImpl &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt) &&
|
||||||
|
(identical(other.deletedAt, deletedAt) ||
|
||||||
|
other.deletedAt == deletedAt) &&
|
||||||
|
(identical(other.prefix, prefix) || other.prefix == prefix) &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.description, description) ||
|
||||||
|
other.description == description) &&
|
||||||
|
const DeepCollectionEquality().equals(other._stickers, _stickers) &&
|
||||||
|
(identical(other.accountId, accountId) ||
|
||||||
|
other.accountId == accountId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
deletedAt,
|
||||||
|
prefix,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
const DeepCollectionEquality().hash(_stickers),
|
||||||
|
accountId);
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$SnStickerPackImplCopyWith<_$SnStickerPackImpl> get copyWith =>
|
||||||
|
__$$SnStickerPackImplCopyWithImpl<_$SnStickerPackImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$SnStickerPackImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _SnStickerPack implements SnStickerPack {
|
||||||
|
const factory _SnStickerPack(
|
||||||
|
{required final int id,
|
||||||
|
required final DateTime createdAt,
|
||||||
|
required final DateTime updatedAt,
|
||||||
|
required final DateTime? deletedAt,
|
||||||
|
required final String prefix,
|
||||||
|
required final String name,
|
||||||
|
required final String description,
|
||||||
|
required final List<SnSticker>? stickers,
|
||||||
|
required final int accountId}) = _$SnStickerPackImpl;
|
||||||
|
|
||||||
|
factory _SnStickerPack.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$SnStickerPackImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get id;
|
||||||
|
@override
|
||||||
|
DateTime get createdAt;
|
||||||
|
@override
|
||||||
|
DateTime get updatedAt;
|
||||||
|
@override
|
||||||
|
DateTime? get deletedAt;
|
||||||
|
@override
|
||||||
|
String get prefix;
|
||||||
|
@override
|
||||||
|
String get name;
|
||||||
|
@override
|
||||||
|
String get description;
|
||||||
|
@override
|
||||||
|
List<SnSticker>? get stickers;
|
||||||
|
@override
|
||||||
|
int get accountId;
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$SnStickerPackImplCopyWith<_$SnStickerPackImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
@ -218,3 +218,66 @@ Map<String, dynamic> _$$SnAttachmentBoostImplToJson(
|
|||||||
'attachment': instance.attachment.toJson(),
|
'attachment': instance.attachment.toJson(),
|
||||||
'account': instance.account,
|
'account': instance.account,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_$SnStickerImpl _$$SnStickerImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnStickerImpl(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
attachmentId: (json['attachment_id'] as num).toInt(),
|
||||||
|
attachment:
|
||||||
|
SnAttachment.fromJson(json['attachment'] as Map<String, dynamic>),
|
||||||
|
packId: (json['pack_id'] as num).toInt(),
|
||||||
|
pack: SnStickerPack.fromJson(json['pack'] as Map<String, dynamic>),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$SnStickerImplToJson(_$SnStickerImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'attachment_id': instance.attachmentId,
|
||||||
|
'attachment': instance.attachment.toJson(),
|
||||||
|
'pack_id': instance.packId,
|
||||||
|
'pack': instance.pack.toJson(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$SnStickerPackImpl _$$SnStickerPackImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnStickerPackImpl(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
prefix: json['prefix'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
stickers: (json['stickers'] as List<dynamic>?)
|
||||||
|
?.map((e) => SnSticker.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$SnStickerPackImplToJson(_$SnStickerPackImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'prefix': instance.prefix,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'stickers': instance.stickers?.map((e) => e.toJson()).toList(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
||||||
|
@ -315,6 +315,7 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo
|
|||||||
}
|
}
|
||||||
|
|
||||||
return MaterialDesktopVideoControlsTheme(
|
return MaterialDesktopVideoControlsTheme(
|
||||||
|
key: Key('material-desktop-video-controls-theme-$_showOriginal'),
|
||||||
normal: MaterialDesktopVideoControlsThemeData(
|
normal: MaterialDesktopVideoControlsThemeData(
|
||||||
buttonBarButtonSize: 24,
|
buttonBarButtonSize: 24,
|
||||||
buttonBarButtonColor: Colors.white,
|
buttonBarButtonColor: Colors.white,
|
||||||
@ -324,14 +325,16 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo
|
|||||||
MaterialDesktopCustomButton(
|
MaterialDesktopCustomButton(
|
||||||
iconSize: 24,
|
iconSize: 24,
|
||||||
onPressed: _toggleOriginal,
|
onPressed: _toggleOriginal,
|
||||||
icon: Builder(builder: (context) {
|
icon: Icon(
|
||||||
return _showOriginal ? const Icon(Symbols.high_quality, size: 24) : const Icon(Symbols.sd, size: 24);
|
_showOriginal ? Symbols.high_quality : Symbols.sd,
|
||||||
}),
|
size: 24,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
fullscreen: const MaterialDesktopVideoControlsThemeData(),
|
fullscreen: const MaterialDesktopVideoControlsThemeData(),
|
||||||
child: MaterialVideoControlsTheme(
|
child: MaterialVideoControlsTheme(
|
||||||
|
key: Key('material-video-controls-theme-$_showOriginal'),
|
||||||
normal: MaterialVideoControlsThemeData(
|
normal: MaterialVideoControlsThemeData(
|
||||||
buttonBarButtonSize: 24,
|
buttonBarButtonSize: 24,
|
||||||
buttonBarButtonColor: Colors.white,
|
buttonBarButtonColor: Colors.white,
|
||||||
|
@ -15,20 +15,22 @@ class AttachmentList extends StatefulWidget {
|
|||||||
final List<SnAttachment?> data;
|
final List<SnAttachment?> data;
|
||||||
final bool bordered;
|
final bool bordered;
|
||||||
final bool gridded;
|
final bool gridded;
|
||||||
final bool noGrow;
|
|
||||||
final BoxFit fit;
|
final BoxFit fit;
|
||||||
final double? maxHeight;
|
final double? maxHeight;
|
||||||
final EdgeInsets? listPadding;
|
final double? minWidth;
|
||||||
|
final double? maxWidth;
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
|
||||||
const AttachmentList({
|
const AttachmentList({
|
||||||
super.key,
|
super.key,
|
||||||
required this.data,
|
required this.data,
|
||||||
this.bordered = false,
|
this.bordered = false,
|
||||||
this.gridded = false,
|
this.gridded = false,
|
||||||
this.noGrow = false,
|
|
||||||
this.fit = BoxFit.cover,
|
this.fit = BoxFit.cover,
|
||||||
this.maxHeight,
|
this.maxHeight,
|
||||||
this.listPadding,
|
this.minWidth,
|
||||||
|
this.maxWidth,
|
||||||
|
this.padding,
|
||||||
});
|
});
|
||||||
|
|
||||||
static const BorderRadius kDefaultRadius = BorderRadius.all(Radius.circular(8));
|
static const BorderRadius kDefaultRadius = BorderRadius.all(Radius.circular(8));
|
||||||
@ -43,8 +45,6 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
(_) => const Uuid().v4(),
|
(_) => const Uuid().v4(),
|
||||||
);
|
);
|
||||||
|
|
||||||
static const double kAttachmentMaxWidth = 640;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
@ -53,8 +53,8 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
widget.bordered ? BorderSide(width: 1, color: Theme.of(context).dividerColor) : BorderSide.none;
|
widget.bordered ? BorderSide(width: 1, color: Theme.of(context).dividerColor) : BorderSide.none;
|
||||||
final backgroundColor = Theme.of(context).colorScheme.surfaceContainer;
|
final backgroundColor = Theme.of(context).colorScheme.surfaceContainer;
|
||||||
final constraints = BoxConstraints(
|
final constraints = BoxConstraints(
|
||||||
minWidth: 80,
|
minWidth: widget.minWidth ?? 80,
|
||||||
maxHeight: widget.maxHeight ?? double.infinity,
|
maxHeight: widget.maxHeight ?? MediaQuery.of(context).size.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.data.isEmpty) return const SizedBox.shrink();
|
if (widget.data.isEmpty) return const SizedBox.shrink();
|
||||||
@ -67,118 +67,77 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
}
|
}
|
||||||
.toDouble();
|
.toDouble();
|
||||||
|
|
||||||
return Padding(
|
return Container(
|
||||||
padding: widget.listPadding ?? EdgeInsets.zero,
|
padding: widget.padding ?? EdgeInsets.zero,
|
||||||
child: Container(
|
constraints: constraints,
|
||||||
constraints: constraints,
|
child: GestureDetector(
|
||||||
width: double.infinity,
|
child: AspectRatio(
|
||||||
child: GestureDetector(
|
aspectRatio: singleAspectRatio,
|
||||||
child: AspectRatio(
|
child: Container(
|
||||||
aspectRatio: singleAspectRatio,
|
decoration: BoxDecoration(
|
||||||
child: Container(
|
color: backgroundColor,
|
||||||
decoration: BoxDecoration(
|
border: Border.fromBorderSide(borderSide),
|
||||||
color: backgroundColor,
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
border: Border.fromBorderSide(borderSide),
|
),
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
child: ClipRRect(
|
||||||
),
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
child: ClipRRect(
|
child: AttachmentItem(
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
data: widget.data[0],
|
||||||
child: AttachmentItem(
|
heroTag: heroTags[0],
|
||||||
data: widget.data[0],
|
fit: widget.fit,
|
||||||
heroTag: heroTags[0],
|
|
||||||
fit: widget.fit,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
|
||||||
if (widget.data.firstOrNull?.mediaType != SnMediaType.image) return;
|
|
||||||
context.pushTransparentRoute(
|
|
||||||
AttachmentZoomView(
|
|
||||||
data: widget.data.where((ele) => ele != null).cast(),
|
|
||||||
initialIndex: 0,
|
|
||||||
heroTags: heroTags,
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.black.withOpacity(0.7),
|
|
||||||
rootNavigator: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (widget.data.firstOrNull?.mediaType != SnMediaType.image) return;
|
||||||
|
context.pushTransparentRoute(
|
||||||
|
AttachmentZoomView(
|
||||||
|
data: widget.data.where((ele) => ele != null).cast(),
|
||||||
|
initialIndex: 0,
|
||||||
|
heroTags: heroTags,
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.black.withOpacity(0.7),
|
||||||
|
rootNavigator: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.gridded) {
|
if (widget.gridded) {
|
||||||
return Padding(
|
return Container(
|
||||||
padding: widget.listPadding ?? EdgeInsets.zero,
|
margin: widget.padding ?? EdgeInsets.zero,
|
||||||
child: Container(
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: backgroundColor,
|
||||||
color: backgroundColor,
|
border: Border(
|
||||||
border: Border(
|
top: borderSide,
|
||||||
top: borderSide,
|
bottom: borderSide,
|
||||||
bottom: borderSide,
|
|
||||||
),
|
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
|
||||||
),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
|
||||||
child: StaggeredGrid.count(
|
|
||||||
crossAxisCount: math.min(widget.data.length, 2),
|
|
||||||
crossAxisSpacing: 4,
|
|
||||||
mainAxisSpacing: 4,
|
|
||||||
children: widget.data
|
|
||||||
.mapIndexed(
|
|
||||||
(idx, ele) => GestureDetector(
|
|
||||||
child: Container(
|
|
||||||
constraints: constraints,
|
|
||||||
child: AttachmentItem(
|
|
||||||
data: ele,
|
|
||||||
heroTag: heroTags[idx],
|
|
||||||
fit: widget.fit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
if (widget.data[idx]!.mediaType != SnMediaType.image) return;
|
|
||||||
context.pushTransparentRoute(
|
|
||||||
AttachmentZoomView(
|
|
||||||
data: widget.data.where((ele) => ele != null).cast(),
|
|
||||||
initialIndex: idx,
|
|
||||||
heroTags: heroTags,
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.black.withOpacity(0.7),
|
|
||||||
rootNavigator: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
),
|
),
|
||||||
);
|
child: ClipRRect(
|
||||||
}
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
|
child: StaggeredGrid.count(
|
||||||
return AspectRatio(
|
crossAxisCount: math.min(widget.data.length, 2),
|
||||||
aspectRatio: (widget.data.firstOrNull?.data['ratio'] ?? 1).toDouble(),
|
crossAxisSpacing: 4,
|
||||||
child: Container(
|
mainAxisSpacing: 4,
|
||||||
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
|
children: widget.data
|
||||||
child: ScrollConfiguration(
|
.mapIndexed(
|
||||||
behavior: _AttachmentListScrollBehavior(),
|
(idx, ele) => GestureDetector(
|
||||||
child: ListView.separated(
|
child: Container(
|
||||||
shrinkWrap: true,
|
constraints: constraints,
|
||||||
itemCount: widget.data.length,
|
child: AttachmentItem(
|
||||||
itemBuilder: (context, idx) {
|
data: ele,
|
||||||
return Container(
|
heroTag: heroTags[idx],
|
||||||
constraints: constraints,
|
fit: BoxFit.cover,
|
||||||
child: AspectRatio(
|
),
|
||||||
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
),
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
|
if (widget.data[idx]!.mediaType != SnMediaType.image) return;
|
||||||
context.pushTransparentRoute(
|
context.pushTransparentRoute(
|
||||||
AttachmentZoomView(
|
AttachmentZoomView(
|
||||||
data:
|
data: widget.data.where((ele) => ele != null).cast(),
|
||||||
widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
|
|
||||||
initialIndex: idx,
|
initialIndex: idx,
|
||||||
heroTags: heroTags,
|
heroTags: heroTags,
|
||||||
),
|
),
|
||||||
@ -186,44 +145,76 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
rootNavigator: true,
|
rootNavigator: true,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Stack(
|
),
|
||||||
fit: StackFit.expand,
|
)
|
||||||
children: [
|
.toList(),
|
||||||
Container(
|
),
|
||||||
decoration: BoxDecoration(
|
),
|
||||||
color: backgroundColor,
|
);
|
||||||
border: Border(
|
}
|
||||||
top: borderSide,
|
|
||||||
bottom: borderSide,
|
return Container(
|
||||||
),
|
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
child: ScrollConfiguration(
|
||||||
|
behavior: _AttachmentListScrollBehavior(),
|
||||||
|
child: ListView.separated(
|
||||||
|
padding: widget.padding,
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: widget.data.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
return Container(
|
||||||
|
constraints: constraints.copyWith(maxWidth: widget.maxWidth),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
|
||||||
|
context.pushTransparentRoute(
|
||||||
|
AttachmentZoomView(
|
||||||
|
data: widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
|
||||||
|
initialIndex: idx,
|
||||||
|
heroTags: heroTags,
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.black.withOpacity(0.7),
|
||||||
|
rootNavigator: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
|
border: Border(
|
||||||
|
top: borderSide,
|
||||||
|
bottom: borderSide,
|
||||||
),
|
),
|
||||||
child: ClipRRect(
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
),
|
||||||
child: AttachmentItem(
|
child: ClipRRect(
|
||||||
data: widget.data[idx],
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
heroTag: heroTags[idx],
|
child: AttachmentItem(
|
||||||
),
|
data: widget.data[idx],
|
||||||
|
heroTag: heroTags[idx],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
),
|
||||||
right: 8,
|
Positioned(
|
||||||
bottom: 8,
|
right: 8,
|
||||||
child: Chip(
|
bottom: 8,
|
||||||
label: Text('${idx + 1}/${widget.data.length}'),
|
child: Chip(
|
||||||
),
|
label: Text('${idx + 1}/${widget.data.length}'),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
separatorBuilder: (context, index) => const Gap(8),
|
},
|
||||||
padding: widget.listPadding,
|
separatorBuilder: (context, index) => const Gap(8),
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -231,7 +231,7 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
|
|||||||
children: [
|
children: [
|
||||||
IgnorePointer(
|
IgnorePointer(
|
||||||
child: AccountImage(
|
child: AccountImage(
|
||||||
content: account!.avatar,
|
content: account?.avatar,
|
||||||
radius: 19,
|
radius: 19,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -246,7 +246,7 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
|
|||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
account.nick,
|
account?.nick ?? 'unknown'.tr(),
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -312,11 +312,6 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
|
|||||||
]),
|
]),
|
||||||
style: metaTextStyle,
|
style: metaTextStyle,
|
||||||
).padding(right: 2),
|
).padding(right: 2),
|
||||||
if (item.metadata['exif']?['ShutterSpeed'] != null)
|
|
||||||
Text(
|
|
||||||
item.metadata['exif']?['ShutterSpeed'],
|
|
||||||
style: metaTextStyle,
|
|
||||||
).padding(right: 2),
|
|
||||||
if (item.metadata['exif']?['ISO'] != null)
|
if (item.metadata['exif']?['ISO'] != null)
|
||||||
Text(
|
Text(
|
||||||
'ISO${item.metadata['exif']?['ISO']}',
|
'ISO${item.metadata['exif']?['ISO']}',
|
||||||
@ -334,7 +329,7 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
|
|||||||
)
|
)
|
||||||
else
|
else
|
||||||
Text(
|
Text(
|
||||||
'${item.size} Bytes',
|
item.size.formatBytes(),
|
||||||
style: metaTextStyle,
|
style: metaTextStyle,
|
||||||
),
|
),
|
||||||
if (item.metadata['width'] != null && item.metadata['height'] != null)
|
if (item.metadata['width'] != null && item.metadata['height'] != null)
|
||||||
|
86
lib/widgets/attachment/pending_attachment_alt.dart
Normal file
86
lib/widgets/attachment/pending_attachment_alt.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:surface/controllers/post_write_controller.dart';
|
||||||
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
|
||||||
|
class PendingAttachmentAltDialog extends StatefulWidget {
|
||||||
|
final PostWriteMedia media;
|
||||||
|
const PendingAttachmentAltDialog({super.key, required this.media});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PendingAttachmentAltDialog> createState() => _PendingAttachmentAltDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PendingAttachmentAltDialogState extends State<PendingAttachmentAltDialog> {
|
||||||
|
final _contentController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_contentController.text = widget.media.attachment!.alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isBusy = false;
|
||||||
|
|
||||||
|
Future<void> _performAction() async {
|
||||||
|
if (_isBusy) return;
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final attach = context.read<SnAttachmentProvider>();
|
||||||
|
final result = await attach.updateOne(
|
||||||
|
widget.media.attachment!,
|
||||||
|
alt: _contentController.text,
|
||||||
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
attach.putCache([result]);
|
||||||
|
Navigator.pop(context, result);
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_contentController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('attachmentSetAlt').tr(),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _contentController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'fieldAttachmentAlt'.tr(),
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
),
|
||||||
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: _isBusy ? null : () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('dialogDismiss'.tr()),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: _isBusy ? null : () => _performAction(),
|
||||||
|
child: Text('dialogConfirm'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ class ChatMessage extends StatelessWidget {
|
|||||||
final Function(SnChatMessage)? onReply;
|
final Function(SnChatMessage)? onReply;
|
||||||
final Function(SnChatMessage)? onEdit;
|
final Function(SnChatMessage)? onEdit;
|
||||||
final Function(SnChatMessage)? onDelete;
|
final Function(SnChatMessage)? onDelete;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
const ChatMessage({
|
const ChatMessage({
|
||||||
super.key,
|
super.key,
|
||||||
@ -35,6 +36,7 @@ class ChatMessage extends StatelessWidget {
|
|||||||
this.onReply,
|
this.onReply,
|
||||||
this.onEdit,
|
this.onEdit,
|
||||||
this.onDelete,
|
this.onDelete,
|
||||||
|
this.padding = const EdgeInsets.only(left: 12, right: 12),
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -53,7 +55,7 @@ class ChatMessage extends StatelessWidget {
|
|||||||
iconOnRightSwipe: Symbols.edit,
|
iconOnRightSwipe: Symbols.edit,
|
||||||
swipeSensitivity: 20,
|
swipeSensitivity: 20,
|
||||||
onLeftSwipe: onReply != null ? (_) => onReply!(data) : null,
|
onLeftSwipe: onReply != null ? (_) => onReply!(data) : null,
|
||||||
onRightSwipe: onEdit != null ? (_) => onEdit!(data) : null,
|
onRightSwipe: (onEdit != null && isOwner) ? (_) => onEdit!(data) : null,
|
||||||
child: ContextMenuArea(
|
child: ContextMenuArea(
|
||||||
contextMenu: ContextMenu(
|
contextMenu: ContextMenu(
|
||||||
entries: [
|
entries: [
|
||||||
@ -87,84 +89,97 @@ class ChatMessage extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
padding: isCompact ? EdgeInsets.zero : padding,
|
||||||
children: [
|
child: Row(
|
||||||
if (!isMerged && !isCompact)
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
AccountImage(
|
children: [
|
||||||
content: user?.avatar,
|
if (!isMerged && !isCompact)
|
||||||
|
AccountImage(
|
||||||
|
content: user?.avatar,
|
||||||
|
)
|
||||||
|
else if (isMerged)
|
||||||
|
const Gap(40),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: 480),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (!isMerged)
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (isCompact)
|
||||||
|
AccountImage(
|
||||||
|
content: user?.avatar,
|
||||||
|
radius: 12,
|
||||||
|
).padding(right: 8),
|
||||||
|
Text(
|
||||||
|
(data.sender.nick?.isNotEmpty ?? false) ? data.sender.nick! : user?.nick ?? 'unknown',
|
||||||
|
).bold(),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
dateFormatter.format(data.createdAt.toLocal()),
|
||||||
|
).fontSize(13),
|
||||||
|
],
|
||||||
|
).height(21),
|
||||||
|
if (isCompact) const Gap(8),
|
||||||
|
if (data.preload?.quoteEvent != null)
|
||||||
|
StyledWidget(Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: 480,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 4,
|
||||||
|
right: 4,
|
||||||
|
top: 8,
|
||||||
|
bottom: 6,
|
||||||
|
),
|
||||||
|
child: ChatMessage(
|
||||||
|
data: data.preload!.quoteEvent!,
|
||||||
|
isCompact: true,
|
||||||
|
onReply: onReply,
|
||||||
|
onEdit: onEdit,
|
||||||
|
onDelete: onDelete,
|
||||||
|
),
|
||||||
|
)).padding(bottom: 4, top: 4),
|
||||||
|
switch (data.type) {
|
||||||
|
'messages.new' => _ChatMessageText(
|
||||||
|
data: data,
|
||||||
|
onReply: onReply,
|
||||||
|
onEdit: onEdit,
|
||||||
|
onDelete: onDelete,
|
||||||
|
),
|
||||||
|
_ => _ChatMessageSystemNotify(data: data),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else if (isMerged)
|
],
|
||||||
const Gap(40),
|
).opacity(isPending ? 0.5 : 1),
|
||||||
const Gap(8),
|
),
|
||||||
Expanded(
|
if (data.body['text'] != null && data.type == 'messages.new' && (data.body['text']?.isNotEmpty ?? false))
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (!isMerged)
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
||||||
textBaseline: TextBaseline.alphabetic,
|
|
||||||
children: [
|
|
||||||
if (isCompact)
|
|
||||||
AccountImage(
|
|
||||||
content: user?.avatar,
|
|
||||||
radius: 12,
|
|
||||||
).padding(right: 6),
|
|
||||||
Text(
|
|
||||||
(data.sender.nick?.isNotEmpty ?? false) ? data.sender.nick! : user?.nick ?? 'unknown',
|
|
||||||
).bold(),
|
|
||||||
const Gap(6),
|
|
||||||
Text(
|
|
||||||
dateFormatter.format(data.createdAt.toLocal()),
|
|
||||||
).fontSize(13),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (isCompact) const Gap(4),
|
|
||||||
if (data.preload?.quoteEvent != null)
|
|
||||||
StyledWidget(Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
border: Border.all(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 4,
|
|
||||||
right: 4,
|
|
||||||
top: 8,
|
|
||||||
bottom: 6,
|
|
||||||
),
|
|
||||||
child: ChatMessage(
|
|
||||||
data: data.preload!.quoteEvent!,
|
|
||||||
isCompact: true,
|
|
||||||
onReply: onReply,
|
|
||||||
onEdit: onEdit,
|
|
||||||
onDelete: onDelete,
|
|
||||||
),
|
|
||||||
)).padding(bottom: 4, top: 4),
|
|
||||||
switch (data.type) {
|
|
||||||
'messages.new' => _ChatMessageText(data: data),
|
|
||||||
_ => _ChatMessageSystemNotify(data: data),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
).opacity(isPending ? 0.5 : 1),
|
|
||||||
if (data.body['text'] != null && (data.body['text']?.isNotEmpty ?? false))
|
|
||||||
LinkPreviewWidget(text: data.body['text']!),
|
LinkPreviewWidget(text: data.body['text']!),
|
||||||
if (data.preload?.attachments?.isNotEmpty ?? false)
|
if (data.preload?.attachments?.isNotEmpty ?? false)
|
||||||
AttachmentList(
|
AttachmentList(
|
||||||
data: data.preload!.attachments!,
|
data: data.preload!.attachments!,
|
||||||
bordered: true,
|
bordered: true,
|
||||||
gridded: true,
|
maxHeight: 560,
|
||||||
noGrow: true,
|
maxWidth: 480,
|
||||||
maxHeight: 520,
|
minWidth: 480,
|
||||||
listPadding: const EdgeInsets.only(top: 8),
|
padding: padding.copyWith(top: 8),
|
||||||
),
|
),
|
||||||
if (!hasMerged && !isCompact) const Gap(12) else if (!isCompact) const Gap(6),
|
if (!hasMerged && !isCompact) const Gap(12) else if (!isCompact) const Gap(8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -174,19 +189,72 @@ class ChatMessage extends StatelessWidget {
|
|||||||
|
|
||||||
class _ChatMessageText extends StatelessWidget {
|
class _ChatMessageText extends StatelessWidget {
|
||||||
final SnChatMessage data;
|
final SnChatMessage data;
|
||||||
|
final Function(SnChatMessage)? onReply;
|
||||||
|
final Function(SnChatMessage)? onEdit;
|
||||||
|
final Function(SnChatMessage)? onDelete;
|
||||||
|
|
||||||
const _ChatMessageText({required this.data});
|
const _ChatMessageText({required this.data, this.onReply, this.onEdit, this.onDelete});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
|
|
||||||
|
final isOwner = ua.isAuthorized && data.sender.accountId == ua.user?.id;
|
||||||
|
|
||||||
if (data.body['text'] != null && data.body['text'].isNotEmpty) {
|
if (data.body['text'] != null && data.body['text'].isNotEmpty) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
MarkdownTextContent(
|
SelectionArea(
|
||||||
content: data.body['text'],
|
contextMenuBuilder: (context, editableTextState) {
|
||||||
isSelectable: true,
|
final List<ContextMenuButtonItem> items = editableTextState.contextMenuButtonItems;
|
||||||
isAutoWarp: true,
|
|
||||||
|
if (onReply != null) {
|
||||||
|
items.insert(
|
||||||
|
0,
|
||||||
|
ContextMenuButtonItem(
|
||||||
|
label: 'reply'.tr(),
|
||||||
|
onPressed: () {
|
||||||
|
ContextMenuController.removeAny();
|
||||||
|
onReply?.call(data);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isOwner && onEdit != null) {
|
||||||
|
items.insert(
|
||||||
|
1,
|
||||||
|
ContextMenuButtonItem(
|
||||||
|
label: 'edit'.tr(),
|
||||||
|
onPressed: () {
|
||||||
|
ContextMenuController.removeAny();
|
||||||
|
onEdit?.call(data);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isOwner && onDelete != null) {
|
||||||
|
items.insert(
|
||||||
|
2,
|
||||||
|
ContextMenuButtonItem(
|
||||||
|
label: 'delete'.tr(),
|
||||||
|
onPressed: () {
|
||||||
|
ContextMenuController.removeAny();
|
||||||
|
onDelete?.call(data);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
||||||
|
anchors: editableTextState.contextMenuAnchors,
|
||||||
|
buttonItems: items,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: MarkdownTextContent(
|
||||||
|
content: data.body['text'],
|
||||||
|
isAutoWarp: true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (data.updatedAt != data.createdAt)
|
if (data.updatedAt != data.createdAt)
|
||||||
Text(
|
Text(
|
||||||
|
@ -11,7 +11,6 @@ import 'package:surface/providers/user_directory.dart';
|
|||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/types/chat.dart';
|
import 'package:surface/types/chat.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/markdown_content.dart';
|
|
||||||
import 'package:surface/widgets/post/post_media_pending_list.dart';
|
import 'package:surface/widgets/post/post_media_pending_list.dart';
|
||||||
|
|
||||||
class ChatMessageInput extends StatefulWidget {
|
class ChatMessageInput extends StatefulWidget {
|
||||||
@ -33,12 +32,24 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
final TextEditingController _contentController = TextEditingController();
|
final TextEditingController _contentController = TextEditingController();
|
||||||
final FocusNode _focusNode = FocusNode();
|
final FocusNode _focusNode = FocusNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_contentController.addListener(() {
|
||||||
|
if (_contentController.text.isNotEmpty) {
|
||||||
|
widget.controller.pingTypingStatus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void setReply(SnChatMessage? value) {
|
void setReply(SnChatMessage? value) {
|
||||||
setState(() => _replyingMessage = value);
|
setState(() => _replyingMessage = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setEdit(SnChatMessage? value) {
|
void setEdit(SnChatMessage? value) {
|
||||||
_contentController.text = value?.body['text'] ?? '';
|
_contentController.text = value?.body['text'] ?? '';
|
||||||
|
_attachments.clear();
|
||||||
|
_attachments.addAll(value?.preload?.attachments?.map((e) => PostWriteMedia(e)) ?? []);
|
||||||
setState(() => _editingMessage = value);
|
setState(() => _editingMessage = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +103,9 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
_attachments[i] = PostWriteMedia(item);
|
setState(() {
|
||||||
|
_attachments[i] = PostWriteMedia(item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@ -161,75 +174,84 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
child: Padding(
|
child: _replyingMessage != null
|
||||||
padding: _replyingMessage != null ? const EdgeInsets.only(top: 8) : EdgeInsets.zero,
|
? Container(
|
||||||
child: _replyingMessage != null
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
? MaterialBanner(
|
decoration: BoxDecoration(
|
||||||
padding: const EdgeInsets.only(left: 16.0),
|
border: Border(
|
||||||
leading: const Icon(Symbols.reply),
|
bottom: BorderSide(
|
||||||
backgroundColor: Colors.transparent,
|
color: Theme.of(context).dividerColor,
|
||||||
content: SingleChildScrollView(
|
width: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (_replyingMessage?.body['text'] != null)
|
|
||||||
MarkdownTextContent(
|
|
||||||
content: _replyingMessage?.body['text'],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
),
|
||||||
TextButton(
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.reply, size: 20),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_replyingMessage?.body['text'] ?? '${_replyingMessage?.sender.nick}',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
InkWell(
|
||||||
child: Text('cancel'.tr()),
|
child: Text('cancel'.tr()),
|
||||||
onPressed: () {
|
onTap: () {
|
||||||
|
_attachments.clear();
|
||||||
setState(() => _replyingMessage = null);
|
setState(() => _replyingMessage = null);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
).padding(vertical: 8),
|
||||||
: const SizedBox.shrink(),
|
)
|
||||||
),
|
: const SizedBox.shrink(),
|
||||||
)
|
)
|
||||||
.height(_replyingMessage != null ? 54 + 8 : 0, animate: true)
|
.height(_replyingMessage != null ? 38 : 0, animate: true)
|
||||||
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
child: Padding(
|
child: _editingMessage != null
|
||||||
padding: _editingMessage != null ? const EdgeInsets.only(top: 8) : EdgeInsets.zero,
|
? Container(
|
||||||
child: _editingMessage != null
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
? MaterialBanner(
|
decoration: BoxDecoration(
|
||||||
padding: const EdgeInsets.only(left: 16.0),
|
border: Border(
|
||||||
leading: const Icon(Symbols.edit),
|
bottom: BorderSide(
|
||||||
backgroundColor: Colors.transparent,
|
color: Theme.of(context).dividerColor,
|
||||||
content: SingleChildScrollView(
|
width: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (_editingMessage?.body['text'] != null)
|
|
||||||
MarkdownTextContent(
|
|
||||||
content: _editingMessage?.body['text'],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
),
|
||||||
TextButton(
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.edit, size: 20),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_editingMessage?.body['text'] ?? '${_editingMessage?.sender.nick}',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
InkWell(
|
||||||
child: Text('cancel'.tr()),
|
child: Text('cancel'.tr()),
|
||||||
onPressed: () {
|
onTap: () {
|
||||||
|
_attachments.clear();
|
||||||
|
_contentController.clear();
|
||||||
setState(() => _editingMessage = null);
|
setState(() => _editingMessage = null);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
).padding(vertical: 8),
|
||||||
: const SizedBox.shrink(),
|
)
|
||||||
),
|
: const SizedBox.shrink(),
|
||||||
)
|
)
|
||||||
.height(_editingMessage != null ? 54 + 8 : 0, animate: true)
|
.height(_editingMessage != null ? 38 : 0, animate: true)
|
||||||
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 56,
|
height: 56,
|
||||||
|
53
lib/widgets/chat/chat_typing_indicator.dart
Normal file
53
lib/widgets/chat/chat_typing_indicator.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/controllers/chat_message_controller.dart';
|
||||||
|
import 'package:surface/providers/user_directory.dart';
|
||||||
|
|
||||||
|
class ChatTypingIndicator extends StatelessWidget {
|
||||||
|
final ChatMessageController controller;
|
||||||
|
|
||||||
|
const ChatTypingIndicator({super.key, required this.controller});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ud = context.read<UserDirectoryProvider>();
|
||||||
|
|
||||||
|
return StyledWidget(controller.typingMembers.isEmpty
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: Container(
|
||||||
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.more_horiz, weight: 600, size: 20),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'messageTyping'.plural(controller.typingMembers.length, args: [
|
||||||
|
controller.typingMembers
|
||||||
|
.map((ele) => (ele.nick?.isNotEmpty ?? false)
|
||||||
|
? ele.nick!
|
||||||
|
: ud.getAccountFromCache(ele.accountId)?.name ?? 'unknown')
|
||||||
|
.join(', '),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.height(controller.typingMembers.isNotEmpty ? 38 : 0, animate: true)
|
||||||
|
.animate(
|
||||||
|
const Duration(milliseconds: 300),
|
||||||
|
Curves.fastLinearToSlowEaseIn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:flutter_context_menu/flutter_context_menu.dart';
|
import 'package:flutter_context_menu/flutter_context_menu.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
|
|
||||||
class ContextMenuArea extends StatelessWidget {
|
class ContextMenuArea extends StatelessWidget {
|
||||||
final ContextMenu contextMenu;
|
final ContextMenu contextMenu;
|
||||||
@ -22,11 +23,10 @@ class ContextMenuArea extends StatelessWidget {
|
|||||||
return Listener(
|
return Listener(
|
||||||
onPointerDown: (event) {
|
onPointerDown: (event) {
|
||||||
mousePosition = event.position;
|
mousePosition = event.position;
|
||||||
final isCollapseDrawer = ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE);
|
final cfg = context.read<ConfigProvider>();
|
||||||
if (!isCollapseDrawer) {
|
if (!cfg.drawerIsCollapsed) {
|
||||||
final isExpandDrawer = ResponsiveBreakpoints.of(context).largerThan(TABLET);
|
|
||||||
// Leave padding for side navigation
|
// Leave padding for side navigation
|
||||||
mousePosition = isExpandDrawer
|
mousePosition = cfg.drawerIsExpanded
|
||||||
? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2)
|
? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2)
|
||||||
: mousePosition.copyWith(dx: mousePosition.dx - 72 * 2);
|
: mousePosition.copyWith(dx: mousePosition.dx - 72 * 2);
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,38 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:dismissible_page/dismissible_page.dart';
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:markdown/markdown.dart' as markdown;
|
import 'package:markdown/markdown.dart' as markdown;
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/sn_sticker.dart';
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/widgets/attachment/attachment_item.dart';
|
import 'package:surface/widgets/attachment/attachment_item.dart';
|
||||||
import 'package:surface/widgets/universal_image.dart';
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
import 'package:syntax_highlight/syntax_highlight.dart';
|
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
import 'package:path/path.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import 'attachment/attachment_zoom.dart';
|
import 'attachment/attachment_zoom.dart';
|
||||||
|
|
||||||
class MarkdownTextContent extends StatelessWidget {
|
class MarkdownTextContent extends StatelessWidget {
|
||||||
final String content;
|
final String content;
|
||||||
final bool isSelectable;
|
|
||||||
final bool isAutoWarp;
|
final bool isAutoWarp;
|
||||||
|
final bool isEnlargeSticker;
|
||||||
final TextScaler? textScaler;
|
final TextScaler? textScaler;
|
||||||
final List<SnAttachment?>? attachments;
|
final List<SnAttachment?>? attachments;
|
||||||
|
|
||||||
const MarkdownTextContent({
|
const MarkdownTextContent({
|
||||||
super.key,
|
super.key,
|
||||||
required this.content,
|
required this.content,
|
||||||
this.isSelectable = false,
|
|
||||||
this.isAutoWarp = false,
|
this.isAutoWarp = false,
|
||||||
|
this.isEnlargeSticker = false,
|
||||||
this.textScaler,
|
this.textScaler,
|
||||||
this.attachments,
|
this.attachments,
|
||||||
});
|
});
|
||||||
|
|
||||||
Widget _buildContent(BuildContext context) {
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Markdown(
|
return Markdown(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
@ -42,33 +41,33 @@ class MarkdownTextContent extends StatelessWidget {
|
|||||||
styleSheet: MarkdownStyleSheet.fromTheme(
|
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||||
Theme.of(context),
|
Theme.of(context),
|
||||||
).copyWith(
|
).copyWith(
|
||||||
textScaler: textScaler,
|
textScaler: textScaler,
|
||||||
blockquote: TextStyle(
|
blockquote: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
blockquoteDecoration: BoxDecoration(
|
blockquoteDecoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||||
),
|
),
|
||||||
horizontalRuleDecoration: BoxDecoration(
|
horizontalRuleDecoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
top: BorderSide(
|
top: BorderSide(
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
codeblockDecoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
color: Theme.of(context).dividerColor,
|
color: Theme.of(context).dividerColor,
|
||||||
width: 0.3,
|
|
||||||
),
|
),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
),
|
||||||
color: Theme.of(context).colorScheme.surface.withOpacity(0.5),
|
),
|
||||||
)),
|
codeblockDecoration: BoxDecoration(
|
||||||
builders: {
|
border: Border.all(
|
||||||
'code': _MarkdownTextCodeElement(),
|
color: Theme.of(context).dividerColor,
|
||||||
},
|
width: 0.3,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||||
|
color: Theme.of(context).colorScheme.surface.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
code: GoogleFonts.robotoMono(height: 1),
|
||||||
|
),
|
||||||
|
builders: {},
|
||||||
softLineBreak: true,
|
softLineBreak: true,
|
||||||
extensionSet: markdown.ExtensionSet(
|
extensionSet: markdown.ExtensionSet(
|
||||||
<markdown.BlockSyntax>[
|
<markdown.BlockSyntax>[
|
||||||
@ -78,6 +77,7 @@ class MarkdownTextContent extends StatelessWidget {
|
|||||||
<markdown.InlineSyntax>[
|
<markdown.InlineSyntax>[
|
||||||
if (isAutoWarp) markdown.LineBreakSyntax(),
|
if (isAutoWarp) markdown.LineBreakSyntax(),
|
||||||
_UserNameCardInlineSyntax(),
|
_UserNameCardInlineSyntax(),
|
||||||
|
_CustomEmoteInlineSyntax(context),
|
||||||
markdown.AutolinkSyntax(),
|
markdown.AutolinkSyntax(),
|
||||||
markdown.AutolinkExtensionSyntax(),
|
markdown.AutolinkExtensionSyntax(),
|
||||||
markdown.CodeSyntax(),
|
markdown.CodeSyntax(),
|
||||||
@ -108,9 +108,41 @@ class MarkdownTextContent extends StatelessWidget {
|
|||||||
if (url.startsWith('solink://')) {
|
if (url.startsWith('solink://')) {
|
||||||
final segments = url.replaceFirst('solink://', '').split('/');
|
final segments = url.replaceFirst('solink://', '').split('/');
|
||||||
switch (segments[0]) {
|
switch (segments[0]) {
|
||||||
|
case 'stickers':
|
||||||
|
final alias = segments[1];
|
||||||
|
final st = context.read<SnStickerProvider>();
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final double size = isEnlargeSticker ? 128 : 32;
|
||||||
|
return Container(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: FutureBuilder<SnSticker?>(
|
||||||
|
future: st.lookupSticker(alias),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
return UniversalImage(
|
||||||
|
sn.getAttachmentUrl(snapshot.data!.attachment.rid),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
cacheHeight: size,
|
||||||
|
cacheWidth: size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
case 'attachments':
|
case 'attachments':
|
||||||
final attachment = attachments?.firstWhere(
|
final attachment = attachments?.firstWhere(
|
||||||
(ele) => ele?.rid == segments[1],
|
(ele) => ele?.rid == segments[1],
|
||||||
orElse: () => null,
|
orElse: () => null,
|
||||||
);
|
);
|
||||||
if (attachment != null) {
|
if (attachment != null) {
|
||||||
@ -168,14 +200,6 @@ class MarkdownTextContent extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (isSelectable) {
|
|
||||||
return SelectionArea(child: _buildContent(context));
|
|
||||||
}
|
|
||||||
return _buildContent(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UserNameCardInlineSyntax extends markdown.InlineSyntax {
|
class _UserNameCardInlineSyntax extends markdown.InlineSyntax {
|
||||||
@ -194,45 +218,24 @@ class _UserNameCardInlineSyntax extends markdown.InlineSyntax {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MarkdownTextCodeElement extends MarkdownElementBuilder {
|
class _CustomEmoteInlineSyntax extends markdown.InlineSyntax {
|
||||||
@override
|
final BuildContext context;
|
||||||
Widget? visitElementAfter(
|
|
||||||
markdown.Element element,
|
|
||||||
TextStyle? preferredStyle,
|
|
||||||
) {
|
|
||||||
var language = '';
|
|
||||||
|
|
||||||
if (element.attributes['class'] != null) {
|
_CustomEmoteInlineSyntax(this.context) : super(r':([-\w]+):');
|
||||||
String lg = element.attributes['class'] as String;
|
|
||||||
language = lg.substring(9).trim();
|
@override
|
||||||
|
bool onMatch(markdown.InlineParser parser, Match match) {
|
||||||
|
final SnStickerProvider st = context.read<SnStickerProvider>();
|
||||||
|
final alias = match[1]!.toUpperCase();
|
||||||
|
if (st.hasNotSticker(alias)) {
|
||||||
|
parser.advanceBy(1);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return SizedBox(
|
|
||||||
child: FutureBuilder(
|
final element = markdown.Element.empty('img');
|
||||||
future: (() async {
|
element.attributes['src'] = 'solink://stickers/$alias';
|
||||||
final docPath = '../../../';
|
parser.addNode(element);
|
||||||
final highlightingPath = join(docPath, 'assets/highlighting', language);
|
|
||||||
await Highlighter.initialize([highlightingPath]);
|
return true;
|
||||||
return Highlighter(
|
|
||||||
language: highlightingPath,
|
|
||||||
theme: PlatformDispatcher.instance.platformBrightness == Brightness.light
|
|
||||||
? await HighlighterTheme.loadLightTheme()
|
|
||||||
: await HighlighterTheme.loadDarkTheme(),
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
|
||||||
final highlighter = snapshot.data!;
|
|
||||||
return Text.rich(
|
|
||||||
highlighter.highlight(element.textContent.trim()),
|
|
||||||
style: GoogleFonts.robotoMono(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Text(
|
|
||||||
element.textContent.trim(),
|
|
||||||
style: GoogleFonts.robotoMono(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
).padding(all: 8);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/navigation.dart';
|
import 'package:surface/providers/navigation.dart';
|
||||||
import 'package:surface/widgets/version_label.dart';
|
import 'package:surface/widgets/version_label.dart';
|
||||||
|
|
||||||
@ -28,8 +32,9 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final nav = context.watch<NavigationProvider>();
|
final nav = context.watch<NavigationProvider>();
|
||||||
|
final cfg = context.watch<ConfigProvider>();
|
||||||
|
|
||||||
final backgroundColor = ResponsiveBreakpoints.of(context).largerThan(TABLET) ? Colors.transparent : null;
|
final backgroundColor = cfg.drawerIsExpanded ? Colors.transparent : null;
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: nav,
|
listenable: nav,
|
||||||
@ -44,6 +49,18 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
selectedIndex: nav.currentIndex,
|
selectedIndex: nav.currentIndex,
|
||||||
children: [
|
children: [
|
||||||
|
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS) && !cfg.drawerIsExpanded)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: WindowTitleBarBox(),
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -6,8 +6,10 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/navigation.dart';
|
import 'package:surface/providers/navigation.dart';
|
||||||
import 'package:surface/widgets/connection_indicator.dart';
|
import 'package:surface/widgets/connection_indicator.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
@ -57,10 +59,11 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final cfg = context.watch<ConfigProvider>();
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
final isCollapseDrawer = ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE);
|
final isCollapseDrawer = cfg.drawerIsCollapsed;
|
||||||
final isExpandDrawer = ResponsiveBreakpoints.of(context).largerThan(TABLET);
|
final isExpandedDrawer = cfg.drawerIsExpanded;
|
||||||
|
|
||||||
final routeName = GoRouter.of(context).routerDelegate.currentConfiguration.last.route.name;
|
final routeName = GoRouter.of(context).routerDelegate.currentConfiguration.last.route.name;
|
||||||
final isShowBottomNavigation = NavigationProvider.kShowBottomNavScreen.contains(routeName)
|
final isShowBottomNavigation = NavigationProvider.kShowBottomNavScreen.contains(routeName)
|
||||||
@ -81,7 +84,7 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: isExpandDrawer ? AppNavigationDrawer(elevation: 0) : AppRailNavigation(),
|
child: isExpandedDrawer ? AppNavigationDrawer(elevation: 0) : AppRailNavigation(),
|
||||||
),
|
),
|
||||||
Expanded(child: body),
|
Expanded(child: body),
|
||||||
],
|
],
|
||||||
@ -147,7 +150,7 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
Expanded(child: innerWidget),
|
Expanded(child: innerWidget),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: !isExpandDrawer ? AppNavigationDrawer() : null,
|
drawer: !isExpandedDrawer ? AppNavigationDrawer() : null,
|
||||||
drawerEdgeDragWidth: isPopable ? 0 : null,
|
drawerEdgeDragWidth: isPopable ? 0 : null,
|
||||||
bottomNavigationBar: isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
bottomNavigationBar: isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
||||||
),
|
),
|
||||||
|
@ -20,6 +20,7 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
import 'package:surface/providers/config.dart';
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/types/reaction.dart';
|
import 'package:surface/types/reaction.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
@ -112,7 +113,7 @@ class PostItem extends StatelessWidget {
|
|||||||
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await FileSaver.instance.saveFile(name: 'Solar Network Post #${data.id}', file: imageFile);
|
await FileSaver.instance.saveFile(name: 'Solar Network Post #${data.id}.png', file: imageFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
await imageFile.delete();
|
await imageFile.delete();
|
||||||
@ -198,6 +199,10 @@ class PostItem extends StatelessWidget {
|
|||||||
).center();
|
).center();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final displayableAttachments = data.preload?.attachments
|
||||||
|
?.where((ele) => ele?.mediaType != SnMediaType.image || data.type != 'article')
|
||||||
|
.toList();
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -247,14 +252,15 @@ class PostItem extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if ((data.preload?.attachments?.isNotEmpty ?? false) && data.type != 'article')
|
if (displayableAttachments?.isNotEmpty ?? false)
|
||||||
AttachmentList(
|
AttachmentList(
|
||||||
data: data.preload!.attachments!,
|
data: displayableAttachments!,
|
||||||
bordered: true,
|
bordered: true,
|
||||||
gridded: true,
|
gridded: true,
|
||||||
maxHeight: showFullPost ? null : 480,
|
maxHeight: showFullPost ? null : 480,
|
||||||
|
minWidth: 640,
|
||||||
fit: showFullPost ? BoxFit.cover : BoxFit.contain,
|
fit: showFullPost ? BoxFit.cover : BoxFit.contain,
|
||||||
listPadding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
),
|
),
|
||||||
if (data.body['content'] != null)
|
if (data.body['content'] != null)
|
||||||
LinkPreviewWidget(
|
LinkPreviewWidget(
|
||||||
@ -336,10 +342,10 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
isRelativeDate: false,
|
isRelativeDate: false,
|
||||||
).padding(horizontal: 16, bottom: 8),
|
).padding(horizontal: 16, bottom: 8),
|
||||||
if (data.type != 'article' && (data.preload?.attachments?.isNotEmpty ?? false))
|
if (data.type != 'article' && (data.preload?.attachments?.isNotEmpty ?? false))
|
||||||
AttachmentList(
|
StyledWidget(AttachmentList(
|
||||||
data: data.preload!.attachments!,
|
data: data.preload!.attachments!,
|
||||||
gridded: true,
|
gridded: true,
|
||||||
).padding(horizontal: 16, bottom: 8),
|
)).padding(horizontal: 16, bottom: 8),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -873,12 +879,17 @@ class _PostContentBody extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (data.body['content'] == null) return const SizedBox.shrink();
|
if (data.body['content'] == null) return const SizedBox.shrink();
|
||||||
return MarkdownTextContent(
|
final content = MarkdownTextContent(
|
||||||
isSelectable: isSelectable,
|
isEnlargeSticker: true,
|
||||||
textScaler: isEnlarge ? TextScaler.linear(1.1) : null,
|
textScaler: isEnlarge ? TextScaler.linear(1.1) : null,
|
||||||
content: data.body['content'],
|
content: data.body['content'],
|
||||||
attachments: data.preload?.attachments,
|
attachments: data.preload?.attachments,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (isSelectable) {
|
||||||
|
return SelectionArea(child: content);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -930,9 +941,10 @@ class _PostQuoteContent extends StatelessWidget {
|
|||||||
child: AttachmentList(
|
child: AttachmentList(
|
||||||
data: child.preload!.attachments!,
|
data: child.preload!.attachments!,
|
||||||
maxHeight: 360,
|
maxHeight: 360,
|
||||||
|
minWidth: 640,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
gridded: true,
|
gridded: true,
|
||||||
listPadding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
),
|
),
|
||||||
).padding(
|
).padding(
|
||||||
top: 8,
|
top: 8,
|
||||||
|
@ -21,6 +21,7 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/widgets/attachment/attachment_input.dart';
|
import 'package:surface/widgets/attachment/attachment_input.dart';
|
||||||
import 'package:surface/widgets/attachment/attachment_zoom.dart';
|
import 'package:surface/widgets/attachment/attachment_zoom.dart';
|
||||||
|
import 'package:surface/widgets/attachment/pending_attachment_alt.dart';
|
||||||
import 'package:surface/widgets/attachment/pending_attachment_boost.dart';
|
import 'package:surface/widgets/attachment/pending_attachment_boost.dart';
|
||||||
import 'package:surface/widgets/context_menu.dart';
|
import 'package:surface/widgets/context_menu.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
@ -157,6 +158,16 @@ class PostMediaPendingList extends StatelessWidget {
|
|||||||
onUpdate!(idx, result);
|
onUpdate!(idx, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _setAlt(BuildContext context, int idx) async {
|
||||||
|
final result = await showDialog<SnAttachment?>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => PendingAttachmentAltDialog(media: attachments[idx]),
|
||||||
|
);
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
onUpdate!(idx, PostWriteMedia(result));
|
||||||
|
}
|
||||||
|
|
||||||
ContextMenu _createContextMenu(BuildContext context, int idx, PostWriteMedia media) {
|
ContextMenu _createContextMenu(BuildContext context, int idx, PostWriteMedia media) {
|
||||||
final canCompressVideo = !kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.isMacOS);
|
final canCompressVideo = !kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.isMacOS);
|
||||||
return ContextMenu(
|
return ContextMenu(
|
||||||
@ -169,6 +180,14 @@ class PostMediaPendingList extends StatelessWidget {
|
|||||||
_compressVideo(context, idx);
|
_compressVideo(context, idx);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (media.attachment != null)
|
||||||
|
MenuItem(
|
||||||
|
label: 'attachmentSetAlt'.tr(),
|
||||||
|
icon: Symbols.description,
|
||||||
|
onSelected: () {
|
||||||
|
_setAlt(context, idx);
|
||||||
|
},
|
||||||
|
),
|
||||||
if (media.attachment != null)
|
if (media.attachment != null)
|
||||||
MenuItem(
|
MenuItem(
|
||||||
label: 'attachmentBoost'.tr(),
|
label: 'attachmentBoost'.tr(),
|
||||||
|
@ -25,7 +25,7 @@ class PostMiniEditor extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PostMiniEditorState extends State<PostMiniEditor> {
|
class _PostMiniEditorState extends State<PostMiniEditor> {
|
||||||
final PostWriteController _writeController = PostWriteController();
|
final PostWriteController _writeController = PostWriteController(doLoadFromTemporary: false);
|
||||||
|
|
||||||
bool _isFetching = false;
|
bool _isFetching = false;
|
||||||
|
|
||||||
|
@ -55,17 +55,20 @@ class UniversalImage extends StatelessWidget {
|
|||||||
? null
|
? null
|
||||||
: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
|
: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
|
||||||
if (loadingProgress == null) return child;
|
if (loadingProgress == null) return child;
|
||||||
return Center(
|
return Container(
|
||||||
child: TweenAnimationBuilder(
|
constraints: BoxConstraints(maxHeight: 80),
|
||||||
tween: Tween(
|
child: Center(
|
||||||
begin: 0,
|
child: TweenAnimationBuilder(
|
||||||
end: loadingProgress.expectedTotalBytes != null
|
tween: Tween(
|
||||||
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
|
begin: 0,
|
||||||
: 0,
|
end: loadingProgress.expectedTotalBytes != null
|
||||||
),
|
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
|
||||||
duration: const Duration(milliseconds: 300),
|
: 0,
|
||||||
builder: (context, value, _) => CircularProgressIndicator(
|
),
|
||||||
value: loadingProgress.expectedTotalBytes != null ? value.toDouble() : null,
|
duration: const Duration(milliseconds: 300),
|
||||||
|
builder: (context, value, _) => CircularProgressIndicator(
|
||||||
|
value: loadingProgress.expectedTotalBytes != null ? value.toDouble() : null,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
58
pubspec.lock
58
pubspec.lock
@ -618,10 +618,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: fl_chart
|
name: fl_chart
|
||||||
sha256: c724234b05e378383e958f3e82ca84a3e1e3c06a0898462044dd8a24b1ee9864
|
sha256: "10ddaf334fe84d59333a12d153043e366f243e0bdfff2df0313e1e249f5bf926"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.70.0"
|
version: "0.70.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -700,18 +700,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_markdown
|
name: flutter_markdown
|
||||||
sha256: "255b00afa1a7bad19727da6a7780cf3db6c3c12e68d302d85e0ff1fdf173db9e"
|
sha256: e37f4c69a07b07bb92622ef6b131a53c9aae48f64b176340af9e8e5238718487
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4+3"
|
version: "0.7.5"
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_native_splash
|
name: flutter_native_splash
|
||||||
sha256: "1152ab0067ca5a2ebeb862fe0a762057202cceb22b7e62692dcbabf6483891bb"
|
sha256: "7062602e0dbd29141fb8eb19220b5871ca650be5197ab9c1f193a28b17537bc7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.3"
|
version: "2.4.4"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -766,10 +766,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_webrtc
|
name: flutter_webrtc
|
||||||
sha256: "3efe9828f19a07d29a51a726759ad0c70a840d231548a1c7d0332075a94db1df"
|
sha256: e82ffd0d0b79621c5554eed73509d7f5bd286d57fef29a573846785c65237fb1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.5+hotfix.1"
|
version: "0.12.5+hotfix.2"
|
||||||
freezed:
|
freezed:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -822,10 +822,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: "2fd11229f59e23e967b0775df8d5948a519cd7e1e8b6e849729e010587b46539"
|
sha256: "7c2d40b59890a929824f30d442e810116caf5088482629c894b9e4478c67472d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.6.2"
|
version: "14.6.3"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -902,10 +902,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.1"
|
version: "4.1.2"
|
||||||
icons_launcher:
|
icons_launcher:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -950,10 +950,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_ios
|
name: image_picker_ios
|
||||||
sha256: "4f0568120c6fcc0aaa04511cb9f9f4d29fc3d0139884b1d06be88dcec7641d6b"
|
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.12+1"
|
version: "0.8.12+2"
|
||||||
image_picker_linux:
|
image_picker_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1086,10 +1086,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: livekit_client
|
name: livekit_client
|
||||||
sha256: "7cdeb3eaeec7fb70a4cf88d9caabccbef9e3bd5f0b23c086320bc5c9acb2770b"
|
sha256: a19bcf8640b45e0730b1e3e3e78be7882dad680c6ebe8ae75294fd8d4612450d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.4"
|
version: "2.3.4+hotfix.2"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1142,10 +1142,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: material_symbols_icons
|
name: material_symbols_icons
|
||||||
sha256: "64404f47f8e0a9d20478468e5decef867a688660bad7173adcd20418d7f892c9"
|
sha256: "89aac72d25dd49303f71b3b1e70f8374791846729365b25bebc2a2531e5b86cd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2801.0"
|
version: "4.2801.1"
|
||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1646,10 +1646,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: "3c7e73920c694a436afaf65ab60ce3453d91f84208d761fbd83fc21182134d93"
|
sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.4"
|
version: "2.3.5"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1795,10 +1795,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqflite_darwin
|
name: sqflite_darwin
|
||||||
sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474"
|
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.1+1"
|
||||||
sqflite_platform_interface:
|
sqflite_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1863,14 +1863,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0+3"
|
version: "3.3.0+3"
|
||||||
syntax_highlight:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: syntax_highlight
|
|
||||||
sha256: ee33b6aa82cc722bb9b40152a792181dee222353b486c0255fde666a3e3a4997
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.0"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2131,10 +2123,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69"
|
sha256: "154360849a56b7b67331c21f09a386562d88903f90a1099c5987afc1912e1f29"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.9.0"
|
version: "5.10.0"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2177,4 +2169,4 @@ packages:
|
|||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.6.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.24.0"
|
||||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 2.2.1+41
|
version: 2.2.2+52
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
@ -53,9 +53,7 @@ dependencies:
|
|||||||
markdown: ^7.2.2
|
markdown: ^7.2.2
|
||||||
flutter_markdown: ^0.7.4+1
|
flutter_markdown: ^0.7.4+1
|
||||||
url_launcher: ^6.3.1
|
url_launcher: ^6.3.1
|
||||||
cached_network_image: ^3.4.1
|
|
||||||
flutter_animate: ^4.5.0
|
flutter_animate: ^4.5.0
|
||||||
syntax_highlight: ^0.4.0
|
|
||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
relative_time: ^5.0.0
|
relative_time: ^5.0.0
|
||||||
@ -116,6 +114,7 @@ dependencies:
|
|||||||
flutter_webrtc: ^0.12.5+hotfix.1
|
flutter_webrtc: ^0.12.5+hotfix.1
|
||||||
slide_countdown: ^2.0.2
|
slide_countdown: ^2.0.2
|
||||||
video_compress: ^3.1.3
|
video_compress: ^3.1.3
|
||||||
|
cached_network_image: ^3.4.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user