Skip to content

Commit 93e853d

Browse files
committed
分类管理、根据 url 获取标题
1 parent 2fb3027 commit 93e853d

File tree

7 files changed

+134
-32
lines changed

7 files changed

+134
-32
lines changed

SwiftPamphletApp.xcodeproj/project.pbxproj

+21
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
08397E272B9EF37600DFDD02 /* InfosView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08397E262B9EF37600DFDD02 /* InfosView.swift */; };
3333
08397E292B9F0A9100DFDD02 /* EditInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08397E282B9F0A9100DFDD02 /* EditInfoView.swift */; };
3434
08397E2D2B9F10AD00DFDD02 /* EditCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08397E2C2B9F10AD00DFDD02 /* EditCategoryView.swift */; };
35+
08397E2F2B9F353B00DFDD02 /* CategoryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08397E2E2B9F353B00DFDD02 /* CategoryListView.swift */; };
36+
08397E322B9F39C100DFDD02 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 08397E312B9F39C100DFDD02 /* SwiftSoup */; };
3537
084417752B99B9060049297D /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084417742B99B9060049297D /* HomeView.swift */; };
3638
084417772B99BA3F0049297D /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084417762B99BA3F0049297D /* SidebarView.swift */; };
3739
084417792B99BE720049297D /* DataLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084417782B99BE720049297D /* DataLink.swift */; };
@@ -290,6 +292,7 @@
290292
08397E262B9EF37600DFDD02 /* InfosView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfosView.swift; sourceTree = "<group>"; };
291293
08397E282B9F0A9100DFDD02 /* EditInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditInfoView.swift; sourceTree = "<group>"; };
292294
08397E2C2B9F10AD00DFDD02 /* EditCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCategoryView.swift; sourceTree = "<group>"; };
295+
08397E2E2B9F353B00DFDD02 /* CategoryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryListView.swift; sourceTree = "<group>"; };
293296
084417742B99B9060049297D /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
294297
084417762B99BA3F0049297D /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
295298
084417782B99BE720049297D /* DataLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataLink.swift; sourceTree = "<group>"; };
@@ -527,6 +530,7 @@
527530
08448F4E279E8CA400B61353 /* Ink in Frameworks */,
528531
08BE634027BF953A002BC6A8 /* CodeEditorView in Frameworks */,
529532
08BF26D32768A5B40064DDAC /* MarkdownUI in Frameworks */,
533+
08397E322B9F39C100DFDD02 /* SwiftSoup in Frameworks */,
530534
08ED80162B9C54DE0069B7EC /* SMNetwork in Frameworks */,
531535
08D107BD278826BB007B7009 /* HTMLEntities in Frameworks */,
532536
);
@@ -567,6 +571,7 @@
567571
children = (
568572
08397E222B9EE8F400DFDD02 /* InfoDataModel.swift */,
569573
08397E242B9EEE1300DFDD02 /* InfoListView.swift */,
574+
08397E2E2B9F353B00DFDD02 /* CategoryListView.swift */,
570575
08397E262B9EF37600DFDD02 /* InfosView.swift */,
571576
08397E282B9F0A9100DFDD02 /* EditInfoView.swift */,
572577
08397E2C2B9F10AD00DFDD02 /* EditCategoryView.swift */,
@@ -1352,6 +1357,7 @@
13521357
08448F4D279E8CA400B61353 /* Ink */,
13531358
08BE633F27BF953A002BC6A8 /* CodeEditorView */,
13541359
08ED80152B9C54DE0069B7EC /* SMNetwork */,
1360+
08397E312B9F39C100DFDD02 /* SwiftSoup */,
13551361
);
13561362
productName = SwiftPamphletApp;
13571363
productReference = 086A5F032744E88E00FECE02 /* 戴铭的开发小册子.app */;
@@ -1390,6 +1396,7 @@
13901396
08448F4C279E8CA400B61353 /* XCRemoteSwiftPackageReference "ink" */,
13911397
08BE633E27BF953A002BC6A8 /* XCRemoteSwiftPackageReference "CodeEditorView" */,
13921398
08ED80142B9C54DE0069B7EC /* XCLocalSwiftPackageReference "SwiftPamphletApp/SharePackage/SMNetwork" */,
1399+
08397E302B9F39C100DFDD02 /* XCRemoteSwiftPackageReference "SwiftSoup" */,
13931400
);
13941401
productRefGroup = 086A5F042744E88E00FECE02 /* Products */;
13951402
projectDirPath = "";
@@ -1603,6 +1610,7 @@
16031610
08397E2D2B9F10AD00DFDD02 /* EditCategoryView.swift in Sources */,
16041611
08BE635027C4C0F2002BC6A8 /* PlayFormView.swift in Sources */,
16051612
08397E252B9EEE1300DFDD02 /* InfoListView.swift in Sources */,
1613+
08397E2F2B9F353B00DFDD02 /* CategoryListView.swift in Sources */,
16061614
086A5F402744EDCE00FECE02 /* RepoVM.swift in Sources */,
16071615
08BE636627C88750002BC6A8 /* PlayProgressView.swift in Sources */,
16081616
084417772B99BA3F0049297D /* SidebarView.swift in Sources */,
@@ -1894,6 +1902,14 @@
18941902
/* End XCLocalSwiftPackageReference section */
18951903

18961904
/* Begin XCRemoteSwiftPackageReference section */
1905+
08397E302B9F39C100DFDD02 /* XCRemoteSwiftPackageReference "SwiftSoup" */ = {
1906+
isa = XCRemoteSwiftPackageReference;
1907+
repositoryURL = "https://github.com/scinfu/SwiftSoup.git";
1908+
requirement = {
1909+
kind = upToNextMajorVersion;
1910+
minimumVersion = 2.7.1;
1911+
};
1912+
};
18971913
08448F072796A83D00B61353 /* XCRemoteSwiftPackageReference "SwiftDate" */ = {
18981914
isa = XCRemoteSwiftPackageReference;
18991915
repositoryURL = "https://github.com/malcommac/SwiftDate";
@@ -1945,6 +1961,11 @@
19451961
/* End XCRemoteSwiftPackageReference section */
19461962

19471963
/* Begin XCSwiftPackageProductDependency section */
1964+
08397E312B9F39C100DFDD02 /* SwiftSoup */ = {
1965+
isa = XCSwiftPackageProductDependency;
1966+
package = 08397E302B9F39C100DFDD02 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
1967+
productName = SwiftSoup;
1968+
};
19481969
08448F082796A83D00B61353 /* SwiftDate */ = {
19491970
isa = XCSwiftPackageProductDependency;
19501971
package = 08448F072796A83D00B61353 /* XCRemoteSwiftPackageReference "SwiftDate" */;

SwiftPamphletApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"originHash" : "444bf76c1a8e33e1b420e260bf0545c937e0cf4b69b77cc9417a74ab5f5a1b40",
2+
"originHash" : "6a874d716a9c7970eea7ff4adf766f0bd0249a42c9e0f1e4db26ad9d1d9123ea",
33
"pins" : [
44
{
55
"identity" : "attributedtext",
@@ -91,6 +91,15 @@
9191
"version" : "6.3.1"
9292
}
9393
},
94+
{
95+
"identity" : "swiftsoup",
96+
"kind" : "remoteSourceControl",
97+
"location" : "https://github.com/scinfu/SwiftSoup.git",
98+
"state" : {
99+
"revision" : "1d39e56d364cba79ce43b341f9661b534cccb18d",
100+
"version" : "2.7.1"
101+
}
102+
},
94103
{
95104
"identity" : "xctest-dynamic-overlay",
96105
"kind" : "remoteSourceControl",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// CategoryListView.swift
3+
// SwiftPamphletApp
4+
//
5+
// Created by Ming Dai on 2024/3/11.
6+
//
7+
8+
import SwiftUI
9+
import SwiftData
10+
11+
struct CategoryListView: View {
12+
@Environment(\.modelContext) var modelContext
13+
@Query var cates: [IOCategory]
14+
15+
var body: some View {
16+
List {
17+
ForEach(cates) { cate in
18+
NavigationLink(value: cate) {
19+
Text(cate.name)
20+
}
21+
}
22+
.onDelete(perform: deleteCates(at:))
23+
}
24+
}
25+
func deleteCates(at offsets: IndexSet) {
26+
for offset in offsets {
27+
let cate = cates[offset]
28+
modelContext.delete(cate)
29+
}
30+
}
31+
}
32+

SwiftPamphletApp/InfoOrganizer/EditCategoryView.swift

+9-4
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ struct EditCategoryView: View {
1212
@Bindable var cate: IOCategory
1313

1414
var body: some View {
15-
Form {
16-
TextField("分类名", text: $cate.name)
15+
VStack {
16+
Form {
17+
TextField("分类名", text: $cate.name)
18+
}
19+
.navigationTitle("编辑分类")
20+
.padding(30)
21+
CategoryListView()
22+
Spacer()
1723
}
18-
.navigationTitle("编辑分类")
19-
.padding(30)
24+
2025
}
2126
}
2227

SwiftPamphletApp/InfoOrganizer/EditInfoView.swift

+55-24
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import SwiftUI
99
import SwiftData
10+
import SwiftSoup
1011

1112
struct EditInfoView: View {
1213
@Bindable var info: IOInfo
@@ -19,42 +20,72 @@ struct EditInfoView: View {
1920
@Environment(\.modelContext) var modelContext
2021

2122
var body: some View {
22-
Form {
23-
Section {
24-
TextField("名字", text: $info.name)
25-
TextField("地址", text: $info.url)
26-
}
27-
28-
Section("选择分类") {
29-
Picker("分类", selection: $info.category) {
30-
Text("未分类")
31-
.tag(Optional<IOCategory>.none)
32-
if categories.isEmpty == false {
33-
Divider()
34-
ForEach(categories) { cate in
35-
Text(cate.name)
36-
.tag(Optional(cate))
23+
VStack {
24+
Form {
25+
Section {
26+
TextField("标题", text: $info.name)
27+
TextField("地址", text: $info.url)
28+
.onChange(of: info.url) { oldValue, newValue in
29+
Task {
30+
await fetchTitleFromUrl(urlString:newValue)
31+
}
32+
}
33+
}
34+
35+
Section("选择分类") {
36+
Picker("分类", selection: $info.category) {
37+
Text("未分类")
38+
.tag(Optional<IOCategory>.none)
39+
if categories.isEmpty == false {
40+
Divider()
41+
ForEach(categories) { cate in
42+
Text(cate.name)
43+
.tag(Optional(cate))
44+
}
3745
}
3846
}
47+
Button("添加和管理分类", action: addCate)
48+
}
49+
50+
Section("描述") {
51+
TextField("详细描述", text: $info.des, axis: .vertical)
3952
}
40-
Button("添加一个新分类", action: addCate)
4153
}
42-
43-
Section("描述") {
44-
TextField("详细描述", text: $info.des, axis: .vertical)
54+
.navigationTitle("编辑资料")
55+
.navigationDestination(for: IOCategory.self) { cate in
56+
EditCategoryView(cate: cate)
4557
}
58+
.padding(30)
59+
Spacer()
4660
}
47-
.navigationTitle("编辑资料")
48-
.navigationDestination(for: IOCategory.self) { cate in
49-
EditCategoryView(cate: cate)
50-
}
51-
.padding(30)
5261
}
5362
func addCate() {
5463
let cate = IOCategory(name: "", createDate: Date.now, updateDate: Date.now)
5564
modelContext.insert(cate)
5665
navigationPath.append(cate)
5766
}
67+
func fetchTitleFromUrl(urlString: String) async {
68+
guard let url = URL(string: urlString) else {
69+
return
70+
}
71+
guard let (data, _) = try? await URLSession.shared.data(from: url) else {
72+
return
73+
}
74+
guard let homepageHTML = String(data: data, encoding: .utf8), let soup = try? SwiftSoup.parse(homepageHTML) else {
75+
return
76+
}
77+
78+
var title = "无标题"
79+
let soupTitle = try? soup.title()
80+
let h1Title = try? soup.select("h1").first()?.text()
81+
if let okH1Title = h1Title {
82+
title = okH1Title
83+
}
84+
if soupTitle?.isEmpty == false {
85+
title = soupTitle ?? "没找到标题"
86+
}
87+
info.name = title
88+
}
5889
}
5990

6091

SwiftPamphletApp/InfoOrganizer/InfoListView.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ struct InfoListView: View {
1212
@Environment(\.modelContext) var modelContext
1313
@State private var path = NavigationPath()
1414
@State private var searchText = ""
15-
@State private var sortOrder = [SortDescriptor(\IOInfo.updateDate)]
15+
@State private var sortOrder = [SortDescriptor(\IOInfo.updateDate, order: .reverse)]
1616

1717
var body: some View {
1818
NavigationStack(path: $path) {
@@ -23,7 +23,7 @@ struct InfoListView: View {
2323
}
2424
.toolbar {
2525
Menu("Sort", systemImage: "arrow.up.arrow.down") {
26-
Picker("Sort", selection: $sortOrder) {
26+
Picker("排序", selection: $sortOrder) {
2727
Text("正序")
2828
.tag([SortDescriptor(\IOInfo.updateDate)])
2929
Text("倒序")

SwiftPamphletApp/InfoOrganizer/InfosView.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ struct InfosView: View {
2727
List {
2828
ForEach(infos) { info in
2929
NavigationLink(value: info) {
30-
Text(info.name)
30+
HStack {
31+
Text(info.name)
32+
Spacer()
33+
Text(info.category?.name ?? "")
34+
}
3135
}
3236
}
3337
.onDelete(perform: { indexSet in

0 commit comments

Comments
 (0)